added test for Oauth2Controller, DomainController, MobileAppController
This commit is contained in:
		
							parent
							
								
									1429d6a97c
								
							
						
					
					
						commit
						b57744f5d5
					
				@ -167,6 +167,9 @@ import org.thingsboard.server.service.sync.vc.EntitiesVersionControlService;
 | 
			
		||||
import org.thingsboard.server.service.telemetry.AlarmSubscriptionService;
 | 
			
		||||
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Objects;
 | 
			
		||||
import java.util.Optional;
 | 
			
		||||
@ -897,4 +900,15 @@ public abstract class BaseController {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected List<OAuth2ClientId> getOAuth2ClientIds(UUID[] ids) throws ThingsboardException {
 | 
			
		||||
        List<UUID> oauth2ClientIds = ids != null ? Arrays.asList(ids) : Collections.emptyList();
 | 
			
		||||
        List<OAuth2ClientId> oAuth2ClientIds = new ArrayList<>();
 | 
			
		||||
        for (UUID id : oauth2ClientIds) {
 | 
			
		||||
            OAuth2ClientId oauth2ClientId = new OAuth2ClientId(id);
 | 
			
		||||
            checkOauth2ClientId(oauth2ClientId, Operation.READ);
 | 
			
		||||
            oAuth2ClientIds.add(oauth2ClientId);
 | 
			
		||||
        }
 | 
			
		||||
        return oAuth2ClientIds;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ package org.thingsboard.server.controller;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Parameter;
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.Schema;
 | 
			
		||||
import jakarta.validation.Valid;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.security.access.prepost.PreAuthorize;
 | 
			
		||||
@ -25,11 +26,11 @@ import org.springframework.web.bind.annotation.DeleteMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.PathVariable;
 | 
			
		||||
import org.springframework.web.bind.annotation.PostMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.PutMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestBody;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestParam;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
import org.thingsboard.server.common.data.audit.ActionType;
 | 
			
		||||
import org.thingsboard.server.common.data.domain.Domain;
 | 
			
		||||
import org.thingsboard.server.common.data.domain.DomainInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
 | 
			
		||||
@ -41,9 +42,6 @@ import org.thingsboard.server.service.entitiy.domain.TbDomainService;
 | 
			
		||||
import org.thingsboard.server.service.security.permission.Operation;
 | 
			
		||||
import org.thingsboard.server.service.security.permission.Resource;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
@ -70,53 +68,30 @@ public class DomainController extends BaseController {
 | 
			
		||||
    @PostMapping(value = "/domain")
 | 
			
		||||
    public Domain saveDomain(
 | 
			
		||||
            @Parameter(description = "A JSON value representing the Domain.", required = true)
 | 
			
		||||
            @RequestBody Domain domain,
 | 
			
		||||
            @RequestBody @Valid Domain domain,
 | 
			
		||||
            @Parameter(description = "A list of oauth2 client registration ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")))
 | 
			
		||||
            @RequestParam(name = "oauth2ClientRegistrationIds", required = false) String[] ids) throws Exception {
 | 
			
		||||
        List<String> oauth2ClientIds = ids != null ? Arrays.asList(ids) : Collections.emptyList();
 | 
			
		||||
            @RequestParam(name = "oauth2ClientIds", required = false) UUID[] ids) throws Exception {
 | 
			
		||||
        domain.setTenantId(getCurrentUser().getTenantId());
 | 
			
		||||
        checkEntity(domain.getId(), domain, Resource.DOMAIN);
 | 
			
		||||
        List<OAuth2ClientId> oAuth2ClientIds = new ArrayList<>();
 | 
			
		||||
        for (String id : oauth2ClientIds) {
 | 
			
		||||
            OAuth2ClientId oauth2ClientId = new OAuth2ClientId(toUUID(id));
 | 
			
		||||
            checkOauth2ClientId(oauth2ClientId, Operation.READ);
 | 
			
		||||
            oAuth2ClientIds.add(oauth2ClientId);
 | 
			
		||||
        }
 | 
			
		||||
        return tbDomainService.save(domain, oAuth2ClientIds, getCurrentUser());
 | 
			
		||||
        return tbDomainService.save(domain, getOAuth2ClientIds(ids), getCurrentUser());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ApiOperation(value = "Update oauth2 clients (updateOauth2Clients)",
 | 
			
		||||
            notes = "Update oauth2 clients for the specified domain. ")
 | 
			
		||||
    @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
 | 
			
		||||
    @PostMapping(value = "/domain/{id}/oauth2Clients")
 | 
			
		||||
    @PutMapping(value = "/domain/{id}/oauth2Clients")
 | 
			
		||||
    public void updateOauth2Clients(@PathVariable UUID id,
 | 
			
		||||
                                         @RequestBody UUID[] oauth2ClientIds) throws ThingsboardException {
 | 
			
		||||
                                    @RequestBody UUID[] clientIds) throws ThingsboardException {
 | 
			
		||||
        DomainId domainId = new DomainId(id);
 | 
			
		||||
        Domain domain = null;
 | 
			
		||||
        try {
 | 
			
		||||
            domain = checkDomainId(domainId, Operation.WRITE);
 | 
			
		||||
            List<OAuth2ClientId> oAuth2ClientIds = new ArrayList<>();
 | 
			
		||||
            for (UUID outh2CLientId : oauth2ClientIds) {
 | 
			
		||||
                OAuth2ClientId oAuth2ClientId = new OAuth2ClientId(outh2CLientId);
 | 
			
		||||
                checkEntityId(oAuth2ClientId, Operation.READ);
 | 
			
		||||
                oAuth2ClientIds.add(oAuth2ClientId);
 | 
			
		||||
            }
 | 
			
		||||
            domainService.updateOauth2Clients(getTenantId(), domainId, oAuth2ClientIds);
 | 
			
		||||
            logEntityActionService.logEntityAction(domain.getTenantId(), domain.getId(), domain,
 | 
			
		||||
                    ActionType.UPDATED, getCurrentUser(), oAuth2ClientIds.toString());
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            if (domain != null) {
 | 
			
		||||
                    logEntityActionService.logEntityAction(getTenantId(), domainId, domain,
 | 
			
		||||
                            ActionType.UPDATED, getCurrentUser(), e);
 | 
			
		||||
            }
 | 
			
		||||
            throw e;
 | 
			
		||||
        }
 | 
			
		||||
        Domain domain = checkDomainId(domainId, Operation.WRITE);
 | 
			
		||||
        List<OAuth2ClientId> oAuth2ClientIds = getOAuth2ClientIds(clientIds);
 | 
			
		||||
        tbDomainService.updateOauth2Clients(domain, oAuth2ClientIds, getCurrentUser());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ApiOperation(value = "Get Domain infos (getDomainInfos)", notes = SYSTEM_AUTHORITY_PARAGRAPH)
 | 
			
		||||
    @ApiOperation(value = "Get Domain infos (getTenantDomainInfos)", notes = SYSTEM_AUTHORITY_PARAGRAPH)
 | 
			
		||||
    @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
 | 
			
		||||
    @GetMapping(value = "/domain/infos")
 | 
			
		||||
    public List<DomainInfo> getDomainInfos() throws ThingsboardException {
 | 
			
		||||
    public List<DomainInfo> getTenantDomainInfos() throws ThingsboardException {
 | 
			
		||||
        return domainService.findDomainInfosByTenantId(getTenantId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ package org.thingsboard.server.controller;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Parameter;
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.ArraySchema;
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.Schema;
 | 
			
		||||
import jakarta.validation.Valid;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.security.access.prepost.PreAuthorize;
 | 
			
		||||
@ -25,11 +26,11 @@ import org.springframework.web.bind.annotation.DeleteMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.PathVariable;
 | 
			
		||||
import org.springframework.web.bind.annotation.PostMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.PutMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestBody;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestParam;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
import org.thingsboard.server.common.data.audit.ActionType;
 | 
			
		||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
 | 
			
		||||
import org.thingsboard.server.common.data.id.MobileAppId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.OAuth2ClientId;
 | 
			
		||||
@ -40,8 +41,8 @@ import org.thingsboard.server.config.annotations.ApiOperation;
 | 
			
		||||
import org.thingsboard.server.queue.util.TbCoreComponent;
 | 
			
		||||
import org.thingsboard.server.service.entitiy.mobile.TbMobileAppService;
 | 
			
		||||
import org.thingsboard.server.service.security.permission.Operation;
 | 
			
		||||
import org.thingsboard.server.service.security.permission.Resource;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
@ -61,57 +62,37 @@ public class MobileAppController extends BaseController {
 | 
			
		||||
    @ApiOperation(value = "Save Or update Mobile app (saveMobileApp)",
 | 
			
		||||
            notes = "Create or update the Mobile app. When creating mobile app, platform generates Mobile App Id as " + UUID_WIKI_LINK +
 | 
			
		||||
                    "The newly created Mobile App Id will be present in the response. " +
 | 
			
		||||
                    "Specify existing Mobile App Id to update the domain. " +
 | 
			
		||||
                    "Specify existing Mobile App Id to update the mobile app. " +
 | 
			
		||||
                    "Referencing non-existing Mobile App Id will cause 'Not Found' error." +
 | 
			
		||||
                    "\n\nMobile app package name is unique for entire platform setup.\n\n")
 | 
			
		||||
    @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
 | 
			
		||||
    @PostMapping(value = "/mobileApp")
 | 
			
		||||
    public MobileApp saveMobileApp(
 | 
			
		||||
            @Parameter(description = "A JSON value representing the Domain.", required = true)
 | 
			
		||||
            @RequestBody MobileApp mobileApp,
 | 
			
		||||
            @Parameter(description = "A JSON value representing the Mobile Application.", required = true)
 | 
			
		||||
            @RequestBody @Valid MobileApp mobileApp,
 | 
			
		||||
            @Parameter(description = "A list of entity group ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string")))
 | 
			
		||||
            @RequestParam(name = "oauth2ClientIds", required = false) UUID[] oauth2ClientIds) throws Exception {
 | 
			
		||||
            @RequestParam(name = "oauth2ClientIds", required = false) UUID[] ids) throws Exception {
 | 
			
		||||
        mobileApp.setTenantId(getCurrentUser().getTenantId());
 | 
			
		||||
 | 
			
		||||
        List<OAuth2ClientId> oAuth2Clients = new ArrayList<>();
 | 
			
		||||
        for (UUID id : oauth2ClientIds) {
 | 
			
		||||
            OAuth2ClientId oauth2ClientId = new OAuth2ClientId(id);
 | 
			
		||||
            checkOauth2ClientId(oauth2ClientId, Operation.READ);
 | 
			
		||||
            oAuth2Clients.add(oauth2ClientId);
 | 
			
		||||
        }
 | 
			
		||||
        return tbMobileAppService.save(mobileApp, oAuth2Clients, getCurrentUser());
 | 
			
		||||
        checkEntity(mobileApp.getId(), mobileApp, Resource.MOBILE_APP);
 | 
			
		||||
        return tbMobileAppService.save(mobileApp, getOAuth2ClientIds(ids), getCurrentUser());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ApiOperation(value = "Update oauth2 clients (updateOauth2Clients)",
 | 
			
		||||
            notes = "Update oauth2 clients to the specified mobile app. ")
 | 
			
		||||
    @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
 | 
			
		||||
    @PostMapping(value = "/mobileApp/{id}/oauth2Clients")
 | 
			
		||||
    @PutMapping(value = "/mobileApp/{id}/oauth2Clients")
 | 
			
		||||
    public void updateOauth2Clients(@PathVariable UUID id,
 | 
			
		||||
                                         @RequestBody UUID[] oauth2ClientIds) throws ThingsboardException {
 | 
			
		||||
                                         @RequestBody UUID[] clientIds) throws ThingsboardException {
 | 
			
		||||
        MobileAppId mobileAppId = new MobileAppId(id);
 | 
			
		||||
        MobileApp mobileApp = null;
 | 
			
		||||
        try {
 | 
			
		||||
            mobileApp = checkMobileAppId(mobileAppId, Operation.WRITE);
 | 
			
		||||
            List<OAuth2ClientId> oAuth2ClientIds = new ArrayList<>();
 | 
			
		||||
            for (UUID outh2CLientId : oauth2ClientIds) {
 | 
			
		||||
                OAuth2ClientId oAuth2ClientId = new OAuth2ClientId(outh2CLientId);
 | 
			
		||||
                checkEntityId(oAuth2ClientId, Operation.READ);
 | 
			
		||||
                oAuth2ClientIds.add(oAuth2ClientId);
 | 
			
		||||
            }
 | 
			
		||||
            mobileAppService.updateOauth2Clients(getTenantId(), mobileAppId, oAuth2ClientIds);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            if (mobileApp != null) {
 | 
			
		||||
                logEntityActionService.logEntityAction(getTenantId(), mobileAppId, mobileApp,
 | 
			
		||||
                        ActionType.UPDATED, getCurrentUser(), e);
 | 
			
		||||
            }
 | 
			
		||||
            throw e;
 | 
			
		||||
        }
 | 
			
		||||
        MobileApp mobileApp = checkMobileAppId(mobileAppId, Operation.WRITE);
 | 
			
		||||
        List<OAuth2ClientId> oAuth2ClientIds = getOAuth2ClientIds(clientIds);
 | 
			
		||||
        tbMobileAppService.updateOauth2Clients(mobileApp, oAuth2ClientIds, getCurrentUser());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @ApiOperation(value = "Get mobile app infos (getMobileAppInfos)", notes = SYSTEM_AUTHORITY_PARAGRAPH)
 | 
			
		||||
    @ApiOperation(value = "Get mobile app infos (getTenantMobileAppInfos)", notes = SYSTEM_AUTHORITY_PARAGRAPH)
 | 
			
		||||
    @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
 | 
			
		||||
    @GetMapping(value = "/mobileApp/infos")
 | 
			
		||||
    public List<MobileAppInfo> getMobileAppInfos() throws ThingsboardException {
 | 
			
		||||
    public List<MobileAppInfo> getTenantMobileAppInfos() throws ThingsboardException {
 | 
			
		||||
        TenantId tenantId = getCurrentUser().getTenantId();
 | 
			
		||||
        return mobileAppService.findMobileAppInfosByTenantId(tenantId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -18,26 +18,25 @@ package org.thingsboard.server.controller;
 | 
			
		||||
import io.swagger.v3.oas.annotations.Parameter;
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.Schema;
 | 
			
		||||
import jakarta.servlet.http.HttpServletRequest;
 | 
			
		||||
import jakarta.validation.Valid;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.springframework.http.HttpStatus;
 | 
			
		||||
import org.springframework.security.access.prepost.PreAuthorize;
 | 
			
		||||
import org.springframework.web.bind.annotation.DeleteMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.GetMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.PathVariable;
 | 
			
		||||
import org.springframework.web.bind.annotation.PostMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestBody;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMapping;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestMethod;
 | 
			
		||||
import org.springframework.web.bind.annotation.RequestParam;
 | 
			
		||||
import org.springframework.web.bind.annotation.ResponseStatus;
 | 
			
		||||
import org.springframework.web.bind.annotation.RestController;
 | 
			
		||||
import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
 | 
			
		||||
import org.thingsboard.server.common.data.id.OAuth2ClientId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.PlatformType;
 | 
			
		||||
import org.thingsboard.server.config.annotations.ApiOperation;
 | 
			
		||||
import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
 | 
			
		||||
@ -69,16 +68,16 @@ public class OAuth2Controller extends BaseController {
 | 
			
		||||
    @ApiOperation(value = "Get OAuth2 clients (getOAuth2Clients)", notes = "Get the list of OAuth2 clients " +
 | 
			
		||||
            "to log in with, available for such domain scheme (HTTP or HTTPS) (if x-forwarded-proto request header is present - " +
 | 
			
		||||
            "the scheme is known from it) and domain name and port (port may be known from x-forwarded-port header)")
 | 
			
		||||
    @PostMapping(value = "/noauth/oauth2Clients")
 | 
			
		||||
    @PostMapping(value = "/noauth/oauth2/client")
 | 
			
		||||
    public List<OAuth2ClientLoginInfo> getOAuth2Clients(HttpServletRequest request,
 | 
			
		||||
                                                        @Parameter(description = "Mobile application package name, to find OAuth2 clients " +
 | 
			
		||||
                                                           "where there is configured mobile application with such package name")
 | 
			
		||||
                                                   @RequestParam(required = false) String pkgName,
 | 
			
		||||
                                                                "where there is configured mobile application with such package name")
 | 
			
		||||
                                                        @RequestParam(required = false) String pkgName,
 | 
			
		||||
                                                        @Parameter(description = "Platform type to search OAuth2 clients for which " +
 | 
			
		||||
                                                           "the usage with this platform type is allowed in the settings. " +
 | 
			
		||||
                                                           "If platform type is not one of allowable values - it will just be ignored",
 | 
			
		||||
                                                           schema = @Schema(allowableValues = {"WEB", "ANDROID", "IOS"}))
 | 
			
		||||
                                                   @RequestParam(required = false) String platform) throws ThingsboardException {
 | 
			
		||||
                                                                "the usage with this platform type is allowed in the settings. " +
 | 
			
		||||
                                                                "If platform type is not one of allowable values - it will just be ignored",
 | 
			
		||||
                                                                schema = @Schema(allowableValues = {"WEB", "ANDROID", "IOS"}))
 | 
			
		||||
                                                        @RequestParam(required = false) String platform) throws ThingsboardException {
 | 
			
		||||
        if (log.isDebugEnabled()) {
 | 
			
		||||
            log.debug("Executing getOAuth2Clients: [{}][{}][{}]", request.getScheme(), request.getServerName(), request.getServerPort());
 | 
			
		||||
            Enumeration<String> headerNames = request.getHeaderNames();
 | 
			
		||||
@ -104,7 +103,7 @@ public class OAuth2Controller extends BaseController {
 | 
			
		||||
    @ApiOperation(value = "Save OAuth2 Client Registration (saveOAuth2Client)", notes = SYSTEM_AUTHORITY_PARAGRAPH)
 | 
			
		||||
    @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
 | 
			
		||||
    @PostMapping(value = "/oauth2/client")
 | 
			
		||||
    public OAuth2Client saveOAuth2Client(@RequestBody OAuth2Client oAuth2Client) throws Exception {
 | 
			
		||||
    public OAuth2Client saveOAuth2Client(@RequestBody @Valid OAuth2Client oAuth2Client) throws Exception {
 | 
			
		||||
        TenantId tenantId = getTenantId();
 | 
			
		||||
        oAuth2Client.setTenantId(tenantId);
 | 
			
		||||
        checkEntity(oAuth2Client.getId(), oAuth2Client, Resource.OAUTH2_CLIENT);
 | 
			
		||||
@ -129,8 +128,7 @@ public class OAuth2Controller extends BaseController {
 | 
			
		||||
    @ApiOperation(value = "Delete oauth2 client (deleteAsset)",
 | 
			
		||||
            notes = "Deletes the asset and all the relations (from and to the asset). Referencing non-existing asset Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
 | 
			
		||||
    @PreAuthorize("hasAuthority('SYS_ADMIN')")
 | 
			
		||||
    @RequestMapping(value = "/oauth2/client/{id}", method = RequestMethod.DELETE)
 | 
			
		||||
    @ResponseStatus(value = HttpStatus.OK)
 | 
			
		||||
    @DeleteMapping(value = "/oauth2/client/{id}")
 | 
			
		||||
    public void deleteOauth2Client(@PathVariable UUID id) throws Exception {
 | 
			
		||||
        OAuth2ClientId oAuth2ClientId = new OAuth2ClientId(id);
 | 
			
		||||
        OAuth2Client oAuth2Client = checkOauth2ClientId(oAuth2ClientId, Operation.DELETE);
 | 
			
		||||
@ -142,7 +140,7 @@ public class OAuth2Controller extends BaseController {
 | 
			
		||||
            "further log in processing. This URL may be configured as 'security.oauth2.loginProcessingUrl' property in yml configuration file, or " +
 | 
			
		||||
            "as 'SECURITY_OAUTH2_LOGIN_PROCESSING_URL' env variable. By default it is '/login/oauth2/code/'" + SYSTEM_AUTHORITY_PARAGRAPH)
 | 
			
		||||
    @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
 | 
			
		||||
    @RequestMapping(value = "/oauth2/loginProcessingUrl", method = RequestMethod.GET)
 | 
			
		||||
    @GetMapping(value = "/oauth2/loginProcessingUrl")
 | 
			
		||||
    public String getLoginProcessingUrl() {
 | 
			
		||||
        return "\"" + oAuth2Configuration.getLoginProcessingUrl() + "\"";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -30,7 +30,6 @@ import org.thingsboard.server.common.data.id.DashboardId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EdgeId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.dao.dashboard.DashboardService;
 | 
			
		||||
import org.thingsboard.server.dao.resource.ImageService;
 | 
			
		||||
import org.thingsboard.server.queue.util.TbCoreComponent;
 | 
			
		||||
import org.thingsboard.server.service.entitiy.AbstractTbEntityService;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -43,13 +43,27 @@ public class DefaultTbDomainService extends AbstractTbEntityService implements T
 | 
			
		||||
        TenantId tenantId = domain.getTenantId();
 | 
			
		||||
        try {
 | 
			
		||||
            Domain savedDomain = checkNotNull(domainService.saveDomain(tenantId, domain));
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, savedDomain.getId(), domain, actionType, user);
 | 
			
		||||
            if (!CollectionUtils.isEmpty(oAuth2Clients)) {
 | 
			
		||||
                domainService.updateOauth2Clients(domain.getTenantId(), savedDomain.getId(), oAuth2Clients);
 | 
			
		||||
            }
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, savedDomain.getId(), domain, actionType, user, oAuth2Clients);
 | 
			
		||||
            return savedDomain;
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.DOMAIN), domain, actionType, user, e);
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.DOMAIN), domain, actionType, user, e, oAuth2Clients);
 | 
			
		||||
            throw e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateOauth2Clients(Domain domain, List<OAuth2ClientId> oAuth2ClientIds, User user) {
 | 
			
		||||
        ActionType actionType = ActionType.UPDATED;
 | 
			
		||||
        TenantId tenantId = domain.getTenantId();
 | 
			
		||||
        DomainId domainId = domain.getId();
 | 
			
		||||
        try {
 | 
			
		||||
            domainService.updateOauth2Clients(tenantId, domainId, oAuth2ClientIds);
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, domainId, domain, actionType, user, oAuth2ClientIds.toString());
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, domainId, domain, actionType, user, e, oAuth2ClientIds.toString());
 | 
			
		||||
            throw e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@ -62,10 +76,9 @@ public class DefaultTbDomainService extends AbstractTbEntityService implements T
 | 
			
		||||
        DomainId domainId = domain.getId();
 | 
			
		||||
        try {
 | 
			
		||||
            domainService.deleteDomainById(tenantId, domainId);
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, domainId, domain, actionType, user, domain.getName());
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, domainId, domain, actionType, user);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.DOMAIN), actionType, user, e,
 | 
			
		||||
                    domainId.toString());
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, domainId, domain, actionType, user, e);
 | 
			
		||||
            throw e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,8 @@ public interface TbDomainService {
 | 
			
		||||
 | 
			
		||||
    Domain save(Domain domain, List<OAuth2ClientId> oAuth2Clients, User user) throws Exception;
 | 
			
		||||
 | 
			
		||||
    void updateOauth2Clients(Domain domain, List<OAuth2ClientId> oAuth2ClientIds, User user);
 | 
			
		||||
 | 
			
		||||
    void delete(Domain domain, User user);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -43,10 +43,10 @@ public class DefaultTbMobileAppService extends AbstractTbEntityService implement
 | 
			
		||||
        TenantId tenantId = mobileApp.getTenantId();
 | 
			
		||||
        try {
 | 
			
		||||
            MobileApp savedMobileApp = checkNotNull(mobileAppService.saveMobileApp(tenantId, mobileApp));
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, savedMobileApp.getId(), mobileApp, actionType, user);
 | 
			
		||||
            if (!CollectionUtils.isEmpty(oauth2Clients)) {
 | 
			
		||||
                mobileAppService.updateOauth2Clients(tenantId, savedMobileApp.getId(), oauth2Clients);
 | 
			
		||||
            }
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, savedMobileApp.getId(), mobileApp, actionType, user);
 | 
			
		||||
            return savedMobileApp;
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.MOBILE_APP), mobileApp, actionType, user, e);
 | 
			
		||||
@ -54,6 +54,20 @@ public class DefaultTbMobileAppService extends AbstractTbEntityService implement
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void updateOauth2Clients(MobileApp mobileApp, List<OAuth2ClientId> oAuth2ClientIds, User user) {
 | 
			
		||||
        ActionType actionType = ActionType.UPDATED;
 | 
			
		||||
        TenantId tenantId = mobileApp.getTenantId();
 | 
			
		||||
        MobileAppId mobileAppId = mobileApp.getId();
 | 
			
		||||
        try {
 | 
			
		||||
            mobileAppService.updateOauth2Clients(tenantId, mobileAppId, oAuth2ClientIds);
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, mobileAppId, mobileApp, actionType, user, oAuth2ClientIds.toString());
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, mobileAppId, mobileApp, actionType, user, e, oAuth2ClientIds.toString());
 | 
			
		||||
            throw e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @Transactional
 | 
			
		||||
    public void delete(MobileApp mobileApp, User user) {
 | 
			
		||||
@ -62,10 +76,9 @@ public class DefaultTbMobileAppService extends AbstractTbEntityService implement
 | 
			
		||||
        MobileAppId mobileAppId = mobileApp.getId();
 | 
			
		||||
        try {
 | 
			
		||||
            mobileAppService.deleteMobileAppById(tenantId, mobileAppId);
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, mobileAppId, mobileApp, actionType, user, mobileApp.getPkgName());
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, mobileAppId, mobileApp, actionType, user);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.MOBILE_APP), actionType, user, e,
 | 
			
		||||
                    mobileAppId.toString());
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, mobileAppId, mobileApp, actionType, user, e);
 | 
			
		||||
            throw e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -25,6 +25,8 @@ public interface TbMobileAppService {
 | 
			
		||||
 | 
			
		||||
    MobileApp save(MobileApp mobileApp, List<OAuth2ClientId> oauth2Clients, User user) throws Exception;
 | 
			
		||||
 | 
			
		||||
    void updateOauth2Clients(MobileApp mobileApp, List<OAuth2ClientId> oAuth2ClientIds, User user);
 | 
			
		||||
 | 
			
		||||
    void delete(MobileApp mobileApp, User user);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -53,10 +53,9 @@ public class DefaultTbOauth2ClientService extends AbstractTbEntityService implem
 | 
			
		||||
        OAuth2ClientId oAuth2ClientId = oAuth2Client.getId();
 | 
			
		||||
        try {
 | 
			
		||||
            oAuth2ClientService.deleteOAuth2ClientById(tenantId, oAuth2ClientId);
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, oAuth2ClientId, oAuth2Client, actionType, user, oAuth2Client.getName());
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, oAuth2ClientId, oAuth2Client, actionType, user);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.OAUTH2_CLIENT), actionType, user, e,
 | 
			
		||||
                    oAuth2ClientId.toString());
 | 
			
		||||
            logEntityActionService.logEntityAction(tenantId, oAuth2ClientId, oAuth2Client, actionType, user, e);
 | 
			
		||||
            throw e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,178 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2024 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.controller;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.core.type.TypeReference;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.junit.After;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.server.common.data.domain.Domain;
 | 
			
		||||
import org.thingsboard.server.common.data.domain.DomainInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.MapperType;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.PlatformType;
 | 
			
		||||
import org.thingsboard.server.dao.service.DaoSqlTest;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.hamcrest.Matchers.containsString;
 | 
			
		||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
@DaoSqlTest
 | 
			
		||||
public class DomainControllerTest extends AbstractControllerTest {
 | 
			
		||||
 | 
			
		||||
    @Before
 | 
			
		||||
    public void setUp() throws Exception {
 | 
			
		||||
        loginSysAdmin();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @After
 | 
			
		||||
    public void tearDown() throws Exception {
 | 
			
		||||
        List<DomainInfo> domains = doGetTyped("/api/domain/infos", new TypeReference<List<DomainInfo>>() {
 | 
			
		||||
        });
 | 
			
		||||
        for (Domain domain : domains) {
 | 
			
		||||
            doDelete("/api/domain/" + domain.getId().getId())
 | 
			
		||||
                    .andExpect(status().isOk());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        List<OAuth2ClientInfo> oAuth2ClientInfos = doGetTyped("/api/oauth2/client/infos", new TypeReference<List<OAuth2ClientInfo>>() {
 | 
			
		||||
        });
 | 
			
		||||
        for (OAuth2ClientInfo oAuth2ClientInfo : oAuth2ClientInfos) {
 | 
			
		||||
            doDelete("/api/oauth2/client/" + oAuth2ClientInfo.getId().getId().toString())
 | 
			
		||||
                    .andExpect(status().isOk());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveDomain() throws Exception {
 | 
			
		||||
        List<DomainInfo> domainInfos = doGetTyped("/api/domain/infos", new TypeReference<List<DomainInfo>>() {
 | 
			
		||||
        });
 | 
			
		||||
        assertThat(domainInfos).isEmpty();
 | 
			
		||||
 | 
			
		||||
        Domain domain = constructDomain(TenantId.SYS_TENANT_ID, "my.test.domain", true, true);
 | 
			
		||||
        Domain savedDomain = doPost("/api/domain", domain, Domain.class);
 | 
			
		||||
 | 
			
		||||
        List<DomainInfo> domainInfos2 = doGetTyped("/api/domain/infos", new TypeReference<List<DomainInfo>>() {
 | 
			
		||||
        });
 | 
			
		||||
        assertThat(domainInfos2).hasSize(1);
 | 
			
		||||
        assertThat(domainInfos2.get(0)).isEqualTo(new DomainInfo(savedDomain, Collections.emptyList()));
 | 
			
		||||
 | 
			
		||||
        DomainInfo retrievedDomainInfo = doGet("/api/domain/info/{id}", DomainInfo.class, savedDomain.getId().getId());
 | 
			
		||||
        assertThat(retrievedDomainInfo).isEqualTo(new DomainInfo(savedDomain, Collections.emptyList()));
 | 
			
		||||
 | 
			
		||||
        doDelete("/api/domain/" + savedDomain.getId().getId());
 | 
			
		||||
        doGet("/api/domain/info/{id}", savedDomain.getId().getId())
 | 
			
		||||
                .andExpect(status().isNotFound());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveDomainWithoutName() throws Exception {
 | 
			
		||||
        Domain domain = constructDomain(TenantId.SYS_TENANT_ID, null, true, true);
 | 
			
		||||
        doPost("/api/domain", domain)
 | 
			
		||||
                .andExpect(status().isBadRequest())
 | 
			
		||||
                .andExpect(statusReason(containsString("name must not be blank")));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testUpdateDomainOauth2Clients() throws Exception {
 | 
			
		||||
        Domain domain = constructDomain(TenantId.SYS_TENANT_ID, "my.test.domain", true, true);
 | 
			
		||||
        Domain savedDomain = doPost("/api/domain", domain, Domain.class);
 | 
			
		||||
 | 
			
		||||
        OAuth2Client oAuth2Client = validClientInfo(TenantId.SYS_TENANT_ID, "test google client");
 | 
			
		||||
        OAuth2Client savedOAuth2Client = doPost("/api/oauth2/client", oAuth2Client, OAuth2Client.class);
 | 
			
		||||
 | 
			
		||||
        OAuth2Client oAuth2Client2 = validClientInfo(TenantId.SYS_TENANT_ID, "test facebook client");
 | 
			
		||||
        OAuth2Client savedOAuth2Client2 = doPost("/api/oauth2/client", oAuth2Client2, OAuth2Client.class);
 | 
			
		||||
 | 
			
		||||
        doPut("/api/domain/" + savedDomain.getId() + "/oauth2Clients", List.of(savedOAuth2Client.getId().getId(), savedOAuth2Client2.getId().getId()));
 | 
			
		||||
 | 
			
		||||
        DomainInfo retrievedDomainInfo = doGet("/api/domain/info/{id}", DomainInfo.class, savedDomain.getId().getId());
 | 
			
		||||
        assertThat(retrievedDomainInfo).isEqualTo(new DomainInfo(savedDomain, List.of(new OAuth2ClientInfo(savedOAuth2Client),
 | 
			
		||||
                new OAuth2ClientInfo(savedOAuth2Client2))));
 | 
			
		||||
 | 
			
		||||
        doPut("/api/domain/" + savedDomain.getId() + "/oauth2Clients", List.of(savedOAuth2Client2.getId().getId()));
 | 
			
		||||
        DomainInfo retrievedDomainInfo2 = doGet("/api/domain/info/{id}", DomainInfo.class, savedDomain.getId().getId());
 | 
			
		||||
        assertThat(retrievedDomainInfo2).isEqualTo(new DomainInfo(savedDomain, List.of(new OAuth2ClientInfo(savedOAuth2Client2))));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testCreateDomainWithOauth2Clients() throws Exception {
 | 
			
		||||
        OAuth2Client oAuth2Client = validClientInfo(TenantId.SYS_TENANT_ID, "test google client");
 | 
			
		||||
        OAuth2Client savedOAuth2Client = doPost("/api/oauth2/client", oAuth2Client, OAuth2Client.class);
 | 
			
		||||
 | 
			
		||||
        Domain domain = constructDomain(TenantId.SYS_TENANT_ID, "my.test.domain", true, true);
 | 
			
		||||
        Domain savedDomain = doPost("/api/domain?oauth2ClientIds=" + savedOAuth2Client.getId().getId(), domain, Domain.class);
 | 
			
		||||
 | 
			
		||||
        DomainInfo retrievedDomainInfo = doGet("/api/domain/info/{id}", DomainInfo.class, savedDomain.getId().getId());
 | 
			
		||||
        assertThat(retrievedDomainInfo).isEqualTo(new DomainInfo(savedDomain, List.of(new OAuth2ClientInfo(savedOAuth2Client))));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Domain constructDomain(TenantId tenantId, String domainName, boolean oauth2Enabled, boolean propagateToEdge) {
 | 
			
		||||
        Domain domain = new Domain();
 | 
			
		||||
        domain.setTenantId(tenantId);
 | 
			
		||||
        domain.setName(domainName);
 | 
			
		||||
        domain.setOauth2Enabled(oauth2Enabled);
 | 
			
		||||
        domain.setPropagateToEdge(propagateToEdge);
 | 
			
		||||
        return domain;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected OAuth2Client validClientInfo(TenantId tenantId, String title) {
 | 
			
		||||
        return validClientInfo(tenantId, title, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected OAuth2Client validClientInfo(TenantId tenantId, String title, List<PlatformType> platforms) {
 | 
			
		||||
        OAuth2Client oAuth2Client = new OAuth2Client();
 | 
			
		||||
        oAuth2Client.setTenantId(tenantId);
 | 
			
		||||
        oAuth2Client.setTitle(title);
 | 
			
		||||
        oAuth2Client.setClientId(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setClientSecret(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setAuthorizationUri(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setAccessTokenUri(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setScope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
 | 
			
		||||
        oAuth2Client.setPlatforms(platforms == null ? Collections.emptyList() : platforms);
 | 
			
		||||
        oAuth2Client.setUserInfoUri(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setUserNameAttributeName(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setJwkSetUri(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setClientAuthenticationMethod(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setLoginButtonLabel(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setLoginButtonIcon(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setAdditionalInfo(JacksonUtil.newObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
 | 
			
		||||
        oAuth2Client.setMapperConfig(
 | 
			
		||||
                OAuth2MapperConfig.builder()
 | 
			
		||||
                        .allowUserCreation(true)
 | 
			
		||||
                        .activateUser(true)
 | 
			
		||||
                        .type(MapperType.CUSTOM)
 | 
			
		||||
                        .custom(
 | 
			
		||||
                                OAuth2CustomMapperConfig.builder()
 | 
			
		||||
                                        .url(UUID.randomUUID().toString())
 | 
			
		||||
                                        .build()
 | 
			
		||||
                        )
 | 
			
		||||
                        .build());
 | 
			
		||||
        return oAuth2Client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,180 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2024 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.controller;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.core.type.TypeReference;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.junit.After;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.mobile.MobileApp;
 | 
			
		||||
import org.thingsboard.server.common.data.mobile.MobileAppInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.MapperType;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.PlatformType;
 | 
			
		||||
import org.thingsboard.server.dao.service.DaoSqlTest;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.hamcrest.Matchers.containsString;
 | 
			
		||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
@DaoSqlTest
 | 
			
		||||
public class MobileAppControllerTest extends AbstractControllerTest {
 | 
			
		||||
 | 
			
		||||
    @Before
 | 
			
		||||
    public void setUp() throws Exception {
 | 
			
		||||
        loginSysAdmin();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @After
 | 
			
		||||
    public void tearDown() throws Exception {
 | 
			
		||||
        List<MobileAppInfo> mobileAppInfos = doGetTyped("/api/mobileApp/infos", new TypeReference<List<MobileAppInfo>>() {
 | 
			
		||||
        });
 | 
			
		||||
        for (MobileApp mobileApp : mobileAppInfos) {
 | 
			
		||||
            doDelete("/api/mobileApp/" + mobileApp.getId().getId())
 | 
			
		||||
                    .andExpect(status().isOk());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        List<OAuth2ClientInfo> oAuth2ClientInfos = doGetTyped("/api/oauth2/client/infos", new TypeReference<List<OAuth2ClientInfo>>() {
 | 
			
		||||
        });
 | 
			
		||||
        for (OAuth2ClientInfo oAuth2ClientInfo : oAuth2ClientInfos) {
 | 
			
		||||
            doDelete("/api/oauth2/client/" + oAuth2ClientInfo.getId().getId().toString())
 | 
			
		||||
                    .andExpect(status().isOk());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveMobileApp() throws Exception {
 | 
			
		||||
        List<MobileAppInfo> MobileAppInfos = doGetTyped("/api/mobileApp/infos", new TypeReference<List<MobileAppInfo>>() {
 | 
			
		||||
        });
 | 
			
		||||
        assertThat(MobileAppInfos).isEmpty();
 | 
			
		||||
 | 
			
		||||
        MobileApp mobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.test.package", true);
 | 
			
		||||
        MobileApp savedMobileApp = doPost("/api/mobileApp", mobileApp, MobileApp.class);
 | 
			
		||||
 | 
			
		||||
        List<MobileAppInfo> MobileAppInfos2 = doGetTyped("/api/mobileApp/infos", new TypeReference<List<MobileAppInfo>>() {
 | 
			
		||||
        });
 | 
			
		||||
        assertThat(MobileAppInfos2).hasSize(1);
 | 
			
		||||
        assertThat(MobileAppInfos2.get(0)).isEqualTo(new MobileAppInfo(savedMobileApp, Collections.emptyList()));
 | 
			
		||||
 | 
			
		||||
        MobileAppInfo retrievedMobileAppInfo = doGet("/api/mobileApp/info/{id}", MobileAppInfo.class, savedMobileApp.getId().getId());
 | 
			
		||||
        assertThat(retrievedMobileAppInfo).isEqualTo(new MobileAppInfo(savedMobileApp, Collections.emptyList()));
 | 
			
		||||
 | 
			
		||||
        doDelete("/api/mobileApp/" + savedMobileApp.getId().getId());
 | 
			
		||||
        doGet("/api/mobileApp/info/{id}", savedMobileApp.getId().getId())
 | 
			
		||||
                .andExpect(status().isNotFound());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveMobileAppWithShortAppSecret() throws Exception {
 | 
			
		||||
        MobileApp mobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "mobileApp.ce", true);
 | 
			
		||||
        mobileApp.setAppSecret("short");
 | 
			
		||||
        doPost("/api/mobileApp", mobileApp)
 | 
			
		||||
                .andExpect(status().isBadRequest())
 | 
			
		||||
                .andExpect(statusReason(containsString("appSecret must be at least 16 characters")));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testUpdateMobileAppOauth2Clients() throws Exception {
 | 
			
		||||
        MobileApp mobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.test.package", true);
 | 
			
		||||
        MobileApp savedMobileApp = doPost("/api/mobileApp", mobileApp, MobileApp.class);
 | 
			
		||||
 | 
			
		||||
        OAuth2Client oAuth2Client = validClientInfo(TenantId.SYS_TENANT_ID, "test google client");
 | 
			
		||||
        OAuth2Client savedOAuth2Client = doPost("/api/oauth2/client", oAuth2Client, OAuth2Client.class);
 | 
			
		||||
 | 
			
		||||
        OAuth2Client oAuth2Client2 = validClientInfo(TenantId.SYS_TENANT_ID, "test facebook client");
 | 
			
		||||
        OAuth2Client savedOAuth2Client2 = doPost("/api/oauth2/client", oAuth2Client2, OAuth2Client.class);
 | 
			
		||||
 | 
			
		||||
        doPut("/api/mobileApp/" + savedMobileApp.getId() + "/oauth2Clients", List.of(savedOAuth2Client.getId().getId(), savedOAuth2Client2.getId().getId()));
 | 
			
		||||
 | 
			
		||||
        MobileAppInfo retrievedMobileAppInfo = doGet("/api/mobileApp/info/{id}", MobileAppInfo.class, savedMobileApp.getId().getId());
 | 
			
		||||
        assertThat(retrievedMobileAppInfo).isEqualTo(new MobileAppInfo(savedMobileApp, List.of(new OAuth2ClientInfo(savedOAuth2Client),
 | 
			
		||||
                new OAuth2ClientInfo(savedOAuth2Client2))));
 | 
			
		||||
 | 
			
		||||
        doPut("/api/mobileApp/" + savedMobileApp.getId() + "/oauth2Clients", List.of(savedOAuth2Client2.getId().getId()));
 | 
			
		||||
        MobileAppInfo retrievedMobileAppInfo2 = doGet("/api/mobileApp/info/{id}", MobileAppInfo.class, savedMobileApp.getId().getId());
 | 
			
		||||
        assertThat(retrievedMobileAppInfo2).isEqualTo(new MobileAppInfo(savedMobileApp, List.of(new OAuth2ClientInfo(savedOAuth2Client2))));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testCreateMobileAppWithOauth2Clients() throws Exception {
 | 
			
		||||
        OAuth2Client oAuth2Client = validClientInfo(TenantId.SYS_TENANT_ID, "test google client");
 | 
			
		||||
        OAuth2Client savedOAuth2Client = doPost("/api/oauth2/client", oAuth2Client, OAuth2Client.class);
 | 
			
		||||
 | 
			
		||||
        MobileApp mobileApp = validMobileApp(TenantId.SYS_TENANT_ID, "my.test.package", true);
 | 
			
		||||
        MobileApp savedMobileApp = doPost("/api/mobileApp?oauth2ClientIds=" + savedOAuth2Client.getId().getId(), mobileApp, MobileApp.class);
 | 
			
		||||
 | 
			
		||||
        MobileAppInfo retrievedMobileAppInfo = doGet("/api/mobileApp/info/{id}", MobileAppInfo.class, savedMobileApp.getId().getId());
 | 
			
		||||
        assertThat(retrievedMobileAppInfo).isEqualTo(new MobileAppInfo(savedMobileApp, List.of(new OAuth2ClientInfo(savedOAuth2Client))));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private MobileApp validMobileApp(TenantId tenantId, String mobileAppName, boolean oauth2Enabled) {
 | 
			
		||||
        MobileApp MobileApp = new MobileApp();
 | 
			
		||||
        MobileApp.setTenantId(tenantId);
 | 
			
		||||
        MobileApp.setPkgName(mobileAppName);
 | 
			
		||||
        MobileApp.setAppSecret(StringUtils.randomAlphanumeric(24));
 | 
			
		||||
        MobileApp.setOauth2Enabled(oauth2Enabled);
 | 
			
		||||
        return MobileApp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected OAuth2Client validClientInfo(TenantId tenantId, String title) {
 | 
			
		||||
        return validClientInfo(tenantId, title, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected OAuth2Client validClientInfo(TenantId tenantId, String title, List<PlatformType> platforms) {
 | 
			
		||||
        OAuth2Client oAuth2Client = new OAuth2Client();
 | 
			
		||||
        oAuth2Client.setTenantId(tenantId);
 | 
			
		||||
        oAuth2Client.setTitle(title);
 | 
			
		||||
        oAuth2Client.setClientId(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setClientSecret(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setAuthorizationUri(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setAccessTokenUri(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setScope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
 | 
			
		||||
        oAuth2Client.setPlatforms(platforms == null ? Collections.emptyList() : platforms);
 | 
			
		||||
        oAuth2Client.setUserInfoUri(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setUserNameAttributeName(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setJwkSetUri(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setClientAuthenticationMethod(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setLoginButtonLabel(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setLoginButtonIcon(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setAdditionalInfo(JacksonUtil.newObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
 | 
			
		||||
        oAuth2Client.setMapperConfig(
 | 
			
		||||
                OAuth2MapperConfig.builder()
 | 
			
		||||
                        .allowUserCreation(true)
 | 
			
		||||
                        .activateUser(true)
 | 
			
		||||
                        .type(MapperType.CUSTOM)
 | 
			
		||||
                        .custom(
 | 
			
		||||
                                OAuth2CustomMapperConfig.builder()
 | 
			
		||||
                                        .url(UUID.randomUUID().toString())
 | 
			
		||||
                                        .build()
 | 
			
		||||
                        )
 | 
			
		||||
                        .build());
 | 
			
		||||
        return oAuth2Client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,118 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2024 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.controller;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.core.type.TypeReference;
 | 
			
		||||
import lombok.extern.slf4j.Slf4j;
 | 
			
		||||
import org.junit.After;
 | 
			
		||||
import org.junit.Before;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.MapperType;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.PlatformType;
 | 
			
		||||
import org.thingsboard.server.dao.service.DaoSqlTest;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 | 
			
		||||
 | 
			
		||||
@Slf4j
 | 
			
		||||
@DaoSqlTest
 | 
			
		||||
public class Oauth2ClientControllerTest extends AbstractControllerTest {
 | 
			
		||||
 | 
			
		||||
    @Before
 | 
			
		||||
    public void setUp() throws Exception {
 | 
			
		||||
        loginSysAdmin();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @After
 | 
			
		||||
    public void tearDown() throws Exception {
 | 
			
		||||
        List<OAuth2ClientInfo> oAuth2ClientInfos = doGetTyped("/api/oauth2/client/infos", new TypeReference<List<OAuth2ClientInfo>>() {
 | 
			
		||||
        });
 | 
			
		||||
        for (OAuth2ClientInfo oAuth2ClientInfo : oAuth2ClientInfos) {
 | 
			
		||||
            doDelete("/api/oauth2/client/" + oAuth2ClientInfo.getId().getId().toString())
 | 
			
		||||
                    .andExpect(status().isOk());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveOauth2Client() throws Exception {
 | 
			
		||||
        loginSysAdmin();
 | 
			
		||||
        List<OAuth2ClientInfo> oAuth2ClientInfos = doGetTyped("/api/oauth2/client/infos",  new TypeReference<List<OAuth2ClientInfo>>() {
 | 
			
		||||
        });
 | 
			
		||||
        assertThat(oAuth2ClientInfos).isEmpty();
 | 
			
		||||
 | 
			
		||||
        OAuth2Client oAuth2Client = validClientInfo(TenantId.SYS_TENANT_ID, "test google client");
 | 
			
		||||
        OAuth2Client savedOAuth2Client = doPost("/api/oauth2/client", oAuth2Client, OAuth2Client.class);
 | 
			
		||||
 | 
			
		||||
        List<OAuth2ClientInfo> oAuth2ClientInfos2 = doGetTyped("/api/oauth2/client/infos", new TypeReference<List<OAuth2ClientInfo>>() {
 | 
			
		||||
        });
 | 
			
		||||
        assertThat(oAuth2ClientInfos2).hasSize(1);
 | 
			
		||||
        assertThat(oAuth2ClientInfos2.get(0)).isEqualTo(new OAuth2ClientInfo(savedOAuth2Client));
 | 
			
		||||
 | 
			
		||||
        OAuth2Client retrievedOAuth2ClientInfo = doGet("/api/oauth2/client/{id}", OAuth2Client.class, savedOAuth2Client.getId().getId());
 | 
			
		||||
        assertThat(retrievedOAuth2ClientInfo).isEqualTo(savedOAuth2Client);
 | 
			
		||||
 | 
			
		||||
        doDelete("/api/oauth2/client/" + savedOAuth2Client.getId().getId());
 | 
			
		||||
        doGet("/api/oauth2/client/{id}", savedOAuth2Client.getId().getId())
 | 
			
		||||
                .andExpect(status().isNotFound());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected OAuth2Client validClientInfo(TenantId tenantId, String title) {
 | 
			
		||||
        return validClientInfo(tenantId, title, null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected OAuth2Client validClientInfo(TenantId tenantId, String title, List<PlatformType> platforms) {
 | 
			
		||||
        OAuth2Client oAuth2Client = new OAuth2Client();
 | 
			
		||||
        oAuth2Client.setTenantId(tenantId);
 | 
			
		||||
        oAuth2Client.setTitle(title);
 | 
			
		||||
        oAuth2Client.setClientId(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setClientSecret(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setAuthorizationUri(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setAccessTokenUri(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setScope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
 | 
			
		||||
        oAuth2Client.setPlatforms(platforms == null ? Collections.emptyList() : platforms);
 | 
			
		||||
        oAuth2Client.setUserInfoUri(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setUserNameAttributeName(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setJwkSetUri(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setClientAuthenticationMethod(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setLoginButtonLabel(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setLoginButtonIcon(UUID.randomUUID().toString());
 | 
			
		||||
        oAuth2Client.setAdditionalInfo(JacksonUtil.newObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
 | 
			
		||||
        oAuth2Client.setMapperConfig(
 | 
			
		||||
                OAuth2MapperConfig.builder()
 | 
			
		||||
                        .allowUserCreation(true)
 | 
			
		||||
                        .activateUser(true)
 | 
			
		||||
                        .type(MapperType.CUSTOM)
 | 
			
		||||
                        .custom(
 | 
			
		||||
                                OAuth2CustomMapperConfig.builder()
 | 
			
		||||
                                        .url(UUID.randomUUID().toString())
 | 
			
		||||
                                        .build()
 | 
			
		||||
                        )
 | 
			
		||||
                        .build());
 | 
			
		||||
        return oAuth2Client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -16,6 +16,7 @@
 | 
			
		||||
package org.thingsboard.server.common.data.domain;
 | 
			
		||||
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.Schema;
 | 
			
		||||
import jakarta.validation.constraints.NotBlank;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
@ -35,6 +36,7 @@ public class Domain extends BaseData<DomainId> implements HasTenantId, HasName {
 | 
			
		||||
    @Schema(description = "JSON object with Tenant Id")
 | 
			
		||||
    private TenantId tenantId;
 | 
			
		||||
    @Schema(description = "Domain name. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
 | 
			
		||||
    @NotBlank
 | 
			
		||||
    private String name;
 | 
			
		||||
    @Schema(description = "Whether OAuth2 settings are enabled or not")
 | 
			
		||||
    private boolean oauth2Enabled;
 | 
			
		||||
 | 
			
		||||
@ -16,8 +16,10 @@
 | 
			
		||||
package org.thingsboard.server.common.data.domain;
 | 
			
		||||
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.Schema;
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.HasOauth2Clients;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
 | 
			
		||||
 | 
			
		||||
@ -25,6 +27,8 @@ import java.util.List;
 | 
			
		||||
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@Data
 | 
			
		||||
@NoArgsConstructor
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
@Schema
 | 
			
		||||
public class DomainInfo extends Domain implements HasOauth2Clients {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.mobile;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.annotation.JsonProperty;
 | 
			
		||||
import io.swagger.v3.oas.annotations.media.Schema;
 | 
			
		||||
import jakarta.validation.constraints.NotEmpty;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.EqualsAndHashCode;
 | 
			
		||||
import lombok.NoArgsConstructor;
 | 
			
		||||
@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.HasName;
 | 
			
		||||
import org.thingsboard.server.common.data.HasTenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.MobileAppId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.validation.Length;
 | 
			
		||||
 | 
			
		||||
@EqualsAndHashCode(callSuper = true)
 | 
			
		||||
@Data
 | 
			
		||||
@ -36,8 +38,11 @@ public class MobileApp extends BaseData<MobileAppId> implements HasTenantId, Has
 | 
			
		||||
    @Schema(description = "JSON object with Tenant Id")
 | 
			
		||||
    private TenantId tenantId;
 | 
			
		||||
    @Schema(description = "Application package name. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
 | 
			
		||||
    @NotEmpty
 | 
			
		||||
    private String pkgName;
 | 
			
		||||
    @Schema(description = "Application secret. The length must be at least 16 characters", requiredMode = Schema.RequiredMode.REQUIRED)
 | 
			
		||||
    @NotEmpty
 | 
			
		||||
    @Length(min = 16, message = "must be at least 16 characters")
 | 
			
		||||
    private String appSecret;
 | 
			
		||||
    @Schema(description = "Whether OAuth2 settings are enabled or not")
 | 
			
		||||
    private boolean oauth2Enabled;
 | 
			
		||||
 | 
			
		||||
@ -32,6 +32,8 @@ public @interface Length {
 | 
			
		||||
 | 
			
		||||
    int max() default 255;
 | 
			
		||||
 | 
			
		||||
    int min() default 0;
 | 
			
		||||
 | 
			
		||||
    Class<?>[] groups() default {};
 | 
			
		||||
 | 
			
		||||
    Class<? extends Payload>[] payload() default {};
 | 
			
		||||
 | 
			
		||||
@ -30,13 +30,12 @@ import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.HasId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.OAuth2ClientId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
 | 
			
		||||
import org.thingsboard.server.dao.entity.AbstractEntityService;
 | 
			
		||||
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
 | 
			
		||||
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
 | 
			
		||||
import org.thingsboard.server.dao.exception.DataValidationException;
 | 
			
		||||
import org.thingsboard.server.dao.oauth2.OAuth2ClientDao;
 | 
			
		||||
import org.thingsboard.server.dao.oauth2.OAuth2Utils;
 | 
			
		||||
import org.thingsboard.server.dao.service.DataValidator;
 | 
			
		||||
import org.thingsboard.server.dao.service.Validator;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
@ -58,13 +57,10 @@ public class DomainServiceImpl extends AbstractEntityService implements DomainSe
 | 
			
		||||
    private OAuth2ClientDao oauth2ClientDao;
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private DomainDao domainDao;
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private DataValidator<Domain> domainValidator;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public Domain saveDomain(TenantId tenantId, Domain domain) {
 | 
			
		||||
        log.trace("Executing saveDomain [{}]", domain);
 | 
			
		||||
        domainValidator.validate(domain, Domain::getTenantId);
 | 
			
		||||
        try {
 | 
			
		||||
            Domain savedDomain = domainDao.save(tenantId, domain);
 | 
			
		||||
            eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entity(savedDomain).build());
 | 
			
		||||
@ -109,7 +105,7 @@ public class DomainServiceImpl extends AbstractEntityService implements DomainSe
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void deleteDomainById(TenantId tenantId, DomainId domainId) {
 | 
			
		||||
        log.trace("Executing deleteDomain [{}]", domainId.getId());
 | 
			
		||||
        log.trace("Executing deleteDomainById [{}]", domainId.getId());
 | 
			
		||||
        domainDao.removeById(tenantId, domainId.getId());
 | 
			
		||||
        eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(domainId).build());
 | 
			
		||||
    }
 | 
			
		||||
@ -127,7 +123,7 @@ public class DomainServiceImpl extends AbstractEntityService implements DomainSe
 | 
			
		||||
        List<DomainInfo> domainInfos = new ArrayList<>();
 | 
			
		||||
        domains.stream().sorted(Comparator.comparing(BaseData::getUuidId)).forEach(domain -> {
 | 
			
		||||
            domainInfos.add(new DomainInfo(domain, oauth2ClientDao.findByDomainId(domain.getUuidId()).stream()
 | 
			
		||||
                    .map(OAuth2Utils::toClientInfo)
 | 
			
		||||
                    .map(OAuth2ClientInfo::new)
 | 
			
		||||
                    .collect(Collectors.toList())));
 | 
			
		||||
        });
 | 
			
		||||
        return domainInfos;
 | 
			
		||||
@ -141,7 +137,7 @@ public class DomainServiceImpl extends AbstractEntityService implements DomainSe
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return new DomainInfo(domain, oauth2ClientDao.findByDomainId(domain.getUuidId()).stream()
 | 
			
		||||
                .map(OAuth2Utils::toClientInfo)
 | 
			
		||||
                .map(OAuth2ClientInfo::new)
 | 
			
		||||
                .collect(Collectors.toList()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -30,13 +30,12 @@ import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.mobile.MobileApp;
 | 
			
		||||
import org.thingsboard.server.common.data.mobile.MobileAppInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.mobile.MobileAppOauth2Client;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
 | 
			
		||||
import org.thingsboard.server.dao.entity.AbstractEntityService;
 | 
			
		||||
import org.thingsboard.server.dao.eventsourcing.DeleteEntityEvent;
 | 
			
		||||
import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent;
 | 
			
		||||
import org.thingsboard.server.dao.exception.DataValidationException;
 | 
			
		||||
import org.thingsboard.server.dao.oauth2.OAuth2ClientDao;
 | 
			
		||||
import org.thingsboard.server.dao.oauth2.OAuth2Utils;
 | 
			
		||||
import org.thingsboard.server.dao.service.DataValidator;
 | 
			
		||||
import org.thingsboard.server.dao.service.Validator;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
@ -58,13 +57,10 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
 | 
			
		||||
    private OAuth2ClientDao oauth2ClientDao;
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private MobileAppDao mobileAppDao;
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private DataValidator<MobileApp> mobileAppValidator;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MobileApp saveMobileApp(TenantId tenantId, MobileApp mobileApp) {
 | 
			
		||||
        log.trace("Executing saveMobileApp [{}]", mobileApp);
 | 
			
		||||
        mobileAppValidator.validate(mobileApp, MobileApp::getTenantId);
 | 
			
		||||
        try {
 | 
			
		||||
            MobileApp savedMobileApp = mobileAppDao.save(tenantId, mobileApp);
 | 
			
		||||
            eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entity(savedMobileApp).build());
 | 
			
		||||
@ -84,7 +80,7 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
 | 
			
		||||
        log.trace("Executing deleteMobileAppById [{}]", mobileAppId.getId());
 | 
			
		||||
        mobileAppDao.removeById(tenantId, mobileAppId.getId());
 | 
			
		||||
        eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(mobileAppId).build());
 | 
			
		||||
   }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MobileApp findMobileAppById(TenantId tenantId, MobileAppId mobileAppId) {
 | 
			
		||||
@ -99,7 +95,7 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
 | 
			
		||||
        List<MobileAppInfo> mobileAppInfos = new ArrayList<>();
 | 
			
		||||
        mobileApps.stream().sorted(Comparator.comparing(BaseData::getUuidId)).forEach(mobileApp -> {
 | 
			
		||||
            mobileAppInfos.add(new MobileAppInfo(mobileApp, oauth2ClientDao.findByMobileAppId(mobileApp.getUuidId()).stream()
 | 
			
		||||
                    .map(OAuth2Utils::toClientInfo)
 | 
			
		||||
                    .map(OAuth2ClientInfo::new)
 | 
			
		||||
                    .collect(Collectors.toList())));
 | 
			
		||||
        });
 | 
			
		||||
        return mobileAppInfos;
 | 
			
		||||
@ -113,7 +109,7 @@ public class MobileAppServiceImpl extends AbstractEntityService implements Mobil
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
        return new MobileAppInfo(mobileApp, oauth2ClientDao.findByMobileAppId(mobileApp.getUuidId()).stream()
 | 
			
		||||
                .map(OAuth2Utils::toClientInfo)
 | 
			
		||||
                .map(OAuth2ClientInfo::new)
 | 
			
		||||
                .collect(Collectors.toList()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -129,7 +129,7 @@ public class OAuth2ClientServiceImpl extends AbstractEntityService implements OA
 | 
			
		||||
        log.trace("Executing findOAuth2ClientInfosByTenantId tenantId=[{}]", tenantId);
 | 
			
		||||
        return oauth2ClientDao.findByTenantId(tenantId.getId())
 | 
			
		||||
                .stream()
 | 
			
		||||
                .map(OAuth2Utils::toClientInfo)
 | 
			
		||||
                .map(OAuth2ClientInfo::new)
 | 
			
		||||
                .collect(Collectors.toList());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,6 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.oauth2;
 | 
			
		||||
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
 | 
			
		||||
 | 
			
		||||
@ -30,13 +29,4 @@ public class OAuth2Utils {
 | 
			
		||||
        return client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static OAuth2ClientInfo toClientInfo(OAuth2Client oAuth2Client) {
 | 
			
		||||
        OAuth2ClientInfo client = new OAuth2ClientInfo();
 | 
			
		||||
        client.setId(oAuth2Client.getId());
 | 
			
		||||
        client.setCreatedTime(oAuth2Client.getCreatedTime());
 | 
			
		||||
        client.setTitle(oAuth2Client.getTitle());
 | 
			
		||||
        client.setPlatforms(oAuth2Client.getPlatforms());
 | 
			
		||||
        return client;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -26,6 +26,7 @@ import jakarta.validation.ConstraintValidatorContext;
 | 
			
		||||
@Slf4j
 | 
			
		||||
public class StringLengthValidator implements ConstraintValidator<Length, Object> {
 | 
			
		||||
    private int max;
 | 
			
		||||
    private int min;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public boolean isValid(Object value, ConstraintValidatorContext context) {
 | 
			
		||||
@ -35,14 +36,15 @@ public class StringLengthValidator implements ConstraintValidator<Length, Object
 | 
			
		||||
        } else {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        if (StringUtils.isEmpty(stringValue)) {
 | 
			
		||||
        if (stringValue == null) {
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return stringValue.length() <= max;
 | 
			
		||||
        return stringValue.length() >= min && stringValue.length() <= max;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void initialize(Length constraintAnnotation) {
 | 
			
		||||
        this.max = constraintAnnotation.max();
 | 
			
		||||
        this.min = constraintAnnotation.min();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,36 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2024 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.service.validator;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.domain.Domain;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.dao.exception.DataValidationException;
 | 
			
		||||
import org.thingsboard.server.dao.service.DataValidator;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class DomainDataValidator extends DataValidator<Domain> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void validateDataImpl(TenantId tenantId, Domain domain) {
 | 
			
		||||
        if (StringUtils.isEmpty(domain.getName())) {
 | 
			
		||||
            throw new DataValidationException("Domain name should be specified!");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -1,42 +0,0 @@
 | 
			
		||||
/**
 | 
			
		||||
 * Copyright © 2016-2024 The Thingsboard Authors
 | 
			
		||||
 *
 | 
			
		||||
 * Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
 * you may not use this file except in compliance with the License.
 | 
			
		||||
 * You may obtain a copy of the License at
 | 
			
		||||
 *
 | 
			
		||||
 *     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
 *
 | 
			
		||||
 * Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
 * distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
 * See the License for the specific language governing permissions and
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.server.dao.service.validator;
 | 
			
		||||
 | 
			
		||||
import lombok.AllArgsConstructor;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.mobile.MobileApp;
 | 
			
		||||
import org.thingsboard.server.dao.exception.DataValidationException;
 | 
			
		||||
import org.thingsboard.server.dao.service.DataValidator;
 | 
			
		||||
 | 
			
		||||
@Component
 | 
			
		||||
@AllArgsConstructor
 | 
			
		||||
public class MobileAppDataValidator extends DataValidator<MobileApp> {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void validateDataImpl(TenantId tenantId, MobileApp mobileApp) {
 | 
			
		||||
        if (StringUtils.isEmpty(mobileApp.getPkgName())) {
 | 
			
		||||
            throw new DataValidationException("Package should be specified!");
 | 
			
		||||
        }
 | 
			
		||||
        if (StringUtils.isEmpty(mobileApp.getAppSecret())) {
 | 
			
		||||
            throw new DataValidationException("Application secret should be specified!");
 | 
			
		||||
        }
 | 
			
		||||
        if (mobileApp.getAppSecret().length() < 16) {
 | 
			
		||||
            throw new DataValidationException("Application secret should be at least 16 characters!");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.domain;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.data.jpa.repository.JpaRepository;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.domain.Domain;
 | 
			
		||||
import org.thingsboard.server.common.data.domain.DomainOauth2Client;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DomainId;
 | 
			
		||||
@ -82,5 +83,9 @@ public class JpaDomainDao extends JpaAbstractDao<DomainEntity, Domain> implement
 | 
			
		||||
        domainRepository.deleteByTenantId(tenantId.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public EntityType getEntityType() {
 | 
			
		||||
        return EntityType.DOMAIN;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.mobile;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.data.jpa.repository.JpaRepository;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.id.MobileAppId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.OAuth2ClientId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
@ -77,5 +78,10 @@ public class JpaMobileAppDao extends JpaAbstractDao<MobileAppEntity, MobileApp>
 | 
			
		||||
        repository.deleteByTenantId(tenantId.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public EntityType getEntityType() {
 | 
			
		||||
        return EntityType.MOBILE_APP;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -18,6 +18,7 @@ package org.thingsboard.server.dao.sql.oauth2;
 | 
			
		||||
import lombok.RequiredArgsConstructor;
 | 
			
		||||
import org.springframework.data.jpa.repository.JpaRepository;
 | 
			
		||||
import org.springframework.stereotype.Component;
 | 
			
		||||
import org.thingsboard.server.common.data.EntityType;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.PlatformType;
 | 
			
		||||
@ -83,4 +84,9 @@ public class JpaOAuth2ClientDao extends JpaAbstractDao<OAuth2ClientEntity, OAuth
 | 
			
		||||
        repository.deleteByTenantId(tenantId.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public EntityType getEntityType() {
 | 
			
		||||
        return EntityType.OAUTH2_CLIENT;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,7 @@ public class DomainServiceTest extends AbstractServiceTest {
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveDomain() {
 | 
			
		||||
        Domain domain = validDomain(TenantId.SYS_TENANT_ID, "test.domain.com", true, true);
 | 
			
		||||
        Domain domain = constructDomain(TenantId.SYS_TENANT_ID, "test.domain.com", true, true);
 | 
			
		||||
        Domain savedDomain = domainService.saveDomain(SYSTEM_TENANT_ID, domain);
 | 
			
		||||
 | 
			
		||||
        Domain retrievedDomain = domainService.findDomainById(savedDomain.getTenantId(), savedDomain.getId());
 | 
			
		||||
@ -87,7 +87,7 @@ public class DomainServiceTest extends AbstractServiceTest {
 | 
			
		||||
    public void testGetTenantDomains() {
 | 
			
		||||
        List<Domain> domains = new ArrayList<>();
 | 
			
		||||
        for (int i = 0; i < 5; i++) {
 | 
			
		||||
            Domain oAuth2Client = validDomain(TenantId.SYS_TENANT_ID, StringUtils.randomAlphabetic(5), true, false);
 | 
			
		||||
            Domain oAuth2Client = constructDomain(TenantId.SYS_TENANT_ID, StringUtils.randomAlphabetic(5), true, false);
 | 
			
		||||
            Domain savedOauth2Client = domainService.saveDomain(SYSTEM_TENANT_ID, oAuth2Client);
 | 
			
		||||
            domains.add(savedOauth2Client);
 | 
			
		||||
        }
 | 
			
		||||
@ -102,7 +102,7 @@ public class DomainServiceTest extends AbstractServiceTest {
 | 
			
		||||
        OAuth2Client savedOauth2Client = oAuth2ClientService.saveOAuth2Client(SYSTEM_TENANT_ID, oAuth2Client);
 | 
			
		||||
        List<OAuth2ClientInfo> infos = oAuth2ClientService.findOAuth2ClientInfosByTenantId(TenantId.SYS_TENANT_ID);
 | 
			
		||||
 | 
			
		||||
        Domain domain = validDomain(TenantId.SYS_TENANT_ID, "test.domain.com", true, true);
 | 
			
		||||
        Domain domain = constructDomain(TenantId.SYS_TENANT_ID, "test.domain.com", true, true);
 | 
			
		||||
        Domain savedDomain = domainService.saveDomain(SYSTEM_TENANT_ID, domain);
 | 
			
		||||
 | 
			
		||||
        domainService.updateOauth2Clients(TenantId.SYS_TENANT_ID, savedDomain.getId(), List.of(savedOauth2Client.getId()));
 | 
			
		||||
@ -116,7 +116,7 @@ public class DomainServiceTest extends AbstractServiceTest {
 | 
			
		||||
        assertThat(oauth2LoginInfo).containsOnly(new OAuth2ClientLoginInfo(savedOauth2Client.getName(), savedOauth2Client.getLoginButtonIcon(), String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, savedOauth2Client.getUuidId().toString())));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Domain validDomain(TenantId tenantId, String domainName, boolean oauth2Enabled, boolean propagateToEdge) {
 | 
			
		||||
    private Domain constructDomain(TenantId tenantId, String domainName, boolean oauth2Enabled, boolean propagateToEdge) {
 | 
			
		||||
        Domain domain = new Domain();
 | 
			
		||||
        domain.setTenantId(tenantId);
 | 
			
		||||
        domain.setName(domainName);
 | 
			
		||||
@ -125,7 +125,4 @@ public class DomainServiceTest extends AbstractServiceTest {
 | 
			
		||||
        return domain;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -22,15 +22,16 @@ import org.thingsboard.server.common.data.StringUtils;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.PlatformType;
 | 
			
		||||
import org.thingsboard.server.dao.oauth2.OAuth2ClientService;
 | 
			
		||||
import org.thingsboard.server.dao.oauth2.OAuth2Utils;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
 | 
			
		||||
 | 
			
		||||
@DaoSqlTest
 | 
			
		||||
public class OAuth2ClientServiceTest extends AbstractServiceTest {
 | 
			
		||||
@ -58,6 +59,35 @@ public class OAuth2ClientServiceTest extends AbstractServiceTest {
 | 
			
		||||
        assertThat(retrievedOauth2Client2).isEqualTo(updatedOauth2Client);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveOauth2ClientWithoutMapper() {
 | 
			
		||||
        OAuth2Client oAuth2Client = validClientInfo(TenantId.SYS_TENANT_ID, "Test google client", List.of(PlatformType.ANDROID));
 | 
			
		||||
        oAuth2Client.setMapperConfig(null);
 | 
			
		||||
 | 
			
		||||
        assertThatThrownBy(() -> {
 | 
			
		||||
            oAuth2ClientService.saveOAuth2Client(TenantId.SYS_TENANT_ID, oAuth2Client);
 | 
			
		||||
        }).hasMessageContaining("mapperConfig must not be null");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveOauth2ClientWithoutCustomConfig() {
 | 
			
		||||
        OAuth2Client oAuth2Client = validClientInfo(TenantId.SYS_TENANT_ID, "Test google client", List.of(PlatformType.ANDROID));
 | 
			
		||||
        oAuth2Client.getMapperConfig().setCustom(null);
 | 
			
		||||
 | 
			
		||||
        assertThatThrownBy(() -> {
 | 
			
		||||
            oAuth2ClientService.saveOAuth2Client(TenantId.SYS_TENANT_ID, oAuth2Client);
 | 
			
		||||
        }).hasMessageContaining("Custom config should be specified!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSaveOauth2ClientWithoutCustomUrl() {
 | 
			
		||||
        OAuth2Client oAuth2Client = validClientInfo(TenantId.SYS_TENANT_ID, "Test google client", List.of(PlatformType.ANDROID));
 | 
			
		||||
        oAuth2Client.getMapperConfig().setCustom(OAuth2CustomMapperConfig.builder().build());
 | 
			
		||||
        assertThatThrownBy(() -> {
 | 
			
		||||
            oAuth2ClientService.saveOAuth2Client(TenantId.SYS_TENANT_ID, oAuth2Client);
 | 
			
		||||
        }).hasMessageContaining("Custom mapper URL should be specified!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testGetTenantOAuth2Clients() {
 | 
			
		||||
        List<OAuth2Client> oAuth2Clients = new ArrayList<>();
 | 
			
		||||
@ -70,7 +100,7 @@ public class OAuth2ClientServiceTest extends AbstractServiceTest {
 | 
			
		||||
        assertThat(retrieved).containsOnlyOnceElementsOf(oAuth2Clients);
 | 
			
		||||
 | 
			
		||||
        List<OAuth2ClientInfo> retrievedInfos = oAuth2ClientService.findOAuth2ClientInfosByTenantId(TenantId.SYS_TENANT_ID);
 | 
			
		||||
        List<OAuth2ClientInfo> oAuth2ClientInfos = oAuth2Clients.stream().map(OAuth2Utils::toClientInfo).collect(Collectors.toList());
 | 
			
		||||
        List<OAuth2ClientInfo> oAuth2ClientInfos = oAuth2Clients.stream().map(OAuth2ClientInfo::new).collect(Collectors.toList());
 | 
			
		||||
        assertThat(retrievedInfos).containsOnlyOnceElementsOf(oAuth2ClientInfos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -86,6 +86,8 @@ import org.thingsboard.server.common.data.asset.AssetSearchQuery;
 | 
			
		||||
import org.thingsboard.server.common.data.audit.ActionType;
 | 
			
		||||
import org.thingsboard.server.common.data.audit.AuditLog;
 | 
			
		||||
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
 | 
			
		||||
import org.thingsboard.server.common.data.domain.Domain;
 | 
			
		||||
import org.thingsboard.server.common.data.domain.DomainInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.Edge;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.EdgeEvent;
 | 
			
		||||
import org.thingsboard.server.common.data.edge.EdgeInfo;
 | 
			
		||||
@ -100,9 +102,12 @@ import org.thingsboard.server.common.data.id.CustomerId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DashboardId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceProfileId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DomainId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EdgeId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.EntityViewId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.MobileAppId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.OAuth2ClientId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.OtaPackageId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.QueueId;
 | 
			
		||||
@ -117,6 +122,10 @@ import org.thingsboard.server.common.data.id.WidgetsBundleId;
 | 
			
		||||
import org.thingsboard.server.common.data.kv.Aggregation;
 | 
			
		||||
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
 | 
			
		||||
import org.thingsboard.server.common.data.kv.TsKvEntry;
 | 
			
		||||
import org.thingsboard.server.common.data.mobile.MobileApp;
 | 
			
		||||
import org.thingsboard.server.common.data.mobile.MobileAppInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2Client;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientLoginInfo;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
 | 
			
		||||
import org.thingsboard.server.common.data.oauth2.PlatformType;
 | 
			
		||||
@ -2070,13 +2079,103 @@ public class RestClient implements Closeable {
 | 
			
		||||
                }, params).getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
//    public OAuth2Info getCurrentOAuth2Info() {
 | 
			
		||||
//        return restTemplate.getForEntity(baseURL + "/api/oauth2/config", OAuth2Info.class).getBody();
 | 
			
		||||
//    }
 | 
			
		||||
//
 | 
			
		||||
//    public OAuth2Info saveOAuth2Info(OAuth2Info oauth2Info) {
 | 
			
		||||
//        return restTemplate.postForEntity(baseURL + "/api/oauth2/config", oauth2Info, OAuth2Info.class).getBody();
 | 
			
		||||
//    }
 | 
			
		||||
    public List<OAuth2ClientInfo> getTenantOAuth2Clients() {
 | 
			
		||||
        return restTemplate.exchange(
 | 
			
		||||
                baseURL + "/api/oauth2/client/infos",
 | 
			
		||||
                HttpMethod.GET,
 | 
			
		||||
                HttpEntity.EMPTY,
 | 
			
		||||
                new ParameterizedTypeReference<List<OAuth2ClientInfo>>() {
 | 
			
		||||
                }).getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Optional<OAuth2Client> getOauth2ClientById(OAuth2ClientId oAuth2ClientId) {
 | 
			
		||||
        try {
 | 
			
		||||
            ResponseEntity<OAuth2Client> oauth2Client = restTemplate.getForEntity(baseURL + "/api/oauth2/client/{id}", OAuth2Client.class, oAuth2ClientId.getId());
 | 
			
		||||
            return Optional.ofNullable(oauth2Client.getBody());
 | 
			
		||||
        } catch (HttpClientErrorException exception) {
 | 
			
		||||
            if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
 | 
			
		||||
                return Optional.empty();
 | 
			
		||||
            } else {
 | 
			
		||||
                throw exception;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public OAuth2Client saveOAuth2Client(OAuth2Client oAuth2Client) {
 | 
			
		||||
        return restTemplate.postForEntity(baseURL + "/api/oauth2/client", oAuth2Client, OAuth2Client.class).getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void deleteOauth2CLient(OAuth2ClientId oAuth2ClientId) {
 | 
			
		||||
        restTemplate.delete(baseURL + "/api/oauth2/client/{id}", oAuth2ClientId.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<DomainInfo> getTenantDomainInfos() {
 | 
			
		||||
        return restTemplate.exchange(
 | 
			
		||||
                baseURL + "/api/domain/infos",
 | 
			
		||||
                HttpMethod.GET,
 | 
			
		||||
                HttpEntity.EMPTY,
 | 
			
		||||
                new ParameterizedTypeReference<List<DomainInfo>>() {
 | 
			
		||||
                }).getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Optional<DomainInfo> getDomainInfoById(DomainId domainId) {
 | 
			
		||||
        try {
 | 
			
		||||
            ResponseEntity<DomainInfo> domainInfo = restTemplate.getForEntity(baseURL + "/api/domain/info/{id}", DomainInfo.class, domainId.getId());
 | 
			
		||||
            return Optional.ofNullable(domainInfo.getBody());
 | 
			
		||||
        } catch (HttpClientErrorException exception) {
 | 
			
		||||
            if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
 | 
			
		||||
                return Optional.empty();
 | 
			
		||||
            } else {
 | 
			
		||||
                throw exception;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Domain saveDomain(Domain domain) {
 | 
			
		||||
        return restTemplate.postForEntity(baseURL + "/api/domain", domain, Domain.class).getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void deleteDomain(DomainId domainId) {
 | 
			
		||||
        restTemplate.delete(baseURL + "/api/domain/{id}", domainId.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void updateDomainOauth2Clients(DomainId domainId, UUID[] oauth2ClientIds) {
 | 
			
		||||
        restTemplate.postForLocation(baseURL + "/api/domain/{id}/oauth2Clients", oauth2ClientIds, domainId.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public List<DomainInfo> getTenantMobileAppInfos() {
 | 
			
		||||
        return restTemplate.exchange(
 | 
			
		||||
                baseURL + "/api/mobileApp/infos",
 | 
			
		||||
                HttpMethod.GET,
 | 
			
		||||
                HttpEntity.EMPTY,
 | 
			
		||||
                new ParameterizedTypeReference<List<DomainInfo>>() {
 | 
			
		||||
                }).getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public Optional<MobileAppInfo> getMobileAppInfoById(MobileAppId mobileAppId) {
 | 
			
		||||
        try {
 | 
			
		||||
            ResponseEntity<MobileAppInfo> mobileAppInfo = restTemplate.getForEntity(baseURL + "/api/mobileApp/info/{id}", MobileAppInfo.class, mobileAppId.getId());
 | 
			
		||||
            return Optional.ofNullable(mobileAppInfo.getBody());
 | 
			
		||||
        } catch (HttpClientErrorException exception) {
 | 
			
		||||
            if (exception.getStatusCode() == HttpStatus.NOT_FOUND) {
 | 
			
		||||
                return Optional.empty();
 | 
			
		||||
            } else {
 | 
			
		||||
                throw exception;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public MobileApp saveMobileApp(MobileApp mobileApp) {
 | 
			
		||||
        return restTemplate.postForEntity(baseURL + "/api/mobileApp", mobileApp, MobileApp.class).getBody();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void deleteMobileApp(MobileAppId mobileAppId) {
 | 
			
		||||
        restTemplate.delete(baseURL + "/api/mobileApp/{id}", mobileAppId.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void updateMobileAppOauth2Clients(MobileAppId mobileAppId, UUID[] oauth2ClientIds) {
 | 
			
		||||
        restTemplate.postForLocation(baseURL + "/api/mobileApp/{id}/oauth2Clients", oauth2ClientIds, mobileAppId.getId());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public String getLoginProcessingUrl() {
 | 
			
		||||
        return restTemplate.getForEntity(baseURL + "/api/oauth2/loginProcessingUrl", String.class).getBody();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user