Browse Source

oauth2 configuration breakdown: initial implementation

pull/11231/head
dashevchenko 2 years ago
parent
commit
f274c274e9
  1. 83
      application/src/main/data/upgrade/3.7.0/schema_update.sql
  2. 6
      application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java
  3. 39
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  4. 142
      application/src/main/java/org/thingsboard/server/controller/DomainController.java
  5. 142
      application/src/main/java/org/thingsboard/server/controller/MobileAppController.java
  6. 73
      application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
  7. 2
      application/src/main/java/org/thingsboard/server/service/edge/DefaultEdgeNotificationService.java
  8. 4
      application/src/main/java/org/thingsboard/server/service/edge/EdgeContextComponent.java
  9. 8
      application/src/main/java/org/thingsboard/server/service/edge/EdgeEventSourcingListener.java
  10. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeGrpcSession.java
  11. 2
      application/src/main/java/org/thingsboard/server/service/edge/rpc/EdgeSyncCursor.java
  12. 6
      application/src/main/java/org/thingsboard/server/service/edge/rpc/constructor/oauth2/OAuth2MsgConstructor.java
  13. 12
      application/src/main/java/org/thingsboard/server/service/edge/rpc/fetch/OAuth2EdgeEventFetcher.java
  14. 4
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessor.java
  15. 14
      application/src/main/java/org/thingsboard/server/service/edge/rpc/processor/oauth2/OAuth2EdgeProcessor.java
  16. 77
      application/src/main/java/org/thingsboard/server/service/entitiy/domain/DefaultTbDomainService.java
  17. 15
      application/src/main/java/org/thingsboard/server/service/entitiy/domain/TbDomainService.java
  18. 74
      application/src/main/java/org/thingsboard/server/service/entitiy/mobile/DefaultTbMobileAppService.java
  19. 14
      application/src/main/java/org/thingsboard/server/service/entitiy/mobile/TbMobileAppService.java
  20. 63
      application/src/main/java/org/thingsboard/server/service/entitiy/oauth2client/DefaultTbOauth2ClientService.java
  21. 31
      application/src/main/java/org/thingsboard/server/service/entitiy/oauth2client/TbOauth2ClientService.java
  22. 8
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  23. 11
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java
  24. 4
      application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java
  25. 4
      application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java
  26. 6
      application/src/main/java/org/thingsboard/server/service/system/DefaultSystemInfoService.java
  27. 84
      application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java
  28. 14
      application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java
  29. 74
      application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java
  30. 4
      application/src/test/java/org/thingsboard/server/service/edge/rpc/processor/BaseEdgeProcessorTest.java
  31. 44
      common/dao-api/src/main/java/org/thingsboard/server/dao/domain/DomainService.java
  32. 41
      common/dao-api/src/main/java/org/thingsboard/server/dao/mobile/MobileAppService.java
  33. 21
      common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientService.java
  34. 5
      common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
  35. 3
      common/data/src/main/java/org/thingsboard/server/common/data/audit/ActionType.java
  36. 52
      common/data/src/main/java/org/thingsboard/server/common/data/domain/Domain.java
  37. 24
      common/data/src/main/java/org/thingsboard/server/common/data/domain/DomainInfo.java
  38. 20
      common/data/src/main/java/org/thingsboard/server/common/data/domain/DomainOauth2Registration.java
  39. 2
      common/data/src/main/java/org/thingsboard/server/common/data/edge/EdgeEventType.java
  40. 14
      common/data/src/main/java/org/thingsboard/server/common/data/id/DomainId.java
  41. 6
      common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java
  42. 14
      common/data/src/main/java/org/thingsboard/server/common/data/id/MobileAppId.java
  43. 8
      common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2RegistrationId.java
  44. 59
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileApp.java
  45. 25
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppInfo.java
  46. 32
      common/data/src/main/java/org/thingsboard/server/common/data/mobile/MobileAppOauth2Registration.java
  47. 12
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/HasOauth2Registrations.java
  48. 42
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java
  49. 38
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java
  50. 42
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java
  51. 46
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java
  52. 29
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java
  53. 58
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java
  54. 39
      dao/src/main/java/org/thingsboard/server/dao/domain/DomainDao.java
  55. 167
      dao/src/main/java/org/thingsboard/server/dao/domain/DomainServiceImpl.java
  56. 36
      dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppDao.java
  57. 163
      dao/src/main/java/org/thingsboard/server/dao/mobile/MobileAppServiceImpl.java
  58. 39
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  59. 81
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainEntity.java
  60. 37
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainOauth2RegistrationCompositeKey.java
  61. 66
      dao/src/main/java/org/thingsboard/server/dao/model/sql/DomainOauth2RegistrationEntity.java
  62. 46
      dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppEntity.java
  63. 37
      dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppOauth2RegistrationCompositeKey.java
  64. 67
      dao/src/main/java/org/thingsboard/server/dao/model/sql/MobileAppOauth2RegistrationEntity.java
  65. 76
      dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java
  66. 70
      dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java
  67. 16
      dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationEntity.java
  68. 61
      dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2RegistrationInfoEntity.java
  69. 6
      dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java
  70. 145
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientServiceImpl.java
  71. 14
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java
  72. 295
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java
  73. 99
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java
  74. 36
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DomainDataValidator.java
  75. 43
      dao/src/main/java/org/thingsboard/server/dao/service/validator/MobileAppDataValidator.java
  76. 114
      dao/src/main/java/org/thingsboard/server/dao/service/validator/Oauth2RegistrationDataValidator.java
  77. 34
      dao/src/main/java/org/thingsboard/server/dao/sql/domain/DomainOauth2RegistrationRepository.java
  78. 39
      dao/src/main/java/org/thingsboard/server/dao/sql/domain/DomainRepository.java
  79. 83
      dao/src/main/java/org/thingsboard/server/dao/sql/domain/JpaDomainDao.java
  80. 77
      dao/src/main/java/org/thingsboard/server/dao/sql/mobile/JpaMobileAppDao.java
  81. 32
      dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppOauth2RegistrationRepository.java
  82. 19
      dao/src/main/java/org/thingsboard/server/dao/sql/mobile/MobileAppRepository.java
  83. 54
      dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java
  84. 54
      dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java
  85. 49
      dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java
  86. 31
      dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2RegistrationDao.java
  87. 69
      dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2RegistrationRepository.java
  88. 2
      dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java
  89. 49
      dao/src/main/resources/sql/schema-entities.sql
  90. 641
      dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ClientServiceTest.java
  91. 668
      dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ServiceTest.java
  92. 15
      rest-client/src/main/java/org/thingsboard/rest/client/RestClient.java

83
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

6
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.springframework.web.util.UriComponentsBuilder;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration; 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.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterNames; import org.thingsboard.server.service.security.auth.oauth2.TbOAuth2ParameterNames;
import org.thingsboard.server.service.security.model.token.OAuth2AppTokenFactory; import org.thingsboard.server.service.security.model.token.OAuth2AppTokenFactory;
@ -70,7 +70,7 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
private ClientRegistrationRepository clientRegistrationRepository; private ClientRegistrationRepository clientRegistrationRepository;
@Autowired @Autowired
private OAuth2Service oAuth2Service; private OAuth2ClientService oAuth2ClientService;
@Autowired @Autowired
private OAuth2AppTokenFactory oAuth2AppTokenFactory; private OAuth2AppTokenFactory oAuth2AppTokenFactory;
@ -131,7 +131,7 @@ public class CustomOAuth2AuthorizationRequestResolver implements OAuth2Authoriza
if (StringUtils.isEmpty(appToken)) { if (StringUtils.isEmpty(appToken)) {
throw new IllegalArgumentException("Invalid application token."); throw new IllegalArgumentException("Invalid application token.");
} else { } else {
String appSecret = this.oAuth2Service.findAppSecret(UUID.fromString(registrationId), appPackage); String appSecret = this.oAuth2ClientService.findAppSecret(UUID.fromString(registrationId), appPackage);
if (StringUtils.isEmpty(appSecret)) { if (StringUtils.isEmpty(appSecret)) {
throw new IllegalArgumentException("Invalid package: " + appPackage + ". No application secret found for Client Registration with given application package."); throw new IllegalArgumentException("Invalid package: " + appPackage + ". No application secret found for Client Registration with given application package.");
} }

39
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.AssetInfo;
import org.thingsboard.server.common.data.asset.AssetProfile; import org.thingsboard.server.common.data.asset.AssetProfile;
import org.thingsboard.server.common.data.audit.ActionType; 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.Edge;
import org.thingsboard.server.common.data.edge.EdgeInfo; import org.thingsboard.server.common.data.edge.EdgeInfo;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; 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.DashboardId;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId; 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.EdgeId;
import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityIdFactory; import org.thingsboard.server.common.data.id.EntityIdFactory;
import org.thingsboard.server.common.data.id.EntityViewId; import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.HasId; 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.OtaPackageId;
import org.thingsboard.server.common.data.id.QueueId; import org.thingsboard.server.common.data.id.QueueId;
import org.thingsboard.server.common.data.id.RpcId; 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.UserId;
import org.thingsboard.server.common.data.id.WidgetTypeId; import org.thingsboard.server.common.data.id.WidgetTypeId;
import org.thingsboard.server.common.data.id.WidgetsBundleId; 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.PageLink;
import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.SortOrder;
import org.thingsboard.server.common.data.page.TimePageLink; 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.DeviceCredentialsService;
import org.thingsboard.server.dao.device.DeviceProfileService; import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService; 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.edge.EdgeService;
import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException; 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.model.ModelConstants;
import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; 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.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
@ -233,7 +241,13 @@ public abstract class BaseController {
protected DashboardService dashboardService; protected DashboardService dashboardService;
@Autowired @Autowired
protected OAuth2Service oAuth2Service; protected OAuth2ClientService oAuth2ClientService;
@Autowired
protected DomainService domainService;
@Autowired
protected MobileAppService mobileAppService;
@Autowired @Autowired
protected OAuth2ConfigTemplateService oAuth2ConfigTemplateService; protected OAuth2ConfigTemplateService oAuth2ConfigTemplateService;
@ -600,6 +614,15 @@ public abstract class BaseController {
case QUEUE: case QUEUE:
checkQueueId(new QueueId(entityId.getId()), operation); checkQueueId(new QueueId(entityId.getId()), operation);
return; 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: default:
checkEntityId(entityId, entitiesService::findEntityByTenantIdAndId, operation); checkEntityId(entityId, entitiesService::findEntityByTenantIdAndId, operation);
} }
@ -776,6 +799,18 @@ public abstract class BaseController {
return queue; 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 extends EntityId> I emptyId(EntityType entityType) { protected <I extends EntityId> I emptyId(EntityType entityType) {
return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID); return (I) EntityIdFactory.getByTypeAndUuid(entityType, ModelConstants.NULL_UUID);
} }

142
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<String> oauth2ClientIds = ids != null ? Arrays.asList(ids) : Collections.emptyList();
domain.setTenantId(getCurrentUser().getTenantId());
checkEntity(domain.getId(), domain, Resource.DOMAIN);
List<OAuth2RegistrationId> 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<OAuth2RegistrationId> 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<DomainInfo> 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);
}
}

142
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<OAuth2RegistrationId> 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<OAuth2RegistrationId> 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<MobileAppInfo> 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);
}
}

