From f274c274e9dbc87bbf545dc466c4d2536c1c85a2 Mon Sep 17 00:00:00 2001 From: dashevchenko Date: Thu, 18 Jul 2024 12:49:48 +0300 Subject: [PATCH] oauth2 configuration breakdown: initial implementation --- .../main/data/upgrade/3.7.0/schema_update.sql | 83 +++ ...tomOAuth2AuthorizationRequestResolver.java | 6 +- .../server/controller/BaseController.java | 39 +- .../server/controller/DomainController.java | 142 ++++ .../controller/MobileAppController.java | 142 ++++ .../server/controller/OAuth2Controller.java | 73 +- .../edge/DefaultEdgeNotificationService.java | 2 +- .../service/edge/EdgeContextComponent.java | 4 +- .../edge/EdgeEventSourcingListener.java | 8 +- .../service/edge/rpc/EdgeGrpcSession.java | 4 +- .../service/edge/rpc/EdgeSyncCursor.java | 2 +- .../oauth2/OAuth2MsgConstructor.java | 6 +- .../rpc/fetch/OAuth2EdgeEventFetcher.java | 12 +- .../edge/rpc/processor/BaseEdgeProcessor.java | 4 +- .../processor/oauth2/OAuth2EdgeProcessor.java | 14 +- .../domain/DefaultTbDomainService.java | 77 ++ .../entitiy/domain/TbDomainService.java | 15 +- .../mobile/DefaultTbMobileAppService.java | 74 ++ .../entitiy/mobile/TbMobileAppService.java | 14 +- .../DefaultTbOauth2ClientService.java | 63 ++ .../oauth2client/TbOauth2ClientService.java | 31 + .../DefaultSystemDataLoaderService.java | 8 +- .../Oauth2AuthenticationSuccessHandler.java | 11 +- .../service/security/permission/Resource.java | 4 +- .../permission/SysAdminPermissions.java | 4 +- .../system/DefaultSystemInfoService.java | 6 +- .../server/controller/HomePageApiTest.java | 84 +-- .../server/edge/AbstractEdgeTest.java | 14 +- .../server/edge/OAuth2EdgeTest.java | 74 +- .../rpc/processor/BaseEdgeProcessorTest.java | 4 +- .../server/dao/domain/DomainService.java | 44 ++ .../server/dao/mobile/MobileAppService.java | 41 ++ ...2Service.java => OAuth2ClientService.java} | 21 +- .../server/common/data/EntityType.java | 5 +- .../server/common/data/audit/ActionType.java | 3 +- .../server/common/data/domain/Domain.java | 52 ++ .../DomainInfo.java} | 24 +- .../data/domain/DomainOauth2Registration.java | 20 +- .../common/data/edge/EdgeEventType.java | 2 +- .../id/{OAuth2DomainId.java => DomainId.java} | 14 +- .../common/data/id/EntityIdFactory.java | 6 + .../{OAuth2MobileId.java => MobileAppId.java} | 14 +- .../common/data/id/OAuth2RegistrationId.java | 8 +- .../server/common/data/mobile/MobileApp.java | 59 ++ .../MobileAppInfo.java} | 25 +- .../mobile/MobileAppOauth2Registration.java | 32 + .../data/oauth2/HasOauth2Registrations.java | 12 +- .../common/data/oauth2/OAuth2Domain.java | 42 -- .../common/data/oauth2/OAuth2DomainInfo.java | 38 - .../common/data/oauth2/OAuth2Mobile.java | 42 -- .../common/data/oauth2/OAuth2ParamsInfo.java | 46 -- .../data/oauth2/OAuth2Registration.java | 29 +- .../data/oauth2/OAuth2RegistrationInfo.java | 58 +- .../server/dao/domain/DomainDao.java | 39 + .../server/dao/domain/DomainServiceImpl.java | 167 +++++ .../server/dao/mobile/MobileAppDao.java | 36 + .../dao/mobile/MobileAppServiceImpl.java | 163 +++++ .../server/dao/model/ModelConstants.java | 39 +- .../server/dao/model/sql/DomainEntity.java | 81 +++ .../DomainOauth2RegistrationCompositeKey.java | 37 + .../sql/DomainOauth2RegistrationEntity.java | 66 ++ ...MobileEntity.java => MobileAppEntity.java} | 46 +- ...bileAppOauth2RegistrationCompositeKey.java | 37 + .../MobileAppOauth2RegistrationEntity.java | 67 ++ .../dao/model/sql/OAuth2DomainEntity.java | 76 -- .../dao/model/sql/OAuth2ParamsEntity.java | 70 -- .../model/sql/OAuth2RegistrationEntity.java | 16 +- .../sql/OAuth2RegistrationInfoEntity.java | 61 ++ .../HybridClientRegistrationRepository.java | 6 +- .../dao/oauth2/OAuth2ClientServiceImpl.java | 145 ++++ .../dao/oauth2/OAuth2RegistrationDao.java | 14 +- .../server/dao/oauth2/OAuth2ServiceImpl.java | 295 -------- .../server/dao/oauth2/OAuth2Utils.java | 99 --- .../validator/DomainDataValidator.java | 36 + .../validator/MobileAppDataValidator.java | 43 ++ .../Oauth2RegistrationDataValidator.java | 114 +++ .../DomainOauth2RegistrationRepository.java | 34 + .../dao/sql/domain/DomainRepository.java | 39 + .../server/dao/sql/domain/JpaDomainDao.java | 83 +++ .../dao/sql/mobile/JpaMobileAppDao.java | 77 ++ ...MobileAppOauth2RegistrationRepository.java | 32 + .../MobileAppRepository.java} | 19 +- .../dao/sql/oauth2/JpaOAuth2DomainDao.java | 54 -- .../dao/sql/oauth2/JpaOAuth2MobileDao.java | 54 -- .../dao/sql/oauth2/JpaOAuth2ParamsDao.java | 49 -- .../sql/oauth2/JpaOAuth2RegistrationDao.java | 31 +- .../oauth2/OAuth2RegistrationRepository.java | 69 +- .../server/dao/tenant/TenantServiceImpl.java | 2 +- .../main/resources/sql/schema-entities.sql | 49 +- .../dao/service/OAuth2ClientServiceTest.java | 641 +++++++++++++++++ .../server/dao/service/OAuth2ServiceTest.java | 668 ------------------ .../thingsboard/rest/client/RestClient.java | 15 +- 92 files changed, 3414 insertions(+), 1916 deletions(-) create mode 100644 application/src/main/data/upgrade/3.7.0/schema_update.sql create mode 100644 application/src/main/java/org/thingsboard/server/controller/DomainController.java create mode 100644 application/src/main/java/org/thingsboard/server/controller/MobileAppController.java create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/domain/DefaultTbDomainService.java rename dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java => application/src/main/java/org/thingsboard/server/service/entitiy/domain/TbDomainService.java (61%) create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/mobile/DefaultTbMobileAppService.java rename dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java => application/src/main/java/org/thingsboard/server/service/entitiy/mobile/TbMobileAppService.java (60%) create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/oauth2client/DefaultTbOauth2ClientService.java create mode 100644 application/src/main/java/org/thingsboard/server/service/entitiy/oauth2client/TbOauth2ClientService.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/domain/DomainService.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppService.java rename common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/{OAuth2Service.java => OAuth2ClientService.java} (50%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/domain/Domain.java rename common/data/src/main/java/org/thingsboard/server/common/data/{oauth2/OAuth2MobileInfo.java => domain/DomainInfo.java} (56%) rename dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2MobileRepository.java => common/data/src/main/java/org/thingsboard/server/common/data/domain/DomainOauth2Registration.java (59%) rename common/data/src/main/java/org/thingsboard/server/common/data/id/{OAuth2DomainId.java => DomainId.java} (68%) rename common/data/src/main/java/org/thingsboard/server/common/data/id/{OAuth2MobileId.java => MobileAppId.java} (68%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileApp.java rename common/data/src/main/java/org/thingsboard/server/common/data/{oauth2/OAuth2Info.java => mobile/MobileAppInfo.java} (58%) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppOauth2Registration.java rename dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java => common/data/src/main/java/org/thingsboard/server/common/data/oauth2/HasOauth2Registrations.java (68%) delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/domain/DomainDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainOauth2RegistrationCompositeKey.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainOauth2RegistrationEntity.java rename dao/src/main/java/org/thingsboard/server/dao/model/sql/{OAuth2MobileEntity.java => MobileAppEntity.java} (56%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppOauth2RegistrationCompositeKey.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppOauth2RegistrationEntity.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationInfoEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/service/validator/Oauth2RegistrationDataValidator.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/domain/DomainOauth2RegistrationRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/domain/DomainRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/domain/JpaDomainDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppOauth2RegistrationRepository.java rename dao/src/main/java/org/thingsboard/server/dao/sql/{oauth2/OAuth2ParamsRepository.java => mobile/MobileAppRepository.java} (50%) delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java create mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ClientServiceTest.java delete mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ServiceTest.java diff --git a/application/src/main/data/upgrade/3.7.0/schema_update.sql b/application/src/main/data/upgrade/3.7.0/schema_update.sql new file mode 100644 index 0000000000..caa98e6470 --- /dev/null +++ b/application/src/main/data/upgrade/3.7.0/schema_update.sql @@ -0,0 +1,83 @@ +-- +-- 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. +-- + +-- OAUTH2 UPDATE START + +ALTER TABLE IF EXISTS oauth2_mobile RENAME TO mobile_app; +ALTER TABLE IF EXISTS oauth2_domain RENAME TO domain; + +ALTER TABLE domain ADD COLUMN IF NOT EXISTS oauth2_enabled boolean, + ADD COLUMN IF NOT EXISTS propagate_to_edge boolean, + ADD COLUMN IF NOT EXISTS tenant_id uuid DEFAULT '13814000-1dd2-11b2-8080-808080808080', + DROP COLUMN IF EXISTS domain_scheme; +ALTER TABLE mobile_app ADD COLUMN IF NOT EXISTS oauth2_enabled boolean, + ADD COLUMN IF NOT EXISTS tenant_id uuid DEFAULT '13814000-1dd2-11b2-8080-808080808080'; +ALTER TABLE oauth2_registration ADD COLUMN IF NOT EXISTS tenant_id uuid DEFAULT '13814000-1dd2-11b2-8080-808080808080'; +ALTER TABLE oauth2_registration ADD COLUMN IF NOT EXISTS title varchar(100); + +CREATE TABLE IF NOT EXISTS domain_oauth2_registration ( + domain_id uuid NOT NULL, + oauth2_registration_id uuid NOT NULL, + CONSTRAINT fk_domain FOREIGN KEY (domain_id) REFERENCES domain(id) ON DELETE CASCADE, + CONSTRAINT fk_oauth2_registration FOREIGN KEY (oauth2_registration_id) REFERENCES oauth2_registration(id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS mobile_app_oauth2_registration ( + mobile_app_id uuid NOT NULL, + oauth2_registration_id uuid NOT NULL, + CONSTRAINT fk_domain FOREIGN KEY (mobile_app_id) REFERENCES mobile_app(id) ON DELETE CASCADE, + CONSTRAINT fk_oauth2_registration FOREIGN KEY (oauth2_registration_id) REFERENCES oauth2_registration(id) ON DELETE CASCADE +); + +DO +$$ + BEGIN + IF EXISTS(SELECT 1 FROM information_schema.tables WHERE table_name = 'oauth2_params') THEN + -- delete duplicated domains + DELETE FROM domain d1 USING domain d2 WHERE d1.created_time < d2.created_time AND d1.domain_name = d2.domain_name; + + UPDATE domain SET oauth2_enabled = p.enabled, + propagate_to_edge = p.edge_enabled + FROM oauth2_params p WHERE p.id = domain.oauth2_params_id; + + UPDATE mobile_app SET oauth2_enabled = p.enabled + FROM oauth2_params p WHERE p.id = mobile_app.oauth2_params_id; + + INSERT INTO domain_oauth2_registration(domain_id, oauth2_registration_id) + (SELECT d.id, r.id FROM domain d LEFT JOIN oauth2_registration r on d.oauth2_params_id = r.oauth2_params_id + WHERE r.platforms IS NULL OR r.platforms IN ('','WEB')); + + INSERT INTO mobile_app_oauth2_registration(mobile_app_id, oauth2_registration_id) + (SELECT m.id, r.id FROM mobile_app m LEFT JOIN oauth2_registration r on m.oauth2_params_id = r.oauth2_params_id + WHERE r.platforms IS NULL OR r.platforms IN ('','ANDROID','IOS')); + + ALTER TABLE mobile_app RENAME CONSTRAINT oauth2_mobile_pkey TO mobile_app_pkey; + ALTER TABLE domain RENAME CONSTRAINT oauth2_domain_pkey TO domain_pkey; + UPDATE oauth2_registration SET title = additional_info::jsonb->>'providerName' WHERE additional_info IS NOT NULL; + + ALTER TABLE domain DROP COLUMN oauth2_params_id; + ALTER TABLE mobile_app DROP COLUMN oauth2_params_id; + ALTER TABLE oauth2_registration DROP COLUMN oauth2_params_id; + + ALTER TABLE mobile_app ADD CONSTRAINT mobile_app_unq_key UNIQUE (pkg_name); + ALTER TABLE domain ADD CONSTRAINT domain_unq_key UNIQUE (domain_name); + + DROP TABLE IF EXISTS oauth2_params; + END IF; + END +$$; + +-- OAUTH2 UPDATE END \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java index 1784c6fa1b..2dd5027a2a 100644 --- a/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java +++ b/application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java @@ -38,7 +38,7 @@ import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; -import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterNames; import org.thingsboard.server.service.security.model.token.OAuth2AppTokenFactory; @@ -70,7 +70,7 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza private ClientRegistrationRepository clientRegistrationRepository; @Autowired - private OAuth2Service oAuth2Service; + private OAuth2ClientService oAuth2ClientService; @Autowired private OAuth2AppTokenFactory oAuth2AppTokenFactory; @@ -131,7 +131,7 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza if (StringUtils.isEmpty(appToken)) { throw new IllegalArgumentException("Invalid application token."); } else { - String appSecret = this.oAuth2Service.findAppSecret(UUID.fromString(registrationId), appPackage); + String appSecret = this.oAuth2ClientService.findAppSecret(UUID.fromString(registrationId), appPackage); if (StringUtils.isEmpty(appSecret)) { throw new IllegalArgumentException("Invalid package: " + appPackage + ". No application secret found for Client Registration with given application package."); } diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 0942329f40..1dbee4abce 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -62,6 +62,7 @@ import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.AssetInfo; import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.domain.Domain; import org.thingsboard.server.common.data.edge.Edge; import org.thingsboard.server.common.data.edge.EdgeInfo; import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; @@ -74,11 +75,14 @@ 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.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; import org.thingsboard.server.common.data.id.OtaPackageId; import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.RpcId; @@ -91,6 +95,8 @@ import org.thingsboard.server.common.data.id.UUIDBased; import org.thingsboard.server.common.data.id.UserId; import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.mobile.MobileApp; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; @@ -117,13 +123,15 @@ import org.thingsboard.server.dao.device.ClaimDevicesService; import org.thingsboard.server.dao.device.DeviceCredentialsService; import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceService; +import org.thingsboard.server.dao.domain.DomainService; import org.thingsboard.server.dao.edge.EdgeService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; +import org.thingsboard.server.dao.mobile.MobileAppService; import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; -import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; @@ -233,7 +241,13 @@ public abstract class BaseController { protected DashboardService dashboardService; @Autowired - protected OAuth2Service oAuth2Service; + protected OAuth2ClientService oAuth2ClientService; + + @Autowired + protected DomainService domainService; + + @Autowired + protected MobileAppService mobileAppService; @Autowired protected OAuth2ConfigTemplateService oAuth2ConfigTemplateService; @@ -600,6 +614,15 @@ public abstract class BaseController { case QUEUE: checkQueueId(new QueueId(entityId.getId()), operation); return; + case OAUTH2_CLIENT: + checkOauth2ClientId(new OAuth2RegistrationId(entityId.getId()), operation); + return; + case DOMAIN: + checkDomainId(new DomainId(entityId.getId()), operation); + return; + case MOBILE_APP: + checkMobileAppId(new MobileAppId(entityId.getId()), operation); + return; default: checkEntityId(entityId, entitiesService::findEntityByTenantIdAndId, operation); } @@ -776,6 +799,18 @@ public abstract class BaseController { return queue; } + OAuth2Registration checkOauth2ClientId(OAuth2RegistrationId oAuth2RegistrationId, Operation operation) throws ThingsboardException { + return checkEntityId(oAuth2RegistrationId, oAuth2ClientService::findOAuth2ClientById, operation); + } + + Domain checkDomainId(DomainId domainId, Operation operation) throws ThingsboardException { + return checkEntityId(domainId, domainService::findDomainById, operation); + } + + MobileApp checkMobileAppId(MobileAppId mobileAppId, Operation operation) throws ThingsboardException { + return checkEntityId(mobileAppId, mobileAppService::findMobileAppById, operation); + } + protected I emptyId(EntityType entityType) { return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); } diff --git a/application/src/main/java/org/thingsboard/server/controller/DomainController.java b/application/src/main/java/org/thingsboard/server/controller/DomainController.java new file mode 100644 index 0000000000..c17fbfd709 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/DomainController.java @@ -0,0 +1,142 @@ +/** + * 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 io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.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; +import org.thingsboard.server.common.data.id.DomainId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.config.annotations.ApiOperation; +import org.thingsboard.server.queue.util.TbCoreComponent; +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; + +import static org.thingsboard.server.common.data.audit.ActionType.UPDATED_OAUTH2_CLIENTS; +import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; + +@RestController +@TbCoreComponent +@RequestMapping("/api") +@RequiredArgsConstructor +@Slf4j +public class DomainController extends BaseController { + + private final TbDomainService tbDomainService; + + @ApiOperation(value = "Save or Update Domain (saveDomain)", + notes = "Create or update the Domain. When creating domain, platform generates Domain Id as " + UUID_WIKI_LINK + + "The newly created Domain Id will be present in the response. " + + "Specify existing Domain Id to update the domain. " + + "Referencing non-existing Domain Id will cause 'Not Found' error." + + "\n\nDomain name is unique for entire platform setup.\n\n") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") + @PostMapping(value = "/domain") + public Domain saveDomain( + @Parameter(description = "A JSON value representing the Domain.", required = true) + @RequestBody 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 oauth2ClientIds = ids != null ? Arrays.asList(ids) : Collections.emptyList(); + domain.setTenantId(getCurrentUser().getTenantId()); + checkEntity(domain.getId(), domain, Resource.DOMAIN); + List oAuth2ClientIds = new ArrayList<>(); + for (String id : oauth2ClientIds) { + OAuth2RegistrationId oauth2ClientId = new OAuth2RegistrationId(toUUID(id)); + checkOauth2ClientId(oauth2ClientId, Operation.READ); + oAuth2ClientIds.add(oauth2ClientId); + } + return tbDomainService.save(domain, oAuth2ClientIds, getCurrentUser()); + } + + @ApiOperation(value = "Update oauth2 clients (updateOauth2Clients)", + notes = "Update oauth2 clients for the specified domain. ") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") + @PostMapping(value = "/domain/{id}/oauth2Clients") + public void updateOauth2Clients(@PathVariable UUID id, + @RequestBody UUID[] oauth2ClientIds) throws ThingsboardException { + DomainId domainId = new DomainId(id); + Domain domain = null; + try { + domain = checkDomainId(domainId, Operation.WRITE); + List oAuth2ClientIds = new ArrayList<>(); + for (UUID outh2CLientId : oauth2ClientIds) { + OAuth2RegistrationId oAuth2RegistrationId = new OAuth2RegistrationId(outh2CLientId); + checkEntityId(oAuth2RegistrationId, Operation.READ); + oAuth2ClientIds.add(oAuth2RegistrationId); + } + domainService.updateOauth2Clients(getTenantId(), domainId, oAuth2ClientIds); + logEntityActionService.logEntityAction(domain.getTenantId(), domain.getId(), domain, + UPDATED_OAUTH2_CLIENTS, getCurrentUser(), oAuth2ClientIds.toString()); + } catch (Exception e) { + if (domain != null) { + logEntityActionService.logEntityAction(getTenantId(), domainId, domain, + ActionType.UPDATED_OAUTH2_CLIENTS, getCurrentUser(), e); + } + throw e; + } + } + + @ApiOperation(value = "Get Domain infos (getDomainInfos)", notes = SYSTEM_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") + @GetMapping(value = "/domain/infos") + public List getDomainInfos() throws ThingsboardException { + return domainService.findDomainInfosByTenantId(getTenantId()); + } + + @ApiOperation(value = "Get Domain info by Id (getDomainInfoById)", notes = SYSTEM_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") + @GetMapping(value = "/domain/info/{id}") + public DomainInfo getDomainInfoById(@PathVariable UUID id) throws ThingsboardException { + DomainId domainId = new DomainId(id); + return checkEntityId(domainId, domainService::findDomainInfoById, Operation.READ); + } + + @ApiOperation(value = "Delete Domain by ID (deleteDomain)", + notes = "Deletes Domain by ID. Referencing non-existing asset Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAuthority('SYS_ADMIN')") + @DeleteMapping(value = "/domain/{id}") + public void deleteDomain(@PathVariable UUID id) throws Exception { + DomainId domainId = new DomainId(id); + checkDomainId(domainId, Operation.DELETE); + domainService.deleteDomainById(getTenantId(), domainId); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/controller/MobileAppController.java b/application/src/main/java/org/thingsboard/server/controller/MobileAppController.java new file mode 100644 index 0000000000..f245ecfdd1 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/MobileAppController.java @@ -0,0 +1,142 @@ +/** + * 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 io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +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.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.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.DomainId; +import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +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.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 java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import static org.thingsboard.server.common.data.audit.ActionType.UPDATED_OAUTH2_CLIENTS; +import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; + +@RestController +@TbCoreComponent +@RequestMapping("/api") +@RequiredArgsConstructor +@Slf4j +public class MobileAppController extends BaseController { + + private final TbMobileAppService tbMobileAppService; + + @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. " + + "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 list of entity group ids, separated by comma ','", array = @ArraySchema(schema = @Schema(type = "string"))) + @RequestParam(name = "oauth2RegistrationIds", required = false) UUID[] oauth2RegistrationIds) throws Exception { + mobileApp.setTenantId(getCurrentUser().getTenantId()); + + List oAuth2Registrations = new ArrayList<>(); + for (UUID id : oauth2RegistrationIds) { + OAuth2RegistrationId oauth2ClientId = new OAuth2RegistrationId(id); + checkOauth2ClientId(oauth2ClientId, Operation.READ); + oAuth2Registrations.add(oauth2ClientId); + } + return tbMobileAppService.save(mobileApp, oAuth2Registrations, getCurrentUser()); + } + + @ApiOperation(value = "Update oauth2 clients (updateOauth2Clients)", + notes = "Update oauth2 clients to the specified mobile app. ") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") + @PostMapping(value = "/mobileApp/{id}/updateOauth2Clients") + public void updateOauth2Clients(@PathVariable UUID id, + @RequestBody UUID[] oauth2ClientIds) throws ThingsboardException { + MobileAppId mobileAppId = new MobileAppId(id); + MobileApp mobileApp = null; + try { + mobileApp = checkMobileAppId(mobileAppId, Operation.WRITE); + List oAuth2ClientIds = new ArrayList<>(); + for (UUID outh2CLientId : oauth2ClientIds) { + OAuth2RegistrationId oAuth2RegistrationId = new OAuth2RegistrationId(outh2CLientId); + checkEntityId(oAuth2RegistrationId, Operation.READ); + oAuth2ClientIds.add(oAuth2RegistrationId); + } + mobileAppService.updateOauth2Clients(getTenantId(), mobileAppId, oAuth2ClientIds); + logEntityActionService.logEntityAction(getTenantId(), mobileAppId, mobileApp, + UPDATED_OAUTH2_CLIENTS, getCurrentUser(), oAuth2ClientIds.toString()); + } catch (Exception e) { + if (mobileApp != null) { + logEntityActionService.logEntityAction(getTenantId(), mobileAppId, mobileApp, + ActionType.UPDATED_OAUTH2_CLIENTS, getCurrentUser(), e); + } + throw e; + } + } + + @ApiOperation(value = "Get mobile app infos (getMobileAppInfos)", notes = SYSTEM_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") + @GetMapping(value = "/mobileApp/infos") + public List getMobileAppInfos() throws ThingsboardException { + TenantId tenantId = getCurrentUser().getTenantId(); + return mobileAppService.findMobileAppInfosByTenantId(tenantId); + } + + @ApiOperation(value = "Get mobile info by id (getMobileAppInfoById)", notes = SYSTEM_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") + @GetMapping(value = "/mobileApp/info/{id}") + public MobileAppInfo getMobileAppInfoById(@PathVariable UUID id) throws ThingsboardException { + MobileAppId mobileAppId = new MobileAppId(id); + return checkEntityId(mobileAppId, mobileAppService::findMobileAppInfoById, Operation.READ); + } + + @ApiOperation(value = "Delete Mobile App by ID (deleteMobileApp)", + notes = "Deletes Mobile App by ID. Referencing non-existing asset Id will cause an error." + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAuthority('SYS_ADMIN')") + @DeleteMapping(value = "/mobileApp/{id}") + public void deleteMobileApp(@PathVariable UUID id) throws Exception { + MobileAppId mobileAppId = new MobileAppId(id); + checkMobileAppId(mobileAppId, Operation.DELETE); + mobileAppService.deleteMobileAppById(getTenantId(), mobileAppId); + } + +} diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index 325cc3b025..1b64e7eb60 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -18,49 +18,59 @@ 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 lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; +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.ResponseBody; 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.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.config.annotations.ApiOperation; import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.entitiy.oauth2client.TbOauth2ClientService; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.utils.MiscUtils; import java.util.Enumeration; import java.util.List; +import java.util.UUID; import static org.thingsboard.server.controller.ControllerConstants.SYSTEM_AUTHORITY_PARAGRAPH; +import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; @RestController @TbCoreComponent @RequestMapping("/api") +@RequiredArgsConstructor @Slf4j public class OAuth2Controller extends BaseController { - @Autowired - private OAuth2Configuration oAuth2Configuration; + private final OAuth2Configuration oAuth2Configuration; + + private final TbOauth2ClientService tbOauth2ClientService; @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)") - @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) - @ResponseBody + @PostMapping(value = "/noauth/oauth2Clients") public List getOAuth2Clients(HttpServletRequest request, @Parameter(description = "Mobile application package name, to find OAuth2 clients " + "where there is configured mobile application with such package name") @@ -85,26 +95,47 @@ public class OAuth2Controller extends BaseController { } catch (Exception e) { } } - return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainNameAndPort(request), pkgName, platformType); + if (StringUtils.isNotEmpty(pkgName)) { + return oAuth2ClientService.getMobileOAuth2Clients(pkgName, platformType); + } else { + return oAuth2ClientService.getWebOAuth2Clients(MiscUtils.getDomainNameAndPort(request), platformType); + } } - @ApiOperation(value = "Get current OAuth2 settings (getCurrentOAuth2Info)", notes = SYSTEM_AUTHORITY_PARAGRAPH) + @ApiOperation(value = "Save OAuth2 Client Registration (saveOAuth2Client)", notes = SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") - @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") - @ResponseBody - public OAuth2Info getCurrentOAuth2Info() throws ThingsboardException { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); - return oAuth2Service.findOAuth2Info(); + @PostMapping(value = "/oauth2/client") + public OAuth2Registration saveOAuth2Client(@RequestBody OAuth2Registration oAuth2Registration) throws Exception { + TenantId tenantId = getTenantId(); + oAuth2Registration.setTenantId(tenantId); + checkEntity(oAuth2Registration.getId(), oAuth2Registration, Resource.OAUTH2_CLIENT); + return tbOauth2ClientService.save(oAuth2Registration, getCurrentUser()); } - @ApiOperation(value = "Save OAuth2 settings (saveOAuth2Info)", notes = SYSTEM_AUTHORITY_PARAGRAPH) + @ApiOperation(value = "Get OAuth2 Client Registration infos (findTenantOAuth2ClientInfos)", notes = SYSTEM_AUTHORITY_PARAGRAPH) @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") - @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) + @GetMapping(value = "/oauth2/client/infos") + public List findTenantOAuth2ClientInfos() throws ThingsboardException { + return oAuth2ClientService.findOauth2ClientInfosByTenantId(getTenantId()); + } + + @ApiOperation(value = "Get OAuth2 Client Registration by id (getOAuth2ClientById)", notes = SYSTEM_AUTHORITY_PARAGRAPH) + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") + @GetMapping(value = "/oauth2/client/{id}") + public OAuth2Registration getOAuth2ClientById(@PathVariable UUID id) throws ThingsboardException { + OAuth2RegistrationId oAuth2RegistrationId = new OAuth2RegistrationId(id); + return checkEntityId(oAuth2RegistrationId, oAuth2ClientService::findOAuth2ClientById, Operation.READ); + } + + @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) - public OAuth2Info saveOAuth2Info(@RequestBody OAuth2Info oauth2Info) throws ThingsboardException { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); - oAuth2Service.saveOAuth2Info(oauth2Info); - return oAuth2Service.findOAuth2Info(); + public void deleteOauth2Client(@PathVariable UUID id) throws Exception { + OAuth2RegistrationId oAuth2RegistrationId = new OAuth2RegistrationId(id); + OAuth2Registration oAuth2Registration = checkOauth2ClientId(oAuth2RegistrationId, Operation.DELETE); + tbOauth2ClientService.delete(oAuth2Registration, getCurrentUser()); } @ApiOperation(value = "Get OAuth2 log in processing URL (getLoginProcessingUrl)", notes = "Returns the URL enclosed in " + @@ -113,9 +144,7 @@ public class OAuth2Controller extends BaseController { "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) - @ResponseBody - public String getLoginProcessingUrl() throws ThingsboardException { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); + public String getLoginProcessingUrl() { return "\"" + oAuth2Configuration.getLoginProcessingUrl() + "\""; } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java index f29255e414..d7b6db7f21 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java @@ -201,7 +201,7 @@ public class DefaultEdgeNotificationService implements EdgeNotificationService { case NOTIFICATION_RULE, NOTIFICATION_TARGET, NOTIFICATION_TEMPLATE -> notificationEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); case TB_RESOURCE -> resourceEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); - case OAUTH2 -> oAuth2EdgeProcessor.processOAuth2Notification(tenantId, edgeNotificationMsg); + case OAUTH2_CLIENT -> oAuth2EdgeProcessor.processOAuth2Notification(tenantId, edgeNotificationMsg); default -> log.warn("[{}] Edge event type [{}] is not designed to be pushed to edge", tenantId, type); } } catch (Exception e) { diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java index 489fc6b6b8..8e609ed545 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java @@ -35,7 +35,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.notification.NotificationRuleService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; -import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.resource.ResourceService; @@ -167,7 +167,7 @@ public class EdgeContextComponent { private NotificationTemplateService notificationTemplateService; @Autowired - private OAuth2Service oAuth2Service; + private OAuth2ClientService oAuth2ClientService; @Autowired private RateLimitService rateLimitService; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java index 651339e3a4..2a11c7df29 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java @@ -34,7 +34,7 @@ import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.rule.RuleChain; @@ -227,8 +227,8 @@ public class EdgeEventSourcingListener { private EdgeEventType getEdgeEventTypeForEntityEvent(Object entity) { if (entity instanceof AlarmComment) { return EdgeEventType.ALARM_COMMENT; - } else if (entity instanceof OAuth2Info) { - return EdgeEventType.OAUTH2; + } else if (entity instanceof OAuth2Registration) { + return EdgeEventType.OAUTH2_CLIENT; } return null; } @@ -236,7 +236,7 @@ public class EdgeEventSourcingListener { private String getBodyMsgForEntityEvent(Object entity) { if (entity instanceof AlarmComment) { return JacksonUtil.toString(entity); - } else if (entity instanceof OAuth2Info) { + } else if (entity instanceof OAuth2Registration) { return JacksonUtil.toString(entity); } return null; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java index 55e8e051ab..68a08920d9 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java @@ -689,8 +689,8 @@ public final class EdgeGrpcSession implements Closeable { return ctx.getNotificationEdgeProcessor().convertNotificationTargetToDownlink(edgeEvent); case NOTIFICATION_TEMPLATE: return ctx.getNotificationEdgeProcessor().convertNotificationTemplateToDownlink(edgeEvent); - case OAUTH2: - return ctx.getOAuth2EdgeProcessor().convertOAuth2EventToDownlink(edgeEvent); + case OAUTH2_CLIENT: + return ctx.getOAuth2EdgeProcessor().convertOAuth2ProviderEventToDownlink(edgeEvent); default: log.warn("[{}] Unsupported edge event type [{}]", this.tenantId, edgeEvent); return null; diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java index 6321991586..436fa37321 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java @@ -86,7 +86,7 @@ public class EdgeSyncCursor { fetchers.add(new TenantWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService())); fetchers.add(new OtaPackagesEdgeEventFetcher(ctx.getOtaPackageService())); fetchers.add(new TenantResourcesEdgeEventFetcher(ctx.getResourceService())); - fetchers.add(new OAuth2EdgeEventFetcher(ctx.getOAuth2Service())); + fetchers.add(new OAuth2EdgeEventFetcher(ctx.getOAuth2ClientService())); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java index c628b042a2..8c734da83c 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java @@ -17,7 +17,7 @@ package org.thingsboard.server.service.edge.rpc.constructor.oauth2; import org.springframework.stereotype.Component; import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -25,8 +25,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent; @TbCoreComponent public class OAuth2MsgConstructor { - public OAuth2UpdateMsg constructOAuth2UpdateMsg(OAuth2Info oAuth2Info) { - return OAuth2UpdateMsg.newBuilder().setEntity(JacksonUtil.toString(oAuth2Info)).build(); + public OAuth2UpdateMsg constructOAuth2UpdateMsg(OAuth2Registration oAuth2Registration) { + return OAuth2UpdateMsg.newBuilder().setEntity(JacksonUtil.toString(oAuth2Registration)).build(); } } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java index 4412f91183..e491f8bf2e 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java @@ -24,10 +24,10 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; -import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import java.util.ArrayList; import java.util.List; @@ -36,7 +36,7 @@ import java.util.List; @Slf4j public class OAuth2EdgeEventFetcher implements EdgeEventFetcher { - private final OAuth2Service oAuth2Service; + private final OAuth2ClientService oAuth2ClientService; @Override public PageLink getPageLink(int pageSize) { @@ -46,9 +46,9 @@ public class OAuth2EdgeEventFetcher implements EdgeEventFetcher { @Override public PageData fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) { List result = new ArrayList<>(); - OAuth2Info oAuth2Info = oAuth2Service.findOAuth2Info(); - result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.OAUTH2, - EdgeEventActionType.ADDED, null, JacksonUtil.valueToTree(oAuth2Info))); + List oauth2Registrations = oAuth2ClientService.findOauth2ClientsByTenantId(TenantId.SYS_TENANT_ID); + result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.OAUTH2_CLIENT, + EdgeEventActionType.ADDED, null, JacksonUtil.valueToTree(oauth2Registrations))); // returns PageData object to be in sync with other fetchers return new PageData<>(result, 1, result.size(), false); } diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java index f46c07ab5a..28a3628f7f 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java @@ -76,7 +76,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.notification.NotificationRuleService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; -import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; @@ -240,7 +240,7 @@ public abstract class BaseEdgeProcessor { protected NotificationTemplateService notificationTemplateService; @Autowired - protected OAuth2Service oAuth2Service; + protected OAuth2ClientService oAuth2ClientService; @Autowired @Lazy diff --git a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java index ce0600b491..a6bcb663a2 100644 --- a/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java +++ b/application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java @@ -25,7 +25,7 @@ import org.thingsboard.server.common.data.edge.EdgeEvent; import org.thingsboard.server.common.data.edge.EdgeEventActionType; import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.OAuth2Info; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.gen.edge.v1.DownlinkMsg; import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg; import org.thingsboard.server.gen.transport.TransportProtos; @@ -37,21 +37,21 @@ import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor; @TbCoreComponent public class OAuth2EdgeProcessor extends BaseEdgeProcessor { - public DownlinkMsg convertOAuth2EventToDownlink(EdgeEvent edgeEvent) { + public DownlinkMsg convertOAuth2ProviderEventToDownlink(EdgeEvent edgeEvent) { DownlinkMsg downlinkMsg = null; - OAuth2Info oAuth2Info = JacksonUtil.convertValue(edgeEvent.getBody(), OAuth2Info.class); - if (oAuth2Info != null) { - OAuth2UpdateMsg oAuth2UpdateMsg = oAuth2MsgConstructor.constructOAuth2UpdateMsg(oAuth2Info); + OAuth2Registration oAuth2Registration = JacksonUtil.convertValue(edgeEvent.getBody(), OAuth2Registration.class); + if (oAuth2Registration != null) { + OAuth2UpdateMsg oAuth2ProviderUpdateMsg = oAuth2MsgConstructor.constructOAuth2UpdateMsg(oAuth2Registration); downlinkMsg = DownlinkMsg.newBuilder() .setDownlinkMsgId(EdgeUtils.nextPositiveInt()) - .addOAuth2UpdateMsg(oAuth2UpdateMsg) + .addOAuth2UpdateMsg(oAuth2ProviderUpdateMsg) .build(); } return downlinkMsg; } public ListenableFuture processOAuth2Notification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { - OAuth2Info oAuth2Info = JacksonUtil.fromString(edgeNotificationMsg.getBody(), OAuth2Info.class); + OAuth2Registration oAuth2Info = JacksonUtil.fromString(edgeNotificationMsg.getBody(), OAuth2Registration.class); if (oAuth2Info == null) { return Futures.immediateFuture(null); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/domain/DefaultTbDomainService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/domain/DefaultTbDomainService.java new file mode 100644 index 0000000000..882a444aed --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/domain/DefaultTbDomainService.java @@ -0,0 +1,77 @@ +/** + * 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.service.entitiy.domain; + +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.id.DomainId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.domain.DomainService; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; + +import java.util.List; + +import static org.thingsboard.server.common.data.audit.ActionType.UPDATED_OAUTH2_CLIENTS; + +@Service +@AllArgsConstructor +public class DefaultTbDomainService extends AbstractTbEntityService implements TbDomainService { + + private final DomainService domainService; + + @Override + public Domain save(Domain domain, List oAuth2Clients, User user) throws Exception { + ActionType actionType = domain.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + 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(domain.getTenantId(), savedDomain.getId(), savedDomain, + UPDATED_OAUTH2_CLIENTS, user, oAuth2Clients.toString()); + } + return savedDomain; + } catch (Exception e) { + logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.DOMAIN), domain, actionType, user, e); + throw e; + } + } + + @Override + @Transactional + public void delete(Domain domain, User user) { + ActionType actionType = ActionType.DELETED; + TenantId tenantId = domain.getTenantId(); + DomainId domainId = domain.getId(); + try { + domainService.deleteDomainById(tenantId, domainId); + logEntityActionService.logEntityAction(tenantId, domainId, domain, actionType, user, domain.getName()); + } catch (Exception e) { + logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.DOMAIN), actionType, user, e, + domainId.toString()); + throw e; + } + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java b/application/src/main/java/org/thingsboard/server/service/entitiy/domain/TbDomainService.java similarity index 61% rename from dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java rename to application/src/main/java/org/thingsboard/server/service/entitiy/domain/TbDomainService.java index eec1bc5f50..4170558e52 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/domain/TbDomainService.java @@ -13,17 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.oauth2; +package org.thingsboard.server.service.entitiy.domain; -import org.springframework.data.jpa.repository.JpaRepository; -import org.thingsboard.server.dao.model.sql.OAuth2DomainEntity; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; import java.util.List; -import java.util.UUID; -public interface OAuth2DomainRepository extends JpaRepository { +public interface TbDomainService { - List findByOauth2ParamsId(UUID oauth2ParamsId); + Domain save(Domain domain, List oAuth2Clients, User user) throws Exception; -} + void delete(Domain domain, User user); +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/mobile/DefaultTbMobileAppService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/mobile/DefaultTbMobileAppService.java new file mode 100644 index 0000000000..6865a837c0 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/mobile/DefaultTbMobileAppService.java @@ -0,0 +1,74 @@ +/** + * 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.service.entitiy.mobile; + +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.mobile.MobileApp; +import org.thingsboard.server.dao.mobile.MobileAppService; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; + +import java.util.List; + +@Service +@AllArgsConstructor +public class DefaultTbMobileAppService extends AbstractTbEntityService implements TbMobileAppService { + + private final MobileAppService mobileAppService; + + @Override + public MobileApp save(MobileApp mobileApp, List oauth2Clients, User user) throws Exception { + ActionType actionType = mobileApp.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + 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(), savedMobileApp, + ActionType.UPDATED_OAUTH2_CLIENTS, user, oauth2Clients.toString()); + } + return savedMobileApp; + } catch (Exception e) { + logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.MOBILE_APP), mobileApp, actionType, user, e); + throw e; + } + } + + @Override + @Transactional + public void delete(MobileApp mobileApp, User user) { + ActionType actionType = ActionType.DELETED; + TenantId tenantId = mobileApp.getTenantId(); + MobileAppId mobileAppId = mobileApp.getId(); + try { + mobileAppService.deleteMobileAppById(tenantId, mobileAppId); + logEntityActionService.logEntityAction(tenantId, mobileAppId, mobileApp, actionType, user, mobileApp.getPkgName()); + } catch (Exception e) { + logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.MOBILE_APP), actionType, user, e, + mobileAppId.toString()); + throw e; + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java b/application/src/main/java/org/thingsboard/server/service/entitiy/mobile/TbMobileAppService.java similarity index 60% rename from dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java rename to application/src/main/java/org/thingsboard/server/service/entitiy/mobile/TbMobileAppService.java index ab1b981dc2..ebcdfc3cd0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/mobile/TbMobileAppService.java @@ -13,16 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.oauth2; +package org.thingsboard.server.service.entitiy.mobile; -import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; -import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.mobile.MobileApp; import java.util.List; -import java.util.UUID; -public interface OAuth2MobileDao extends Dao { +public interface TbMobileAppService { - List findByOAuth2ParamsId(UUID oauth2ParamsId); + MobileApp save(MobileApp mobileApp, List oauth2Clients, User user) throws Exception; + + void delete(MobileApp mobileApp, User user); } diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/oauth2client/DefaultTbOauth2ClientService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/oauth2client/DefaultTbOauth2ClientService.java new file mode 100644 index 0000000000..e24f173c6a --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/oauth2client/DefaultTbOauth2ClientService.java @@ -0,0 +1,63 @@ +/** + * 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.service.entitiy.oauth2client; + +import lombok.AllArgsConstructor; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.audit.ActionType; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.dao.oauth2.OAuth2ClientService; +import org.thingsboard.server.service.entitiy.AbstractTbEntityService; + +@Service +@AllArgsConstructor +public class DefaultTbOauth2ClientService extends AbstractTbEntityService implements TbOauth2ClientService { + + private final OAuth2ClientService oAuth2ClientService; + + @Override + public OAuth2Registration save(OAuth2Registration oAuth2Registration, User user) throws Exception { + ActionType actionType = oAuth2Registration.getId() == null ? ActionType.ADDED : ActionType.UPDATED; + TenantId tenantId = oAuth2Registration.getTenantId(); + try { + OAuth2Registration savedRegistration = checkNotNull(oAuth2ClientService.saveOAuth2Client(tenantId, oAuth2Registration)); + logEntityActionService.logEntityAction(tenantId, savedRegistration.getId(), oAuth2Registration, actionType, user); + return savedRegistration; + } catch (Exception e) { + logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.OAUTH2_CLIENT), oAuth2Registration, actionType, user, e); + throw e; + } + } + + @Override + public void delete(OAuth2Registration oAuth2Registration, User user) { + ActionType actionType = ActionType.DELETED; + TenantId tenantId = oAuth2Registration.getTenantId(); + OAuth2RegistrationId oAuth2RegistrationId = oAuth2Registration.getId(); + try { + oAuth2ClientService.deleteById(tenantId, oAuth2RegistrationId); + logEntityActionService.logEntityAction(tenantId, oAuth2RegistrationId, oAuth2Registration, actionType, user, oAuth2Registration.getName()); + } catch (Exception e) { + logEntityActionService.logEntityAction(tenantId, emptyId(EntityType.OAUTH2_CLIENT), actionType, user, e, + oAuth2RegistrationId.toString()); + throw e; + } + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/oauth2client/TbOauth2ClientService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/oauth2client/TbOauth2ClientService.java new file mode 100644 index 0000000000..de6d45a6c0 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/oauth2client/TbOauth2ClientService.java @@ -0,0 +1,31 @@ +/** + * 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.service.entitiy.oauth2client; + +import org.thingsboard.server.common.data.User; +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; + +import java.util.List; + +public interface TbOauth2ClientService { + + OAuth2Registration save(OAuth2Registration oAuth2Registration, User user) throws Exception; + + void delete(OAuth2Registration oAuth2Registration, User user); + +} diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index 7588726a73..3168e22f6f 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -69,7 +69,7 @@ import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BooleanDataEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; +import org.thingsboard.server.common.data.mobile.MobileApp; import org.thingsboard.server.common.data.page.PageDataIterable; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.query.BooleanFilterPredicate; @@ -100,7 +100,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.notification.NotificationSettingsService; import org.thingsboard.server.dao.notification.NotificationTargetService; -import org.thingsboard.server.dao.oauth2.OAuth2MobileDao; +import org.thingsboard.server.dao.mobile.MobileAppDao; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.settings.AdminSettingsService; @@ -149,7 +149,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { private final DeviceConnectivityConfiguration connectivityConfiguration; private final QueueService queueService; private final JwtSettingsService jwtSettingsService; - private final OAuth2MobileDao oAuth2MobileDao; + private final MobileAppDao oAuth2MobileDao; private final NotificationSettingsService notificationSettingsService; private final NotificationTargetService notificationTargetService; @@ -308,7 +308,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { jwtSettingsService.saveJwtSettings(jwtSettings); } - List mobiles = oAuth2MobileDao.find(TenantId.SYS_TENANT_ID); + List mobiles = oAuth2MobileDao.findByTenantId(TenantId.SYS_TENANT_ID); if (CollectionUtils.isNotEmpty(mobiles)) { mobiles.stream() .filter(config -> !validateKeyLength(config.getAppSecret())) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 09ebb75e29..b6e0d39ec6 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -28,10 +28,11 @@ import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.common.data.security.model.JwtPair; -import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails; import org.thingsboard.server.service.security.model.SecurityUser; @@ -56,7 +57,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS private final JwtTokenFactory tokenFactory; private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; - private final OAuth2Service oAuth2Service; + private final OAuth2ClientService oAuth2ClientService; private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; private final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository; private final SystemSecurityService systemSecurityService; @@ -64,13 +65,13 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS @Autowired public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory, final OAuth2ClientMapperProvider oauth2ClientMapperProvider, - final OAuth2Service oAuth2Service, + final OAuth2ClientService oAuth2ClientService, final OAuth2AuthorizedClientService oAuth2AuthorizedClientService, final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository, final SystemSecurityService systemSecurityService) { this.tokenFactory = tokenFactory; this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; - this.oAuth2Service = oAuth2Service; + this.oAuth2ClientService = oAuth2ClientService; this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; this.httpCookieOAuth2AuthorizationRequestRepository = httpCookieOAuth2AuthorizationRequestRepository; this.systemSecurityService = systemSecurityService; @@ -96,7 +97,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS try { OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; - OAuth2Registration registration = oAuth2Service.findRegistration(UUID.fromString(token.getAuthorizedClientRegistrationId())); + OAuth2Registration registration = oAuth2ClientService.findOAuth2ClientById(TenantId.SYS_TENANT_ID, new OAuth2RegistrationId(UUID.fromString(token.getAuthorizedClientRegistrationId()))); OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient( token.getAuthorizedClientRegistrationId(), token.getPrincipal().getName()); diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index d43663fb97..16a3c4be59 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -33,7 +33,9 @@ public enum Resource { USER(EntityType.USER), WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE), WIDGET_TYPE(EntityType.WIDGET_TYPE), - OAUTH2_CONFIGURATION_INFO(), + OAUTH2_CLIENT(EntityType.OAUTH2_CLIENT), + DOMAIN(EntityType.DOMAIN), + MOBILE_APP(EntityType.MOBILE_APP), OAUTH2_CONFIGURATION_TEMPLATE(), TENANT_PROFILE(EntityType.TENANT_PROFILE), DEVICE_PROFILE(EntityType.DEVICE_PROFILE), diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java index d906938e47..4790a07965 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java @@ -35,7 +35,9 @@ public class SysAdminPermissions extends AbstractPermissions { put(Resource.USER, userPermissionChecker); put(Resource.WIDGETS_BUNDLE, systemEntityPermissionChecker); put(Resource.WIDGET_TYPE, systemEntityPermissionChecker); - put(Resource.OAUTH2_CONFIGURATION_INFO, PermissionChecker.allowAllPermissionChecker); + put(Resource.OAUTH2_CLIENT, PermissionChecker.allowAllPermissionChecker); + put(Resource.MOBILE_APP, PermissionChecker.allowAllPermissionChecker); + put(Resource.DOMAIN, PermissionChecker.allowAllPermissionChecker); put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, PermissionChecker.allowAllPermissionChecker); put(Resource.TENANT_PROFILE, PermissionChecker.allowAllPermissionChecker); put(Resource.TB_RESOURCE, systemEntityPermissionChecker); diff --git a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java index cd95e9504d..ab61140f30 100644 --- a/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java +++ b/application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java @@ -38,7 +38,7 @@ import org.thingsboard.server.common.data.kv.LongDataEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.stats.TbApiUsageStateClient; -import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.domain.DomainService; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; import org.thingsboard.server.queue.discovery.DiscoveryService; @@ -89,7 +89,7 @@ public class DefaultSystemInfoService extends TbApplicationEventListener oAuth2UpdateMsgOpt = edgeImitator.findMessageByType(OAuth2UpdateMsg.class); Assert.assertTrue(oAuth2UpdateMsgOpt.isPresent()); - OAuth2UpdateMsg oAuth2UpdateMsg = oAuth2UpdateMsgOpt.get(); - OAuth2Info oAuth2Info = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); - Assert.assertNotNull(oAuth2Info); - OAuth2Info auth2Info = doGet("/api/oauth2/config", OAuth2Info.class); + OAuth2UpdateMsg oAuth2ProviderUpdateMsg = oAuth2UpdateMsgOpt.get(); + OAuth2Registration oAuth2Registration = JacksonUtil.fromString(oAuth2ProviderUpdateMsg.getEntity(), OAuth2Registration.class, true); + Assert.assertNotNull(oAuth2Registration); + OAuth2Registration auth2Info = doGet("/api/oauth2/config", OAuth2Registration.class); Assert.assertNotNull(auth2Info); - Assert.assertEquals(oAuth2Info, auth2Info); - testAutoGeneratedCodeByProtobuf(oAuth2UpdateMsg); + Assert.assertEquals(oAuth2Registration, auth2Info); + testAutoGeneratedCodeByProtobuf(oAuth2ProviderUpdateMsg); } private void validateSyncCompleted() { diff --git a/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java index c25410b562..f3e5e30457 100644 --- a/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java @@ -15,19 +15,14 @@ */ package org.thingsboard.server.edge; -import com.google.common.collect.Lists; import com.google.protobuf.AbstractMessage; import org.junit.Assert; import org.junit.Test; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.oauth2.MapperType; import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; -import org.thingsboard.server.common.data.oauth2.OAuth2DomainInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2Info; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; -import org.thingsboard.server.common.data.oauth2.OAuth2ParamsInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; -import org.thingsboard.server.common.data.oauth2.SchemeType; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg; @@ -45,59 +40,47 @@ public class OAuth2EdgeTest extends AbstractEdgeTest { // enable oauth edgeImitator.allowIgnoredTypes(); edgeImitator.expectMessageAmount(1); - OAuth2Info oAuth2Info = createDefaultOAuth2Info(); - oAuth2Info = doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class); + OAuth2Registration oAuth2Registration = createDefaultOAuth2Info(); + oAuth2Registration = doPost("/api/oauth2/config", oAuth2Registration, OAuth2Registration.class); Assert.assertTrue(edgeImitator.waitForMessages()); AbstractMessage latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg); - OAuth2UpdateMsg oAuth2UpdateMsg = (OAuth2UpdateMsg) latestMessage; - OAuth2Info result = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); - Assert.assertEquals(oAuth2Info, result); + OAuth2UpdateMsg oAuth2ProviderUpdateMsg = (OAuth2UpdateMsg) latestMessage; + OAuth2Registration result = JacksonUtil.fromString(oAuth2ProviderUpdateMsg.getEntity(), OAuth2Registration.class, true); + Assert.assertEquals(oAuth2Registration, result); // disable oauth support edgeImitator.expectMessageAmount(1); - oAuth2Info.setEnabled(false); - oAuth2Info.setEdgeEnabled(false); - doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class); + doPost("/api/oauth2/config", oAuth2Registration, OAuth2Registration.class); Assert.assertTrue(edgeImitator.waitForMessages()); latestMessage = edgeImitator.getLatestMessage(); Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg); - oAuth2UpdateMsg = (OAuth2UpdateMsg) latestMessage; - result = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); - Assert.assertEquals(oAuth2Info, result); + oAuth2ProviderUpdateMsg = (OAuth2UpdateMsg) latestMessage; + result = JacksonUtil.fromString(oAuth2ProviderUpdateMsg.getEntity(), OAuth2Registration.class, true); + Assert.assertEquals(oAuth2Registration, result); edgeImitator.ignoreType(OAuth2UpdateMsg.class); loginTenantAdmin(); } - private OAuth2Info createDefaultOAuth2Info() { - return new OAuth2Info(true, true, Lists.newArrayList( - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("domain").scheme(SchemeType.MIXED).build() - )) - .mobileInfos(Collections.emptyList()) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo() - )) - .build() - )); + private OAuth2Registration createDefaultOAuth2Info() { + return validRegistrationInfo(); } - private OAuth2RegistrationInfo validRegistrationInfo() { - return OAuth2RegistrationInfo.builder() - .clientId(UUID.randomUUID().toString()) - .clientSecret(UUID.randomUUID().toString()) - .authorizationUri(UUID.randomUUID().toString()) - .accessTokenUri(UUID.randomUUID().toString()) - .scope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString())) - .platforms(Collections.emptyList()) - .userInfoUri(UUID.randomUUID().toString()) - .userNameAttributeName(UUID.randomUUID().toString()) - .jwkSetUri(UUID.randomUUID().toString()) - .clientAuthenticationMethod(UUID.randomUUID().toString()) - .loginButtonLabel(UUID.randomUUID().toString()) - .mapperConfig( + private OAuth2Registration validRegistrationInfo() { + OAuth2Registration oAuth2Registration = new OAuth2Registration(); + oAuth2Registration.setClientId(UUID.randomUUID().toString()); + oAuth2Registration.setClientSecret(UUID.randomUUID().toString()); + oAuth2Registration.setAuthorizationUri(UUID.randomUUID().toString()); + oAuth2Registration.setAccessTokenUri(UUID.randomUUID().toString()); + oAuth2Registration.setScope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString())); + oAuth2Registration.setPlatforms(Collections.emptyList()); + oAuth2Registration.setUserInfoUri(UUID.randomUUID().toString()); + oAuth2Registration.setUserNameAttributeName(UUID.randomUUID().toString()); + oAuth2Registration.setJwkSetUri(UUID.randomUUID().toString()); + oAuth2Registration.setClientAuthenticationMethod(UUID.randomUUID().toString()); + oAuth2Registration.setLoginButtonLabel(UUID.randomUUID().toString()); + oAuth2Registration.setMapperConfig( OAuth2MapperConfig.builder() .type(MapperType.CUSTOM) .custom( @@ -105,9 +88,8 @@ public class OAuth2EdgeTest extends AbstractEdgeTest { .url(UUID.randomUUID().toString()) .build() ) - .build() - ) - .build(); + .build()); + return oAuth2Registration; } } diff --git a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java index 9e4fd65ab4..ac21141b75 100644 --- a/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java +++ b/application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java @@ -49,7 +49,7 @@ import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.notification.NotificationRuleService; import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTemplateService; -import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.oauth2.OAuth2ClientService; import org.thingsboard.server.dao.ota.OtaPackageService; import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.relation.RelationService; @@ -257,7 +257,7 @@ public abstract class BaseEdgeProcessorTest { protected ResourceService resourceService; @MockBean - protected OAuth2Service oAuth2Service; + protected OAuth2ClientService oAuth2ClientService; @MockBean @Lazy diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/domain/DomainService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/domain/DomainService.java new file mode 100644 index 0000000000..84ab33fbce --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/domain/DomainService.java @@ -0,0 +1,44 @@ +/** + * 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.domain; + +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.domain.DomainInfo; +import org.thingsboard.server.common.data.id.DomainId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.entity.EntityDaoService; + +import java.util.List; +import java.util.UUID; + +public interface DomainService extends EntityDaoService { + + Domain saveDomain(TenantId tenantId, Domain domain); + + void deleteDomainById(TenantId tenantId, DomainId domainId); + + Domain findDomainById(TenantId tenantId, DomainId domainId); + + List findDomainInfosByTenantId(TenantId tenantId); + + DomainInfo findDomainInfoById(TenantId tenantId, DomainId domainId); + + boolean isOauth2Enabled(TenantId tenantId); + + void updateOauth2Clients(TenantId tenantId, DomainId domainId, List oAuth2ClientIds); + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppService.java new file mode 100644 index 0000000000..da26f2f41a --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppService.java @@ -0,0 +1,41 @@ +/** + * 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.mobile; + +import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +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.dao.entity.EntityDaoService; + +import java.util.List; + +public interface MobileAppService extends EntityDaoService { + + MobileApp saveMobileApp(TenantId tenantId, MobileApp mobileApp); + + void deleteMobileAppById(TenantId tenantId, MobileAppId mobileAppId); + + MobileApp findMobileAppById(TenantId tenantId, MobileAppId mobileAppId); + + List findMobileAppInfosByTenantId(TenantId tenantId); + + MobileAppInfo findMobileAppInfoById(TenantId tenantId, MobileAppId mobileAppId); + + void updateOauth2Clients(TenantId tenantId, MobileAppId mobileAppId, List oAuth2ClientIds); + +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientService.java similarity index 50% rename from common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java rename to common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientService.java index 11129002e7..dd9921d5d1 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientService.java @@ -15,25 +15,32 @@ */ package org.thingsboard.server.dao.oauth2; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2Info; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; import org.thingsboard.server.common.data.oauth2.PlatformType; +import org.thingsboard.server.dao.entity.EntityDaoService; import java.util.List; import java.util.UUID; -public interface OAuth2Service { +public interface OAuth2ClientService extends EntityDaoService { - List getOAuth2Clients(String domainScheme, String domainName, String pkgName, PlatformType platformType); + List getWebOAuth2Clients(String domainName, PlatformType platformType); - void saveOAuth2Info(OAuth2Info oauth2Info); + List getMobileOAuth2Clients(String pkgName, PlatformType platformType); - OAuth2Info findOAuth2Info(); + List findOauth2ClientInfosByTenantId(TenantId tenantId); - OAuth2Registration findRegistration(UUID id); + List findOauth2ClientsByTenantId(TenantId tenantId); - List findAllRegistrations(); + OAuth2Registration saveOAuth2Client(TenantId tenantId, OAuth2Registration oAuth2Registration); + + OAuth2Registration findOAuth2ClientById(TenantId tenantId, OAuth2RegistrationId providerId); String findAppSecret(UUID registrationId, String pkgName); + + void deleteById(TenantId tenantId, OAuth2RegistrationId oAuth2RegistrationId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index 6748aebb50..b5ce79e20f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -57,7 +57,10 @@ public enum EntityType { NOTIFICATION_REQUEST(31), NOTIFICATION(32), NOTIFICATION_RULE(33), - QUEUE_STATS(34); + QUEUE_STATS(34), + OAUTH2_CLIENT(35), + DOMAIN(36), + MOBILE_APP(37); @Getter private final int protoNumber; // Corresponds to EntityTypeProto diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java b/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java index d3c0889d6b..edcc216d1e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java @@ -57,7 +57,8 @@ public enum ActionType { ADDED_COMMENT(false, TbMsgType.COMMENT_CREATED), UPDATED_COMMENT(false, TbMsgType.COMMENT_UPDATED), DELETED_COMMENT(false, null), - SMS_SENT(false, null); + SMS_SENT(false, null), + UPDATED_OAUTH2_CLIENTS(false, null); @Getter private final boolean isRead; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/domain/Domain.java b/common/data/src/main/java/org/thingsboard/server/common/data/domain/Domain.java new file mode 100644 index 0000000000..ed154e656f --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/domain/Domain.java @@ -0,0 +1,52 @@ +/** + * 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.common.data.domain; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.DomainId; +import org.thingsboard.server.common.data.id.TenantId; + +@EqualsAndHashCode(callSuper = true) +@Data +@ToString +@NoArgsConstructor +public class Domain extends BaseData implements HasTenantId, HasName { + + @Schema(description = "JSON object with Tenant Id") + private TenantId tenantId; + @Schema(description = "Domain name. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) + private String name; + @Schema(description = "Whether OAuth2 settings are enabled or not") + private boolean oauth2Enabled; + @Schema(description = "Whether OAuth2 settings are enabled on Edge or not") + private boolean propagateToEdge; + + public Domain(Domain domain) { + super(domain); + this.tenantId = domain.tenantId; + this.name = domain.name; + this.oauth2Enabled = domain.oauth2Enabled; + this.propagateToEdge = domain.propagateToEdge; + } + +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/domain/DomainInfo.java similarity index 56% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java rename to common/data/src/main/java/org/thingsboard/server/common/data/domain/DomainInfo.java index 053762bdf0..edca39a828 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/domain/DomainInfo.java @@ -13,26 +13,32 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.domain; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; import lombok.ToString; +import org.thingsboard.server.common.data.oauth2.HasOauth2Registrations; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; -@EqualsAndHashCode +import java.util.List; + +@EqualsAndHashCode(callSuper = true) @Data @ToString -@NoArgsConstructor @AllArgsConstructor @Builder @Schema -public class OAuth2MobileInfo { - @Schema(description = "Application package name. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) - private String pkgName; - @Schema(description = "Application secret. The length must be at least 16 characters", requiredMode = Schema.RequiredMode.REQUIRED) - private String appSecret; +public class DomainInfo extends Domain implements HasOauth2Registrations { + + @Schema(description = "List of available oauth2 client registration") + private List oauth2RegistrationInfos; + + public DomainInfo(Domain domain, List oauth2RegistrationInfos) { + super(domain); + this.oauth2RegistrationInfos = oauth2RegistrationInfos; + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2MobileRepository.java b/common/data/src/main/java/org/thingsboard/server/common/data/domain/DomainOauth2Registration.java similarity index 59% rename from dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2MobileRepository.java rename to common/data/src/main/java/org/thingsboard/server/common/data/domain/DomainOauth2Registration.java index 2456494a74..13b3b15093 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2MobileRepository.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/domain/DomainOauth2Registration.java @@ -13,16 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.oauth2; +package org.thingsboard.server.common.data.domain; -import org.springframework.data.jpa.repository.JpaRepository; -import org.thingsboard.server.dao.model.sql.OAuth2MobileEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.id.DomainId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; -import java.util.List; -import java.util.UUID; +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DomainOauth2Registration { -public interface OAuth2MobileRepository extends JpaRepository { - - List findByOauth2ParamsId(UUID oauth2ParamsId); + private DomainId domainId; + private OAuth2RegistrationId oAuth2RegistrationId; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java index 44a3ed8efe..141966c446 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java @@ -45,7 +45,7 @@ public enum EdgeEventType { NOTIFICATION_TARGET (true, EntityType.NOTIFICATION_TARGET), NOTIFICATION_TEMPLATE (true, EntityType.NOTIFICATION_TEMPLATE), TB_RESOURCE(true, EntityType.TB_RESOURCE), - OAUTH2(true, null); + OAUTH2_CLIENT(true, EntityType.OAUTH2_CLIENT); private final boolean allEdgesRelated; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/DomainId.java similarity index 68% rename from common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/DomainId.java index 14899d9dc1..c3d4aab159 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/DomainId.java @@ -17,17 +17,23 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class OAuth2DomainId extends UUIDBased { +public class DomainId extends UUIDBased implements EntityId { @JsonCreator - public OAuth2DomainId(@JsonProperty("id") UUID id) { + public DomainId(@JsonProperty("id") UUID id) { super(id); } - public static OAuth2DomainId fromString(String oauth2DomainId) { - return new OAuth2DomainId(UUID.fromString(oauth2DomainId)); + public static DomainId fromString(String oauth2DomainId) { + return new DomainId(UUID.fromString(oauth2DomainId)); + } + + @Override + public EntityType getEntityType() { + return EntityType.DOMAIN; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index ad2df69349..48e841fb46 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -105,6 +105,12 @@ public class EntityIdFactory { return new NotificationId(uuid); case QUEUE_STATS: return new QueueStatsId(uuid); + case OAUTH2_CLIENT: + return new OAuth2RegistrationId(uuid); + case MOBILE_APP: + return new MobileAppId(uuid); + case DOMAIN: + return new DomainId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/MobileAppId.java similarity index 68% rename from common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/MobileAppId.java index 6b280dfc60..114e4351f3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/MobileAppId.java @@ -17,17 +17,23 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class OAuth2MobileId extends UUIDBased { +public class MobileAppId extends UUIDBased implements EntityId{ @JsonCreator - public OAuth2MobileId(@JsonProperty("id") UUID id) { + public MobileAppId(@JsonProperty("id") UUID id) { super(id); } - public static OAuth2MobileId fromString(String oauth2MobileId) { - return new OAuth2MobileId(UUID.fromString(oauth2MobileId)); + public static MobileAppId fromString(String mobileAppId) { + return new MobileAppId(UUID.fromString(mobileAppId)); + } + + @Override + public EntityType getEntityType() { + return EntityType.MOBILE_APP; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java index 28156417db..5b363d6cfd 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java @@ -17,10 +17,11 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class OAuth2RegistrationId extends UUIDBased { +public class OAuth2RegistrationId extends UUIDBased implements EntityId { @JsonCreator public OAuth2RegistrationId(@JsonProperty("id") UUID id) { @@ -30,4 +31,9 @@ public class OAuth2RegistrationId extends UUIDBased { public static OAuth2RegistrationId fromString(String oauth2RegistrationId) { return new OAuth2RegistrationId(UUID.fromString(oauth2RegistrationId)); } + + @Override + public EntityType getEntityType() { + return EntityType.OAUTH2_CLIENT; + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileApp.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileApp.java new file mode 100644 index 0000000000..576c0920c9 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileApp.java @@ -0,0 +1,59 @@ +/** + * 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.common.data.mobile; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.thingsboard.server.common.data.BaseData; +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; + +@EqualsAndHashCode(callSuper = true) +@Data +@ToString +@NoArgsConstructor +public class MobileApp extends BaseData implements HasTenantId, HasName { + + @Schema(description = "JSON object with Tenant Id") + private TenantId tenantId; + @Schema(description = "Application package name. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) + private String pkgName; + @Schema(description = "Application secret. The length must be at least 16 characters", requiredMode = Schema.RequiredMode.REQUIRED) + private String appSecret; + @Schema(description = "Whether OAuth2 settings are enabled or not") + private boolean oauth2Enabled; + + public MobileApp(MobileApp mobile) { + super(mobile); + this.tenantId = mobile.tenantId; + this.pkgName = mobile.pkgName; + this.appSecret = mobile.appSecret; + this.oauth2Enabled = mobile.oauth2Enabled; + } + + @Override + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @Schema(description = "Mobile app package name", example = "my.mobile.app", accessMode = Schema.AccessMode.READ_ONLY) + public String getName() { + return pkgName; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppInfo.java similarity index 58% rename from common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java rename to common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppInfo.java index 0a2ace050c..519fd7bf23 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppInfo.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.common.data.oauth2; +package org.thingsboard.server.common.data.mobile; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; @@ -22,21 +22,26 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; +import org.thingsboard.server.common.data.oauth2.HasOauth2Registrations; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; import java.util.List; -@EqualsAndHashCode +@EqualsAndHashCode(callSuper = true) @Data @ToString -@Builder(toBuilder = true) @NoArgsConstructor @AllArgsConstructor +@Builder @Schema -public class OAuth2Info { - @Schema(description = "Whether OAuth2 settings are enabled or not") - private boolean enabled; - @Schema(description = "Whether OAuth2 settings are enabled on Edge or not") - private boolean edgeEnabled; - @Schema(description = "List of configured OAuth2 clients. Cannot contain null values", requiredMode = Schema.RequiredMode.REQUIRED) - private List oauth2ParamsInfos; +public class MobileAppInfo extends MobileApp implements HasOauth2Registrations { + + @Schema(description = "List of available oauth2 client registrations") + private List oauth2RegistrationInfos; + + public MobileAppInfo(MobileApp mobileApp, List oauth2RegistrationInfos) { + super(mobileApp); + this.oauth2RegistrationInfos = oauth2RegistrationInfos; + } + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppOauth2Registration.java b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppOauth2Registration.java new file mode 100644 index 0000000000..b309aafe32 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppOauth2Registration.java @@ -0,0 +1,32 @@ +/** + * 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.common.data.mobile; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MobileAppOauth2Registration { + + private MobileAppId mobileAppId; + private OAuth2RegistrationId oAuth2RegistrationId; + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/HasOauth2Registrations.java similarity index 68% rename from dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java rename to common/data/src/main/java/org/thingsboard/server/common/data/oauth2/HasOauth2Registrations.java index 52bde2cd8c..51524f458d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/HasOauth2Registrations.java @@ -13,16 +13,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.oauth2; +package org.thingsboard.server.common.data.oauth2; -import org.thingsboard.server.common.data.oauth2.OAuth2Domain; -import org.thingsboard.server.dao.Dao; +import org.thingsboard.server.common.data.EntityInfo; + +import java.io.Serializable; import java.util.List; -import java.util.UUID; -public interface OAuth2DomainDao extends Dao { +public interface HasOauth2Registrations extends Serializable { - List findByOAuth2ParamsId(UUID oauth2ParamsId); + List getOauth2RegistrationInfos(); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java deleted file mode 100644 index 6fa0475361..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java +++ /dev/null @@ -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.common.data.oauth2; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.ToString; -import org.thingsboard.server.common.data.BaseData; -import org.thingsboard.server.common.data.id.OAuth2DomainId; -import org.thingsboard.server.common.data.id.OAuth2ParamsId; - -@EqualsAndHashCode(callSuper = true) -@Data -@ToString -@NoArgsConstructor -public class OAuth2Domain extends BaseData { - - private OAuth2ParamsId oauth2ParamsId; - private String domainName; - private SchemeType domainScheme; - - public OAuth2Domain(OAuth2Domain domain) { - super(domain); - this.oauth2ParamsId = domain.oauth2ParamsId; - this.domainName = domain.domainName; - this.domainScheme = domain.domainScheme; - } -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java deleted file mode 100644 index dc70213e95..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java +++ /dev/null @@ -1,38 +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.common.data.oauth2; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.ToString; - -@EqualsAndHashCode -@Data -@ToString -@NoArgsConstructor -@AllArgsConstructor -@Builder -@Schema -public class OAuth2DomainInfo { - @Schema(description = "Domain scheme. Mixed scheme means than both HTTP and HTTPS are going to be used", requiredMode = Schema.RequiredMode.REQUIRED) - private SchemeType scheme; - @Schema(description = "Domain name. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) - private String name; -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java deleted file mode 100644 index fd9bbe527f..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java +++ /dev/null @@ -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.common.data.oauth2; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.ToString; -import org.thingsboard.server.common.data.BaseData; -import org.thingsboard.server.common.data.id.OAuth2MobileId; -import org.thingsboard.server.common.data.id.OAuth2ParamsId; - -@EqualsAndHashCode(callSuper = true) -@Data -@ToString -@NoArgsConstructor -public class OAuth2Mobile extends BaseData { - - private OAuth2ParamsId oauth2ParamsId; - private String pkgName; - private String appSecret; - - public OAuth2Mobile(OAuth2Mobile mobile) { - super(mobile); - this.oauth2ParamsId = mobile.oauth2ParamsId; - this.pkgName = mobile.pkgName; - this.appSecret = mobile.appSecret; - } -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java deleted file mode 100644 index c1b6124b8f..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java +++ /dev/null @@ -1,46 +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.common.data.oauth2; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.ToString; - -import java.util.List; - -@EqualsAndHashCode -@Data -@ToString -@Builder(toBuilder = true) -@NoArgsConstructor -@AllArgsConstructor -@Schema -public class OAuth2ParamsInfo { - - @Schema(description = "List of configured domains where OAuth2 platform will redirect a user after successful " + - "authentication. Cannot be empty. There have to be only one domain with specific name with scheme type 'MIXED'. " + - "Configured domains with the same name must have different scheme types", requiredMode = Schema.RequiredMode.REQUIRED) - private List domainInfos; - @Schema(description = "Mobile applications settings. Application package name must be unique within the list", requiredMode = Schema.RequiredMode.REQUIRED) - private List mobileInfos; - @Schema(description = "List of OAuth2 provider settings. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) - private List clientRegistrations; - -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java index 76c1953387..5a4ec293be 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java @@ -16,14 +16,17 @@ package org.thingsboard.server.common.data.oauth2; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.NoArgsConstructor; import lombok.ToString; import org.thingsboard.server.common.data.BaseDataWithAdditionalInfo; import org.thingsboard.server.common.data.HasName; -import org.thingsboard.server.common.data.id.OAuth2ParamsId; +import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; import java.util.List; @@ -31,26 +34,44 @@ import java.util.List; @Data @ToString(exclude = {"clientSecret"}) @NoArgsConstructor -public class OAuth2Registration extends BaseDataWithAdditionalInfo implements HasName { +public class OAuth2Registration extends BaseDataWithAdditionalInfo implements HasName, HasTenantId { - private OAuth2ParamsId oauth2ParamsId; + @Schema(description = "JSON object with Tenant Id") + private TenantId tenantId; + @Schema(description = "Oauth2 client title") + private String title; + @Schema(description = "Config for mapping OAuth2 log in response to platform entities", requiredMode = Schema.RequiredMode.REQUIRED) private OAuth2MapperConfig mapperConfig; + @Schema(description = "OAuth2 client ID. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) private String clientId; + @Schema(description = "OAuth2 client secret. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) private String clientSecret; + @Schema(description = "Authorization URI of the OAuth2 provider. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) private String authorizationUri; + @Schema(description = "Access token URI of the OAuth2 provider. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) private String accessTokenUri; + @Schema(description = "OAuth scopes that will be requested from OAuth2 platform. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) private List scope; + @Schema(description = "User info URI of the OAuth2 provider") private String userInfoUri; + @Schema(description = "Name of the username attribute in OAuth2 provider response. Cannot be empty") private String userNameAttributeName; + @Schema(description = "JSON Web Key URI of the OAuth2 provider") private String jwkSetUri; + @Schema(description = "Client authentication method to use: 'BASIC' or 'POST'. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) private String clientAuthenticationMethod; + @Schema(description = "OAuth2 provider label. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) private String loginButtonLabel; + @Schema(description = "Log in button icon for OAuth2 provider") private String loginButtonIcon; + @Schema(description = "List of platforms for which usage of the OAuth2 client is allowed (empty for all allowed)") private List platforms; + @Schema(description = "Additional info of OAuth2 client (e.g. providerName)", requiredMode = Schema.RequiredMode.REQUIRED) + private JsonNode additionalInfo; public OAuth2Registration(OAuth2Registration registration) { super(registration); - this.oauth2ParamsId = registration.oauth2ParamsId; + this.tenantId = registration.tenantId; this.mapperConfig = registration.mapperConfig; this.clientId = registration.clientId; this.clientSecret = registration.clientSecret; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java index 36bae12b23..4e360993c1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java @@ -15,51 +15,35 @@ */ package org.thingsboard.server.common.data.oauth2; -import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Builder; import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.ToString; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; import java.util.List; -@EqualsAndHashCode @Data -@ToString(exclude = {"clientSecret"}) -@NoArgsConstructor -@AllArgsConstructor -@Builder @Schema -public class OAuth2RegistrationInfo { - @Schema(description = "Config for mapping OAuth2 log in response to platform entities", requiredMode = Schema.RequiredMode.REQUIRED) - private OAuth2MapperConfig mapperConfig; - @Schema(description = "OAuth2 client ID. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) - private String clientId; - @Schema(description = "OAuth2 client secret. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) - private String clientSecret; - @Schema(description = "Authorization URI of the OAuth2 provider. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) - private String authorizationUri; - @Schema(description = "Access token URI of the OAuth2 provider. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) - private String accessTokenUri; - @Schema(description = "OAuth scopes that will be requested from OAuth2 platform. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) - private List scope; - @Schema(description = "User info URI of the OAuth2 provider") - private String userInfoUri; - @Schema(description = "Name of the username attribute in OAuth2 provider response. Cannot be empty") - private String userNameAttributeName; - @Schema(description = "JSON Web Key URI of the OAuth2 provider") - private String jwkSetUri; - @Schema(description = "Client authentication method to use: 'BASIC' or 'POST'. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) - private String clientAuthenticationMethod; - @Schema(description = "OAuth2 provider label. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) - private String loginButtonLabel; - @Schema(description = "Log in button icon for OAuth2 provider") - private String loginButtonIcon; +@EqualsAndHashCode(callSuper = true) +public class OAuth2RegistrationInfo extends BaseData { + @Schema(description = "Oauth2 client registration title (e.g. Google)") + private String title; @Schema(description = "List of platforms for which usage of the OAuth2 client is allowed (empty for all allowed)") private List platforms; - @Schema(description = "Additional info of OAuth2 client (e.g. providerName)", requiredMode = Schema.RequiredMode.REQUIRED) - private JsonNode additionalInfo; + + public OAuth2RegistrationInfo() { + super(); + } + + public OAuth2RegistrationInfo(OAuth2RegistrationId id) { + super(id); + } + + public OAuth2RegistrationInfo(OAuth2RegistrationId id, String title, List platforms) { + super(id); + this.title = title; + this.platforms = platforms; + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/domain/DomainDao.java b/dao/src/main/java/org/thingsboard/server/dao/domain/DomainDao.java new file mode 100644 index 0000000000..898598b38f --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/domain/DomainDao.java @@ -0,0 +1,39 @@ +/** + * 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.domain; + +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.domain.DomainOauth2Registration; +import org.thingsboard.server.common.data.id.DomainId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.Dao; + +import java.util.List; + +public interface DomainDao extends Dao { + + List findByTenantId(TenantId tenantId); + + int countDomainByTenantIdAndOauth2Enabled(TenantId tenantId, boolean oauth2Enabled); + + List findOauth2ClientsByDomainId(TenantId tenantId, DomainId domainId); + + void saveOauth2Clients(DomainOauth2Registration domainOauth2Registration); + + void removeOauth2Clients(DomainId domainId, OAuth2RegistrationId oAuth2RegistrationId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java new file mode 100644 index 0000000000..e260d5b154 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java @@ -0,0 +1,167 @@ +/** + * 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.domain; + +import lombok.extern.slf4j.Slf4j; +import org.hibernate.exception.ConstraintViolationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.domain.DomainInfo; +import org.thingsboard.server.common.data.domain.DomainOauth2Registration; +import org.thingsboard.server.common.data.id.DomainId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; +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.OAuth2RegistrationDao; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.service.Validator; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +import static org.thingsboard.server.dao.service.Validator.validateIds; + +@Slf4j +@Service +public class DomainServiceImpl extends AbstractEntityService implements DomainService { + + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + public static final String INCORRECT_DOMAIN_ID = "Incorrect domainId "; + + @Autowired + private OAuth2RegistrationDao oauth2RegistrationDao; + @Autowired + private DomainDao domainDao; + @Autowired + private DataValidator 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()); + return savedDomain; + } catch (Exception t) { + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("domain_unq_key")) { + throw new DataValidationException("Domain with such name and scheme already exists!"); + } else { + throw t; + } + } + } + + @Override + public void updateOauth2Clients(TenantId tenantId, DomainId domainId, List oAuth2ClientIds) { + log.trace("Executing addOauth2Clients, domainId [{}], oAuth2ClientIds [{}]", domainId, oAuth2ClientIds); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(domainId, id -> INCORRECT_DOMAIN_ID + id); + Validator.checkNotNull(oAuth2ClientIds, "Incorrect oAuth2ClientIds " + oAuth2ClientIds); + if (!oAuth2ClientIds.isEmpty()) { + validateIds(oAuth2ClientIds, ids -> "Incorrect oAuth2ClientIds " + ids); + } + List oauth2Clients = new ArrayList<>(); + for (OAuth2RegistrationId oAuth2RegistrationId: oAuth2ClientIds) { + oauth2Clients.add(new DomainOauth2Registration(domainId, oAuth2RegistrationId)); + } + List existingClients = domainDao.findOauth2ClientsByDomainId(tenantId, domainId); + List toRemove = existingClients.stream() + .map(DomainOauth2Registration::getOAuth2RegistrationId) + .filter(clientId -> oAuth2ClientIds.stream().noneMatch(oauth2ClientId -> + oauth2ClientId.equals(clientId))).toList(); + for (OAuth2RegistrationId clientId : toRemove) { + domainDao.removeOauth2Clients(domainId, clientId); + } + for (DomainOauth2Registration domainOauth2Registration : oauth2Clients) { + domainDao.saveOauth2Clients(domainOauth2Registration); + } + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId) + .entityId(domainId).created(false).build()); + } + + @Override + public void deleteDomainById(TenantId tenantId, DomainId domainId) { + log.trace("Executing deleteDomain [{}]", domainId.getId()); + domainDao.removeById(tenantId, domainId.getId()); + eventPublisher.publishEvent(DeleteEntityEvent.builder().tenantId(tenantId).entityId(domainId).build()); + } + + @Override + public Domain findDomainById(TenantId tenantId, DomainId domainId) { + log.trace("Executing findDomainInfo [{}] [{}]", tenantId, domainId); + return domainDao.findById(tenantId, domainId.getId()); + } + + @Override + public List findDomainInfosByTenantId(TenantId tenantId) { + log.trace("Executing findDomainInfo [{}]", tenantId); + List domains = domainDao.findByTenantId(tenantId); + List domainInfos = new ArrayList<>(); + domains.stream().sorted(Comparator.comparing(BaseData::getUuidId)).forEach(domain -> { + domainInfos.add(new DomainInfo(domain, oauth2RegistrationDao.findInfosByDomainId(domain.getUuidId()))); + }); + return domainInfos; + } + + @Override + public DomainInfo findDomainInfoById(TenantId tenantId, DomainId domainId) { + log.trace("Executing findDomainInfoById [{}] [{}]", tenantId, domainId); + Domain domain = domainDao.findById(tenantId, domainId.getId()); + if (domain == null) { + return null; + } + return new DomainInfo(domain, oauth2RegistrationDao.findInfosByDomainId(domain.getUuidId())); + } + + @Override + public boolean isOauth2Enabled(TenantId tenantId) { + log.trace("Executing isOauth2Enabled [{}] ", tenantId); + return domainDao.countDomainByTenantIdAndOauth2Enabled(tenantId, true) > 0; + } + + @Override + public Optional> findEntity(TenantId tenantId, EntityId entityId) { + return Optional.ofNullable(findDomainById(tenantId, new DomainId(entityId.getId()))); + } + + @Override + public EntityType getEntityType() { + return EntityType.DOMAIN; + } + + @Override + @Transactional + public void deleteEntity(TenantId tenantId, EntityId id, boolean force) { + Domain domain = domainDao.findById(tenantId, id.getId()); + if (domain == null) { + return; + } + deleteDomainById(tenantId, domain.getId()); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppDao.java b/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppDao.java new file mode 100644 index 0000000000..caa77b5f7a --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppDao.java @@ -0,0 +1,36 @@ +/** + * 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.mobile; + +import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.mobile.MobileApp; +import org.thingsboard.server.common.data.mobile.MobileAppOauth2Registration; +import org.thingsboard.server.dao.Dao; + +import java.util.List; + +public interface MobileAppDao extends Dao { + + List findByTenantId(TenantId tenantId); + + List findOauth2ClientsByMobileAppId(TenantId tenantId, MobileAppId mobileAppId); + + void saveOauth2Clients(MobileAppOauth2Registration mobileAppOauth2Registration); + + void removeOauth2Clients(MobileAppId mobileAppId, OAuth2RegistrationId oAuth2RegistrationId); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java new file mode 100644 index 0000000000..9f049518e5 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java @@ -0,0 +1,163 @@ +/** + * 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.mobile; + +import lombok.extern.slf4j.Slf4j; +import org.hibernate.exception.ConstraintViolationException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +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.MobileAppOauth2Registration; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; +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.OAuth2RegistrationDao; +import org.thingsboard.server.dao.service.DataValidator; +import org.thingsboard.server.dao.service.Validator; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static org.thingsboard.server.dao.service.Validator.validateIds; + +@Slf4j +@Service +public class MobileAppServiceImpl extends AbstractEntityService implements MobileAppService { + + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + public static final String INCORRECT_MOBILE_APP_ID = "Incorrect mobileApppId "; + + @Autowired + private OAuth2RegistrationDao oauth2RegistrationDao; + @Autowired + private MobileAppDao mobileAppDao; + @Autowired + private DataValidator 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()); + return savedMobileApp; + } catch (Exception t) { + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("mobile_app_unq_key")) { + throw new DataValidationException("Mobile app with such package already exists!"); + } else { + throw t; + } + } + } + + @Override + public void deleteMobileAppById(TenantId tenantId, MobileAppId mobileAppId) { + 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) { + log.trace("Executing findMobileAppById [{}] [{}]", tenantId, mobileAppId); + return mobileAppDao.findById(tenantId, mobileAppId.getId()); + } + + @Override + public List findMobileAppInfosByTenantId(TenantId tenantId) { + log.trace("Executing findMobileAppInfosByTenantId [{}]", tenantId); + List mobileApps = mobileAppDao.findByTenantId(tenantId); + List mobileAppInfos = new ArrayList<>(); + mobileApps.stream().sorted(Comparator.comparing(BaseData::getUuidId)).forEach(mobileApp -> { + mobileAppInfos.add(new MobileAppInfo(mobileApp, oauth2RegistrationDao.findInfosByMobileAppId(mobileApp.getUuidId()))); + }); + return mobileAppInfos; + } + + @Override + public MobileAppInfo findMobileAppInfoById(TenantId tenantId, MobileAppId mobileAppId) { + log.trace("Executing findMobileAppInfoById [{}] [{}]", tenantId, mobileAppId); + MobileApp mobileApp = mobileAppDao.findById(tenantId, mobileAppId.getId()); + if (mobileApp == null) { + return null; + } + return new MobileAppInfo(mobileApp, oauth2RegistrationDao.findInfosByMobileAppId(mobileApp.getUuidId())); + } + + @Override + public void updateOauth2Clients(TenantId tenantId, MobileAppId mobileAppId, List oAuth2ClientIds) { + log.trace("Executing updateOauth2Clients, mobileAppId [{}], oAuth2ClientIds [{}]", mobileAppId, oAuth2ClientIds); + Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id); + Validator.validateId(mobileAppId, id -> INCORRECT_MOBILE_APP_ID + id); + Validator.checkNotNull(oAuth2ClientIds, "Incorrect oAuth2ClientIds " + oAuth2ClientIds); + if (!oAuth2ClientIds.isEmpty()) { + validateIds(oAuth2ClientIds, ids -> "Incorrect oAuth2ClientIds " + ids); + } + List oauth2Clients = new ArrayList<>(); + for (OAuth2RegistrationId oAuth2RegistrationId: oAuth2ClientIds) { + oauth2Clients.add(new MobileAppOauth2Registration(mobileAppId, oAuth2RegistrationId)); + } + List existingClients = mobileAppDao.findOauth2ClientsByMobileAppId(tenantId, mobileAppId); + List toRemove = existingClients.stream() + .map(MobileAppOauth2Registration::getOAuth2RegistrationId) + .filter(clientId -> oAuth2ClientIds.stream().noneMatch(oauth2ClientId -> + oauth2ClientId.equals(clientId))).toList(); + for (OAuth2RegistrationId clientId : toRemove) { + mobileAppDao.removeOauth2Clients(mobileAppId, clientId); + } + for (MobileAppOauth2Registration mobileAppOauth2Registration : oauth2Clients) { + mobileAppDao.saveOauth2Clients(mobileAppOauth2Registration); + } + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId) + .entityId(mobileAppId).created(false).build()); + } + + @Override + public Optional> findEntity(TenantId tenantId, EntityId entityId) { + return Optional.ofNullable(findMobileAppById(tenantId, new MobileAppId(entityId.getId()))); + } + + @Override + public EntityType getEntityType() { + return EntityType.MOBILE_APP; + } + + @Override + @Transactional + public void deleteEntity(TenantId tenantId, EntityId id, boolean force) { + MobileApp mobileApp = mobileAppDao.findById(tenantId, id.getId()); + if (mobileApp == null) { + return; + } + deleteMobileAppById(tenantId, mobileApp.getId()); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index fb77ad6987..0b540066a7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -424,24 +424,37 @@ public class ModelConstants { public static final String RULE_NODE_STATE_DATA_PROPERTY = "state_data"; /** - * OAuth2 client registration constants. + * Domain constants. */ - public static final String OAUTH2_PARAMS_TABLE_NAME = "oauth2_params"; - public static final String OAUTH2_PARAMS_ENABLED_PROPERTY = "enabled"; - public static final String OAUTH2_PARAMS_EDGE_ENABLED_PROPERTY = "edge_enabled"; - public static final String OAUTH2_PARAMS_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; + public static final String DOMAIN_TABLE_NAME = "domain"; + public static final String DOMAIN_DOMAIN_NAME_PROPERTY = "domain_name"; + public static final String DOMAIN_OAUTH2_ENABLED_PROPERTY = "oauth2_enabled"; + public static final String DOMAIN_PROPAGATE_TO_EDGE_PROPERTY = "propagate_to_edge"; - public static final String OAUTH2_REGISTRATION_TABLE_NAME = "oauth2_registration"; - public static final String OAUTH2_DOMAIN_TABLE_NAME = "oauth2_domain"; - public static final String OAUTH2_MOBILE_TABLE_NAME = "oauth2_mobile"; - public static final String OAUTH2_PARAMS_ID_PROPERTY = "oauth2_params_id"; - public static final String OAUTH2_PKG_NAME_PROPERTY = "pkg_name"; - public static final String OAUTH2_APP_SECRET_PROPERTY = "app_secret"; + public static final String DOMAIN_OAUTH2_REGISTRATION_TABLE_NAME = "domain_oauth2_registration"; + public static final String DOMAIN_OAUTH2_PROVIDER_PROVIDER_ID_PROPERTY = "oauth2_registration_id"; + public static final String DOMAIN_OAUTH2_PROVIDER_DOMAIN_ID_PROPERTY = "domain_id"; + + /** + * Mobile application constants. + */ + public static final String MOBILE_APP_TABLE_NAME = "mobile_app"; + public static final String MOBILE_APP_PKG_NAME_PROPERTY = "pkg_name"; + public static final String MOBILE_APP_APP_SECRET_PROPERTY = "app_secret"; + public static final String MOBILE_APP_OAUTH2_ENABLED_PROPERTY = "oauth2_enabled"; + + public static final String MOBILE_APP_OAUTH2_REGISTRATION_TABLE_NAME = "mobile_app_oauth2_registration"; + public static final String MOBILE_APP_OAUTH2_REGISTRATION_REGISTRATION_ID_PROPERTY = "oauth2_registration_id"; + public static final String MOBILE_APP_OAUTH2_REGISTRATION_MOBILE_APP_ID_PROPERTY = "mobile_app_id"; + + /** + * OAuth2 client registration constants. + */ + public static final String OAUTH2_REGISTRATION_TABLE_NAME = "oauth2_registration"; public static final String OAUTH2_CLIENT_REGISTRATION_TEMPLATE_TABLE_NAME = "oauth2_client_registration_template"; public static final String OAUTH2_TEMPLATE_PROVIDER_ID_PROPERTY = "provider_id"; - public static final String OAUTH2_DOMAIN_NAME_PROPERTY = "domain_name"; - public static final String OAUTH2_DOMAIN_SCHEME_PROPERTY = "domain_scheme"; + public static final String OAUTH2_CLIENT_TITLE_PROPERTY = "title"; public static final String OAUTH2_CLIENT_ID_PROPERTY = "client_id"; public static final String OAUTH2_CLIENT_SECRET_PROPERTY = "client_secret"; public static final String OAUTH2_AUTHORIZATION_URI_PROPERTY = "authorization_uri"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainEntity.java new file mode 100644 index 0000000000..6138e8f114 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainEntity.java @@ -0,0 +1,81 @@ +/** + * 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.model.sql; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.id.DomainId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; + +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_COLUMN; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@Table(name = ModelConstants.DOMAIN_TABLE_NAME) +public class DomainEntity extends BaseSqlEntity { + + @Column(name = TENANT_ID_COLUMN) + private UUID tenantId; + + @Column(name = ModelConstants.DOMAIN_DOMAIN_NAME_PROPERTY) + private String name; + + @Column(name = ModelConstants.DOMAIN_OAUTH2_ENABLED_PROPERTY) + private Boolean oauth2Enabled; + + @Column(name = ModelConstants.DOMAIN_PROPAGATE_TO_EDGE_PROPERTY) + private Boolean propagateToEdge; + + public DomainEntity(Domain domain) { + if (domain.getId() != null) { + this.setUuid(domain.getId().getId()); + } + if (domain.getTenantId() != null) { + this.tenantId = domain.getTenantId().getId(); + } + this.setCreatedTime(domain.getCreatedTime()); + this.name = domain.getName(); + this.oauth2Enabled = domain.isOauth2Enabled(); + this.propagateToEdge = domain.isPropagateToEdge(); + } + + public DomainEntity() { + super(); + } + + @Override + public Domain toData() { + Domain domain = new Domain(); + domain.setId(new DomainId(id)); + if (tenantId != null) { + domain.setTenantId(TenantId.fromUUID(tenantId)); + } + domain.setCreatedTime(createdTime); + domain.setName(name); + domain.setOauth2Enabled(oauth2Enabled); + domain.setPropagateToEdge(propagateToEdge); + return domain; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainOauth2RegistrationCompositeKey.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainOauth2RegistrationCompositeKey.java new file mode 100644 index 0000000000..33b6047808 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainOauth2RegistrationCompositeKey.java @@ -0,0 +1,37 @@ +/** + * 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.model.sql; + +import jakarta.persistence.Transient; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.UUID; + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class DomainOauth2RegistrationCompositeKey implements Serializable { + + @Transient + private static final long serialVersionUID = -245388185894468455L; + + private UUID domainId; + private UUID oauth2RegistrationId; + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainOauth2RegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainOauth2RegistrationEntity.java new file mode 100644 index 0000000000..0699c3e7a6 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainOauth2RegistrationEntity.java @@ -0,0 +1,66 @@ +/** + * 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.model.sql; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.Table; +import lombok.Data; +import org.thingsboard.server.common.data.domain.DomainOauth2Registration; +import org.thingsboard.server.common.data.id.DomainId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.model.ToData; + +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.DOMAIN_OAUTH2_PROVIDER_DOMAIN_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.DOMAIN_OAUTH2_REGISTRATION_TABLE_NAME; + +@Data +@Entity +@Table(name = DOMAIN_OAUTH2_REGISTRATION_TABLE_NAME) +@IdClass(DomainOauth2RegistrationCompositeKey.class) +public final class DomainOauth2RegistrationEntity implements ToData { + + @Id + @Column(name = DOMAIN_OAUTH2_PROVIDER_DOMAIN_ID_PROPERTY, columnDefinition = "uuid") + private UUID domainId; + + @Id + @Column(name = ModelConstants.DOMAIN_OAUTH2_PROVIDER_PROVIDER_ID_PROPERTY, columnDefinition = "uuid") + private UUID oauth2RegistrationId; + + + public DomainOauth2RegistrationEntity() { + super(); + } + + public DomainOauth2RegistrationEntity(DomainOauth2Registration domainOauth2Registration) { + domainId = domainOauth2Registration.getDomainId().getId(); + oauth2RegistrationId = domainOauth2Registration.getOAuth2RegistrationId().getId(); + } + + @Override + public DomainOauth2Registration toData() { + DomainOauth2Registration result = new DomainOauth2Registration(); + result.setDomainId(new DomainId(domainId)); + result.setOAuth2RegistrationId(new OAuth2RegistrationId(oauth2RegistrationId)); + return result; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppEntity.java similarity index 56% rename from dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java rename to dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppEntity.java index 78ec508ad0..e61a32e2b3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppEntity.java @@ -17,56 +17,66 @@ package org.thingsboard.server.dao.model.sql; import lombok.Data; import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.id.OAuth2MobileId; -import org.thingsboard.server.common.data.id.OAuth2ParamsId; -import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; +import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.mobile.MobileApp; import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.ModelConstants; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; + import java.util.UUID; +import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_COLUMN; + @Data @EqualsAndHashCode(callSuper = true) @Entity -@Table(name = ModelConstants.OAUTH2_MOBILE_TABLE_NAME) -public class OAuth2MobileEntity extends BaseSqlEntity { +@Table(name = ModelConstants.MOBILE_APP_TABLE_NAME) +public class MobileAppEntity extends BaseSqlEntity { - @Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY) - private UUID oauth2ParamsId; + @Column(name = TENANT_ID_COLUMN) + private UUID tenantId; - @Column(name = ModelConstants.OAUTH2_PKG_NAME_PROPERTY) + @Column(name = ModelConstants.MOBILE_APP_PKG_NAME_PROPERTY) private String pkgName; - @Column(name = ModelConstants.OAUTH2_APP_SECRET_PROPERTY) + @Column(name = ModelConstants.MOBILE_APP_APP_SECRET_PROPERTY) private String appSecret; - public OAuth2MobileEntity() { + @Column(name = ModelConstants.MOBILE_APP_OAUTH2_ENABLED_PROPERTY) + private Boolean oauth2Enabled; + + public MobileAppEntity() { super(); } - public OAuth2MobileEntity(OAuth2Mobile mobile) { + public MobileAppEntity(MobileApp mobile) { if (mobile.getId() != null) { this.setUuid(mobile.getId().getId()); } - this.setCreatedTime(mobile.getCreatedTime()); - if (mobile.getOauth2ParamsId() != null) { - this.oauth2ParamsId = mobile.getOauth2ParamsId().getId(); + if (mobile.getTenantId() != null) { + this.tenantId = mobile.getTenantId().getId(); } + this.setCreatedTime(mobile.getCreatedTime()); this.pkgName = mobile.getPkgName(); this.appSecret = mobile.getAppSecret(); + this.oauth2Enabled = mobile.isOauth2Enabled(); } @Override - public OAuth2Mobile toData() { - OAuth2Mobile mobile = new OAuth2Mobile(); - mobile.setId(new OAuth2MobileId(id)); + public MobileApp toData() { + MobileApp mobile = new MobileApp(); + mobile.setId(new MobileAppId(id)); + if (tenantId != null) { + mobile.setTenantId(TenantId.fromUUID(tenantId)); + } mobile.setCreatedTime(createdTime); - mobile.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId)); mobile.setPkgName(pkgName); mobile.setAppSecret(appSecret); + mobile.setOauth2Enabled(oauth2Enabled); return mobile; } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppOauth2RegistrationCompositeKey.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppOauth2RegistrationCompositeKey.java new file mode 100644 index 0000000000..feccbe8aaa --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppOauth2RegistrationCompositeKey.java @@ -0,0 +1,37 @@ +/** + * 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.model.sql; + +import jakarta.persistence.Transient; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.UUID; + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class MobileAppOauth2RegistrationCompositeKey implements Serializable { + + @Transient + private static final long serialVersionUID = -245388185894468455L; + + private UUID mobileAppId; + private UUID oauth2RegistrationId; + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppOauth2RegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppOauth2RegistrationEntity.java new file mode 100644 index 0000000000..c68690e167 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppOauth2RegistrationEntity.java @@ -0,0 +1,67 @@ +/** + * 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.model.sql; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.Table; +import lombok.Data; +import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.mobile.MobileAppOauth2Registration; +import org.thingsboard.server.dao.model.ToData; + +import java.util.UUID; + +import static org.thingsboard.server.dao.model.ModelConstants.MOBILE_APP_OAUTH2_REGISTRATION_MOBILE_APP_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.MOBILE_APP_OAUTH2_REGISTRATION_REGISTRATION_ID_PROPERTY; +import static org.thingsboard.server.dao.model.ModelConstants.MOBILE_APP_OAUTH2_REGISTRATION_TABLE_NAME; + +@Data +@Entity +@Table(name = MOBILE_APP_OAUTH2_REGISTRATION_TABLE_NAME) +@IdClass(MobileAppOauth2RegistrationCompositeKey.class) +public final class MobileAppOauth2RegistrationEntity implements ToData { + + @Id + @Column(name = MOBILE_APP_OAUTH2_REGISTRATION_MOBILE_APP_ID_PROPERTY, columnDefinition = "uuid") + private UUID mobileAppId; + + @Id + @Column(name = MOBILE_APP_OAUTH2_REGISTRATION_REGISTRATION_ID_PROPERTY, columnDefinition = "uuid") + private UUID oauth2RegistrationId; + + + public MobileAppOauth2RegistrationEntity() { + super(); + } + + public MobileAppOauth2RegistrationEntity(MobileAppOauth2Registration domainOauth2Provider) { + mobileAppId = domainOauth2Provider.getMobileAppId().getId(); + oauth2RegistrationId = domainOauth2Provider.getOAuth2RegistrationId().getId(); + } + + + @Override + public MobileAppOauth2Registration toData() { + MobileAppOauth2Registration result = new MobileAppOauth2Registration(); + result.setMobileAppId(new MobileAppId(mobileAppId)); + result.setOAuth2RegistrationId(new OAuth2RegistrationId(oauth2RegistrationId)); + return result; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java deleted file mode 100644 index 7841a11378..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java +++ /dev/null @@ -1,76 +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.model.sql; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.thingsboard.server.common.data.id.OAuth2DomainId; -import org.thingsboard.server.common.data.id.OAuth2ParamsId; -import org.thingsboard.server.common.data.oauth2.OAuth2Domain; -import org.thingsboard.server.common.data.oauth2.SchemeType; -import org.thingsboard.server.dao.model.BaseSqlEntity; -import org.thingsboard.server.dao.model.ModelConstants; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.Table; -import java.util.UUID; - -@Data -@EqualsAndHashCode(callSuper = true) -@Entity -@Table(name = ModelConstants.OAUTH2_DOMAIN_TABLE_NAME) -public class OAuth2DomainEntity extends BaseSqlEntity { - - @Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY) - private UUID oauth2ParamsId; - - @Column(name = ModelConstants.OAUTH2_DOMAIN_NAME_PROPERTY) - private String domainName; - - @Enumerated(EnumType.STRING) - @Column(name = ModelConstants.OAUTH2_DOMAIN_SCHEME_PROPERTY) - private SchemeType domainScheme; - - public OAuth2DomainEntity() { - super(); - } - - public OAuth2DomainEntity(OAuth2Domain domain) { - if (domain.getId() != null) { - this.setUuid(domain.getId().getId()); - } - this.setCreatedTime(domain.getCreatedTime()); - if (domain.getOauth2ParamsId() != null) { - this.oauth2ParamsId = domain.getOauth2ParamsId().getId(); - } - this.domainName = domain.getDomainName(); - this.domainScheme = domain.getDomainScheme(); - } - - @Override - public OAuth2Domain toData() { - OAuth2Domain domain = new OAuth2Domain(); - domain.setId(new OAuth2DomainId(id)); - domain.setCreatedTime(createdTime); - domain.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId)); - domain.setDomainName(domainName); - domain.setDomainScheme(domainScheme); - return domain; - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java deleted file mode 100644 index 4983172d3e..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java +++ /dev/null @@ -1,70 +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.model.sql; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import org.thingsboard.server.common.data.id.OAuth2ParamsId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.OAuth2Params; -import org.thingsboard.server.dao.model.BaseSqlEntity; -import org.thingsboard.server.dao.model.ModelConstants; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Table; -import java.util.UUID; - -@Data -@EqualsAndHashCode(callSuper = true) -@Entity -@Table(name = ModelConstants.OAUTH2_PARAMS_TABLE_NAME) -@NoArgsConstructor -public class OAuth2ParamsEntity extends BaseSqlEntity { - - @Column(name = ModelConstants.OAUTH2_PARAMS_ENABLED_PROPERTY) - private Boolean enabled; - - @Column(name = ModelConstants.OAUTH2_PARAMS_EDGE_ENABLED_PROPERTY) - private Boolean edgeEnabled; - - @Column(name = ModelConstants.OAUTH2_PARAMS_TENANT_ID_PROPERTY) - private UUID tenantId; - - public OAuth2ParamsEntity(OAuth2Params oauth2Params) { - if (oauth2Params.getId() != null) { - this.setUuid(oauth2Params.getUuidId()); - } - this.setCreatedTime(oauth2Params.getCreatedTime()); - this.enabled = oauth2Params.isEnabled(); - this.edgeEnabled = oauth2Params.isEdgeEnabled(); - if (oauth2Params.getTenantId() != null) { - this.tenantId = oauth2Params.getTenantId().getId(); - } - } - - @Override - public OAuth2Params toData() { - OAuth2Params oauth2Params = new OAuth2Params(); - oauth2Params.setId(new OAuth2ParamsId(id)); - oauth2Params.setCreatedTime(createdTime); - oauth2Params.setTenantId(TenantId.fromUUID(tenantId)); - oauth2Params.setEnabled(enabled); - oauth2Params.setEdgeEnabled(edgeEnabled); - return oauth2Params; - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java index 45ef7466f9..9ba8bd0ae1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java @@ -25,8 +25,8 @@ import jakarta.persistence.Table; import lombok.Data; import lombok.EqualsAndHashCode; import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.id.OAuth2ParamsId; import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.MapperType; import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; @@ -49,8 +49,10 @@ import java.util.stream.Collectors; @Table(name = ModelConstants.OAUTH2_REGISTRATION_TABLE_NAME) public class OAuth2RegistrationEntity extends BaseSqlEntity { - @Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY) - private UUID oauth2ParamsId; + @Column(name = ModelConstants.TENANT_ID_COLUMN) + private UUID tenantId; + @Column(name = ModelConstants.OAUTH2_CLIENT_TITLE_PROPERTY) + private String title; @Column(name = ModelConstants.OAUTH2_CLIENT_ID_PROPERTY) private String clientId; @Column(name = ModelConstants.OAUTH2_CLIENT_SECRET_PROPERTY) @@ -121,9 +123,10 @@ public class OAuth2RegistrationEntity extends BaseSqlEntity this.setUuid(registration.getId().getId()); } this.setCreatedTime(registration.getCreatedTime()); - if (registration.getOauth2ParamsId() != null) { - this.oauth2ParamsId = registration.getOauth2ParamsId().getId(); + if (registration.getTenantId() != null) { + this.tenantId = registration.getTenantId().getId(); } + this.title = registration.getTitle(); this.clientId = registration.getClientId(); this.clientSecret = registration.getClientSecret(); this.authorizationUri = registration.getAuthorizationUri(); @@ -168,7 +171,8 @@ public class OAuth2RegistrationEntity extends BaseSqlEntity OAuth2Registration registration = new OAuth2Registration(); registration.setId(new OAuth2RegistrationId(id)); registration.setCreatedTime(createdTime); - registration.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId)); + registration.setTenantId(new TenantId(tenantId)); + registration.setTitle(title); registration.setAdditionalInfo(additionalInfo); registration.setMapperConfig( OAuth2MapperConfig.builder() diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationInfoEntity.java new file mode 100644 index 0000000000..855dcb3446 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationInfoEntity.java @@ -0,0 +1,61 @@ +/** + * 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.model.sql; + +import jakarta.persistence.Entity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.thingsboard.server.common.data.StringUtils; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; +import org.thingsboard.server.common.data.oauth2.PlatformType; +import org.thingsboard.server.dao.model.BaseSqlEntity; + +import java.util.Arrays; +import java.util.Collections; +import java.util.UUID; +import java.util.stream.Collectors; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +public class OAuth2RegistrationInfoEntity extends BaseSqlEntity { + + private String platforms; + private String title; + + public OAuth2RegistrationInfoEntity() { + super(); + } + + public OAuth2RegistrationInfoEntity(UUID id, long createdTime, String platforms, String title) { + this.id = id; + this.createdTime = createdTime; + this.platforms = platforms; + this.title = title; + } + + @Override + public OAuth2RegistrationInfo toData() { + OAuth2RegistrationInfo oAuth2RegistrationInfo = new OAuth2RegistrationInfo(); + oAuth2RegistrationInfo.setId(new OAuth2RegistrationId(id)); + oAuth2RegistrationInfo.setCreatedTime(createdTime); + oAuth2RegistrationInfo.setTitle(title); + oAuth2RegistrationInfo.setPlatforms(StringUtils.isNotEmpty(platforms) ? Arrays.stream(platforms.split(",")) + .map(str -> PlatformType.valueOf(str)).collect(Collectors.toList()) : Collections.emptyList()); + return oAuth2RegistrationInfo; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java index 004c89bd54..4ac2de4297 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java @@ -21,6 +21,8 @@ import org.springframework.security.oauth2.client.registration.ClientRegistratio import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import java.util.UUID; @@ -30,11 +32,11 @@ public class HybridClientRegistrationRepository implements ClientRegistrationRep private static final String defaultRedirectUriTemplate = "{baseUrl}/login/oauth2/code/{registrationId}"; @Autowired - private OAuth2Service oAuth2Service; + private OAuth2ClientService oAuth2ClientService; @Override public ClientRegistration findByRegistrationId(String registrationId) { - OAuth2Registration registration = oAuth2Service.findRegistration(UUID.fromString(registrationId)); + OAuth2Registration registration = oAuth2ClientService.findOAuth2ClientById(TenantId.SYS_TENANT_ID, new OAuth2RegistrationId(UUID.fromString(registrationId))); return registration == null ? null : toSpringClientRegistration(registration); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java new file mode 100644 index 0000000000..2e2a1fa5c0 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java @@ -0,0 +1,145 @@ +/** + * 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.oauth2; + +import jakarta.transaction.Transactional; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.HasId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; +import org.thingsboard.server.common.data.oauth2.PlatformType; +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.service.DataValidator; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validateString; + +@Slf4j +@Service +public class OAuth2ClientServiceImpl extends AbstractEntityService implements OAuth2ClientService { + + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; + public static final String INCORRECT_CLIENT_REGISTRATION_ID = "Incorrect clientRegistrationId "; + public static final String INCORRECT_DOMAIN_NAME = "Incorrect domainName "; + + @Autowired + private OAuth2RegistrationDao oauth2RegistrationDao; + @Autowired + private DataValidator oAuth2RegistrationDataValidator; + + @Override + public List getWebOAuth2Clients(String domainName, PlatformType platformType) { + log.trace("Executing getOAuth2Clients [{}] ", domainName); + validateString(domainName, dn -> INCORRECT_DOMAIN_NAME + dn); + return oauth2RegistrationDao.findEnabledByDomainNameAndPlatformType(domainName, platformType) + .stream() + .map(OAuth2Utils::toClientInfo) + .collect(Collectors.toList()); + } + + @Override + public List getMobileOAuth2Clients(String pkgName, PlatformType platformType) { + log.trace("Executing getOAuth2Clients pkgName=[{}] platformType=[{}]",pkgName, platformType); + return oauth2RegistrationDao.findEnabledByPckNameAndPlatformType(pkgName, platformType) + .stream() + .map(OAuth2Utils::toClientInfo) + .collect(Collectors.toList()); + } + + @Override + @Transactional + public OAuth2Registration saveOAuth2Client(TenantId tenantId, OAuth2Registration oAuth2Registration) { + log.trace("Executing saveOAuth2Client [{}]", oAuth2Registration); + oAuth2RegistrationDataValidator.validate(oAuth2Registration, OAuth2Registration::getTenantId); + OAuth2Registration savedOauth2Registration = oauth2RegistrationDao.save(tenantId, oAuth2Registration); + eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(TenantId.SYS_TENANT_ID).entity(oAuth2Registration).build()); + return savedOauth2Registration; + } + + @Override + public OAuth2Registration findOAuth2ClientById(TenantId tenantId, OAuth2RegistrationId oAuth2RegistrationId) { + log.trace("Executing findOAuth2ClientById [{}]", oAuth2RegistrationId); + validateId(oAuth2RegistrationId, uuid -> INCORRECT_CLIENT_REGISTRATION_ID + uuid); + return oauth2RegistrationDao.findById(tenantId, oAuth2RegistrationId.getId()); + } + + @Override + public List findOauth2ClientInfosByTenantId(TenantId tenantId) { + log.trace("Executing findOauth2ClientInfosByTenantId"); + return oauth2RegistrationDao.findInfosByTenantId(tenantId.getId()); + } + + @Override + public List findOauth2ClientsByTenantId(TenantId tenantId) { + log.trace("Executing findOauth2ClientsByTenantId [{}]", tenantId); + return oauth2RegistrationDao.findByTenantId(tenantId.getId()); + } + + @Override + public String findAppSecret(UUID id, String pkgName) { + log.trace("Executing findAppSecret [{}][{}]", id, pkgName); + validateId(id, uuid -> INCORRECT_CLIENT_REGISTRATION_ID + uuid); + validateString(pkgName, "Incorrect package name"); + return oauth2RegistrationDao.findAppSecret(id, pkgName); + } + + @Override + @Transactional + public void deleteById(TenantId tenantId, OAuth2RegistrationId oAuth2RegistrationId) { + log.trace("[{}][{}] Executing deleteById [{}]", tenantId, oAuth2RegistrationId); + oauth2RegistrationDao.removeById(tenantId, oAuth2RegistrationId.getId()); + eventPublisher.publishEvent(DeleteEntityEvent.builder() + .tenantId(tenantId) + .entityId(oAuth2RegistrationId) + .build()); + + } + + @Override + public Optional> findEntity(TenantId tenantId, EntityId entityId) { + return Optional.ofNullable(findOAuth2ClientById(tenantId, new OAuth2RegistrationId(entityId.getId()))); + } + + @Override + @Transactional + public void deleteEntity(TenantId tenantId, EntityId id, boolean force) { + OAuth2Registration oAuth2Registration = oauth2RegistrationDao.findById(tenantId, id.getId()); + if (oAuth2Registration == null) { + return; + } + deleteById(tenantId, oAuth2Registration.getId()); + } + + @Override + public EntityType getEntityType() { + return EntityType.OAUTH2_CLIENT; + } + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java index 96a91f7d3b..a0dcd22801 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java @@ -16,8 +16,8 @@ package org.thingsboard.server.dao.oauth2; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; import org.thingsboard.server.common.data.oauth2.PlatformType; -import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.Dao; import java.util.List; @@ -25,9 +25,17 @@ import java.util.UUID; public interface OAuth2RegistrationDao extends Dao { - List findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(List domainSchemes, String domainName, String pkgName, PlatformType platformType); + List findInfosByTenantId(UUID tenantId); - List findByOAuth2ParamsId(UUID oauth2ParamsId); + List findByTenantId(UUID tenantId); + + List findEnabledByDomainNameAndPlatformType(String domainName, PlatformType platformType); + + List findEnabledByPckNameAndPlatformType(String pkgName, PlatformType platformType); + + List findInfosByDomainId(UUID domainId); + + List findInfosByMobileAppId(UUID domainId); String findAppSecret(UUID id, String pkgName); diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java deleted file mode 100644 index 5bb7ae163e..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ /dev/null @@ -1,295 +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.oauth2; - -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections4.CollectionUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.BaseData; -import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.MapperType; -import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; -import org.thingsboard.server.common.data.oauth2.OAuth2Domain; -import org.thingsboard.server.common.data.oauth2.OAuth2DomainInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2Info; -import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; -import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; -import org.thingsboard.server.common.data.oauth2.OAuth2MobileInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2Params; -import org.thingsboard.server.common.data.oauth2.OAuth2ParamsInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2Registration; -import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; -import org.thingsboard.server.common.data.oauth2.PlatformType; -import org.thingsboard.server.common.data.oauth2.SchemeType; -import org.thingsboard.server.common.data.oauth2.TenantNameStrategyType; -import org.thingsboard.server.dao.entity.AbstractEntityService; -import org.thingsboard.server.dao.eventsourcing.SaveEntityEvent; -import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.exception.IncorrectParameterException; - -import jakarta.transaction.Transactional; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; -import java.util.UUID; -import java.util.function.Consumer; -import java.util.stream.Collectors; - -import static org.thingsboard.server.dao.service.Validator.validateId; -import static org.thingsboard.server.dao.service.Validator.validateString; - -@Slf4j -@Service -public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Service { - - public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; - public static final String INCORRECT_CLIENT_REGISTRATION_ID = "Incorrect clientRegistrationId "; - public static final String INCORRECT_DOMAIN_NAME = "Incorrect domainName "; - public static final String INCORRECT_DOMAIN_SCHEME = "Incorrect domainScheme "; - - @Autowired - private OAuth2ParamsDao oauth2ParamsDao; - @Autowired - private OAuth2RegistrationDao oauth2RegistrationDao; - @Autowired - private OAuth2DomainDao oauth2DomainDao; - @Autowired - private OAuth2MobileDao oauth2MobileDao; - - @Override - public List getOAuth2Clients(String domainSchemeStr, String domainName, String pkgName, PlatformType platformType) { - log.trace("Executing getOAuth2Clients [{}://{}] pkgName=[{}] platformType=[{}]", domainSchemeStr, domainName, pkgName, platformType); - if (domainSchemeStr == null) { - throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME); - } - SchemeType domainScheme; - try { - domainScheme = SchemeType.valueOf(domainSchemeStr.toUpperCase()); - } catch (IllegalArgumentException e){ - throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME); - } - validateString(domainName, dn -> INCORRECT_DOMAIN_NAME + dn); - return oauth2RegistrationDao.findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType( - Arrays.asList(domainScheme, SchemeType.MIXED), domainName, pkgName, platformType) - .stream() - .map(OAuth2Utils::toClientInfo) - .collect(Collectors.toList()); - } - - @Override - @Transactional - public void saveOAuth2Info(OAuth2Info oauth2Info) { - log.trace("Executing saveOAuth2Info [{}]", oauth2Info); - oauth2InfoValidator.accept(oauth2Info); - oauth2ParamsDao.deleteAll(); - oauth2Info.getOauth2ParamsInfos().forEach(oauth2ParamsInfo -> { - OAuth2Params oauth2Params = OAuth2Utils.infoToOAuth2Params(oauth2Info); - OAuth2Params savedOauth2Params = oauth2ParamsDao.save(TenantId.SYS_TENANT_ID, oauth2Params); - oauth2ParamsInfo.getClientRegistrations().forEach(registrationInfo -> { - OAuth2Registration registration = OAuth2Utils.toOAuth2Registration(savedOauth2Params.getId(), registrationInfo); - oauth2RegistrationDao.save(TenantId.SYS_TENANT_ID, registration); - }); - oauth2ParamsInfo.getDomainInfos().forEach(domainInfo -> { - OAuth2Domain domain = OAuth2Utils.toOAuth2Domain(savedOauth2Params.getId(), domainInfo); - oauth2DomainDao.save(TenantId.SYS_TENANT_ID, domain); - }); - if (oauth2ParamsInfo.getMobileInfos() != null) { - oauth2ParamsInfo.getMobileInfos().forEach(mobileInfo -> { - OAuth2Mobile mobile = OAuth2Utils.toOAuth2Mobile(savedOauth2Params.getId(), mobileInfo); - oauth2MobileDao.save(TenantId.SYS_TENANT_ID, mobile); - }); - } - }); - eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(TenantId.SYS_TENANT_ID).entity(oauth2Info).build()); - } - - @Override - public OAuth2Info findOAuth2Info() { - log.trace("Executing findOAuth2Info"); - OAuth2Info oauth2Info = new OAuth2Info(); - List oauth2ParamsList = oauth2ParamsDao.find(TenantId.SYS_TENANT_ID); - oauth2Info.setEnabled(oauth2ParamsList.stream().anyMatch(OAuth2Params::isEnabled)); - oauth2Info.setEdgeEnabled(oauth2ParamsList.stream().anyMatch(OAuth2Params::isEdgeEnabled)); - List oauth2ParamsInfos = new ArrayList<>(); - oauth2Info.setOauth2ParamsInfos(oauth2ParamsInfos); - oauth2ParamsList.stream().sorted(Comparator.comparing(BaseData::getUuidId)).forEach(oauth2Params -> { - List registrations = oauth2RegistrationDao.findByOAuth2ParamsId(oauth2Params.getId().getId()); - List domains = oauth2DomainDao.findByOAuth2ParamsId(oauth2Params.getId().getId()); - List mobiles = oauth2MobileDao.findByOAuth2ParamsId(oauth2Params.getId().getId()); - oauth2ParamsInfos.add(OAuth2Utils.toOAuth2ParamsInfo(registrations, domains, mobiles)); - }); - return oauth2Info; - } - - @Override - public OAuth2Registration findRegistration(UUID id) { - log.trace("Executing findRegistration [{}]", id); - validateId(id, uuid -> INCORRECT_CLIENT_REGISTRATION_ID + uuid); - return oauth2RegistrationDao.findById(null, id); - } - - @Override - public String findAppSecret(UUID id, String pkgName) { - log.trace("Executing findAppSecret [{}][{}]", id, pkgName); - validateId(id, uuid -> INCORRECT_CLIENT_REGISTRATION_ID + uuid); - validateString(pkgName, "Incorrect package name"); - return oauth2RegistrationDao.findAppSecret(id, pkgName); - } - - - @Override - public List findAllRegistrations() { - log.trace("Executing findAllRegistrations"); - return oauth2RegistrationDao.find(TenantId.SYS_TENANT_ID); - } - - private final Consumer oauth2InfoValidator = oauth2Info -> { - if (oauth2Info == null - || oauth2Info.getOauth2ParamsInfos() == null) { - throw new DataValidationException("OAuth2 param infos should be specified!"); - } - for (OAuth2ParamsInfo oauth2Params : oauth2Info.getOauth2ParamsInfos()) { - if (oauth2Params.getDomainInfos() == null - || oauth2Params.getDomainInfos().isEmpty()) { - throw new DataValidationException("List of domain configuration should be specified!"); - } - for (OAuth2DomainInfo domainInfo : oauth2Params.getDomainInfos()) { - if (StringUtils.isEmpty(domainInfo.getName())) { - throw new DataValidationException("Domain name should be specified!"); - } - if (domainInfo.getScheme() == null) { - throw new DataValidationException("Domain scheme should be specified!"); - } - } - oauth2Params.getDomainInfos().stream() - .collect(Collectors.groupingBy(OAuth2DomainInfo::getName)) - .forEach((domainName, domainInfos) -> { - if (domainInfos.size() > 1 && domainInfos.stream().anyMatch(domainInfo -> domainInfo.getScheme() == SchemeType.MIXED)) { - throw new DataValidationException("MIXED scheme type shouldn't be combined with another scheme type!"); - } - domainInfos.stream() - .collect(Collectors.groupingBy(OAuth2DomainInfo::getScheme)) - .forEach((schemeType, domainInfosBySchemeType) -> { - if (domainInfosBySchemeType.size() > 1) { - throw new DataValidationException("Domain name and protocol must be unique within OAuth2 parameters!"); - } - }); - }); - if (oauth2Params.getMobileInfos() != null) { - for (OAuth2MobileInfo mobileInfo : oauth2Params.getMobileInfos()) { - if (StringUtils.isEmpty(mobileInfo.getPkgName())) { - throw new DataValidationException("Package should be specified!"); - } - if (StringUtils.isEmpty(mobileInfo.getAppSecret())) { - throw new DataValidationException("Application secret should be specified!"); - } - if (mobileInfo.getAppSecret().length() < 16) { - throw new DataValidationException("Application secret should be at least 16 characters!"); - } - } - oauth2Params.getMobileInfos().stream() - .collect(Collectors.groupingBy(OAuth2MobileInfo::getPkgName)) - .forEach((pkgName, mobileInfos) -> { - if (mobileInfos.size() > 1) { - throw new DataValidationException("Mobile app package name must be unique within OAuth2 parameters!"); - } - }); - } - if (oauth2Params.getClientRegistrations() == null || oauth2Params.getClientRegistrations().isEmpty()) { - throw new DataValidationException("Client registrations should be specified!"); - } - for (OAuth2RegistrationInfo clientRegistration : oauth2Params.getClientRegistrations()) { - if (StringUtils.isEmpty(clientRegistration.getClientId())) { - throw new DataValidationException("Client ID should be specified!"); - } - if (StringUtils.isEmpty(clientRegistration.getClientSecret())) { - throw new DataValidationException("Client secret should be specified!"); - } - if (StringUtils.isEmpty(clientRegistration.getAuthorizationUri())) { - throw new DataValidationException("Authorization uri should be specified!"); - } - if (StringUtils.isEmpty(clientRegistration.getAccessTokenUri())) { - throw new DataValidationException("Token uri should be specified!"); - } - if (CollectionUtils.isEmpty(clientRegistration.getScope())) { - throw new DataValidationException("Scope should be specified!"); - } - if (StringUtils.isEmpty(clientRegistration.getUserNameAttributeName())) { - throw new DataValidationException("User name attribute name should be specified!"); - } - if (StringUtils.isEmpty(clientRegistration.getClientAuthenticationMethod())) { - throw new DataValidationException("Client authentication method should be specified!"); - } - if (StringUtils.isEmpty(clientRegistration.getLoginButtonLabel())) { - throw new DataValidationException("Login button label should be specified!"); - } - OAuth2MapperConfig mapperConfig = clientRegistration.getMapperConfig(); - if (mapperConfig == null) { - throw new DataValidationException("Mapper config should be specified!"); - } - if (mapperConfig.getType() == null) { - throw new DataValidationException("Mapper config type should be specified!"); - } - if (mapperConfig.getType() == MapperType.BASIC) { - OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); - if (basicConfig == null) { - throw new DataValidationException("Basic config should be specified!"); - } - if (StringUtils.isEmpty(basicConfig.getEmailAttributeKey())) { - throw new DataValidationException("Email attribute key should be specified!"); - } - if (basicConfig.getTenantNameStrategy() == null) { - throw new DataValidationException("Tenant name strategy should be specified!"); - } - if (basicConfig.getTenantNameStrategy() == TenantNameStrategyType.CUSTOM - && StringUtils.isEmpty(basicConfig.getTenantNamePattern())) { - throw new DataValidationException("Tenant name pattern should be specified!"); - } - } - if (mapperConfig.getType() == MapperType.GITHUB) { - OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); - if (basicConfig == null) { - throw new DataValidationException("Basic config should be specified!"); - } - if (!StringUtils.isEmpty(basicConfig.getEmailAttributeKey())) { - throw new DataValidationException("Email attribute key cannot be configured for GITHUB mapper type!"); - } - if (basicConfig.getTenantNameStrategy() == null) { - throw new DataValidationException("Tenant name strategy should be specified!"); - } - if (basicConfig.getTenantNameStrategy() == TenantNameStrategyType.CUSTOM - && StringUtils.isEmpty(basicConfig.getTenantNamePattern())) { - throw new DataValidationException("Tenant name pattern should be specified!"); - } - } - if (mapperConfig.getType() == MapperType.CUSTOM) { - OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom(); - if (customConfig == null) { - throw new DataValidationException("Custom config should be specified!"); - } - if (StringUtils.isEmpty(customConfig.getUrl())) { - throw new DataValidationException("Custom mapper URL should be specified!"); - } - } - } - } - }; -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java index 8d30fc905d..c3fc12748b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -15,23 +15,8 @@ */ package org.thingsboard.server.dao.oauth2; -import org.thingsboard.server.common.data.BaseData; -import org.thingsboard.server.common.data.id.OAuth2ParamsId; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2Domain; -import org.thingsboard.server.common.data.oauth2.OAuth2DomainInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2Info; -import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; -import org.thingsboard.server.common.data.oauth2.OAuth2MobileInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2Params; -import org.thingsboard.server.common.data.oauth2.OAuth2ParamsInfo; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; -import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; - -import java.util.Comparator; -import java.util.List; -import java.util.stream.Collectors; public class OAuth2Utils { public static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; @@ -44,88 +29,4 @@ public class OAuth2Utils { return client; } - public static OAuth2ParamsInfo toOAuth2ParamsInfo(List registrations, List domains, List mobiles) { - OAuth2ParamsInfo oauth2ParamsInfo = new OAuth2ParamsInfo(); - oauth2ParamsInfo.setClientRegistrations(registrations.stream().sorted(Comparator.comparing(BaseData::getUuidId)).map(OAuth2Utils::toOAuth2RegistrationInfo).collect(Collectors.toList())); - oauth2ParamsInfo.setDomainInfos(domains.stream().sorted(Comparator.comparing(BaseData::getUuidId)).map(OAuth2Utils::toOAuth2DomainInfo).collect(Collectors.toList())); - oauth2ParamsInfo.setMobileInfos(mobiles.stream().sorted(Comparator.comparing(BaseData::getUuidId)).map(OAuth2Utils::toOAuth2MobileInfo).collect(Collectors.toList())); - return oauth2ParamsInfo; - } - - public static OAuth2RegistrationInfo toOAuth2RegistrationInfo(OAuth2Registration registration) { - return OAuth2RegistrationInfo.builder() - .mapperConfig(registration.getMapperConfig()) - .clientId(registration.getClientId()) - .clientSecret(registration.getClientSecret()) - .authorizationUri(registration.getAuthorizationUri()) - .accessTokenUri(registration.getAccessTokenUri()) - .scope(registration.getScope()) - .platforms(registration.getPlatforms()) - .userInfoUri(registration.getUserInfoUri()) - .userNameAttributeName(registration.getUserNameAttributeName()) - .jwkSetUri(registration.getJwkSetUri()) - .clientAuthenticationMethod(registration.getClientAuthenticationMethod()) - .loginButtonLabel(registration.getLoginButtonLabel()) - .loginButtonIcon(registration.getLoginButtonIcon()) - .additionalInfo(registration.getAdditionalInfo()) - .build(); - } - - public static OAuth2DomainInfo toOAuth2DomainInfo(OAuth2Domain domain) { - return OAuth2DomainInfo.builder() - .name(domain.getDomainName()) - .scheme(domain.getDomainScheme()) - .build(); - } - - public static OAuth2MobileInfo toOAuth2MobileInfo(OAuth2Mobile mobile) { - return OAuth2MobileInfo.builder() - .pkgName(mobile.getPkgName()) - .appSecret(mobile.getAppSecret()) - .build(); - } - - public static OAuth2Params infoToOAuth2Params(OAuth2Info oauth2Info) { - OAuth2Params oauth2Params = new OAuth2Params(); - oauth2Params.setEnabled(oauth2Info.isEnabled()); - oauth2Params.setEdgeEnabled(oauth2Info.isEdgeEnabled()); - oauth2Params.setTenantId(TenantId.SYS_TENANT_ID); - return oauth2Params; - } - - public static OAuth2Registration toOAuth2Registration(OAuth2ParamsId oauth2ParamsId, OAuth2RegistrationInfo registrationInfo) { - OAuth2Registration registration = new OAuth2Registration(); - registration.setOauth2ParamsId(oauth2ParamsId); - registration.setMapperConfig(registrationInfo.getMapperConfig()); - registration.setClientId(registrationInfo.getClientId()); - registration.setClientSecret(registrationInfo.getClientSecret()); - registration.setAuthorizationUri(registrationInfo.getAuthorizationUri()); - registration.setAccessTokenUri(registrationInfo.getAccessTokenUri()); - registration.setScope(registrationInfo.getScope()); - registration.setPlatforms(registrationInfo.getPlatforms()); - registration.setUserInfoUri(registrationInfo.getUserInfoUri()); - registration.setUserNameAttributeName(registrationInfo.getUserNameAttributeName()); - registration.setJwkSetUri(registrationInfo.getJwkSetUri()); - registration.setClientAuthenticationMethod(registrationInfo.getClientAuthenticationMethod()); - registration.setLoginButtonLabel(registrationInfo.getLoginButtonLabel()); - registration.setLoginButtonIcon(registrationInfo.getLoginButtonIcon()); - registration.setAdditionalInfo(registrationInfo.getAdditionalInfo()); - return registration; - } - - public static OAuth2Domain toOAuth2Domain(OAuth2ParamsId oauth2ParamsId, OAuth2DomainInfo domainInfo) { - OAuth2Domain domain = new OAuth2Domain(); - domain.setOauth2ParamsId(oauth2ParamsId); - domain.setDomainName(domainInfo.getName()); - domain.setDomainScheme(domainInfo.getScheme()); - return domain; - } - - public static OAuth2Mobile toOAuth2Mobile(OAuth2ParamsId oauth2ParamsId, OAuth2MobileInfo mobileInfo) { - OAuth2Mobile mobile = new OAuth2Mobile(); - mobile.setOauth2ParamsId(oauth2ParamsId); - mobile.setPkgName(mobileInfo.getPkgName()); - mobile.setAppSecret(mobileInfo.getAppSecret()); - return mobile; - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java new file mode 100644 index 0000000000..644e35f14a --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java @@ -0,0 +1,36 @@ +/** + * 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 { + + @Override + protected void validateDataImpl(TenantId tenantId, Domain domain) { + if (StringUtils.isEmpty(domain.getName())) { + throw new DataValidationException("Domain name should be specified!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java new file mode 100644 index 0000000000..a96f7a0ac6 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java @@ -0,0 +1,43 @@ +/** + * 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.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 { + + @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!"); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/service/validator/Oauth2RegistrationDataValidator.java b/dao/src/main/java/org/thingsboard/server/dao/service/validator/Oauth2RegistrationDataValidator.java new file mode 100644 index 0000000000..e5efbd13e7 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/service/validator/Oauth2RegistrationDataValidator.java @@ -0,0 +1,114 @@ +/** + * 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.apache.commons.collections4.CollectionUtils; +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.oauth2.MapperType; +import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.TenantNameStrategyType; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.service.DataValidator; + +@Component +@AllArgsConstructor +public class Oauth2RegistrationDataValidator extends DataValidator { + + @Override + protected void validateDataImpl(TenantId tenantId, OAuth2Registration oAuth2Registration) { + if (StringUtils.isEmpty(oAuth2Registration.getClientId())) { + throw new DataValidationException("Client ID should be specified!"); + } + if (StringUtils.isEmpty(oAuth2Registration.getClientId())) { + throw new DataValidationException("Client ID should be specified!"); + } + if (StringUtils.isEmpty(oAuth2Registration.getClientSecret())) { + throw new DataValidationException("Client secret should be specified!"); + } + if (StringUtils.isEmpty(oAuth2Registration.getAuthorizationUri())) { + throw new DataValidationException("Authorization uri should be specified!"); + } + if (StringUtils.isEmpty(oAuth2Registration.getAccessTokenUri())) { + throw new DataValidationException("Token uri should be specified!"); + } + if (CollectionUtils.isEmpty(oAuth2Registration.getScope())) { + throw new DataValidationException("Scope should be specified!"); + } + if (StringUtils.isEmpty(oAuth2Registration.getUserNameAttributeName())) { + throw new DataValidationException("User name attribute name should be specified!"); + } + if (StringUtils.isEmpty(oAuth2Registration.getClientAuthenticationMethod())) { + throw new DataValidationException("Client authentication method should be specified!"); + } + if (StringUtils.isEmpty(oAuth2Registration.getLoginButtonLabel())) { + throw new DataValidationException("Login button label should be specified!"); + } + OAuth2MapperConfig mapperConfig = oAuth2Registration.getMapperConfig(); + if (mapperConfig == null) { + throw new DataValidationException("Mapper config should be specified!"); + } + if (mapperConfig.getType() == null) { + throw new DataValidationException("Mapper config type should be specified!"); + } + if (mapperConfig.getType() == MapperType.BASIC) { + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); + if (basicConfig == null) { + throw new DataValidationException("Basic config should be specified!"); + } + if (StringUtils.isEmpty(basicConfig.getEmailAttributeKey())) { + throw new DataValidationException("Email attribute key should be specified!"); + } + if (basicConfig.getTenantNameStrategy() == null) { + throw new DataValidationException("Tenant name strategy should be specified!"); + } + if (basicConfig.getTenantNameStrategy() == TenantNameStrategyType.CUSTOM + && StringUtils.isEmpty(basicConfig.getTenantNamePattern())) { + throw new DataValidationException("Tenant name pattern should be specified!"); + } + } + if (mapperConfig.getType() == MapperType.GITHUB) { + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); + if (basicConfig == null) { + throw new DataValidationException("Basic config should be specified!"); + } + if (!StringUtils.isEmpty(basicConfig.getEmailAttributeKey())) { + throw new DataValidationException("Email attribute key cannot be configured for GITHUB mapper type!"); + } + if (basicConfig.getTenantNameStrategy() == null) { + throw new DataValidationException("Tenant name strategy should be specified!"); + } + if (basicConfig.getTenantNameStrategy() == TenantNameStrategyType.CUSTOM + && StringUtils.isEmpty(basicConfig.getTenantNamePattern())) { + throw new DataValidationException("Tenant name pattern should be specified!"); + } + } + if (mapperConfig.getType() == MapperType.CUSTOM) { + OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom(); + if (customConfig == null) { + throw new DataValidationException("Custom config should be specified!"); + } + if (StringUtils.isEmpty(customConfig.getUrl())) { + throw new DataValidationException("Custom mapper URL should be specified!"); + } + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/domain/DomainOauth2RegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/domain/DomainOauth2RegistrationRepository.java new file mode 100644 index 0000000000..dd15bcacab --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/domain/DomainOauth2RegistrationRepository.java @@ -0,0 +1,34 @@ +/** + * 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.sql.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.dao.model.sql.DomainOauth2RegistrationCompositeKey; +import org.thingsboard.server.dao.model.sql.DomainOauth2RegistrationEntity; +import org.thingsboard.server.dao.model.sql.OAuth2RegistrationEntity; + +import java.util.List; +import java.util.UUID; + +public interface DomainOauth2RegistrationRepository extends JpaRepository { + + List findAllByDomainId(@Param("domainId") UUID domainId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/domain/DomainRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/domain/DomainRepository.java new file mode 100644 index 0000000000..1de8f939dd --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/domain/DomainRepository.java @@ -0,0 +1,39 @@ +/** + * 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.sql.domain; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.dao.model.sql.DomainEntity; + +import java.util.List; +import java.util.UUID; + +public interface DomainRepository extends JpaRepository { + + List findByTenantId(@Param("tenantId") UUID tenantId); + + @Transactional + @Modifying + @Query("DELETE FROM MobileAppEntity r WHERE r.tenantId = :tenantId") + void deleteByTenantId(@Param("tenantId") UUID tenantId); + + int countByTenantIdAndOauth2Enabled(@Param("tenantId") UUID tenantId, @Param("oauth2Enabled") boolean oauth2Enabled); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/domain/JpaDomainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/domain/JpaDomainDao.java new file mode 100644 index 0000000000..f4e10bda7a --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/domain/JpaDomainDao.java @@ -0,0 +1,83 @@ +/** + * 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.sql.domain; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.domain.Domain; +import org.thingsboard.server.common.data.domain.DomainOauth2Registration; +import org.thingsboard.server.common.data.id.DomainId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.domain.DomainDao; +import org.thingsboard.server.dao.model.sql.DomainEntity; +import org.thingsboard.server.dao.model.sql.DomainOauth2RegistrationCompositeKey; +import org.thingsboard.server.dao.model.sql.DomainOauth2RegistrationEntity; +import org.thingsboard.server.dao.model.sql.WidgetsBundleWidgetEntity; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +@SqlDao +public class JpaDomainDao extends JpaAbstractDao implements DomainDao { + + private final DomainRepository domainRepository; + private final DomainOauth2RegistrationRepository domainOauth2RegistrationRepository; + + @Override + protected Class getEntityClass() { + return DomainEntity.class; + } + + @Override + protected JpaRepository getRepository() { + return domainRepository; + } + + @Override + public List findByTenantId(TenantId tenantId) { + return DaoUtil.convertDataList(domainRepository.findByTenantId(tenantId.getId())); + } + + @Override + public int countDomainByTenantIdAndOauth2Enabled(TenantId tenantId, boolean enabled) { + return domainRepository.countByTenantIdAndOauth2Enabled(tenantId.getId(), enabled); + } + + @Override + public List findOauth2ClientsByDomainId(TenantId tenantId, DomainId domainId) { + return DaoUtil.convertDataList(domainOauth2RegistrationRepository.findAllByDomainId(domainId.getId())); + } + + @Override + public void saveOauth2Clients(DomainOauth2Registration domainOauth2Registration) { + domainOauth2RegistrationRepository.save(new DomainOauth2RegistrationEntity(domainOauth2Registration)); + } + + @Override + public void removeOauth2Clients(DomainId domainId, OAuth2RegistrationId oAuth2RegistrationId) { + domainOauth2RegistrationRepository.deleteById(new DomainOauth2RegistrationCompositeKey(domainId.getId(), oAuth2RegistrationId.getId())); + } + +} + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppDao.java new file mode 100644 index 0000000000..96e40a742f --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppDao.java @@ -0,0 +1,77 @@ +/** + * 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.sql.mobile; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.MobileAppId; +import org.thingsboard.server.common.data.id.OAuth2RegistrationId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.mobile.MobileApp; +import org.thingsboard.server.common.data.mobile.MobileAppOauth2Registration; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.mobile.MobileAppDao; +import org.thingsboard.server.dao.model.sql.MobileAppEntity; +import org.thingsboard.server.dao.model.sql.MobileAppOauth2RegistrationCompositeKey; +import org.thingsboard.server.dao.model.sql.MobileAppOauth2RegistrationEntity; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +@SqlDao +public class JpaMobileAppDao extends JpaAbstractDao implements MobileAppDao { + + private final MobileAppRepository repository; + private final MobileAppOauth2RegistrationRepository mobileOauth2ProviderRepository; + + @Override + protected Class getEntityClass() { + return MobileAppEntity.class; + } + + @Override + protected JpaRepository getRepository() { + return repository; + } + + @Override + public List findByTenantId(TenantId tenantId) { + return DaoUtil.convertDataList(repository.findByTenantId(tenantId.getId())); + } + + @Override + public List findOauth2ClientsByMobileAppId(TenantId tenantId, MobileAppId mobileAppId) { + return DaoUtil.convertDataList(mobileOauth2ProviderRepository.findAllByMobileAppId(mobileAppId.getId())); + } + + @Override + public void saveOauth2Clients(MobileAppOauth2Registration mobileAppOauth2Registration) { + mobileOauth2ProviderRepository.save(new MobileAppOauth2RegistrationEntity(mobileAppOauth2Registration)); + } + + @Override + public void removeOauth2Clients(MobileAppId mobileAppId, OAuth2RegistrationId oAuth2RegistrationId) { + mobileOauth2ProviderRepository.deleteById(new MobileAppOauth2RegistrationCompositeKey(mobileAppId.getId(), oAuth2RegistrationId.getId())); + + } + +} + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppOauth2RegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppOauth2RegistrationRepository.java new file mode 100644 index 0000000000..c00f65d18f --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppOauth2RegistrationRepository.java @@ -0,0 +1,32 @@ +/** + * 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.sql.mobile; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.repository.query.Param; +import org.thingsboard.server.common.data.mobile.MobileAppOauth2Registration; +import org.thingsboard.server.dao.model.sql.DomainOauth2RegistrationEntity; +import org.thingsboard.server.dao.model.sql.MobileAppOauth2RegistrationCompositeKey; +import org.thingsboard.server.dao.model.sql.MobileAppOauth2RegistrationEntity; + +import java.util.List; +import java.util.UUID; + +public interface MobileAppOauth2RegistrationRepository extends JpaRepository { + + List findAllByMobileAppId(@Param("mobileAppId") UUID mobileAppId); + +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ParamsRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppRepository.java similarity index 50% rename from dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ParamsRepository.java rename to dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppRepository.java index ed637b269b..a74a32b7d6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ParamsRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppRepository.java @@ -13,12 +13,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.thingsboard.server.dao.sql.oauth2; +package org.thingsboard.server.dao.sql.mobile; import org.springframework.data.jpa.repository.JpaRepository; -import org.thingsboard.server.dao.model.sql.OAuth2ParamsEntity; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.dao.model.sql.MobileAppEntity; +import java.util.List; import java.util.UUID; -public interface OAuth2ParamsRepository extends JpaRepository { +public interface MobileAppRepository extends JpaRepository { + + List findByTenantId(@Param("tenantId") UUID tenantId); + + @Transactional + @Modifying + @Query("DELETE FROM MobileAppEntity r WHERE r.tenantId = :tenantId") + void deleteByTenantId(@Param("tenantId") UUID tenantId); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java deleted file mode 100644 index 2eac50ba02..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java +++ /dev/null @@ -1,54 +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.sql.oauth2; - -import lombok.RequiredArgsConstructor; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.oauth2.OAuth2Domain; -import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.model.sql.OAuth2DomainEntity; -import org.thingsboard.server.dao.oauth2.OAuth2DomainDao; -import org.thingsboard.server.dao.sql.JpaAbstractDao; -import org.thingsboard.server.dao.util.SqlDao; - -import java.util.List; -import java.util.UUID; - -@Component -@RequiredArgsConstructor -@SqlDao -public class JpaOAuth2DomainDao extends JpaAbstractDao implements OAuth2DomainDao { - - private final OAuth2DomainRepository repository; - - @Override - protected Class getEntityClass() { - return OAuth2DomainEntity.class; - } - - @Override - protected JpaRepository getRepository() { - return repository; - } - - @Override - public List findByOAuth2ParamsId(UUID oauth2ParamsId) { - return DaoUtil.convertDataList(repository.findByOauth2ParamsId(oauth2ParamsId)); - } - -} - diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java deleted file mode 100644 index c5c8a50636..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java +++ /dev/null @@ -1,54 +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.sql.oauth2; - -import lombok.RequiredArgsConstructor; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; -import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.model.sql.OAuth2MobileEntity; -import org.thingsboard.server.dao.oauth2.OAuth2MobileDao; -import org.thingsboard.server.dao.sql.JpaAbstractDao; -import org.thingsboard.server.dao.util.SqlDao; - -import java.util.List; -import java.util.UUID; - -@Component -@RequiredArgsConstructor -@SqlDao -public class JpaOAuth2MobileDao extends JpaAbstractDao implements OAuth2MobileDao { - - private final OAuth2MobileRepository repository; - - @Override - protected Class getEntityClass() { - return OAuth2MobileEntity.class; - } - - @Override - protected JpaRepository getRepository() { - return repository; - } - - @Override - public List findByOAuth2ParamsId(UUID oauth2ParamsId) { - return DaoUtil.convertDataList(repository.findByOauth2ParamsId(oauth2ParamsId)); - } - -} - diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java deleted file mode 100644 index e4de6c2ea8..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java +++ /dev/null @@ -1,49 +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.sql.oauth2; - -import lombok.RequiredArgsConstructor; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Component; -import org.thingsboard.server.common.data.oauth2.OAuth2Params; -import org.thingsboard.server.dao.model.sql.OAuth2ParamsEntity; -import org.thingsboard.server.dao.oauth2.OAuth2ParamsDao; -import org.thingsboard.server.dao.sql.JpaAbstractDao; -import org.thingsboard.server.dao.util.SqlDao; - -import java.util.UUID; - -@Component -@RequiredArgsConstructor -@SqlDao -public class JpaOAuth2ParamsDao extends JpaAbstractDao implements OAuth2ParamsDao { - private final OAuth2ParamsRepository repository; - - @Override - protected Class getEntityClass() { - return OAuth2ParamsEntity.class; - } - - @Override - protected JpaRepository getRepository() { - return repository; - } - - @Override - public void deleteAll() { - repository.deleteAll(); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java index 09060d5106..a4e94eb6d9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java @@ -19,8 +19,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.oauth2.OAuth2Registration; +import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; import org.thingsboard.server.common.data.oauth2.PlatformType; -import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.OAuth2RegistrationEntity; import org.thingsboard.server.dao.oauth2.OAuth2RegistrationDao; @@ -48,14 +48,35 @@ public class JpaOAuth2RegistrationDao extends JpaAbstractDao findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(List domainSchemes, String domainName, String pkgName, PlatformType platformType) { - return DaoUtil.convertDataList(repository.findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(domainSchemes, domainName, pkgName, + public List findInfosByTenantId(UUID tenantId) { + return DaoUtil.convertDataList(repository.findInfosByTenantId(tenantId)); + } + + @Override + public List findByTenantId(UUID tenantId) { + return DaoUtil.convertDataList(repository.findByTenantId(tenantId)); + } + + @Override + public List findEnabledByDomainNameAndPlatformType(String domainName, PlatformType platformType) { + return DaoUtil.convertDataList(repository.findEnabledByDomainNameAndPlatformType(domainName, platformType != null ? "%" + platformType.name() + "%" : null)); } @Override - public List findByOAuth2ParamsId(UUID oauth2ParamsId) { - return DaoUtil.convertDataList(repository.findByOauth2ParamsId(oauth2ParamsId)); + public List findEnabledByPckNameAndPlatformType(String pkgName, PlatformType platformType) { + return DaoUtil.convertDataList(repository.findEnabledByPkgNameAndPlatformType(pkgName, + platformType != null ? "%" + platformType.name() + "%" : null)); + } + + @Override + public List findInfosByDomainId(UUID oauth2ParamsId) { + return DaoUtil.convertDataList(repository.findInfosByDomainId(oauth2ParamsId)); + } + + @Override + public List findInfosByMobileAppId(UUID mobileAppId) { + return DaoUtil.convertDataList(repository.findInfosByMobileAppId(mobileAppId)); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java index a8315eb654..16fbe74aed 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java @@ -18,36 +18,59 @@ package org.thingsboard.server.dao.sql.oauth2; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; -import org.thingsboard.server.common.data.oauth2.SchemeType; import org.thingsboard.server.dao.model.sql.OAuth2RegistrationEntity; +import org.thingsboard.server.dao.model.sql.OAuth2RegistrationInfoEntity; import java.util.List; import java.util.UUID; public interface OAuth2RegistrationRepository extends JpaRepository { - @Query("SELECT reg " + - "FROM OAuth2RegistrationEntity reg " + - "LEFT JOIN OAuth2ParamsEntity params on reg.oauth2ParamsId = params.id " + - "LEFT JOIN OAuth2DomainEntity domain on reg.oauth2ParamsId = domain.oauth2ParamsId " + - "WHERE params.enabled = true " + - "AND domain.domainName = :domainName " + - "AND domain.domainScheme IN (:domainSchemes) " + - "AND (:pkgName IS NULL OR EXISTS (SELECT mobile FROM OAuth2MobileEntity mobile WHERE mobile.oauth2ParamsId = reg.oauth2ParamsId AND mobile.pkgName = :pkgName)) " + - "AND (:platformFilter IS NULL OR reg.platforms IS NULL OR reg.platforms = '' OR reg.platforms LIKE :platformFilter)") - List findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(@Param("domainSchemes") List domainSchemes, - @Param("domainName") String domainName, - @Param("pkgName") String pkgName, - @Param("platformFilter") String platformFilter); - - List findByOauth2ParamsId(UUID oauth2ParamsId); - - @Query("SELECT mobile.appSecret " + - "FROM OAuth2MobileEntity mobile " + - "LEFT JOIN OAuth2RegistrationEntity reg on mobile.oauth2ParamsId = reg.oauth2ParamsId " + - "WHERE reg.id = :registrationId " + - "AND mobile.pkgName = :pkgName") + List findByTenantId(@Param("tenantId") UUID tenantId); + + @Query("SELECT new org.thingsboard.server.dao.model.sql.OAuth2RegistrationInfoEntity(r.id, r.createdTime, r.platforms, r.title) " + + "FROM OAuth2RegistrationEntity r " + + "WHERE r.tenantId = :tenantId") + List findInfosByTenantId(@Param("tenantId") UUID tenantId); + + @Query(value = "SELECT r " + + "FROM oauth2_registration r " + + "LEFT JOIN domain_oauth2_registration dr on dr.oauth2_registration_id = r.id " + + "LEFT JOIN domain d on dr.domain_id = d.id " + + "WHERE d.oauth2_enabled = true " + + "AND d.domain_name = :domainName " + + "AND (:platformFilter IS NULL OR r.platforms IS NULL OR r.platforms = '' OR r.platforms LIKE :platformFilter)", nativeQuery = true) + List findEnabledByDomainNameAndPlatformType(@Param("domainName") String domainName, + @Param("platformFilter") String platformFilter); + + @Query(value = "SELECT r " + + "FROM oauth2_registration r " + + "LEFT JOIN mobile_app_oauth2_registration mr on mr.oauth2_registration_id = r.id " + + "LEFT JOIN mobile_app m on mr.mobile_app_id = m.id " + + "WHERE m.oauth2_enabled = true " + + "AND m.pck_name = :pkgName " + + "AND (:platformFilter IS NULL OR r.platforms IS NULL OR r.platforms = '' OR r.platforms LIKE :platformFilter)", nativeQuery = true) + List findEnabledByPkgNameAndPlatformType(@Param("pkgName") String pkgName, + @Param("platformFilter") String platformFilter); + + @Query("SELECT new org.thingsboard.server.dao.model.sql.OAuth2RegistrationInfoEntity(r.id, r.createdTime, r.platforms, r.title) " + + "FROM OAuth2RegistrationEntity r " + + "LEFT JOIN DomainOauth2RegistrationEntity dr on dr.oauth2RegistrationId = r.id " + + "WHERE dr.domainId = :domainId ") + List findInfosByDomainId(UUID domainId); + + @Query("SELECT new org.thingsboard.server.dao.model.sql.OAuth2RegistrationInfoEntity(r.id, r.createdTime, r.platforms, r.title) " + + "FROM OAuth2RegistrationEntity r " + + "LEFT JOIN MobileAppOauth2RegistrationEntity mr on mr.oauth2RegistrationId = r.id " + + "WHERE mr.mobileAppId = :mobileAppId ") + List findInfosByMobileAppId(UUID mobileAppId); + + @Query("SELECT m.appSecret " + + "FROM MobileAppEntity m " + + "LEFT JOIN MobileAppOauth2RegistrationEntity mp on m.id = mp.mobileAppId " + + "LEFT JOIN OAuth2RegistrationEntity p on mp.oauth2RegistrationId = p.id " + + "WHERE p.id = :registrationId " + + "AND m.pkgName = :pkgName") String findAppSecret(@Param("registrationId") UUID id, @Param("pkgName") String pkgName); - } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index e86abdbb8b..75d04757ad 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -177,7 +177,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService { +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// }); +// } +// +// @Test +// public void testSaveHttpsAndMixedDomainsTogether() { +// OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(), +// OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), +// OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() +// )) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo(), +// validRegistrationInfo() +// )) +// .build() +// )); +// Assertions.assertThrows(DataValidationException.class, () -> { +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// }); +// } +// +// @Test +// public void testCreateAndFindParams() { +// OAuth2Info oAuth2Info = createDefaultOAuth2Info(); +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); +// Assert.assertNotNull(foundOAuth2Info); +// // TODO ask if it's safe to check equality on AdditionalProperties +// Assert.assertEquals(oAuth2Info, foundOAuth2Info); +// } +// +// @Test +// public void testDisableParams() { +// OAuth2Info oAuth2Info = createDefaultOAuth2Info(); +// oAuth2Info.setEnabled(true); +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); +// Assert.assertNotNull(foundOAuth2Info); +// Assert.assertEquals(oAuth2Info, foundOAuth2Info); +// +// oAuth2Info.setEnabled(false); +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// OAuth2Info foundDisabledOAuth2Info = oAuth2Service.findOAuth2Info(); +// Assert.assertEquals(oAuth2Info, foundDisabledOAuth2Info); +// } +// +// @Test +// public void testClearDomainParams() { +// OAuth2Info oAuth2Info = createDefaultOAuth2Info(); +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); +// Assert.assertNotNull(foundOAuth2Info); +// Assert.assertEquals(oAuth2Info, foundOAuth2Info); +// +// oAuth2Service.saveOAuth2Info(EMPTY_PARAMS); +// OAuth2Info foundAfterClearClientsParams = oAuth2Service.findOAuth2Info(); +// Assert.assertNotNull(foundAfterClearClientsParams); +// Assert.assertEquals(EMPTY_PARAMS, foundAfterClearClientsParams); +// } +// +// @Test +// public void testUpdateClientsParams() { +// OAuth2Info oAuth2Info = createDefaultOAuth2Info(); +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); +// Assert.assertNotNull(foundOAuth2Info); +// Assert.assertEquals(oAuth2Info, foundOAuth2Info); +// +// OAuth2Info newOAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("another-domain").scheme(SchemeType.HTTPS).build() +// )) +// .mobileInfos(Collections.emptyList()) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo() +// )) +// .build(), +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("test-domain").scheme(SchemeType.MIXED).build() +// )) +// .mobileInfos(Collections.emptyList()) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo() +// )) +// .build() +// )); +// oAuth2Service.saveOAuth2Info(newOAuth2Info); +// OAuth2Info foundAfterUpdateOAuth2Info = oAuth2Service.findOAuth2Info(); +// Assert.assertNotNull(foundAfterUpdateOAuth2Info); +// Assert.assertEquals(newOAuth2Info, foundAfterUpdateOAuth2Info); +// } +// +// @Test +// public void testGetOAuth2Clients() { +// List firstGroup = Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo(), +// validRegistrationInfo(), +// validRegistrationInfo() +// ); +// List secondGroup = Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo() +// ); +// List thirdGroup = Lists.newArrayList( +// validRegistrationInfo() +// ); +// OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), +// OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() +// )) +// .mobileInfos(Collections.emptyList()) +// .clientRegistrations(firstGroup) +// .build(), +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() +// )) +// .mobileInfos(Collections.emptyList()) +// .clientRegistrations(secondGroup) +// .build(), +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), +// OAuth2DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() +// )) +// .mobileInfos(Collections.emptyList()) +// .clientRegistrations(thirdGroup) +// .build() +// )); +// +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); +// Assert.assertNotNull(foundOAuth2Info); +// Assert.assertEquals(oAuth2Info, foundOAuth2Info); +// +// List firstGroupClientInfos = firstGroup.stream() +// .map(registrationInfo -> new OAuth2ClientInfo( +// registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) +// .collect(Collectors.toList()); +// List secondGroupClientInfos = secondGroup.stream() +// .map(registrationInfo -> new OAuth2ClientInfo( +// registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) +// .collect(Collectors.toList()); +// List thirdGroupClientInfos = thirdGroup.stream() +// .map(registrationInfo -> new OAuth2ClientInfo( +// registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) +// .collect(Collectors.toList()); +// +// List nonExistentDomainClients = oAuth2Service.getOAuth2Clients("http", "non-existent-domain", null, null); +// Assert.assertTrue(nonExistentDomainClients.isEmpty()); +// +// List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null, null); +// Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); +// firstGroupClientInfos.forEach(firstGroupClientInfo -> { +// Assert.assertTrue( +// firstDomainHttpClients.stream().anyMatch(clientInfo -> +// clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) +// && clientInfo.getName().equals(firstGroupClientInfo.getName())) +// ); +// }); +// +// List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null, null); +// Assert.assertTrue(firstDomainHttpsClients.isEmpty()); +// +// List fourthDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "fourth-domain", null, null); +// Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpClients.size()); +// secondGroupClientInfos.forEach(secondGroupClientInfo -> { +// Assert.assertTrue( +// fourthDomainHttpClients.stream().anyMatch(clientInfo -> +// clientInfo.getIcon().equals(secondGroupClientInfo.getIcon()) +// && clientInfo.getName().equals(secondGroupClientInfo.getName())) +// ); +// }); +// List fourthDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "fourth-domain", null, null); +// Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpsClients.size()); +// secondGroupClientInfos.forEach(secondGroupClientInfo -> { +// Assert.assertTrue( +// fourthDomainHttpsClients.stream().anyMatch(clientInfo -> +// clientInfo.getIcon().equals(secondGroupClientInfo.getIcon()) +// && clientInfo.getName().equals(secondGroupClientInfo.getName())) +// ); +// }); +// +// List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null, null); +// Assert.assertEquals(firstGroupClientInfos.size() + secondGroupClientInfos.size(), secondDomainHttpClients.size()); +// firstGroupClientInfos.forEach(firstGroupClientInfo -> { +// Assert.assertTrue( +// secondDomainHttpClients.stream().anyMatch(clientInfo -> +// clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) +// && clientInfo.getName().equals(firstGroupClientInfo.getName())) +// ); +// }); +// secondGroupClientInfos.forEach(secondGroupClientInfo -> { +// Assert.assertTrue( +// secondDomainHttpClients.stream().anyMatch(clientInfo -> +// clientInfo.getIcon().equals(secondGroupClientInfo.getIcon()) +// && clientInfo.getName().equals(secondGroupClientInfo.getName())) +// ); +// }); +// +// List secondDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "second-domain", null, null); +// Assert.assertEquals(firstGroupClientInfos.size() + thirdGroupClientInfos.size(), secondDomainHttpsClients.size()); +// firstGroupClientInfos.forEach(firstGroupClientInfo -> { +// Assert.assertTrue( +// secondDomainHttpsClients.stream().anyMatch(clientInfo -> +// clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) +// && clientInfo.getName().equals(firstGroupClientInfo.getName())) +// ); +// }); +// thirdGroupClientInfos.forEach(thirdGroupClientInfo -> { +// Assert.assertTrue( +// secondDomainHttpsClients.stream().anyMatch(clientInfo -> +// clientInfo.getIcon().equals(thirdGroupClientInfo.getIcon()) +// && clientInfo.getName().equals(thirdGroupClientInfo.getName())) +// ); +// }); +// } +// +// @Test +// public void testGetOAuth2ClientsForHttpAndHttps() { +// List firstGroup = Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo(), +// validRegistrationInfo(), +// validRegistrationInfo() +// ); +// OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), +// OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build() +// )) +// .mobileInfos(Collections.emptyList()) +// .clientRegistrations(firstGroup) +// .build() +// )); +// +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); +// Assert.assertNotNull(foundOAuth2Info); +// Assert.assertEquals(oAuth2Info, foundOAuth2Info); +// +// List firstGroupClientInfos = firstGroup.stream() +// .map(registrationInfo -> new OAuth2ClientInfo( +// registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) +// .collect(Collectors.toList()); +// +// List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null, null); +// Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); +// firstGroupClientInfos.forEach(firstGroupClientInfo -> { +// Assert.assertTrue( +// firstDomainHttpClients.stream().anyMatch(clientInfo -> +// clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) +// && clientInfo.getName().equals(firstGroupClientInfo.getName())) +// ); +// }); +// +// List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null, null); +// Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpsClients.size()); +// firstGroupClientInfos.forEach(firstGroupClientInfo -> { +// Assert.assertTrue( +// firstDomainHttpsClients.stream().anyMatch(clientInfo -> +// clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) +// && clientInfo.getName().equals(firstGroupClientInfo.getName())) +// ); +// }); +// } +// +// @Test +// public void testGetDisabledOAuth2Clients() { +// OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), +// OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() +// )) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo(), +// validRegistrationInfo() +// )) +// .build(), +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() +// )) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo() +// )) +// .build() +// )); +// +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// +// List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null, null); +// Assert.assertEquals(5, secondDomainHttpClients.size()); +// +// oAuth2Info.setEnabled(false); +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// +// List secondDomainHttpDisabledClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null, null); +// Assert.assertEquals(0, secondDomainHttpDisabledClients.size()); +// } +// +// @Test +// public void testFindAllRegistrations() { +// OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), +// OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() +// )) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo(), +// validRegistrationInfo() +// )) +// .build(), +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() +// )) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo() +// )) +// .build(), +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), +// OAuth2DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() +// )) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo() +// )) +// .build() +// )); +// +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// List foundRegistrations = oAuth2Service.findOauth2ProvidersByTenantId(); +// Assert.assertEquals(6, foundRegistrations.size()); +// oAuth2Info.getOauth2ParamsInfos().stream() +// .flatMap(paramsInfo -> paramsInfo.getClientRegistrations().stream()) +// .forEach(registrationInfo -> +// Assert.assertTrue( +// foundRegistrations.stream() +// .anyMatch(registration -> registration.getClientId().equals(registrationInfo.getClientId())) +// ) +// ); +// } +// +// @Test +// public void testFindRegistrationById() { +// OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), +// OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() +// )) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo(), +// validRegistrationInfo() +// )) +// .build(), +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() +// )) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo() +// )) +// .build(), +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), +// OAuth2DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() +// )) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo() +// )) +// .build() +// )); +// +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// List foundRegistrations = oAuth2Service.findOauth2ProvidersByTenantId(); +// foundRegistrations.forEach(registration -> { +// OAuth2Provider foundRegistration = oAuth2Service.findProvider(registration.getUuidId()); +// Assert.assertEquals(registration, foundRegistration); +// }); +// } +// +// @Test +// public void testFindAppSecret() { +// OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), +// OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() +// )) +// .mobileInfos(Lists.newArrayList( +// validMobileInfo("com.test.pkg1", "testPkg1AppSecret"), +// validMobileInfo("com.test.pkg2", "testPkg2AppSecret") +// )) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo(), +// validRegistrationInfo() +// )) +// .build(), +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() +// )) +// .mobileInfos(Collections.emptyList()) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo() +// )) +// .build() +// )); +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// +// OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); +// Assert.assertEquals(oAuth2Info, foundOAuth2Info); +// +// List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", null); +// Assert.assertEquals(3, firstDomainHttpClients.size()); +// for (OAuth2ClientInfo clientInfo : firstDomainHttpClients) { +// String[] segments = clientInfo.getUrl().split("/"); +// String registrationId = segments[segments.length-1]; +// String appSecret = oAuth2Service.findAppSecret(UUID.fromString(registrationId), "com.test.pkg1"); +// Assert.assertNotNull(appSecret); +// Assert.assertEquals("testPkg1AppSecret", appSecret); +// appSecret = oAuth2Service.findAppSecret(UUID.fromString(registrationId), "com.test.pkg2"); +// Assert.assertNotNull(appSecret); +// Assert.assertEquals("testPkg2AppSecret", appSecret); +// appSecret = oAuth2Service.findAppSecret(UUID.fromString(registrationId), "com.test.pkg3"); +// Assert.assertNull(appSecret); +// } +// } +// +// @Test +// public void testFindClientsByPackageAndPlatform() { +// OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), +// OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() +// )) +// .mobileInfos(Lists.newArrayList( +// validMobileInfo("com.test.pkg1", "testPkg1Callback"), +// validMobileInfo("com.test.pkg2", "testPkg2Callback") +// )) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo("Google", Arrays.asList(PlatformType.WEB, PlatformType.ANDROID)), +// validRegistrationInfo("Facebook", Arrays.asList(PlatformType.IOS)), +// validRegistrationInfo("GitHub", Collections.emptyList()) +// )) +// .build(), +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() +// )) +// .mobileInfos(Collections.emptyList()) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo() +// )) +// .build() +// )); +// oAuth2Service.saveOAuth2Info(oAuth2Info); +// +// OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); +// Assert.assertEquals(oAuth2Info, foundOAuth2Info); +// +// List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null, null); +// Assert.assertEquals(3, firstDomainHttpClients.size()); +// List pkg1Clients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", null); +// Assert.assertEquals(3, pkg1Clients.size()); +// List pkg1AndroidClients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", PlatformType.ANDROID); +// Assert.assertEquals(2, pkg1AndroidClients.size()); +// Assert.assertTrue(pkg1AndroidClients.stream().anyMatch(client -> client.getName().equals("Google"))); +// Assert.assertTrue(pkg1AndroidClients.stream().anyMatch(client -> client.getName().equals("GitHub"))); +// List pkg1IOSClients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", PlatformType.IOS); +// Assert.assertEquals(2, pkg1IOSClients.size()); +// Assert.assertTrue(pkg1IOSClients.stream().anyMatch(client -> client.getName().equals("Facebook"))); +// Assert.assertTrue(pkg1IOSClients.stream().anyMatch(client -> client.getName().equals("GitHub"))); +// } +// +// private OAuth2Info createDefaultOAuth2Info() { +// return new OAuth2Info(true, false, Lists.newArrayList( +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), +// OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() +// )) +// .mobileInfos(Collections.emptyList()) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo(), +// validRegistrationInfo(), +// validRegistrationInfo() +// )) +// .build(), +// OAuth2ParamsInfo.builder() +// .domainInfos(Lists.newArrayList( +// OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), +// OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() +// )) +// .mobileInfos(Collections.emptyList()) +// .clientRegistrations(Lists.newArrayList( +// validRegistrationInfo(), +// validRegistrationInfo() +// )) +// .build() +// )); +// } +// +// private OAuth2RegistrationInfo validRegistrationInfo() { +// return validRegistrationInfo(null, Collections.emptyList()); +// } +// +// private OAuth2RegistrationInfo validRegistrationInfo(String label, List platforms) { +// return OAuth2RegistrationInfo.builder() +// .clientId(UUID.randomUUID().toString()) +// .clientSecret(UUID.randomUUID().toString()) +// .authorizationUri(UUID.randomUUID().toString()) +// .accessTokenUri(UUID.randomUUID().toString()) +// .scope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString())) +// .platforms(platforms == null ? Collections.emptyList() : platforms) +// .userInfoUri(UUID.randomUUID().toString()) +// .userNameAttributeName(UUID.randomUUID().toString()) +// .jwkSetUri(UUID.randomUUID().toString()) +// .clientAuthenticationMethod(UUID.randomUUID().toString()) +// .loginButtonLabel(label != null ? label : UUID.randomUUID().toString()) +// .loginButtonIcon(UUID.randomUUID().toString()) +// .additionalInfo(JacksonUtil.newObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString())) +// .mapperConfig( +// OAuth2MapperConfig.builder() +// .allowUserCreation(true) +// .activateUser(true) +// .type(MapperType.CUSTOM) +// .custom( +// OAuth2CustomMapperConfig.builder() +// .url(UUID.randomUUID().toString()) +// .build() +// ) +// .build() +// ) +// .build(); +// } +// +// private MobileAppInfo validMobileInfo(String pkgName, String appSecret) { +// return MobileAppInfo.builder().pkgName(pkgName) +// .appSecret(appSecret != null ? appSecret : StringUtils.randomAlphanumeric(24)) +// .build(); +// } + +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ServiceTest.java deleted file mode 100644 index 281709c9af..0000000000 --- a/dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ServiceTest.java +++ /dev/null @@ -1,668 +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; - -import com.google.common.collect.Lists; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.jupiter.api.Assertions; -import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.common.util.JacksonUtil; -import org.thingsboard.server.common.data.StringUtils; -import org.thingsboard.server.common.data.oauth2.MapperType; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; -import org.thingsboard.server.common.data.oauth2.OAuth2DomainInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2Info; -import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; -import org.thingsboard.server.common.data.oauth2.OAuth2MobileInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ParamsInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2Registration; -import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo; -import org.thingsboard.server.common.data.oauth2.PlatformType; -import org.thingsboard.server.common.data.oauth2.SchemeType; -import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.oauth2.OAuth2Service; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.UUID; -import java.util.stream.Collectors; - -@DaoSqlTest -public class OAuth2ServiceTest extends AbstractServiceTest { - private static final OAuth2Info EMPTY_PARAMS = new OAuth2Info(false, false, Collections.emptyList()); - - @Autowired - protected OAuth2Service oAuth2Service; - - @Before - public void beforeRun() { - Assert.assertTrue(oAuth2Service.findAllRegistrations().isEmpty()); - } - - @After - public void after() { - oAuth2Service.saveOAuth2Info(EMPTY_PARAMS); - Assert.assertTrue(oAuth2Service.findAllRegistrations().isEmpty()); - Assert.assertTrue(oAuth2Service.findOAuth2Info().getOauth2ParamsInfos().isEmpty()); - } - - @Test - public void testSaveHttpAndMixedDomainsTogether() { - OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), - OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() - )) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo(), - validRegistrationInfo() - )) - .build() - )); - Assertions.assertThrows(DataValidationException.class, () -> { - oAuth2Service.saveOAuth2Info(oAuth2Info); - }); - } - - @Test - public void testSaveHttpsAndMixedDomainsTogether() { - OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(), - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), - OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() - )) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo(), - validRegistrationInfo() - )) - .build() - )); - Assertions.assertThrows(DataValidationException.class, () -> { - oAuth2Service.saveOAuth2Info(oAuth2Info); - }); - } - - @Test - public void testCreateAndFindParams() { - OAuth2Info oAuth2Info = createDefaultOAuth2Info(); - oAuth2Service.saveOAuth2Info(oAuth2Info); - OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); - Assert.assertNotNull(foundOAuth2Info); - // TODO ask if it's safe to check equality on AdditionalProperties - Assert.assertEquals(oAuth2Info, foundOAuth2Info); - } - - @Test - public void testDisableParams() { - OAuth2Info oAuth2Info = createDefaultOAuth2Info(); - oAuth2Info.setEnabled(true); - oAuth2Service.saveOAuth2Info(oAuth2Info); - OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); - Assert.assertNotNull(foundOAuth2Info); - Assert.assertEquals(oAuth2Info, foundOAuth2Info); - - oAuth2Info.setEnabled(false); - oAuth2Service.saveOAuth2Info(oAuth2Info); - OAuth2Info foundDisabledOAuth2Info = oAuth2Service.findOAuth2Info(); - Assert.assertEquals(oAuth2Info, foundDisabledOAuth2Info); - } - - @Test - public void testClearDomainParams() { - OAuth2Info oAuth2Info = createDefaultOAuth2Info(); - oAuth2Service.saveOAuth2Info(oAuth2Info); - OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); - Assert.assertNotNull(foundOAuth2Info); - Assert.assertEquals(oAuth2Info, foundOAuth2Info); - - oAuth2Service.saveOAuth2Info(EMPTY_PARAMS); - OAuth2Info foundAfterClearClientsParams = oAuth2Service.findOAuth2Info(); - Assert.assertNotNull(foundAfterClearClientsParams); - Assert.assertEquals(EMPTY_PARAMS, foundAfterClearClientsParams); - } - - @Test - public void testUpdateClientsParams() { - OAuth2Info oAuth2Info = createDefaultOAuth2Info(); - oAuth2Service.saveOAuth2Info(oAuth2Info); - OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); - Assert.assertNotNull(foundOAuth2Info); - Assert.assertEquals(oAuth2Info, foundOAuth2Info); - - OAuth2Info newOAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("another-domain").scheme(SchemeType.HTTPS).build() - )) - .mobileInfos(Collections.emptyList()) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo() - )) - .build(), - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("test-domain").scheme(SchemeType.MIXED).build() - )) - .mobileInfos(Collections.emptyList()) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo() - )) - .build() - )); - oAuth2Service.saveOAuth2Info(newOAuth2Info); - OAuth2Info foundAfterUpdateOAuth2Info = oAuth2Service.findOAuth2Info(); - Assert.assertNotNull(foundAfterUpdateOAuth2Info); - Assert.assertEquals(newOAuth2Info, foundAfterUpdateOAuth2Info); - } - - @Test - public void testGetOAuth2Clients() { - List firstGroup = Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo(), - validRegistrationInfo(), - validRegistrationInfo() - ); - List secondGroup = Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo() - ); - List thirdGroup = Lists.newArrayList( - validRegistrationInfo() - ); - OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() - )) - .mobileInfos(Collections.emptyList()) - .clientRegistrations(firstGroup) - .build(), - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() - )) - .mobileInfos(Collections.emptyList()) - .clientRegistrations(secondGroup) - .build(), - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), - OAuth2DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() - )) - .mobileInfos(Collections.emptyList()) - .clientRegistrations(thirdGroup) - .build() - )); - - oAuth2Service.saveOAuth2Info(oAuth2Info); - OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); - Assert.assertNotNull(foundOAuth2Info); - Assert.assertEquals(oAuth2Info, foundOAuth2Info); - - List firstGroupClientInfos = firstGroup.stream() - .map(registrationInfo -> new OAuth2ClientInfo( - registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) - .collect(Collectors.toList()); - List secondGroupClientInfos = secondGroup.stream() - .map(registrationInfo -> new OAuth2ClientInfo( - registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) - .collect(Collectors.toList()); - List thirdGroupClientInfos = thirdGroup.stream() - .map(registrationInfo -> new OAuth2ClientInfo( - registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) - .collect(Collectors.toList()); - - List nonExistentDomainClients = oAuth2Service.getOAuth2Clients("http", "non-existent-domain", null, null); - Assert.assertTrue(nonExistentDomainClients.isEmpty()); - - List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null, null); - Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); - firstGroupClientInfos.forEach(firstGroupClientInfo -> { - Assert.assertTrue( - firstDomainHttpClients.stream().anyMatch(clientInfo -> - clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) - && clientInfo.getName().equals(firstGroupClientInfo.getName())) - ); - }); - - List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null, null); - Assert.assertTrue(firstDomainHttpsClients.isEmpty()); - - List fourthDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "fourth-domain", null, null); - Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpClients.size()); - secondGroupClientInfos.forEach(secondGroupClientInfo -> { - Assert.assertTrue( - fourthDomainHttpClients.stream().anyMatch(clientInfo -> - clientInfo.getIcon().equals(secondGroupClientInfo.getIcon()) - && clientInfo.getName().equals(secondGroupClientInfo.getName())) - ); - }); - List fourthDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "fourth-domain", null, null); - Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpsClients.size()); - secondGroupClientInfos.forEach(secondGroupClientInfo -> { - Assert.assertTrue( - fourthDomainHttpsClients.stream().anyMatch(clientInfo -> - clientInfo.getIcon().equals(secondGroupClientInfo.getIcon()) - && clientInfo.getName().equals(secondGroupClientInfo.getName())) - ); - }); - - List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null, null); - Assert.assertEquals(firstGroupClientInfos.size() + secondGroupClientInfos.size(), secondDomainHttpClients.size()); - firstGroupClientInfos.forEach(firstGroupClientInfo -> { - Assert.assertTrue( - secondDomainHttpClients.stream().anyMatch(clientInfo -> - clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) - && clientInfo.getName().equals(firstGroupClientInfo.getName())) - ); - }); - secondGroupClientInfos.forEach(secondGroupClientInfo -> { - Assert.assertTrue( - secondDomainHttpClients.stream().anyMatch(clientInfo -> - clientInfo.getIcon().equals(secondGroupClientInfo.getIcon()) - && clientInfo.getName().equals(secondGroupClientInfo.getName())) - ); - }); - - List secondDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "second-domain", null, null); - Assert.assertEquals(firstGroupClientInfos.size() + thirdGroupClientInfos.size(), secondDomainHttpsClients.size()); - firstGroupClientInfos.forEach(firstGroupClientInfo -> { - Assert.assertTrue( - secondDomainHttpsClients.stream().anyMatch(clientInfo -> - clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) - && clientInfo.getName().equals(firstGroupClientInfo.getName())) - ); - }); - thirdGroupClientInfos.forEach(thirdGroupClientInfo -> { - Assert.assertTrue( - secondDomainHttpsClients.stream().anyMatch(clientInfo -> - clientInfo.getIcon().equals(thirdGroupClientInfo.getIcon()) - && clientInfo.getName().equals(thirdGroupClientInfo.getName())) - ); - }); - } - - @Test - public void testGetOAuth2ClientsForHttpAndHttps() { - List firstGroup = Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo(), - validRegistrationInfo(), - validRegistrationInfo() - ); - OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build() - )) - .mobileInfos(Collections.emptyList()) - .clientRegistrations(firstGroup) - .build() - )); - - oAuth2Service.saveOAuth2Info(oAuth2Info); - OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); - Assert.assertNotNull(foundOAuth2Info); - Assert.assertEquals(oAuth2Info, foundOAuth2Info); - - List firstGroupClientInfos = firstGroup.stream() - .map(registrationInfo -> new OAuth2ClientInfo( - registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null)) - .collect(Collectors.toList()); - - List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null, null); - Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); - firstGroupClientInfos.forEach(firstGroupClientInfo -> { - Assert.assertTrue( - firstDomainHttpClients.stream().anyMatch(clientInfo -> - clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) - && clientInfo.getName().equals(firstGroupClientInfo.getName())) - ); - }); - - List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null, null); - Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpsClients.size()); - firstGroupClientInfos.forEach(firstGroupClientInfo -> { - Assert.assertTrue( - firstDomainHttpsClients.stream().anyMatch(clientInfo -> - clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) - && clientInfo.getName().equals(firstGroupClientInfo.getName())) - ); - }); - } - - @Test - public void testGetDisabledOAuth2Clients() { - OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() - )) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo(), - validRegistrationInfo() - )) - .build(), - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() - )) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo() - )) - .build() - )); - - oAuth2Service.saveOAuth2Info(oAuth2Info); - - List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null, null); - Assert.assertEquals(5, secondDomainHttpClients.size()); - - oAuth2Info.setEnabled(false); - oAuth2Service.saveOAuth2Info(oAuth2Info); - - List secondDomainHttpDisabledClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null, null); - Assert.assertEquals(0, secondDomainHttpDisabledClients.size()); - } - - @Test - public void testFindAllRegistrations() { - OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() - )) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo(), - validRegistrationInfo() - )) - .build(), - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() - )) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo() - )) - .build(), - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), - OAuth2DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() - )) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo() - )) - .build() - )); - - oAuth2Service.saveOAuth2Info(oAuth2Info); - List foundRegistrations = oAuth2Service.findAllRegistrations(); - Assert.assertEquals(6, foundRegistrations.size()); - oAuth2Info.getOauth2ParamsInfos().stream() - .flatMap(paramsInfo -> paramsInfo.getClientRegistrations().stream()) - .forEach(registrationInfo -> - Assert.assertTrue( - foundRegistrations.stream() - .anyMatch(registration -> registration.getClientId().equals(registrationInfo.getClientId())) - ) - ); - } - - @Test - public void testFindRegistrationById() { - OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() - )) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo(), - validRegistrationInfo() - )) - .build(), - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() - )) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo() - )) - .build(), - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), - OAuth2DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() - )) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo() - )) - .build() - )); - - oAuth2Service.saveOAuth2Info(oAuth2Info); - List foundRegistrations = oAuth2Service.findAllRegistrations(); - foundRegistrations.forEach(registration -> { - OAuth2Registration foundRegistration = oAuth2Service.findRegistration(registration.getUuidId()); - Assert.assertEquals(registration, foundRegistration); - }); - } - - @Test - public void testFindAppSecret() { - OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() - )) - .mobileInfos(Lists.newArrayList( - validMobileInfo("com.test.pkg1", "testPkg1AppSecret"), - validMobileInfo("com.test.pkg2", "testPkg2AppSecret") - )) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo(), - validRegistrationInfo() - )) - .build(), - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() - )) - .mobileInfos(Collections.emptyList()) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo() - )) - .build() - )); - oAuth2Service.saveOAuth2Info(oAuth2Info); - - OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); - Assert.assertEquals(oAuth2Info, foundOAuth2Info); - - List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", null); - Assert.assertEquals(3, firstDomainHttpClients.size()); - for (OAuth2ClientInfo clientInfo : firstDomainHttpClients) { - String[] segments = clientInfo.getUrl().split("/"); - String registrationId = segments[segments.length-1]; - String appSecret = oAuth2Service.findAppSecret(UUID.fromString(registrationId), "com.test.pkg1"); - Assert.assertNotNull(appSecret); - Assert.assertEquals("testPkg1AppSecret", appSecret); - appSecret = oAuth2Service.findAppSecret(UUID.fromString(registrationId), "com.test.pkg2"); - Assert.assertNotNull(appSecret); - Assert.assertEquals("testPkg2AppSecret", appSecret); - appSecret = oAuth2Service.findAppSecret(UUID.fromString(registrationId), "com.test.pkg3"); - Assert.assertNull(appSecret); - } - } - - @Test - public void testFindClientsByPackageAndPlatform() { - OAuth2Info oAuth2Info = new OAuth2Info(true, false, Lists.newArrayList( - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() - )) - .mobileInfos(Lists.newArrayList( - validMobileInfo("com.test.pkg1", "testPkg1Callback"), - validMobileInfo("com.test.pkg2", "testPkg2Callback") - )) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo("Google", Arrays.asList(PlatformType.WEB, PlatformType.ANDROID)), - validRegistrationInfo("Facebook", Arrays.asList(PlatformType.IOS)), - validRegistrationInfo("GitHub", Collections.emptyList()) - )) - .build(), - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() - )) - .mobileInfos(Collections.emptyList()) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo() - )) - .build() - )); - oAuth2Service.saveOAuth2Info(oAuth2Info); - - OAuth2Info foundOAuth2Info = oAuth2Service.findOAuth2Info(); - Assert.assertEquals(oAuth2Info, foundOAuth2Info); - - List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null, null); - Assert.assertEquals(3, firstDomainHttpClients.size()); - List pkg1Clients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", null); - Assert.assertEquals(3, pkg1Clients.size()); - List pkg1AndroidClients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", PlatformType.ANDROID); - Assert.assertEquals(2, pkg1AndroidClients.size()); - Assert.assertTrue(pkg1AndroidClients.stream().anyMatch(client -> client.getName().equals("Google"))); - Assert.assertTrue(pkg1AndroidClients.stream().anyMatch(client -> client.getName().equals("GitHub"))); - List pkg1IOSClients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", PlatformType.IOS); - Assert.assertEquals(2, pkg1IOSClients.size()); - Assert.assertTrue(pkg1IOSClients.stream().anyMatch(client -> client.getName().equals("Facebook"))); - Assert.assertTrue(pkg1IOSClients.stream().anyMatch(client -> client.getName().equals("GitHub"))); - } - - private OAuth2Info createDefaultOAuth2Info() { - return new OAuth2Info(true, false, Lists.newArrayList( - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - OAuth2DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() - )) - .mobileInfos(Collections.emptyList()) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo(), - validRegistrationInfo(), - validRegistrationInfo() - )) - .build(), - OAuth2ParamsInfo.builder() - .domainInfos(Lists.newArrayList( - OAuth2DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), - OAuth2DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() - )) - .mobileInfos(Collections.emptyList()) - .clientRegistrations(Lists.newArrayList( - validRegistrationInfo(), - validRegistrationInfo() - )) - .build() - )); - } - - private OAuth2RegistrationInfo validRegistrationInfo() { - return validRegistrationInfo(null, Collections.emptyList()); - } - - private OAuth2RegistrationInfo validRegistrationInfo(String label, List platforms) { - return OAuth2RegistrationInfo.builder() - .clientId(UUID.randomUUID().toString()) - .clientSecret(UUID.randomUUID().toString()) - .authorizationUri(UUID.randomUUID().toString()) - .accessTokenUri(UUID.randomUUID().toString()) - .scope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString())) - .platforms(platforms == null ? Collections.emptyList() : platforms) - .userInfoUri(UUID.randomUUID().toString()) - .userNameAttributeName(UUID.randomUUID().toString()) - .jwkSetUri(UUID.randomUUID().toString()) - .clientAuthenticationMethod(UUID.randomUUID().toString()) - .loginButtonLabel(label != null ? label : UUID.randomUUID().toString()) - .loginButtonIcon(UUID.randomUUID().toString()) - .additionalInfo(JacksonUtil.newObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString())) - .mapperConfig( - OAuth2MapperConfig.builder() - .allowUserCreation(true) - .activateUser(true) - .type(MapperType.CUSTOM) - .custom( - OAuth2CustomMapperConfig.builder() - .url(UUID.randomUUID().toString()) - .build() - ) - .build() - ) - .build(); - } - - private OAuth2MobileInfo validMobileInfo(String pkgName, String appSecret) { - return OAuth2MobileInfo.builder().pkgName(pkgName) - .appSecret(appSecret != null ? appSecret : StringUtils.randomAlphanumeric(24)) - .build(); - } - -} diff --git a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java index 8783770be3..1415edddc8 100644 --- a/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java +++ b/rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java @@ -119,7 +119,6 @@ import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.TsKvEntry; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; -import org.thingsboard.server.common.data.oauth2.OAuth2Info; import org.thingsboard.server.common.data.oauth2.PlatformType; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.OtaPackageType; @@ -2071,13 +2070,13 @@ 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 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 String getLoginProcessingUrl() { return restTemplate.getForEntity(baseURL + "/api/oauth2/loginProcessingUrl", String.class).getBody();