73
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.Parameter;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize; 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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; 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.ResponseStatus;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.exception.ThingsboardException; 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.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.common.data.oauth2.PlatformType;
import org.thingsboard.server.config.annotations.ApiOperation; import org.thingsboard.server.config.annotations.ApiOperation;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration; import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.queue.util.TbCoreComponent; 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.Operation;
import org.thingsboard.server.service.security.permission.Resource; import org.thingsboard.server.service.security.permission.Resource;
import org.thingsboard.server.utils.MiscUtils; import org.thingsboard.server.utils.MiscUtils;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; 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.SYSTEM_AUTHORITY_PARAGRAPH;
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
@RestController @RestController
@TbCoreComponent @TbCoreComponent
@RequestMapping("/api") @RequestMapping("/api")
@RequiredArgsConstructor
@Slf4j @Slf4j
public class OAuth2Controller extends BaseController { public class OAuth2Controller extends BaseController {
@Autowired private final OAuth2Configuration oAuth2Configuration;
private OAuth2Configuration oAuth2Configuration;
private final TbOauth2ClientService tbOauth2ClientService;
@ApiOperation(value = "Get OAuth2 clients (getOAuth2Clients)", notes = "Get the list of OAuth2 clients " + @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 - " + "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)") "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) @PostMapping(value = "/noauth/oauth2Clients")
@ResponseBody
public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request, public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request,
@Parameter(description = "Mobile application package name, to find OAuth2 clients " + @Parameter(description = "Mobile application package name, to find OAuth2 clients " +
"where there is configured mobile application with such package name") "where there is configured mobile application with such package name")
@ -85,26 +95,47 @@ public class OAuth2Controller extends BaseController {
} catch (Exception e) { } 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')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") @PostMapping(value = "/oauth2/client")
@ResponseBody public OAuth2Registration saveOAuth2Client(@RequestBody OAuth2Registration oAuth2Registration) throws Exception {
public OAuth2Info getCurrentOAuth2Info() throws ThingsboardException { TenantId tenantId = getTenantId();
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); oAuth2Registration.setTenantId(tenantId);
return oAuth2Service.findOAuth2Info(); 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')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) @GetMapping(value = "/oauth2/client/infos")
public List<OAuth2RegistrationInfo> 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) @ResponseStatus(value = HttpStatus.OK)
public OAuth2Info saveOAuth2Info(@RequestBody OAuth2Info oauth2Info) throws ThingsboardException { public void deleteOauth2Client(@PathVariable UUID id) throws Exception {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); OAuth2RegistrationId oAuth2RegistrationId = new OAuth2RegistrationId(id);
oAuth2Service.saveOAuth2Info(oauth2Info); OAuth2Registration oAuth2Registration = checkOauth2ClientId(oAuth2RegistrationId, Operation.DELETE);
return oAuth2Service.findOAuth2Info(); tbOauth2ClientService.delete(oAuth2Registration, getCurrentUser());
} }
@ApiOperation(value = "Get OAuth2 log in processing URL (getLoginProcessingUrl)", notes = "Returns the URL enclosed in " + @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) "as 'SECURITY_OAUTH2_LOGIN_PROCESSING_URL' env variable. By default it is '/login/oauth2/code/'" + SYSTEM_AUTHORITY_PARAGRAPH)
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/oauth2/loginProcessingUrl", method = RequestMethod.GET) @RequestMapping(value = "/oauth2/loginProcessingUrl", method = RequestMethod.GET)
@ResponseBody public String getLoginProcessingUrl() {
public String getLoginProcessingUrl() throws ThingsboardException {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ);
return "\"" + oAuth2Configuration.getLoginProcessingUrl() + "\""; return "\"" + oAuth2Configuration.getLoginProcessingUrl() + "\"";
} }

2
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 -> case NOTIFICATION_RULE, NOTIFICATION_TARGET, NOTIFICATION_TEMPLATE ->
notificationEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg); notificationEdgeProcessor.processEntityNotification(tenantId, edgeNotificationMsg);
case TB_RESOURCE -> resourceEdgeProcessor.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); default -> log.warn("[{}] Edge event type [{}] is not designed to be pushed to edge", tenantId, type);
} }
} catch (Exception e) { } catch (Exception e) {

4
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.NotificationRuleService;
import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTargetService;
import org.thingsboard.server.dao.notification.NotificationTemplateService; 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.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.resource.ResourceService; import org.thingsboard.server.dao.resource.ResourceService;
@ -167,7 +167,7 @@ public class EdgeContextComponent {
private NotificationTemplateService notificationTemplateService; private NotificationTemplateService notificationTemplateService;
@Autowired @Autowired
private OAuth2Service oAuth2Service; private OAuth2ClientService oAuth2ClientService;
@Autowired @Autowired
private RateLimitService rateLimitService; private RateLimitService rateLimitService;

8
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.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.TenantId; 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.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup; import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
@ -227,8 +227,8 @@ public class EdgeEventSourcingListener {
private EdgeEventType getEdgeEventTypeForEntityEvent(Object entity) { private EdgeEventType getEdgeEventTypeForEntityEvent(Object entity) {
if (entity instanceof AlarmComment) { if (entity instanceof AlarmComment) {
return EdgeEventType.ALARM_COMMENT; return EdgeEventType.ALARM_COMMENT;
} else if (entity instanceof OAuth2Info) { } else if (entity instanceof OAuth2Registration) {
return EdgeEventType.OAUTH2; return EdgeEventType.OAUTH2_CLIENT;
} }
return null; return null;
} }
@ -236,7 +236,7 @@ public class EdgeEventSourcingListener {
private String getBodyMsgForEntityEvent(Object entity) { private String getBodyMsgForEntityEvent(Object entity) {
if (entity instanceof AlarmComment) { if (entity instanceof AlarmComment) {
return JacksonUtil.toString(entity); return JacksonUtil.toString(entity);
} else if (entity instanceof OAuth2Info) { } else if (entity instanceof OAuth2Registration) {
return JacksonUtil.toString(entity); return JacksonUtil.toString(entity);
} }
return null; return null;

4
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); return ctx.getNotificationEdgeProcessor().convertNotificationTargetToDownlink(edgeEvent);
case NOTIFICATION_TEMPLATE: case NOTIFICATION_TEMPLATE:
return ctx.getNotificationEdgeProcessor().convertNotificationTemplateToDownlink(edgeEvent); return ctx.getNotificationEdgeProcessor().convertNotificationTemplateToDownlink(edgeEvent);
case OAUTH2: case OAUTH2_CLIENT:
return ctx.getOAuth2EdgeProcessor().convertOAuth2EventToDownlink(edgeEvent); return ctx.getOAuth2EdgeProcessor().convertOAuth2ProviderEventToDownlink(edgeEvent);
default: default:
log.warn("[{}] Unsupported edge event type [{}]", this.tenantId, edgeEvent); log.warn("[{}] Unsupported edge event type [{}]", this.tenantId, edgeEvent);
return null; return null;

2
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 TenantWidgetsBundlesEdgeEventFetcher(ctx.getWidgetsBundleService()));
fetchers.add(new OtaPackagesEdgeEventFetcher(ctx.getOtaPackageService())); fetchers.add(new OtaPackagesEdgeEventFetcher(ctx.getOtaPackageService()));
fetchers.add(new TenantResourcesEdgeEventFetcher(ctx.getResourceService())); fetchers.add(new TenantResourcesEdgeEventFetcher(ctx.getResourceService()));
fetchers.add(new OAuth2EdgeEventFetcher(ctx.getOAuth2Service())); fetchers.add(new OAuth2EdgeEventFetcher(ctx.getOAuth2ClientService()));
} }
} }

6
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.springframework.stereotype.Component;
import org.thingsboard.common.util.JacksonUtil; 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.gen.edge.v1.OAuth2UpdateMsg;
import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.queue.util.TbCoreComponent;
@ -25,8 +25,8 @@ import org.thingsboard.server.queue.util.TbCoreComponent;
@TbCoreComponent @TbCoreComponent
public class OAuth2MsgConstructor { public class OAuth2MsgConstructor {
public OAuth2UpdateMsg constructOAuth2UpdateMsg(OAuth2Info oAuth2Info) { public OAuth2UpdateMsg constructOAuth2UpdateMsg(OAuth2Registration oAuth2Registration) {
return OAuth2UpdateMsg.newBuilder().setEntity(JacksonUtil.toString(oAuth2Info)).build(); return OAuth2UpdateMsg.newBuilder().setEntity(JacksonUtil.toString(oAuth2Registration)).build();
} }
} }

12
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.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.TenantId; 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.PageData;
import org.thingsboard.server.common.data.page.PageLink; 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.ArrayList;
import java.util.List; import java.util.List;
@ -36,7 +36,7 @@ import java.util.List;
@Slf4j @Slf4j
public class OAuth2EdgeEventFetcher implements EdgeEventFetcher { public class OAuth2EdgeEventFetcher implements EdgeEventFetcher {
private final OAuth2Service oAuth2Service; private final OAuth2ClientService oAuth2ClientService;
@Override @Override
public PageLink getPageLink(int pageSize) { public PageLink getPageLink(int pageSize) {
@ -46,9 +46,9 @@ public class OAuth2EdgeEventFetcher implements EdgeEventFetcher {
@Override @Override
public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) { public PageData<EdgeEvent> fetchEdgeEvents(TenantId tenantId, Edge edge, PageLink pageLink) {
List<EdgeEvent> result = new ArrayList<>(); List<EdgeEvent> result = new ArrayList<>();
OAuth2Info oAuth2Info = oAuth2Service.findOAuth2Info(); List<OAuth2Registration> oauth2Registrations = oAuth2ClientService.findOauth2ClientsByTenantId(TenantId.SYS_TENANT_ID);
result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.OAUTH2, result.add(EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.OAUTH2_CLIENT,
EdgeEventActionType.ADDED, null, JacksonUtil.valueToTree(oAuth2Info))); EdgeEventActionType.ADDED, null, JacksonUtil.valueToTree(oauth2Registrations)));
// returns PageData object to be in sync with other fetchers // returns PageData object to be in sync with other fetchers
return new PageData<>(result, 1, result.size(), false); return new PageData<>(result, 1, result.size(), false);
} }

4
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.NotificationRuleService;
import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTargetService;
import org.thingsboard.server.dao.notification.NotificationTemplateService; 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.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
@ -240,7 +240,7 @@ public abstract class BaseEdgeProcessor {
protected NotificationTemplateService notificationTemplateService; protected NotificationTemplateService notificationTemplateService;
@Autowired @Autowired
protected OAuth2Service oAuth2Service; protected OAuth2ClientService oAuth2ClientService;
@Autowired @Autowired
@Lazy @Lazy

14
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.EdgeEventActionType;
import org.thingsboard.server.common.data.edge.EdgeEventType; import org.thingsboard.server.common.data.edge.EdgeEventType;
import org.thingsboard.server.common.data.id.TenantId; 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.DownlinkMsg;
import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg; import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg;
import org.thingsboard.server.gen.transport.TransportProtos; import org.thingsboard.server.gen.transport.TransportProtos;
@ -37,21 +37,21 @@ import org.thingsboard.server.service.edge.rpc.processor.BaseEdgeProcessor;
@TbCoreComponent @TbCoreComponent
public class OAuth2EdgeProcessor extends BaseEdgeProcessor { public class OAuth2EdgeProcessor extends BaseEdgeProcessor {
public DownlinkMsg convertOAuth2EventToDownlink(EdgeEvent edgeEvent) { public DownlinkMsg convertOAuth2ProviderEventToDownlink(EdgeEvent edgeEvent) {
DownlinkMsg downlinkMsg = null; DownlinkMsg downlinkMsg = null;
OAuth2Info oAuth2Info = JacksonUtil.convertValue(edgeEvent.getBody(), OAuth2Info.class); OAuth2Registration oAuth2Registration = JacksonUtil.convertValue(edgeEvent.getBody(), OAuth2Registration.class);
if (oAuth2Info != null) { if (oAuth2Registration != null) {
OAuth2UpdateMsg oAuth2UpdateMsg = oAuth2MsgConstructor.constructOAuth2UpdateMsg(oAuth2Info); OAuth2UpdateMsg oAuth2ProviderUpdateMsg = oAuth2MsgConstructor.constructOAuth2UpdateMsg(oAuth2Registration);
downlinkMsg = DownlinkMsg.newBuilder() downlinkMsg = DownlinkMsg.newBuilder()
.setDownlinkMsgId(EdgeUtils.nextPositiveInt()) .setDownlinkMsgId(EdgeUtils.nextPositiveInt())
.addOAuth2UpdateMsg(oAuth2UpdateMsg) .addOAuth2UpdateMsg(oAuth2ProviderUpdateMsg)
.build(); .build();
} }
return downlinkMsg; return downlinkMsg;
} }
public ListenableFuture<Void> processOAuth2Notification(TenantId tenantId, TransportProtos.EdgeNotificationMsgProto edgeNotificationMsg) { public ListenableFuture<Void> 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) { if (oAuth2Info == null) {
return Futures.immediateFuture(null); return Futures.immediateFuture(null);
} }

77
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<OAuth2RegistrationId> 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;
}
}
}

15
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2DomainRepository.java → 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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.common.data.User;
import org.thingsboard.server.dao.model.sql.OAuth2DomainEntity; import org.thingsboard.server.common.data.domain.Domain;
import org.thingsboard.server.common.data.id.OAuth2RegistrationId;
import java.util.List; import java.util.List;
import java.util.UUID;
public interface OAuth2DomainRepository extends JpaRepository<OAuth2DomainEntity, UUID> { public interface TbDomainService {
List<OAuth2DomainEntity> findByOauth2ParamsId(UUID oauth2ParamsId); Domain save(Domain domain, List<OAuth2RegistrationId> oAuth2Clients, User user) throws Exception;
} void delete(Domain domain, User user);
}

74
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<OAuth2RegistrationId> 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;
}
}
}

14
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2MobileDao.java → 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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.common.data.User;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.common.data.id.OAuth2RegistrationId;
import org.thingsboard.server.common.data.mobile.MobileApp;
import java.util.List; import java.util.List;
import java.util.UUID;
public interface OAuth2MobileDao extends Dao<OAuth2Mobile> { public interface TbMobileAppService {
List<OAuth2Mobile> findByOAuth2ParamsId(UUID oauth2ParamsId); MobileApp save(MobileApp mobileApp, List<OAuth2RegistrationId> oauth2Clients, User user) throws Exception;
void delete(MobileApp mobileApp, User user);
} }

63
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;
}
}
}

31
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);
}

8
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.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.DoubleDataEntry; import org.thingsboard.server.common.data.kv.DoubleDataEntry;
import org.thingsboard.server.common.data.kv.LongDataEntry; 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.PageDataIterable;
import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.query.BooleanFilterPredicate; 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.exception.DataValidationException;
import org.thingsboard.server.dao.notification.NotificationSettingsService; import org.thingsboard.server.dao.notification.NotificationSettingsService;
import org.thingsboard.server.dao.notification.NotificationTargetService; 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.queue.QueueService;
import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.settings.AdminSettingsService;
@ -149,7 +149,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
private final DeviceConnectivityConfiguration connectivityConfiguration; private final DeviceConnectivityConfiguration connectivityConfiguration;
private final QueueService queueService; private final QueueService queueService;
private final JwtSettingsService jwtSettingsService; private final JwtSettingsService jwtSettingsService;
private final OAuth2MobileDao oAuth2MobileDao; private final MobileAppDao oAuth2MobileDao;
private final NotificationSettingsService notificationSettingsService; private final NotificationSettingsService notificationSettingsService;
private final NotificationTargetService notificationTargetService; private final NotificationTargetService notificationTargetService;
@ -308,7 +308,7 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
jwtSettingsService.saveJwtSettings(jwtSettings); jwtSettingsService.saveJwtSettings(jwtSettings);
} }
List<OAuth2Mobile> mobiles = oAuth2MobileDao.find(TenantId.SYS_TENANT_ID); List<MobileApp> mobiles = oAuth2MobileDao.findByTenantId(TenantId.SYS_TENANT_ID);
if (CollectionUtils.isNotEmpty(mobiles)) { if (CollectionUtils.isNotEmpty(mobiles)) {
mobiles.stream() mobiles.stream()
.filter(config -> !validateKeyLength(config.getAppSecret())) .filter(config -> !validateKeyLength(config.getAppSecret()))

11
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.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId; 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.id.TenantId;
import org.thingsboard.server.common.data.oauth2.OAuth2Registration; import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import org.thingsboard.server.common.data.security.model.JwtPair; 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.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails; import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
import org.thingsboard.server.service.security.model.SecurityUser; import org.thingsboard.server.service.security.model.SecurityUser;
@ -56,7 +57,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
private final JwtTokenFactory tokenFactory; private final JwtTokenFactory tokenFactory;
private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; private final OAuth2ClientMapperProvider oauth2ClientMapperProvider;
private final OAuth2Service oAuth2Service; private final OAuth2ClientService oAuth2ClientService;
private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
private final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository; private final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
private final SystemSecurityService systemSecurityService; private final SystemSecurityService systemSecurityService;
@ -64,13 +65,13 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
@Autowired @Autowired
public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory, public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory,
final OAuth2ClientMapperProvider oauth2ClientMapperProvider, final OAuth2ClientMapperProvider oauth2ClientMapperProvider,
final OAuth2Service oAuth2Service, final OAuth2ClientService oAuth2ClientService,
final OAuth2AuthorizedClientService oAuth2AuthorizedClientService, final OAuth2AuthorizedClientService oAuth2AuthorizedClientService,
final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository, final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository,
final SystemSecurityService systemSecurityService) { final SystemSecurityService systemSecurityService) {
this.tokenFactory = tokenFactory; this.tokenFactory = tokenFactory;
this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; this.oauth2ClientMapperProvider = oauth2ClientMapperProvider;
this.oAuth2Service = oAuth2Service; this.oAuth2ClientService = oAuth2ClientService;
this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService;
this.httpCookieOAuth2AuthorizationRequestRepository = httpCookieOAuth2AuthorizationRequestRepository; this.httpCookieOAuth2AuthorizationRequestRepository = httpCookieOAuth2AuthorizationRequestRepository;
this.systemSecurityService = systemSecurityService; this.systemSecurityService = systemSecurityService;
@ -96,7 +97,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
try { try {
OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; 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( OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient(
token.getAuthorizedClientRegistrationId(), token.getAuthorizedClientRegistrationId(),
token.getPrincipal().getName()); token.getPrincipal().getName());

4
application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java

@ -33,7 +33,9 @@ public enum Resource {
USER(EntityType.USER), USER(EntityType.USER),
WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE), WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE),
WIDGET_TYPE(EntityType.WIDGET_TYPE), WIDGET_TYPE(EntityType.WIDGET_TYPE),
OAUTH2_CONFIGURATION_INFO(), OAUTH2_CLIENT(EntityType.OAUTH2_CLIENT),
DOMAIN(EntityType.DOMAIN),
MOBILE_APP(EntityType.MOBILE_APP),
OAUTH2_CONFIGURATION_TEMPLATE(), OAUTH2_CONFIGURATION_TEMPLATE(),
TENANT_PROFILE(EntityType.TENANT_PROFILE), TENANT_PROFILE(EntityType.TENANT_PROFILE),
DEVICE_PROFILE(EntityType.DEVICE_PROFILE), DEVICE_PROFILE(EntityType.DEVICE_PROFILE),

4
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.USER, userPermissionChecker);
put(Resource.WIDGETS_BUNDLE, systemEntityPermissionChecker); put(Resource.WIDGETS_BUNDLE, systemEntityPermissionChecker);
put(Resource.WIDGET_TYPE, 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.OAUTH2_CONFIGURATION_TEMPLATE, PermissionChecker.allowAllPermissionChecker);
put(Resource.TENANT_PROFILE, PermissionChecker.allowAllPermissionChecker); put(Resource.TENANT_PROFILE, PermissionChecker.allowAllPermissionChecker);
put(Resource.TB_RESOURCE, systemEntityPermissionChecker); put(Resource.TB_RESOURCE, systemEntityPermissionChecker);

6
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.data.kv.TsKvEntry;
import org.thingsboard.server.common.msg.queue.ServiceType; import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.stats.TbApiUsageStateClient; 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.dao.settings.AdminSettingsService;
import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo; import org.thingsboard.server.gen.transport.TransportProtos.ServiceInfo;
import org.thingsboard.server.queue.discovery.DiscoveryService; import org.thingsboard.server.queue.discovery.DiscoveryService;
@ -89,7 +89,7 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
private final TelemetrySubscriptionService telemetryService; private final TelemetrySubscriptionService telemetryService;
private final TbApiUsageStateClient apiUsageStateClient; private final TbApiUsageStateClient apiUsageStateClient;
private final AdminSettingsService adminSettingsService; private final AdminSettingsService adminSettingsService;
private final OAuth2Service oAuth2Service; private final DomainService domainService;
private final MailService mailService; private final MailService mailService;
private final SmsService smsService; private final SmsService smsService;
private volatile ScheduledExecutorService scheduler; private volatile ScheduledExecutorService scheduler;
@ -143,7 +143,7 @@ public class DefaultSystemInfoService extends TbApplicationEventListener<Partiti
FeaturesInfo featuresInfo = new FeaturesInfo(); FeaturesInfo featuresInfo = new FeaturesInfo();
featuresInfo.setEmailEnabled(isEmailEnabled()); featuresInfo.setEmailEnabled(isEmailEnabled());
featuresInfo.setSmsEnabled(smsService.isConfigured(TenantId.SYS_TENANT_ID)); featuresInfo.setSmsEnabled(smsService.isConfigured(TenantId.SYS_TENANT_ID));
featuresInfo.setOauthEnabled(oAuth2Service.findOAuth2Info().isEnabled()); featuresInfo.setOauthEnabled(domainService.isOauth2Enabled(TenantId.SYS_TENANT_ID));
featuresInfo.setTwoFaEnabled(isTwoFaEnabled()); featuresInfo.setTwoFaEnabled(isTwoFaEnabled());
featuresInfo.setNotificationEnabled(isSlackEnabled()); featuresInfo.setNotificationEnabled(isSlackEnabled());
return featuresInfo; return featuresInfo;

84
application/src/test/java/org/thingsboard/server/controller/HomePageApiTest.java

@ -17,7 +17,6 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -39,15 +38,9 @@ import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.UsageInfo; import org.thingsboard.server.common.data.UsageInfo;
import org.thingsboard.server.common.data.User; import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.asset.Asset;
import org.thingsboard.server.common.data.domain.Domain;
import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.MapperType; import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
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.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.query.ApiUsageStateFilter; import org.thingsboard.server.common.data.query.ApiUsageStateFilter;
import org.thingsboard.server.common.data.query.EntityCountQuery; import org.thingsboard.server.common.data.query.EntityCountQuery;
@ -65,7 +58,6 @@ import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityCountUpdate;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate; import org.thingsboard.server.service.ws.telemetry.cmd.v2.EntityDataUpdate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@ -369,9 +361,8 @@ public class HomePageApiTest extends AbstractControllerTest {
Assert.assertTrue(featuresInfo.isNotificationEnabled()); Assert.assertTrue(featuresInfo.isNotificationEnabled());
Assert.assertFalse(featuresInfo.isOauthEnabled()); Assert.assertFalse(featuresInfo.isOauthEnabled());
OAuth2Info oAuth2Info = createDefaultOAuth2Info(); Domain domain = createDomain(TenantId.SYS_TENANT_ID, "mydomain", true);
doPost("/api/domain", domain).andExpect(status().isOk());
doPost("/api/oauth2/config", oAuth2Info).andExpect(status().isOk());
featuresInfo = doGet("/api/admin/featuresInfo", FeaturesInfo.class); featuresInfo = doGet("/api/admin/featuresInfo", FeaturesInfo.class);
Assert.assertNotNull(featuresInfo); Assert.assertNotNull(featuresInfo);
@ -493,43 +484,38 @@ public class HomePageApiTest extends AbstractControllerTest {
return doPostWithResponse("/api/entitiesQuery/count", query, Long.class); return doPostWithResponse("/api/entitiesQuery/count", query, Long.class);
} }
private OAuth2Info createDefaultOAuth2Info() { private Domain createDomain(TenantId tenantId, String domainName, boolean oauth2Enabled) {
return new OAuth2Info(true, false, Lists.newArrayList( Domain domain = new Domain();
OAuth2ParamsInfo.builder() domain.setTenantId(tenantId);
.domainInfos(Lists.newArrayList( domain.setName(domainName);
OAuth2DomainInfo.builder().name("domain").scheme(SchemeType.MIXED).build() domain.setOauth2Enabled(oauth2Enabled);
)) return domain;
.mobileInfos(Collections.emptyList())
.clientRegistrations(Lists.newArrayList(
validRegistrationInfo()
))
.build()
));
} }
private OAuth2RegistrationInfo validRegistrationInfo() { private OAuth2Registration validRegistration() {
return OAuth2RegistrationInfo.builder() OAuth2Registration oAuth2Registration = new OAuth2Registration();
.clientId(UUID.randomUUID().toString()) oAuth2Registration.setClientId(UUID.randomUUID().toString());
.clientSecret(UUID.randomUUID().toString()) // .clientSecret(UUID.randomUUID().toString())
.authorizationUri(UUID.randomUUID().toString()) // .authorizationUri(UUID.randomUUID().toString())
.accessTokenUri(UUID.randomUUID().toString()) // .accessTokenUri(UUID.randomUUID().toString())
.scope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString())) // .scope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString()))
.platforms(Collections.emptyList()) // .platforms(Collections.emptyList())
.userInfoUri(UUID.randomUUID().toString()) // .userInfoUri(UUID.randomUUID().toString())
.userNameAttributeName(UUID.randomUUID().toString()) // .userNameAttributeName(UUID.randomUUID().toString())
.jwkSetUri(UUID.randomUUID().toString()) // .jwkSetUri(UUID.randomUUID().toString())
.clientAuthenticationMethod(UUID.randomUUID().toString()) // .clientAuthenticationMethod(UUID.randomUUID().toString())
.loginButtonLabel(UUID.randomUUID().toString()) // .loginButtonLabel(UUID.randomUUID().toString())
.mapperConfig( // .mapperConfig(
OAuth2MapperConfig.builder() // OAuth2MapperConfig.builder()
.type(MapperType.CUSTOM) // .type(MapperType.CUSTOM)
.custom( // .custom(
OAuth2CustomMapperConfig.builder() // OAuth2CustomMapperConfig.builder()
.url(UUID.randomUUID().toString()) // .url(UUID.randomUUID().toString())
.build() // .build()
) // )
.build() // .build()
) // )
.build(); // .build();
return oAuth2Registration;
} }
} }

14
application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java

@ -63,7 +63,7 @@ import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.EdgeId; import org.thingsboard.server.common.data.id.EdgeId;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId; 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.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageType;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
@ -544,13 +544,13 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
private void validateOAuth2() throws Exception { private void validateOAuth2() throws Exception {
Optional<OAuth2UpdateMsg> oAuth2UpdateMsgOpt = edgeImitator.findMessageByType(OAuth2UpdateMsg.class); Optional<OAuth2UpdateMsg> oAuth2UpdateMsgOpt = edgeImitator.findMessageByType(OAuth2UpdateMsg.class);
Assert.assertTrue(oAuth2UpdateMsgOpt.isPresent()); Assert.assertTrue(oAuth2UpdateMsgOpt.isPresent());
OAuth2UpdateMsg oAuth2UpdateMsg = oAuth2UpdateMsgOpt.get(); OAuth2UpdateMsg oAuth2ProviderUpdateMsg = oAuth2UpdateMsgOpt.get();
OAuth2Info oAuth2Info = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); OAuth2Registration oAuth2Registration = JacksonUtil.fromString(oAuth2ProviderUpdateMsg.getEntity(), OAuth2Registration.class, true);
Assert.assertNotNull(oAuth2Info); Assert.assertNotNull(oAuth2Registration);
OAuth2Info auth2Info = doGet("/api/oauth2/config", OAuth2Info.class); OAuth2Registration auth2Info = doGet("/api/oauth2/config", OAuth2Registration.class);
Assert.assertNotNull(auth2Info); Assert.assertNotNull(auth2Info);
Assert.assertEquals(oAuth2Info, auth2Info); Assert.assertEquals(oAuth2Registration, auth2Info);
testAutoGeneratedCodeByProtobuf(oAuth2UpdateMsg); testAutoGeneratedCodeByProtobuf(oAuth2ProviderUpdateMsg);
} }
private void validateSyncCompleted() { private void validateSyncCompleted() {

74
application/src/test/java/org/thingsboard/server/edge/OAuth2EdgeTest.java

@ -15,19 +15,14 @@
*/ */
package org.thingsboard.server.edge; package org.thingsboard.server.edge;
import com.google.common.collect.Lists;
import com.google.protobuf.AbstractMessage; import com.google.protobuf.AbstractMessage;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.oauth2.MapperType; import org.thingsboard.server.common.data.oauth2.MapperType;
import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; 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.OAuth2MapperConfig;
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.SchemeType;
import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg; import org.thingsboard.server.gen.edge.v1.OAuth2UpdateMsg;
@ -45,59 +40,47 @@ public class OAuth2EdgeTest extends AbstractEdgeTest {
// enable oauth // enable oauth
edgeImitator.allowIgnoredTypes(); edgeImitator.allowIgnoredTypes();
edgeImitator.expectMessageAmount(1); edgeImitator.expectMessageAmount(1);
OAuth2Info oAuth2Info = createDefaultOAuth2Info(); OAuth2Registration oAuth2Registration = createDefaultOAuth2Info();
oAuth2Info = doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class); oAuth2Registration = doPost("/api/oauth2/config", oAuth2Registration, OAuth2Registration.class);
Assert.assertTrue(edgeImitator.waitForMessages()); Assert.assertTrue(edgeImitator.waitForMessages());
AbstractMessage latestMessage = edgeImitator.getLatestMessage(); AbstractMessage latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg); Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg);
OAuth2UpdateMsg oAuth2UpdateMsg = (OAuth2UpdateMsg) latestMessage; OAuth2UpdateMsg oAuth2ProviderUpdateMsg = (OAuth2UpdateMsg) latestMessage;
OAuth2Info result = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); OAuth2Registration result = JacksonUtil.fromString(oAuth2ProviderUpdateMsg.getEntity(), OAuth2Registration.class, true);
Assert.assertEquals(oAuth2Info, result); Assert.assertEquals(oAuth2Registration, result);
// disable oauth support // disable oauth support
edgeImitator.expectMessageAmount(1); edgeImitator.expectMessageAmount(1);
oAuth2Info.setEnabled(false); doPost("/api/oauth2/config", oAuth2Registration, OAuth2Registration.class);
oAuth2Info.setEdgeEnabled(false);
doPost("/api/oauth2/config", oAuth2Info, OAuth2Info.class);
Assert.assertTrue(edgeImitator.waitForMessages()); Assert.assertTrue(edgeImitator.waitForMessages());
latestMessage = edgeImitator.getLatestMessage(); latestMessage = edgeImitator.getLatestMessage();
Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg); Assert.assertTrue(latestMessage instanceof OAuth2UpdateMsg);
oAuth2UpdateMsg = (OAuth2UpdateMsg) latestMessage; oAuth2ProviderUpdateMsg = (OAuth2UpdateMsg) latestMessage;
result = JacksonUtil.fromString(oAuth2UpdateMsg.getEntity(), OAuth2Info.class, true); result = JacksonUtil.fromString(oAuth2ProviderUpdateMsg.getEntity(), OAuth2Registration.class, true);
Assert.assertEquals(oAuth2Info, result); Assert.assertEquals(oAuth2Registration, result);
edgeImitator.ignoreType(OAuth2UpdateMsg.class); edgeImitator.ignoreType(OAuth2UpdateMsg.class);
loginTenantAdmin(); loginTenantAdmin();
} }
private OAuth2Info createDefaultOAuth2Info() { private OAuth2Registration createDefaultOAuth2Info() {
return new OAuth2Info(true, true, Lists.newArrayList( return validRegistrationInfo();
OAuth2ParamsInfo.builder()
.domainInfos(Lists.newArrayList(
OAuth2DomainInfo.builder().name("domain").scheme(SchemeType.MIXED).build()
))
.mobileInfos(Collections.emptyList())
.clientRegistrations(Lists.newArrayList(
validRegistrationInfo()
))
.build()
));
} }
private OAuth2RegistrationInfo validRegistrationInfo() { private OAuth2Registration validRegistrationInfo() {
return OAuth2RegistrationInfo.builder() OAuth2Registration oAuth2Registration = new OAuth2Registration();
.clientId(UUID.randomUUID().toString()) oAuth2Registration.setClientId(UUID.randomUUID().toString());
.clientSecret(UUID.randomUUID().toString()) oAuth2Registration.setClientSecret(UUID.randomUUID().toString());
.authorizationUri(UUID.randomUUID().toString()) oAuth2Registration.setAuthorizationUri(UUID.randomUUID().toString());
.accessTokenUri(UUID.randomUUID().toString()) oAuth2Registration.setAccessTokenUri(UUID.randomUUID().toString());
.scope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString())) oAuth2Registration.setScope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString()));
.platforms(Collections.emptyList()) oAuth2Registration.setPlatforms(Collections.emptyList());
.userInfoUri(UUID.randomUUID().toString()) oAuth2Registration.setUserInfoUri(UUID.randomUUID().toString());
.userNameAttributeName(UUID.randomUUID().toString()) oAuth2Registration.setUserNameAttributeName(UUID.randomUUID().toString());
.jwkSetUri(UUID.randomUUID().toString()) oAuth2Registration.setJwkSetUri(UUID.randomUUID().toString());
.clientAuthenticationMethod(UUID.randomUUID().toString()) oAuth2Registration.setClientAuthenticationMethod(UUID.randomUUID().toString());
.loginButtonLabel(UUID.randomUUID().toString()) oAuth2Registration.setLoginButtonLabel(UUID.randomUUID().toString());
.mapperConfig( oAuth2Registration.setMapperConfig(
OAuth2MapperConfig.builder() OAuth2MapperConfig.builder()
.type(MapperType.CUSTOM) .type(MapperType.CUSTOM)
.custom( .custom(
@ -105,9 +88,8 @@ public class OAuth2EdgeTest extends AbstractEdgeTest {
.url(UUID.randomUUID().toString()) .url(UUID.randomUUID().toString())
.build() .build()
) )
.build() .build());
) return oAuth2Registration;
.build();
} }
} }

4
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.NotificationRuleService;
import org.thingsboard.server.dao.notification.NotificationTargetService; import org.thingsboard.server.dao.notification.NotificationTargetService;
import org.thingsboard.server.dao.notification.NotificationTemplateService; 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.ota.OtaPackageService;
import org.thingsboard.server.dao.queue.QueueService; import org.thingsboard.server.dao.queue.QueueService;
import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.relation.RelationService;
@ -257,7 +257,7 @@ public abstract class BaseEdgeProcessorTest {
protected ResourceService resourceService; protected ResourceService resourceService;
@MockBean @MockBean
protected OAuth2Service oAuth2Service; protected OAuth2ClientService oAuth2ClientService;
@MockBean @MockBean
@Lazy @Lazy

44
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<DomainInfo> findDomainInfosByTenantId(TenantId tenantId);
DomainInfo findDomainInfoById(TenantId tenantId, DomainId domainId);
boolean isOauth2Enabled(TenantId tenantId);
void updateOauth2Clients(TenantId tenantId, DomainId domainId, List<OAuth2RegistrationId> oAuth2ClientIds);
}

41
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<MobileAppInfo> findMobileAppInfosByTenantId(TenantId tenantId);
MobileAppInfo findMobileAppInfoById(TenantId tenantId, MobileAppId mobileAppId);
void updateOauth2Clients(TenantId tenantId, MobileAppId mobileAppId, List<OAuth2RegistrationId> oAuth2ClientIds);
}

21
common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java → common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientService.java

@ -15,25 +15,32 @@
*/ */
package org.thingsboard.server.dao.oauth2; 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.OAuth2ClientInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2Info;
import org.thingsboard.server.common.data.oauth2.OAuth2Registration; 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.PlatformType;
import org.thingsboard.server.dao.entity.EntityDaoService;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
public interface OAuth2Service { public interface OAuth2ClientService extends EntityDaoService {
List<OAuth2ClientInfo> getOAuth2Clients(String domainScheme, String domainName, String pkgName, PlatformType platformType); List<OAuth2ClientInfo> getWebOAuth2Clients(String domainName, PlatformType platformType);
void saveOAuth2Info(OAuth2Info oauth2Info); List<OAuth2ClientInfo> getMobileOAuth2Clients(String pkgName, PlatformType platformType);
OAuth2Info findOAuth2Info(); List<OAuth2RegistrationInfo> findOauth2ClientInfosByTenantId(TenantId tenantId);
OAuth2Registration findRegistration(UUID id); List<OAuth2Registration> findOauth2ClientsByTenantId(TenantId tenantId);
List<OAuth2Registration> findAllRegistrations(); OAuth2Registration saveOAuth2Client(TenantId tenantId, OAuth2Registration oAuth2Registration);
OAuth2Registration findOAuth2ClientById(TenantId tenantId, OAuth2RegistrationId providerId);
String findAppSecret(UUID registrationId, String pkgName); String findAppSecret(UUID registrationId, String pkgName);
void deleteById(TenantId tenantId, OAuth2RegistrationId oAuth2RegistrationId);
} }

5
common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java

@ -57,7 +57,10 @@ public enum EntityType {
NOTIFICATION_REQUEST(31), NOTIFICATION_REQUEST(31),
NOTIFICATION(32), NOTIFICATION(32),
NOTIFICATION_RULE(33), NOTIFICATION_RULE(33),
QUEUE_STATS(34); QUEUE_STATS(34),
OAUTH2_CLIENT(35),
DOMAIN(36),
MOBILE_APP(37);
@Getter @Getter
private final int protoNumber; // Corresponds to EntityTypeProto private final int protoNumber; // Corresponds to EntityTypeProto

3
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), ADDED_COMMENT(false, TbMsgType.COMMENT_CREATED),
UPDATED_COMMENT(false, TbMsgType.COMMENT_UPDATED), UPDATED_COMMENT(false, TbMsgType.COMMENT_UPDATED),
DELETED_COMMENT(false, null), DELETED_COMMENT(false, null),
SMS_SENT(false, null); SMS_SENT(false, null),
UPDATED_OAUTH2_CLIENTS(false, null);
@Getter @Getter
private final boolean isRead; private final boolean isRead;

52
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<DomainId> implements HasTenantId, HasName {
@Schema(description = "JSON object with Tenant Id")
private TenantId tenantId;
@Schema(description = "Domain name. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
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;
}
}

24
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MobileInfo.java → 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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 io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString; 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 @Data
@ToString @ToString
@NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Builder @Builder
@Schema @Schema
public class OAuth2MobileInfo { public class DomainInfo extends Domain implements HasOauth2Registrations {
@Schema(description = "Application package name. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
private String pkgName; @Schema(description = "List of available oauth2 client registration")
@Schema(description = "Application secret. The length must be at least 16 characters", requiredMode = Schema.RequiredMode.REQUIRED) private List<OAuth2RegistrationInfo> oauth2RegistrationInfos;
private String appSecret;
public DomainInfo(Domain domain, List<OAuth2RegistrationInfo> oauth2RegistrationInfos) {
super(domain);
this.oauth2RegistrationInfos = oauth2RegistrationInfos;
}
} }

20
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

@ -13,16 +13,20 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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 lombok.AllArgsConstructor;
import org.thingsboard.server.dao.model.sql.OAuth2MobileEntity; 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; @Data
import java.util.UUID; @NoArgsConstructor
@AllArgsConstructor
public class DomainOauth2Registration {
public interface OAuth2MobileRepository extends JpaRepository<OAuth2MobileEntity, UUID> { private DomainId domainId;
private OAuth2RegistrationId oAuth2RegistrationId;
List<OAuth2MobileEntity> findByOauth2ParamsId(UUID oauth2ParamsId);
} }

2
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_TARGET (true, EntityType.NOTIFICATION_TARGET),
NOTIFICATION_TEMPLATE (true, EntityType.NOTIFICATION_TEMPLATE), NOTIFICATION_TEMPLATE (true, EntityType.NOTIFICATION_TEMPLATE),
TB_RESOURCE(true, EntityType.TB_RESOURCE), TB_RESOURCE(true, EntityType.TB_RESOURCE),
OAUTH2(true, null); OAUTH2_CLIENT(true, EntityType.OAUTH2_CLIENT);
private final boolean allEdgesRelated; private final boolean allEdgesRelated;

14
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2DomainId.java → 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.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.thingsboard.server.common.data.EntityType;
import java.util.UUID; import java.util.UUID;
public class OAuth2DomainId extends UUIDBased { public class DomainId extends UUIDBased implements EntityId {
@JsonCreator @JsonCreator
public OAuth2DomainId(@JsonProperty("id") UUID id) { public DomainId(@JsonProperty("id") UUID id) {
super(id); super(id);
} }
public static OAuth2DomainId fromString(String oauth2DomainId) { public static DomainId fromString(String oauth2DomainId) {
return new OAuth2DomainId(UUID.fromString(oauth2DomainId)); return new DomainId(UUID.fromString(oauth2DomainId));
}
@Override
public EntityType getEntityType() {
return EntityType.DOMAIN;
} }
} }

6
common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java

@ -105,6 +105,12 @@ public class EntityIdFactory {
return new NotificationId(uuid); return new NotificationId(uuid);
case QUEUE_STATS: case QUEUE_STATS:
return new QueueStatsId(uuid); 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!"); throw new IllegalArgumentException("EntityType " + type + " is not supported!");
} }

14
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2MobileId.java → 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.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.thingsboard.server.common.data.EntityType;
import java.util.UUID; import java.util.UUID;
public class OAuth2MobileId extends UUIDBased { public class MobileAppId extends UUIDBased implements EntityId{
@JsonCreator @JsonCreator
public OAuth2MobileId(@JsonProperty("id") UUID id) { public MobileAppId(@JsonProperty("id") UUID id) {
super(id); super(id);
} }
public static OAuth2MobileId fromString(String oauth2MobileId) { public static MobileAppId fromString(String mobileAppId) {
return new OAuth2MobileId(UUID.fromString(oauth2MobileId)); return new MobileAppId(UUID.fromString(mobileAppId));
}
@Override
public EntityType getEntityType() {
return EntityType.MOBILE_APP;
} }
} }

8
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.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import org.thingsboard.server.common.data.EntityType;
import java.util.UUID; import java.util.UUID;
public class OAuth2RegistrationId extends UUIDBased { public class OAuth2RegistrationId extends UUIDBased implements EntityId {
@JsonCreator @JsonCreator
public OAuth2RegistrationId(@JsonProperty("id") UUID id) { public OAuth2RegistrationId(@JsonProperty("id") UUID id) {
@ -30,4 +31,9 @@ public class OAuth2RegistrationId extends UUIDBased {
public static OAuth2RegistrationId fromString(String oauth2RegistrationId) { public static OAuth2RegistrationId fromString(String oauth2RegistrationId) {
return new OAuth2RegistrationId(UUID.fromString(oauth2RegistrationId)); return new OAuth2RegistrationId(UUID.fromString(oauth2RegistrationId));
} }
@Override
public EntityType getEntityType() {
return EntityType.OAUTH2_CLIENT;
}
} }

59
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<MobileAppId> 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;
}
}

25
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Info.java → 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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 io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
@ -22,21 +22,26 @@ import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.data.oauth2.HasOauth2Registrations;
import org.thingsboard.server.common.data.oauth2.OAuth2RegistrationInfo;
import java.util.List; import java.util.List;
@EqualsAndHashCode @EqualsAndHashCode(callSuper = true)
@Data @Data
@ToString @ToString
@Builder(toBuilder = true)
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
@Builder
@Schema @Schema
public class OAuth2Info { public class MobileAppInfo extends MobileApp implements HasOauth2Registrations {
@Schema(description = "Whether OAuth2 settings are enabled or not")
private boolean enabled; @Schema(description = "List of available oauth2 client registrations")
@Schema(description = "Whether OAuth2 settings are enabled on Edge or not") private List<OAuth2RegistrationInfo> oauth2RegistrationInfos;
private boolean edgeEnabled;
@Schema(description = "List of configured OAuth2 clients. Cannot contain null values", requiredMode = Schema.RequiredMode.REQUIRED) public MobileAppInfo(MobileApp mobileApp, List<OAuth2RegistrationInfo> oauth2RegistrationInfos) {
private List<OAuth2ParamsInfo> oauth2ParamsInfos; super(mobileApp);
this.oauth2RegistrationInfos = oauth2RegistrationInfos;
}
} }

32
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;
}

12
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2DomainDao.java → 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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.List;
import java.util.UUID;
public interface OAuth2DomainDao extends Dao<OAuth2Domain> { public interface HasOauth2Registrations extends Serializable {
List<OAuth2Domain> findByOAuth2ParamsId(UUID oauth2ParamsId); List<OAuth2RegistrationInfo> getOauth2RegistrationInfos();
} }

42
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Domain.java

@ -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<OAuth2DomainId> {
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;
}
}

38
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2DomainInfo.java

@ -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;
}

42
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Mobile.java

@ -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<OAuth2MobileId> {
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;
}
}

46
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ParamsInfo.java

@ -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<OAuth2DomainInfo> domainInfos;
@Schema(description = "Mobile applications settings. Application package name must be unique within the list", requiredMode = Schema.RequiredMode.REQUIRED)
private List<OAuth2MobileInfo> mobileInfos;
@Schema(description = "List of OAuth2 provider settings. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
private List<OAuth2RegistrationInfo> clientRegistrations;
}

29
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2Registration.java

@ -16,14 +16,17 @@
package org.thingsboard.server.common.data.oauth2; package org.thingsboard.server.common.data.oauth2;
import com.fasterxml.jackson.annotation.JsonProperty; 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.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.ToString; import lombok.ToString;
import org.thingsboard.server.common.data.BaseDataWithAdditionalInfo; import org.thingsboard.server.common.data.BaseDataWithAdditionalInfo;
import org.thingsboard.server.common.data.HasName; 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.OAuth2RegistrationId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.List; import java.util.List;
@ -31,26 +34,44 @@ import java.util.List;
@Data @Data
@ToString(exclude = {"clientSecret"}) @ToString(exclude = {"clientSecret"})
@NoArgsConstructor @NoArgsConstructor
public class OAuth2Registration extends BaseDataWithAdditionalInfo<OAuth2RegistrationId> implements HasName { public class OAuth2Registration extends BaseDataWithAdditionalInfo<OAuth2RegistrationId> 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; private OAuth2MapperConfig mapperConfig;
@Schema(description = "OAuth2 client ID. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
private String clientId; private String clientId;
@Schema(description = "OAuth2 client secret. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
private String clientSecret; private String clientSecret;
@Schema(description = "Authorization URI of the OAuth2 provider. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
private String authorizationUri; private String authorizationUri;
@Schema(description = "Access token URI of the OAuth2 provider. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
private String accessTokenUri; private String accessTokenUri;
@Schema(description = "OAuth scopes that will be requested from OAuth2 platform. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
private List<String> scope; private List<String> scope;
@Schema(description = "User info URI of the OAuth2 provider")
private String userInfoUri; private String userInfoUri;
@Schema(description = "Name of the username attribute in OAuth2 provider response. Cannot be empty")
private String userNameAttributeName; private String userNameAttributeName;
@Schema(description = "JSON Web Key URI of the OAuth2 provider")
private String jwkSetUri; private String jwkSetUri;
@Schema(description = "Client authentication method to use: 'BASIC' or 'POST'. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
private String clientAuthenticationMethod; private String clientAuthenticationMethod;
@Schema(description = "OAuth2 provider label. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED)
private String loginButtonLabel; private String loginButtonLabel;
@Schema(description = "Log in button icon for OAuth2 provider")
private String loginButtonIcon; private String loginButtonIcon;
@Schema(description = "List of platforms for which usage of the OAuth2 client is allowed (empty for all allowed)")
private List<PlatformType> platforms; private List<PlatformType> platforms;
@Schema(description = "Additional info of OAuth2 client (e.g. providerName)", requiredMode = Schema.RequiredMode.REQUIRED)
private JsonNode additionalInfo;
public OAuth2Registration(OAuth2Registration registration) { public OAuth2Registration(OAuth2Registration registration) {
super(registration); super(registration);
this.oauth2ParamsId = registration.oauth2ParamsId; this.tenantId = registration.tenantId;
this.mapperConfig = registration.mapperConfig; this.mapperConfig = registration.mapperConfig;
this.clientId = registration.clientId; this.clientId = registration.clientId;
this.clientSecret = registration.clientSecret; this.clientSecret = registration.clientSecret;

58
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2RegistrationInfo.java

@ -15,51 +15,35 @@
*/ */
package org.thingsboard.server.common.data.oauth2; package org.thingsboard.server.common.data.oauth2;
import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import org.thingsboard.server.common.data.BaseData;
import lombok.ToString; import org.thingsboard.server.common.data.id.OAuth2RegistrationId;
import java.util.List; import java.util.List;
@EqualsAndHashCode
@Data @Data
@ToString(exclude = {"clientSecret"})
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Schema @Schema
public class OAuth2RegistrationInfo { @EqualsAndHashCode(callSuper = true)
@Schema(description = "Config for mapping OAuth2 log in response to platform entities", requiredMode = Schema.RequiredMode.REQUIRED) public class OAuth2RegistrationInfo extends BaseData<OAuth2RegistrationId> {
private OAuth2MapperConfig mapperConfig; @Schema(description = "Oauth2 client registration title (e.g. Google)")
@Schema(description = "OAuth2 client ID. Cannot be empty", requiredMode = Schema.RequiredMode.REQUIRED) private String title;
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<String> 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)") @Schema(description = "List of platforms for which usage of the OAuth2 client is allowed (empty for all allowed)")
private List<PlatformType> platforms; private List<PlatformType> 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<PlatformType> platforms) {
super(id);
this.title = title;
this.platforms = platforms;
}
} }

39
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<Domain> {
List<Domain> findByTenantId(TenantId tenantId);
int countDomainByTenantIdAndOauth2Enabled(TenantId tenantId, boolean oauth2Enabled);
List<DomainOauth2Registration> findOauth2ClientsByDomainId(TenantId tenantId, DomainId domainId);
void saveOauth2Clients(DomainOauth2Registration domainOauth2Registration);
void removeOauth2Clients(DomainId domainId, OAuth2RegistrationId oAuth2RegistrationId);
}

167
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<Domain> domainValidator;
@Override
public Domain saveDomain(TenantId tenantId, Domain domain) {
log.trace("Executing saveDomain [{}]", domain);
domainValidator.validate(domain, Domain::getTenantId);
try {
Domain savedDomain = domainDao.save(tenantId, domain);
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entity(savedDomain).build());
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<OAuth2RegistrationId> 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<DomainOauth2Registration> oauth2Clients = new ArrayList<>();
for (OAuth2RegistrationId oAuth2RegistrationId: oAuth2ClientIds) {
oauth2Clients.add(new DomainOauth2Registration(domainId, oAuth2RegistrationId));
}
List<DomainOauth2Registration> existingClients = domainDao.findOauth2ClientsByDomainId(tenantId, domainId);
List<OAuth2RegistrationId> 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<DomainInfo> findDomainInfosByTenantId(TenantId tenantId) {
log.trace("Executing findDomainInfo [{}]", tenantId);
List<Domain> domains = domainDao.findByTenantId(tenantId);
List<DomainInfo> 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<HasId<?>> 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());
}
}

36
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<MobileApp> {
List<MobileApp> findByTenantId(TenantId tenantId);
List<MobileAppOauth2Registration> findOauth2ClientsByMobileAppId(TenantId tenantId, MobileAppId mobileAppId);
void saveOauth2Clients(MobileAppOauth2Registration mobileAppOauth2Registration);
void removeOauth2Clients(MobileAppId mobileAppId, OAuth2RegistrationId oAuth2RegistrationId);
}

163
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<MobileApp> mobileAppValidator;
@Override
public MobileApp saveMobileApp(TenantId tenantId, MobileApp mobileApp) {
log.trace("Executing saveMobileApp [{}]", mobileApp);
mobileAppValidator.validate(mobileApp, MobileApp::getTenantId);
try {
MobileApp savedMobileApp = mobileAppDao.save(tenantId, mobileApp);
eventPublisher.publishEvent(SaveEntityEvent.builder().tenantId(tenantId).entity(savedMobileApp).build());
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<MobileAppInfo> findMobileAppInfosByTenantId(TenantId tenantId) {
log.trace("Executing findMobileAppInfosByTenantId [{}]", tenantId);
List<MobileApp> mobileApps = mobileAppDao.findByTenantId(tenantId);
List<MobileAppInfo> 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<OAuth2RegistrationId> 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<MobileAppOauth2Registration> oauth2Clients = new ArrayList<>();
for (OAuth2RegistrationId oAuth2RegistrationId: oAuth2ClientIds) {
oauth2Clients.add(new MobileAppOauth2Registration(mobileAppId, oAuth2RegistrationId));
}
List<MobileAppOauth2Registration> existingClients = mobileAppDao.findOauth2ClientsByMobileAppId(tenantId, mobileAppId);
List<OAuth2RegistrationId> 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<HasId<?>> 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());
}
}

39
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"; 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 DOMAIN_TABLE_NAME = "domain";
public static final String OAUTH2_PARAMS_ENABLED_PROPERTY = "enabled"; public static final String DOMAIN_DOMAIN_NAME_PROPERTY = "domain_name";
public static final String OAUTH2_PARAMS_EDGE_ENABLED_PROPERTY = "edge_enabled"; public static final String DOMAIN_OAUTH2_ENABLED_PROPERTY = "oauth2_enabled";
public static final String OAUTH2_PARAMS_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; 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 DOMAIN_OAUTH2_REGISTRATION_TABLE_NAME = "domain_oauth2_registration";
public static final String OAUTH2_DOMAIN_TABLE_NAME = "oauth2_domain"; public static final String DOMAIN_OAUTH2_PROVIDER_PROVIDER_ID_PROPERTY = "oauth2_registration_id";
public static final String OAUTH2_MOBILE_TABLE_NAME = "oauth2_mobile"; public static final String DOMAIN_OAUTH2_PROVIDER_DOMAIN_ID_PROPERTY = "domain_id";
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"; * 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_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_TEMPLATE_PROVIDER_ID_PROPERTY = "provider_id";
public static final String OAUTH2_DOMAIN_NAME_PROPERTY = "domain_name"; public static final String OAUTH2_CLIENT_TITLE_PROPERTY = "title";
public static final String OAUTH2_DOMAIN_SCHEME_PROPERTY = "domain_scheme";
public static final String OAUTH2_CLIENT_ID_PROPERTY = "client_id"; 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_CLIENT_SECRET_PROPERTY = "client_secret";
public static final String OAUTH2_AUTHORIZATION_URI_PROPERTY = "authorization_uri"; public static final String OAUTH2_AUTHORIZATION_URI_PROPERTY = "authorization_uri";

81
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<Domain> {
@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;
}
}

37
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;
}

66
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<DomainOauth2Registration> {
@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;
}
}

46
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MobileEntity.java → 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.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.id.OAuth2MobileId; import org.thingsboard.server.common.data.id.MobileAppId;
import org.thingsboard.server.common.data.id.OAuth2ParamsId; import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.OAuth2Mobile; import org.thingsboard.server.common.data.mobile.MobileApp;
import org.thingsboard.server.dao.model.BaseSqlEntity; import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants; import org.thingsboard.server.dao.model.ModelConstants;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.util.UUID; import java.util.UUID;
import static org.thingsboard.server.dao.model.ModelConstants.TENANT_ID_COLUMN;
@Data @Data
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Entity @Entity
@Table(name = ModelConstants.OAUTH2_MOBILE_TABLE_NAME) @Table(name = ModelConstants.MOBILE_APP_TABLE_NAME)
public class OAuth2MobileEntity extends BaseSqlEntity<OAuth2Mobile> { public class MobileAppEntity extends BaseSqlEntity<MobileApp> {
@Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY) @Column(name = TENANT_ID_COLUMN)
private UUID oauth2ParamsId; private UUID tenantId;
@Column(name = ModelConstants.OAUTH2_PKG_NAME_PROPERTY) @Column(name = ModelConstants.MOBILE_APP_PKG_NAME_PROPERTY)
private String pkgName; private String pkgName;
@Column(name = ModelConstants.OAUTH2_APP_SECRET_PROPERTY) @Column(name = ModelConstants.MOBILE_APP_APP_SECRET_PROPERTY)
private String appSecret; private String appSecret;
public OAuth2MobileEntity() { @Column(name = ModelConstants.MOBILE_APP_OAUTH2_ENABLED_PROPERTY)
private Boolean oauth2Enabled;
public MobileAppEntity() {
super(); super();
} }
public OAuth2MobileEntity(OAuth2Mobile mobile) { public MobileAppEntity(MobileApp mobile) {
if (mobile.getId() != null) { if (mobile.getId() != null) {
this.setUuid(mobile.getId().getId()); this.setUuid(mobile.getId().getId());
} }
this.setCreatedTime(mobile.getCreatedTime()); if (mobile.getTenantId() != null) {
if (mobile.getOauth2ParamsId() != null) { this.tenantId = mobile.getTenantId().getId();
this.oauth2ParamsId = mobile.getOauth2ParamsId().getId();
} }
this.setCreatedTime(mobile.getCreatedTime());
this.pkgName = mobile.getPkgName(); this.pkgName = mobile.getPkgName();
this.appSecret = mobile.getAppSecret(); this.appSecret = mobile.getAppSecret();
this.oauth2Enabled = mobile.isOauth2Enabled();
} }
@Override @Override
public OAuth2Mobile toData() { public MobileApp toData() {
OAuth2Mobile mobile = new OAuth2Mobile(); MobileApp mobile = new MobileApp();
mobile.setId(new OAuth2MobileId(id)); mobile.setId(new MobileAppId(id));
if (tenantId != null) {
mobile.setTenantId(TenantId.fromUUID(tenantId));
}
mobile.setCreatedTime(createdTime); mobile.setCreatedTime(createdTime);
mobile.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId));
mobile.setPkgName(pkgName); mobile.setPkgName(pkgName);
mobile.setAppSecret(appSecret); mobile.setAppSecret(appSecret);
mobile.setOauth2Enabled(oauth2Enabled);
return mobile; return mobile;
} }
} }

37
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;
}

67
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<MobileAppOauth2Registration> {
@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;
}
}

76
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2DomainEntity.java

@ -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<OAuth2Domain> {
@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;
}
}

70
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ParamsEntity.java

@ -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<OAuth2Params> {
@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;
}
}

16
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.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.StringUtils; 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.OAuth2RegistrationId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.MapperType; import org.thingsboard.server.common.data.oauth2.MapperType;
import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig;
@ -49,8 +49,10 @@ import java.util.stream.Collectors;
@Table(name = ModelConstants.OAUTH2_REGISTRATION_TABLE_NAME) @Table(name = ModelConstants.OAUTH2_REGISTRATION_TABLE_NAME)
public class OAuth2RegistrationEntity extends BaseSqlEntity<OAuth2Registration> { public class OAuth2RegistrationEntity extends BaseSqlEntity<OAuth2Registration> {
@Column(name = ModelConstants.OAUTH2_PARAMS_ID_PROPERTY) @Column(name = ModelConstants.TENANT_ID_COLUMN)
private UUID oauth2ParamsId; private UUID tenantId;
@Column(name = ModelConstants.OAUTH2_CLIENT_TITLE_PROPERTY)
private String title;
@Column(name = ModelConstants.OAUTH2_CLIENT_ID_PROPERTY) @Column(name = ModelConstants.OAUTH2_CLIENT_ID_PROPERTY)
private String clientId; private String clientId;
@Column(name = ModelConstants.OAUTH2_CLIENT_SECRET_PROPERTY) @Column(name = ModelConstants.OAUTH2_CLIENT_SECRET_PROPERTY)
@ -121,9 +123,10 @@ public class OAuth2RegistrationEntity extends BaseSqlEntity<OAuth2Registration>
this.setUuid(registration.getId().getId()); this.setUuid(registration.getId().getId());
} }
this.setCreatedTime(registration.getCreatedTime()); this.setCreatedTime(registration.getCreatedTime());
if (registration.getOauth2ParamsId() != null) { if (registration.getTenantId() != null) {
this.oauth2ParamsId = registration.getOauth2ParamsId().getId(); this.tenantId = registration.getTenantId().getId();
} }
this.title = registration.getTitle();
this.clientId = registration.getClientId(); this.clientId = registration.getClientId();
this.clientSecret = registration.getClientSecret(); this.clientSecret = registration.getClientSecret();
this.authorizationUri = registration.getAuthorizationUri(); this.authorizationUri = registration.getAuthorizationUri();
@ -168,7 +171,8 @@ public class OAuth2RegistrationEntity extends BaseSqlEntity<OAuth2Registration>
OAuth2Registration registration = new OAuth2Registration(); OAuth2Registration registration = new OAuth2Registration();
registration.setId(new OAuth2RegistrationId(id)); registration.setId(new OAuth2RegistrationId(id));
registration.setCreatedTime(createdTime); registration.setCreatedTime(createdTime);
registration.setOauth2ParamsId(new OAuth2ParamsId(oauth2ParamsId)); registration.setTenantId(new TenantId(tenantId));
registration.setTitle(title);
registration.setAdditionalInfo(additionalInfo); registration.setAdditionalInfo(additionalInfo);
registration.setMapperConfig( registration.setMapperConfig(
OAuth2MapperConfig.builder() OAuth2MapperConfig.builder()

61
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<OAuth2RegistrationInfo> {
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;
}
}

6
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.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.stereotype.Component; 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 org.thingsboard.server.common.data.oauth2.OAuth2Registration;
import java.util.UUID; import java.util.UUID;
@ -30,11 +32,11 @@ public class HybridClientRegistrationRepository implements ClientRegistrationRep
private static final String defaultRedirectUriTemplate = "{baseUrl}/login/oauth2/code/{registrationId}"; private static final String defaultRedirectUriTemplate = "{baseUrl}/login/oauth2/code/{registrationId}";
@Autowired @Autowired
private OAuth2Service oAuth2Service; private OAuth2ClientService oAuth2ClientService;
@Override @Override
public ClientRegistration findByRegistrationId(String registrationId) { 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 ? return registration == null ?
null : toSpringClientRegistration(registration); null : toSpringClientRegistration(registration);
} }

145
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<OAuth2Registration> oAuth2RegistrationDataValidator;
@Override
public List<OAuth2ClientInfo> 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<OAuth2ClientInfo> 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<OAuth2RegistrationInfo> findOauth2ClientInfosByTenantId(TenantId tenantId) {
log.trace("Executing findOauth2ClientInfosByTenantId");
return oauth2RegistrationDao.findInfosByTenantId(tenantId.getId());
}
@Override
public List<OAuth2Registration> 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<HasId<?>> 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;
}
}

14
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2RegistrationDao.java

@ -16,8 +16,8 @@
package org.thingsboard.server.dao.oauth2; package org.thingsboard.server.dao.oauth2;
import org.thingsboard.server.common.data.oauth2.OAuth2Registration; 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.PlatformType;
import org.thingsboard.server.common.data.oauth2.SchemeType;
import org.thingsboard.server.dao.Dao; import org.thingsboard.server.dao.Dao;
import java.util.List; import java.util.List;
@ -25,9 +25,17 @@ import java.util.UUID;
public interface OAuth2RegistrationDao extends Dao<OAuth2Registration> { public interface OAuth2RegistrationDao extends Dao<OAuth2Registration> {
List<OAuth2Registration> findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(List<SchemeType> domainSchemes, String domainName, String pkgName, PlatformType platformType); List<OAuth2RegistrationInfo> findInfosByTenantId(UUID tenantId);
List<OAuth2Registration> findByOAuth2ParamsId(UUID oauth2ParamsId); List<OAuth2Registration> findByTenantId(UUID tenantId);
List<OAuth2Registration> findEnabledByDomainNameAndPlatformType(String domainName, PlatformType platformType);
List<OAuth2Registration> findEnabledByPckNameAndPlatformType(String pkgName, PlatformType platformType);
List<OAuth2RegistrationInfo> findInfosByDomainId(UUID domainId);
List<OAuth2RegistrationInfo> findInfosByMobileAppId(UUID domainId);
String findAppSecret(UUID id, String pkgName); String findAppSecret(UUID id, String pkgName);

295
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java

@ -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<OAuth2ClientInfo> 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<OAuth2Params> oauth2ParamsList = oauth2ParamsDao.find(TenantId.SYS_TENANT_ID);
oauth2Info.setEnabled(oauth2ParamsList.stream().anyMatch(OAuth2Params::isEnabled));
oauth2Info.setEdgeEnabled(oauth2ParamsList.stream().anyMatch(OAuth2Params::isEdgeEnabled));
List<OAuth2ParamsInfo> oauth2ParamsInfos = new ArrayList<>();
oauth2Info.setOauth2ParamsInfos(oauth2ParamsInfos);
oauth2ParamsList.stream().sorted(Comparator.comparing(BaseData::getUuidId)).forEach(oauth2Params -> {
List<OAuth2Registration> registrations = oauth2RegistrationDao.findByOAuth2ParamsId(oauth2Params.getId().getId());
List<OAuth2Domain> domains = oauth2DomainDao.findByOAuth2ParamsId(oauth2Params.getId().getId());
List<OAuth2Mobile> 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<OAuth2Registration> findAllRegistrations() {
log.trace("Executing findAllRegistrations");
return oauth2RegistrationDao.find(TenantId.SYS_TENANT_ID);
}
private final Consumer<OAuth2Info> 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!");
}
}
}
}
};
}

99
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java

@ -15,23 +15,8 @@
*/ */
package org.thingsboard.server.dao.oauth2; 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.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.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 class OAuth2Utils {
public static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; public static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s";
@ -44,88 +29,4 @@ public class OAuth2Utils {
return client; return client;
} }
public static OAuth2ParamsInfo toOAuth2ParamsInfo(List<OAuth2Registration> registrations, List<OAuth2Domain> domains, List<OAuth2Mobile> 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;
}
} }

36
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<Domain> {
@Override
protected void validateDataImpl(TenantId tenantId, Domain domain) {
if (StringUtils.isEmpty(domain.getName())) {
throw new DataValidationException("Domain name should be specified!");
}
}
}

43
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<MobileApp> {
@Override
protected void validateDataImpl(TenantId tenantId, MobileApp mobileApp) {
if (StringUtils.isEmpty(mobileApp.getPkgName())) {
throw new DataValidationException("Package should be specified!");
}
if (StringUtils.isEmpty(mobileApp.getAppSecret())) {
throw new DataValidationException("Application secret should be specified!");
}
if (mobileApp.getAppSecret().length() < 16) {
throw new DataValidationException("Application secret should be at least 16 characters!");
}
}
}

114
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<OAuth2Registration> {
@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!");
}
}
}
}

34
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<DomainOauth2RegistrationEntity, DomainOauth2RegistrationCompositeKey> {
List<DomainOauth2RegistrationEntity> findAllByDomainId(@Param("domainId") UUID domainId);
}

39
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<DomainEntity, UUID> {
List<DomainEntity> 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);
}

83
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<DomainEntity, Domain> implements DomainDao {
private final DomainRepository domainRepository;
private final DomainOauth2RegistrationRepository domainOauth2RegistrationRepository;
@Override
protected Class<DomainEntity> getEntityClass() {
return DomainEntity.class;
}
@Override
protected JpaRepository<DomainEntity, UUID> getRepository() {
return domainRepository;
}
@Override
public List<Domain> 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<DomainOauth2Registration> 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()));
}
}

77
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<MobileAppEntity, MobileApp> implements MobileAppDao {
private final MobileAppRepository repository;
private final MobileAppOauth2RegistrationRepository mobileOauth2ProviderRepository;
@Override
protected Class<MobileAppEntity> getEntityClass() {
return MobileAppEntity.class;
}
@Override
protected JpaRepository<MobileAppEntity, UUID> getRepository() {
return repository;
}
@Override
public List<MobileApp> findByTenantId(TenantId tenantId) {
return DaoUtil.convertDataList(repository.findByTenantId(tenantId.getId()));
}
@Override
public List<MobileAppOauth2Registration> 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()));
}
}

32
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<MobileAppOauth2RegistrationEntity, MobileAppOauth2RegistrationCompositeKey> {
List<MobileAppOauth2RegistrationEntity> findAllByMobileAppId(@Param("mobileAppId") UUID mobileAppId);
}

19
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ParamsRepository.java → 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 * See the License for the specific language governing permissions and
* limitations under the License. * 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.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; import java.util.UUID;
public interface OAuth2ParamsRepository extends JpaRepository<OAuth2ParamsEntity, UUID> { public interface MobileAppRepository extends JpaRepository<MobileAppEntity, UUID> {
List<MobileAppEntity> findByTenantId(@Param("tenantId") UUID tenantId);
@Transactional
@Modifying
@Query("DELETE FROM MobileAppEntity r WHERE r.tenantId = :tenantId")
void deleteByTenantId(@Param("tenantId") UUID tenantId);
} }

54
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2DomainDao.java

@ -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<OAuth2DomainEntity, OAuth2Domain> implements OAuth2DomainDao {
private final OAuth2DomainRepository repository;
@Override
protected Class<OAuth2DomainEntity> getEntityClass() {
return OAuth2DomainEntity.class;
}
@Override
protected JpaRepository<OAuth2DomainEntity, UUID> getRepository() {
return repository;
}
@Override
public List<OAuth2Domain> findByOAuth2ParamsId(UUID oauth2ParamsId) {
return DaoUtil.convertDataList(repository.findByOauth2ParamsId(oauth2ParamsId));
}
}

54
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2MobileDao.java

@ -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<OAuth2MobileEntity, OAuth2Mobile> implements OAuth2MobileDao {
private final OAuth2MobileRepository repository;
@Override
protected Class<OAuth2MobileEntity> getEntityClass() {
return OAuth2MobileEntity.class;
}
@Override
protected JpaRepository<OAuth2MobileEntity, UUID> getRepository() {
return repository;
}
@Override
public List<OAuth2Mobile> findByOAuth2ParamsId(UUID oauth2ParamsId) {
return DaoUtil.convertDataList(repository.findByOauth2ParamsId(oauth2ParamsId));
}
}

49
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ParamsDao.java

@ -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<OAuth2ParamsEntity, OAuth2Params> implements OAuth2ParamsDao {
private final OAuth2ParamsRepository repository;
@Override
protected Class<OAuth2ParamsEntity> getEntityClass() {
return OAuth2ParamsEntity.class;
}
@Override
protected JpaRepository<OAuth2ParamsEntity, UUID> getRepository() {
return repository;
}
@Override
public void deleteAll() {
repository.deleteAll();
}
}

31
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.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.oauth2.OAuth2Registration; 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.PlatformType;
import org.thingsboard.server.common.data.oauth2.SchemeType;
import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.OAuth2RegistrationEntity; import org.thingsboard.server.dao.model.sql.OAuth2RegistrationEntity;
import org.thingsboard.server.dao.oauth2.OAuth2RegistrationDao; import org.thingsboard.server.dao.oauth2.OAuth2RegistrationDao;
@ -48,14 +48,35 @@ public class JpaOAuth2RegistrationDao extends JpaAbstractDao<OAuth2RegistrationE
} }
@Override @Override
public List<OAuth2Registration> findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(List<SchemeType> domainSchemes, String domainName, String pkgName, PlatformType platformType) { public List<OAuth2RegistrationInfo> findInfosByTenantId(UUID tenantId) {
return DaoUtil.convertDataList(repository.findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(domainSchemes, domainName, pkgName, return DaoUtil.convertDataList(repository.findInfosByTenantId(tenantId));
}
@Override
public List<OAuth2Registration> findByTenantId(UUID tenantId) {
return DaoUtil.convertDataList(repository.findByTenantId(tenantId));
}
@Override
public List<OAuth2Registration> findEnabledByDomainNameAndPlatformType(String domainName, PlatformType platformType) {
return DaoUtil.convertDataList(repository.findEnabledByDomainNameAndPlatformType(domainName,
platformType != null ? "%" + platformType.name() + "%" : null)); platformType != null ? "%" + platformType.name() + "%" : null));
} }
@Override @Override
public List<OAuth2Registration> findByOAuth2ParamsId(UUID oauth2ParamsId) { public List<OAuth2Registration> findEnabledByPckNameAndPlatformType(String pkgName, PlatformType platformType) {
return DaoUtil.convertDataList(repository.findByOauth2ParamsId(oauth2ParamsId)); return DaoUtil.convertDataList(repository.findEnabledByPkgNameAndPlatformType(pkgName,
platformType != null ? "%" + platformType.name() + "%" : null));
}
@Override
public List<OAuth2RegistrationInfo> findInfosByDomainId(UUID oauth2ParamsId) {
return DaoUtil.convertDataList(repository.findInfosByDomainId(oauth2ParamsId));
}
@Override
public List<OAuth2RegistrationInfo> findInfosByMobileAppId(UUID mobileAppId) {
return DaoUtil.convertDataList(repository.findInfosByMobileAppId(mobileAppId));
} }
@Override @Override

69
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.JpaRepository;
import org.springframework.data.jpa.repository.Query; import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param; 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.OAuth2RegistrationEntity;
import org.thingsboard.server.dao.model.sql.OAuth2RegistrationInfoEntity;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
public interface OAuth2RegistrationRepository extends JpaRepository<OAuth2RegistrationEntity, UUID> { public interface OAuth2RegistrationRepository extends JpaRepository<OAuth2RegistrationEntity, UUID> {
@Query("SELECT reg " + List<OAuth2RegistrationEntity> findByTenantId(@Param("tenantId") UUID tenantId);
"FROM OAuth2RegistrationEntity reg " +
"LEFT JOIN OAuth2ParamsEntity params on reg.oauth2ParamsId = params.id " + @Query("SELECT new org.thingsboard.server.dao.model.sql.OAuth2RegistrationInfoEntity(r.id, r.createdTime, r.platforms, r.title) " +
"LEFT JOIN OAuth2DomainEntity domain on reg.oauth2ParamsId = domain.oauth2ParamsId " + "FROM OAuth2RegistrationEntity r " +
"WHERE params.enabled = true " + "WHERE r.tenantId = :tenantId")
"AND domain.domainName = :domainName " + List<OAuth2RegistrationInfoEntity> findInfosByTenantId(@Param("tenantId") UUID tenantId);
"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)) " + @Query(value = "SELECT r " +
"AND (:platformFilter IS NULL OR reg.platforms IS NULL OR reg.platforms = '' OR reg.platforms LIKE :platformFilter)") "FROM oauth2_registration r " +
List<OAuth2RegistrationEntity> findEnabledByDomainSchemesDomainNameAndPkgNameAndPlatformType(@Param("domainSchemes") List<SchemeType> domainSchemes, "LEFT JOIN domain_oauth2_registration dr on dr.oauth2_registration_id = r.id " +
@Param("domainName") String domainName, "LEFT JOIN domain d on dr.domain_id = d.id " +
@Param("pkgName") String pkgName, "WHERE d.oauth2_enabled = true " +
@Param("platformFilter") String platformFilter); "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<OAuth2RegistrationEntity> findByOauth2ParamsId(UUID oauth2ParamsId); List<OAuth2RegistrationEntity> findEnabledByDomainNameAndPlatformType(@Param("domainName") String domainName,
@Param("platformFilter") String platformFilter);
@Query("SELECT mobile.appSecret " +
"FROM OAuth2MobileEntity mobile " + @Query(value = "SELECT r " +
"LEFT JOIN OAuth2RegistrationEntity reg on mobile.oauth2ParamsId = reg.oauth2ParamsId " + "FROM oauth2_registration r " +
"WHERE reg.id = :registrationId " + "LEFT JOIN mobile_app_oauth2_registration mr on mr.oauth2_registration_id = r.id " +
"AND mobile.pkgName = :pkgName") "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<OAuth2RegistrationEntity> 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<OAuth2RegistrationInfoEntity> 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<OAuth2RegistrationInfoEntity> 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, String findAppSecret(@Param("registrationId") UUID id,
@Param("pkgName") String pkgName); @Param("pkgName") String pkgName);
} }

2
dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java

@ -177,7 +177,7 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Ten
EntityType.DASHBOARD, EntityType.EDGE, EntityType.RULE_CHAIN, EntityType.API_USAGE_STATE, EntityType.DASHBOARD, EntityType.EDGE, EntityType.RULE_CHAIN, EntityType.API_USAGE_STATE,
EntityType.TB_RESOURCE, EntityType.OTA_PACKAGE, EntityType.RPC, EntityType.QUEUE, EntityType.TB_RESOURCE, EntityType.OTA_PACKAGE, EntityType.RPC, EntityType.QUEUE,
EntityType.NOTIFICATION_REQUEST, EntityType.NOTIFICATION_RULE, EntityType.NOTIFICATION_TEMPLATE, EntityType.NOTIFICATION_REQUEST, EntityType.NOTIFICATION_RULE, EntityType.NOTIFICATION_TEMPLATE,
EntityType.NOTIFICATION_TARGET, EntityType.QUEUE_STATS, EntityType.CUSTOMER EntityType.DOMAIN, EntityType.MOBILE_APP, EntityType.OAUTH2_CLIENT
); );
} }

49
dao/src/main/resources/sql/schema-entities.sql

@ -558,17 +558,9 @@ CREATE TABLE IF NOT EXISTS key_dictionary
CONSTRAINT key_dictionary_id_pkey PRIMARY KEY (key) CONSTRAINT key_dictionary_id_pkey PRIMARY KEY (key)
); );
CREATE TABLE IF NOT EXISTS oauth2_params (
id uuid NOT NULL CONSTRAINT oauth2_params_pkey PRIMARY KEY,
enabled boolean,
edge_enabled boolean,
tenant_id uuid,
created_time bigint NOT NULL
);
CREATE TABLE IF NOT EXISTS oauth2_registration ( CREATE TABLE IF NOT EXISTS oauth2_registration (
id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY, id uuid NOT NULL CONSTRAINT oauth2_registration_pkey PRIMARY KEY,
oauth2_params_id uuid NOT NULL, tenant_id uuid NOT NULL,
created_time bigint NOT NULL, created_time bigint NOT NULL,
additional_info varchar, additional_info varchar,
client_id varchar(255), client_id varchar(255),
@ -597,28 +589,41 @@ CREATE TABLE IF NOT EXISTS oauth2_registration (
custom_url varchar(255), custom_url varchar(255),
custom_username varchar(255), custom_username varchar(255),
custom_password varchar(255), custom_password varchar(255),
custom_send_token boolean, custom_send_token boolean
CONSTRAINT fk_registration_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE
); );
CREATE TABLE IF NOT EXISTS oauth2_domain ( CREATE TABLE IF NOT EXISTS domain (
id uuid NOT NULL CONSTRAINT oauth2_domain_pkey PRIMARY KEY, id uuid NOT NULL CONSTRAINT domain_pkey PRIMARY KEY,
oauth2_params_id uuid NOT NULL, tenant_id uuid NOT NULL,
created_time bigint NOT NULL, created_time bigint NOT NULL,
domain_name varchar(255), domain_name varchar(255),
domain_scheme varchar(31), oauth2_enabled boolean,
CONSTRAINT fk_domain_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, edge_enabled boolean,
CONSTRAINT oauth2_domain_unq_key UNIQUE (oauth2_params_id, domain_name, domain_scheme) CONSTRAINT domain_unq_key UNIQUE (domain_name)
); );
CREATE TABLE IF NOT EXISTS oauth2_mobile ( CREATE TABLE IF NOT EXISTS mobile_app (
id uuid NOT NULL CONSTRAINT oauth2_mobile_pkey PRIMARY KEY, id uuid NOT NULL CONSTRAINT mobile_app_pkey PRIMARY KEY,
oauth2_params_id uuid NOT NULL, tenant_id uuid,
created_time bigint NOT NULL, created_time bigint NOT NULL,
pkg_name varchar(255), pkg_name varchar(255),
app_secret varchar(2048), app_secret varchar(2048),
CONSTRAINT fk_mobile_oauth2_params FOREIGN KEY (oauth2_params_id) REFERENCES oauth2_params(id) ON DELETE CASCADE, oauth2_enabled boolean,
CONSTRAINT oauth2_mobile_unq_key UNIQUE (oauth2_params_id, pkg_name) CONSTRAINT mobile_app_unq_key UNIQUE (pkg_name)
);
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
); );
CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( CREATE TABLE IF NOT EXISTS oauth2_client_registration_template (

641
dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ClientServiceTest.java

@ -0,0 +1,641 @@
/**
* 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 org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.dao.oauth2.OAuth2ClientService;
@DaoSqlTest
public class OAuth2ClientServiceTest extends AbstractServiceTest {
// private static final OAuth2Info EMPTY_PARAMS = new OAuth2Info(false, false, Collections.emptyList());
@Autowired
protected OAuth2ClientService oAuth2ClientService;
// @Before
// public void beforeRun() {
// Assert.assertTrue(oAuth2Service.findOauth2ProvidersByTenantId().isEmpty());
// }
//
// @After
// public void after() {
// oAuth2Service.saveOAuth2Info(EMPTY_PARAMS);
// Assert.assertTrue(oAuth2Service.findOauth2ProvidersByTenantId().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<OAuth2RegistrationInfo> firstGroup = Lists.newArrayList(
// validRegistrationInfo(),
// validRegistrationInfo(),
// validRegistrationInfo(),
// validRegistrationInfo()
// );
// List<OAuth2RegistrationInfo> secondGroup = Lists.newArrayList(
// validRegistrationInfo(),
// validRegistrationInfo()
// );
// List<OAuth2RegistrationInfo> 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<OAuth2ClientInfo> firstGroupClientInfos = firstGroup.stream()
// .map(registrationInfo -> new OAuth2ClientInfo(
// registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null))
// .collect(Collectors.toList());
// List<OAuth2ClientInfo> secondGroupClientInfos = secondGroup.stream()
// .map(registrationInfo -> new OAuth2ClientInfo(
// registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null))
// .collect(Collectors.toList());
// List<OAuth2ClientInfo> thirdGroupClientInfos = thirdGroup.stream()
// .map(registrationInfo -> new OAuth2ClientInfo(
// registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null))
// .collect(Collectors.toList());
//
// List<OAuth2ClientInfo> nonExistentDomainClients = oAuth2Service.getOAuth2Clients("http", "non-existent-domain", null, null);
// Assert.assertTrue(nonExistentDomainClients.isEmpty());
//
// List<OAuth2ClientInfo> 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<OAuth2ClientInfo> firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null, null);
// Assert.assertTrue(firstDomainHttpsClients.isEmpty());
//
// List<OAuth2ClientInfo> 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<OAuth2ClientInfo> 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<OAuth2ClientInfo> 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<OAuth2ClientInfo> 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<OAuth2RegistrationInfo> 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<OAuth2ClientInfo> firstGroupClientInfos = firstGroup.stream()
// .map(registrationInfo -> new OAuth2ClientInfo(
// registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null))
// .collect(Collectors.toList());
//
// List<OAuth2ClientInfo> 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<OAuth2ClientInfo> 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<OAuth2ClientInfo> secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null, null);
// Assert.assertEquals(5, secondDomainHttpClients.size());
//
// oAuth2Info.setEnabled(false);
// oAuth2Service.saveOAuth2Info(oAuth2Info);
//
// List<OAuth2ClientInfo> 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<OAuth2Provider> 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<OAuth2Provider> 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<OAuth2ClientInfo> 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<OAuth2ClientInfo> firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null, null);
// Assert.assertEquals(3, firstDomainHttpClients.size());
// List<OAuth2ClientInfo> pkg1Clients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", null);
// Assert.assertEquals(3, pkg1Clients.size());
// List<OAuth2ClientInfo> 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<OAuth2ClientInfo> 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<PlatformType> 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();
// }
}

668
dao/src/test/java/org/thingsboard/server/dao/service/OAuth2ServiceTest.java

@ -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<OAuth2RegistrationInfo> firstGroup = Lists.newArrayList(
validRegistrationInfo(),
validRegistrationInfo(),
validRegistrationInfo(),
validRegistrationInfo()
);
List<OAuth2RegistrationInfo> secondGroup = Lists.newArrayList(
validRegistrationInfo(),
validRegistrationInfo()
);
List<OAuth2RegistrationInfo> 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<OAuth2ClientInfo> firstGroupClientInfos = firstGroup.stream()
.map(registrationInfo -> new OAuth2ClientInfo(
registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null))
.collect(Collectors.toList());
List<OAuth2ClientInfo> secondGroupClientInfos = secondGroup.stream()
.map(registrationInfo -> new OAuth2ClientInfo(
registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null))
.collect(Collectors.toList());
List<OAuth2ClientInfo> thirdGroupClientInfos = thirdGroup.stream()
.map(registrationInfo -> new OAuth2ClientInfo(
registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null))
.collect(Collectors.toList());
List<OAuth2ClientInfo> nonExistentDomainClients = oAuth2Service.getOAuth2Clients("http", "non-existent-domain", null, null);
Assert.assertTrue(nonExistentDomainClients.isEmpty());
List<OAuth2ClientInfo> 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<OAuth2ClientInfo> firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain", null, null);
Assert.assertTrue(firstDomainHttpsClients.isEmpty());
List<OAuth2ClientInfo> 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<OAuth2ClientInfo> 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<OAuth2ClientInfo> 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<OAuth2ClientInfo> 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<OAuth2RegistrationInfo> 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<OAuth2ClientInfo> firstGroupClientInfos = firstGroup.stream()
.map(registrationInfo -> new OAuth2ClientInfo(
registrationInfo.getLoginButtonLabel(), registrationInfo.getLoginButtonIcon(), null))
.collect(Collectors.toList());
List<OAuth2ClientInfo> 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<OAuth2ClientInfo> 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<OAuth2ClientInfo> secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain", null, null);
Assert.assertEquals(5, secondDomainHttpClients.size());
oAuth2Info.setEnabled(false);
oAuth2Service.saveOAuth2Info(oAuth2Info);
List<OAuth2ClientInfo> 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<OAuth2Registration> 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<OAuth2Registration> 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<OAuth2ClientInfo> 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<OAuth2ClientInfo> firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain", null, null);
Assert.assertEquals(3, firstDomainHttpClients.size());
List<OAuth2ClientInfo> pkg1Clients = oAuth2Service.getOAuth2Clients("http", "first-domain", "com.test.pkg1", null);
Assert.assertEquals(3, pkg1Clients.size());
List<OAuth2ClientInfo> 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<OAuth2ClientInfo> 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<PlatformType> 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();
}
}

15
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.kv.TsKvEntry;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; 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.oauth2.PlatformType;
import org.thingsboard.server.common.data.ota.ChecksumAlgorithm; import org.thingsboard.server.common.data.ota.ChecksumAlgorithm;
import org.thingsboard.server.common.data.ota.OtaPackageType; import org.thingsboard.server.common.data.ota.OtaPackageType;
@ -2071,13 +2070,13 @@ public class RestClient implements Closeable {
}, params).getBody(); }, params).getBody();
} }
public OAuth2Info getCurrentOAuth2Info() { // public OAuth2Info getCurrentOAuth2Info() {
return restTemplate.getForEntity(baseURL + "/api/oauth2/config", OAuth2Info.class).getBody(); // return restTemplate.getForEntity(baseURL + "/api/oauth2/config", OAuth2Info.class).getBody();
} // }
//
public OAuth2Info saveOAuth2Info(OAuth2Info oauth2Info) { // public OAuth2Info saveOAuth2Info(OAuth2Info oauth2Info) {
return restTemplate.postForEntity(baseURL + "/api/oauth2/config", oauth2Info, OAuth2Info.class).getBody(); // return restTemplate.postForEntity(baseURL + "/api/oauth2/config", oauth2Info, OAuth2Info.class).getBody();
} // }
public String getLoginProcessingUrl() { public String getLoginProcessingUrl() {
return restTemplate.getForEntity(baseURL + "/api/oauth2/loginProcessingUrl", String.class).getBody(); return restTemplate.getForEntity(baseURL + "/api/oauth2/loginProcessingUrl", String.class).getBody();

Loading…
Cancel
Save