Browse Source

Merge branch 'master' of github.com:thingsboard/thingsboard into feature/device-profile-telemetry-proto-schema

pull/3740/head
ShvaykaD 6 years ago
parent
commit
3cd88e6d15
  1. 20
      application/src/main/data/json/system/oauth2_config_templates/facebook_config.json
  2. 17
      application/src/main/data/json/system/oauth2_config_templates/github_config.json
  3. 21
      application/src/main/data/json/system/oauth2_config_templates/google_config.json
  4. 4
      application/src/main/data/json/system/widget_bundles/input_widgets.json
  5. 21
      application/src/main/data/json/tenant/rule_chains/root_rule_chain.json
  6. 0
      application/src/main/data/upgrade/3.1.1/schema_update_after.sql
  7. 69
      application/src/main/data/upgrade/3.1.1/schema_update_before.sql
  8. 37
      application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java
  9. 4
      application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java
  10. 267
      application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java
  11. 8
      application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java
  12. 15
      application/src/main/java/org/thingsboard/server/controller/AuthController.java
  13. 26
      application/src/main/java/org/thingsboard/server/controller/BaseController.java
  14. 5
      application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java
  15. 77
      application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java
  16. 73
      application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java
  17. 2
      application/src/main/java/org/thingsboard/server/controller/RuleChainController.java
  18. 9
      application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java
  19. 5
      application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
  20. 24
      application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java
  21. 6
      application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java
  22. 2
      application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java
  23. 130
      application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java
  24. 4
      application/src/main/java/org/thingsboard/server/service/profile/DefaultTbDeviceProfileCache.java
  25. 3
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java
  26. 78
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicMapperUtils.java
  27. 60
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java
  28. 14
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java
  29. 91
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java
  30. 4
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java
  31. 17
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java
  32. 28
      application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java
  33. 2
      application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java
  34. 6
      application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java
  35. 3
      application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java
  36. 3
      application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java
  37. 4
      application/src/main/java/org/thingsboard/server/service/state/DeviceStateData.java
  38. 24
      application/src/main/java/org/thingsboard/server/utils/MiscUtils.java
  39. 63
      application/src/main/resources/thingsboard.yml
  40. 30
      application/src/test/java/org/thingsboard/server/mqtt/AbstractMqttIntegrationTest.java
  41. 8
      application/src/test/java/org/thingsboard/server/mqtt/attributes/request/AbstractMqttAttributesRequestIntegrationTest.java
  42. 9
      application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/AbstractMqttAttributesUpdatesIntegrationTest.java
  43. 35
      application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimDeviceTest.java
  44. 1
      application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimJsonDeviceTest.java
  45. 4
      application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimProtoDeviceTest.java
  46. 24
      application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java
  47. 15
      application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesIntegrationTest.java
  48. 23
      application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java
  49. 6
      application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesJsonIntegrationTest.java
  50. 5
      application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesProtoIntegrationTest.java
  51. 2
      common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProfileService.java
  52. 31
      common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateService.java
  53. 12
      common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java
  54. 1
      common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleNodeStateService.java
  55. 4
      common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java
  56. 2
      common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java
  57. 10
      common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java
  58. 33
      common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java
  59. 33
      common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationTemplateId.java
  60. 35
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java
  61. 29
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/DomainInfo.java
  62. 39
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistrationInfo.java
  63. 20
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java
  64. 33
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java
  65. 19
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientInfo.java
  66. 42
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java
  67. 76
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationInfo.java
  68. 78
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java
  69. 32
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java
  70. 30
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java
  71. 29
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java
  72. 33
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java
  73. 20
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/SchemeType.java
  74. 20
      common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java
  75. 3
      common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValueSourceType.java
  76. 4
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java
  77. 4
      common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java
  78. 2
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java
  79. 4
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java
  80. 47
      dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java
  81. 231
      dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java
  82. 48
      dao/src/main/java/org/thingsboard/server/dao/model/sql/ExtendedOAuth2ClientRegistrationInfoEntity.java
  83. 75
      dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java
  84. 51
      dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationInfoEntity.java
  85. 160
      dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationTemplateEntity.java
  86. 59
      dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java
  87. 47
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientMapperConfig.java
  88. 23
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java
  89. 34
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationInfoDao.java
  90. 25
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationTemplateDao.java
  91. 103
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java
  92. 53
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java
  93. 203
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java
  94. 102
      dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java
  95. 11
      dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleNodeStateService.java
  96. 4
      dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeStateDao.java
  97. 9
      dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java
  98. 23
      dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java
  99. 48
      dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java
  100. 76
      dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationInfoDao.java

20
application/src/main/data/json/system/oauth2_config_templates/facebook_config.json

@ -0,0 +1,20 @@
{
"providerId": "Facebook",
"accessTokenUri": "https://graph.facebook.com/v2.8/oauth/access_token",
"authorizationUri": "https://www.facebook.com/v2.8/dialog/oauth",
"scope": ["email","public_profile"],
"jwkSetUri": null,
"userInfoUri": "https://graph.facebook.com/me?fields=id,name,first_name,last_name,email",
"clientAuthenticationMethod": "BASIC",
"userNameAttributeName": "email",
"basic": {
"emailAttributeKey": "email",
"firstNameAttributeKey": "first_name",
"lastNameAttributeKey": "last_name",
"tenantNameStrategy": "DOMAIN"
},
"comment": null,
"loginButtonIcon": "mdi:facebook",
"loginButtonLabel": "Facebook",
"helpLink": "https://developers.facebook.com/docs/facebook-login/web#logindialog"
}

17
application/src/main/data/json/system/oauth2_config_templates/github_config.json

@ -0,0 +1,17 @@
{
"providerId": "Github",
"accessTokenUri": "https://github.com/login/oauth/access_token",
"authorizationUri": "https://github.com/login/oauth/authorize",
"scope": ["read:user","user:email"],
"jwkSetUri": null,
"userInfoUri": "https://api.github.com/user",
"clientAuthenticationMethod": "BASIC",
"userNameAttributeName": "login",
"basic": {
"tenantNameStrategy": "DOMAIN"
},
"comment": "In order to log into ThingsBoard you need to have user's email. You may configure and use Custom OAuth2 Mapper to get email information. Please refer to <a href=\"https://docs.github.com/en/rest/reference/users#list-email-addresses-for-the-authenticated-user\">Github Documentation</a>",
"loginButtonIcon": "mdi:github",
"loginButtonLabel": "Github",
"helpLink": "https://docs.github.com/en/developers/apps/creating-an-oauth-app"
}

21
application/src/main/data/json/system/oauth2_config_templates/google_config.json

@ -0,0 +1,21 @@
{
"providerId": "Google",
"additionalInfo": null,
"accessTokenUri": "https://oauth2.googleapis.com/token",
"authorizationUri": "https://accounts.google.com/o/oauth2/v2/auth",
"scope": ["email","openid","profile"],
"jwkSetUri": "https://www.googleapis.com/oauth2/v3/certs",
"userInfoUri": "https://openidconnect.googleapis.com/v1/userinfo",
"clientAuthenticationMethod": "BASIC",
"userNameAttributeName": "email",
"basic": {
"emailAttributeKey": "email",
"firstNameAttributeKey": "given_name",
"lastNameAttributeKey": "family_name",
"tenantNameStrategy": "DOMAIN"
},
"comment": null,
"loginButtonIcon": "mdi:google",
"loginButtonLabel": "Google",
"helpLink": "https://developers.google.com/adwords/api/docs/guides/authentication"
}

4
application/src/main/data/json/system/widget_bundles/input_widgets.json

File diff suppressed because one or more lines are too long

21
application/src/main/data/json/tenant/rule_chains/root_rule_chain.json

@ -8,7 +8,7 @@
"configuration": null
},
"metadata": {
"firstNodeIndex": 2,
"firstNodeIndex": 6,
"nodes": [
{
"additionalInfo": {
@ -82,9 +82,28 @@
"configuration": {
"timeoutInSeconds": 60
}
},
{
"additionalInfo": {
"description": "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.",
"layoutX": 204,
"layoutY": 240
},
"type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode",
"name": "Device Profile Node",
"debugMode": false,
"configuration": {
"persistAlarmRulesState": false,
"fetchAlarmRulesStateOnStart": false
}
}
],
"connections": [
{
"fromIndex": 6,
"toIndex": 2,
"type": "Success"
},
{
"fromIndex": 2,
"toIndex": 4,

0
application/src/main/data/upgrade/3.1.2/schema_update_after.sql → application/src/main/data/upgrade/3.1.1/schema_update_after.sql

69
application/src/main/data/upgrade/3.1.2/schema_update_before.sql → application/src/main/data/upgrade/3.1.1/schema_update_before.sql

@ -14,6 +14,75 @@
-- limitations under the License.
--
CREATE TABLE IF NOT EXISTS oauth2_client_registration_info (
id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY,
enabled boolean,
created_time bigint NOT NULL,
additional_info varchar,
client_id varchar(255),
client_secret varchar(255),
authorization_uri varchar(255),
token_uri varchar(255),
scope varchar(255),
user_info_uri varchar(255),
user_name_attribute_name varchar(255),
jwk_set_uri varchar(255),
client_authentication_method varchar(255),
login_button_label varchar(255),
login_button_icon varchar(255),
allow_user_creation boolean,
activate_user boolean,
type varchar(31),
basic_email_attribute_key varchar(31),
basic_first_name_attribute_key varchar(31),
basic_last_name_attribute_key varchar(31),
basic_tenant_name_strategy varchar(31),
basic_tenant_name_pattern varchar(255),
basic_customer_name_pattern varchar(255),
basic_default_dashboard_name varchar(255),
basic_always_full_screen boolean,
custom_url varchar(255),
custom_username varchar(255),
custom_password varchar(255),
custom_send_token boolean
);
CREATE TABLE IF NOT EXISTS oauth2_client_registration (
id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY,
created_time bigint NOT NULL,
domain_name varchar(255),
domain_scheme varchar(31),
client_registration_info_id uuid
);
CREATE TABLE IF NOT EXISTS oauth2_client_registration_template (
id uuid NOT NULL CONSTRAINT oauth2_client_registration_template_pkey PRIMARY KEY,
created_time bigint NOT NULL,
additional_info varchar,
provider_id varchar(255),
authorization_uri varchar(255),
token_uri varchar(255),
scope varchar(255),
user_info_uri varchar(255),
user_name_attribute_name varchar(255),
jwk_set_uri varchar(255),
client_authentication_method varchar(255),
type varchar(31),
basic_email_attribute_key varchar(31),
basic_first_name_attribute_key varchar(31),
basic_last_name_attribute_key varchar(31),
basic_tenant_name_strategy varchar(31),
basic_tenant_name_pattern varchar(255),
basic_customer_name_pattern varchar(255),
basic_default_dashboard_name varchar(255),
basic_always_full_screen boolean,
comment varchar,
login_button_icon varchar(255),
login_button_label varchar(255),
help_link varchar(255),
CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id)
);
CREATE TABLE IF NOT EXISTS device_profile (
id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY,
created_time bigint NOT NULL,

37
application/src/main/java/org/thingsboard/server/actors/ruleChain/DefaultTbContext.java

@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import io.netty.channel.EventLoopGroup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.thingsboard.common.util.ListeningExecutor;
import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
@ -47,6 +48,7 @@ import org.thingsboard.server.common.data.rule.RuleNodeState;
import org.thingsboard.server.common.msg.TbActorMsg;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.queue.ServiceQueue;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.common.msg.queue.TopicPartitionInfo;
import org.thingsboard.server.dao.asset.AssetService;
@ -125,7 +127,7 @@ class DefaultTbContext implements TbContext {
@Override
public void enqueue(TbMsg tbMsg, String queueName, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator());
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
enqueue(tpi, tbMsg, onFailure, onSuccess);
}
@ -142,46 +144,57 @@ class DefaultTbContext implements TbContext {
@Override
public void enqueueForTellFailure(TbMsg tbMsg, String failureMessage) {
TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator());
TopicPartitionInfo tpi = resolvePartition(tbMsg);
enqueueForTellNext(tpi, tbMsg, Collections.singleton(TbRelationTypes.FAILURE), failureMessage, null, null);
}
@Override
public void enqueueForTellNext(TbMsg tbMsg, String relationType) {
TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator());
TopicPartitionInfo tpi = resolvePartition(tbMsg);
enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, null, null);
}
@Override
public void enqueueForTellNext(TbMsg tbMsg, Set<String> relationTypes) {
TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator());
TopicPartitionInfo tpi = resolvePartition(tbMsg);
enqueueForTellNext(tpi, tbMsg, relationTypes, null, null, null);
}
@Override
public void enqueueForTellNext(TbMsg tbMsg, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator());
TopicPartitionInfo tpi = resolvePartition(tbMsg);
enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure);
}
@Override
public void enqueueForTellNext(TbMsg tbMsg, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, getTenantId(), tbMsg.getOriginator());
TopicPartitionInfo tpi = resolvePartition(tbMsg);
enqueueForTellNext(tpi, tbMsg, relationTypes, null, onSuccess, onFailure);
}
@Override
public void enqueueForTellNext(TbMsg tbMsg, String queueName, String relationType, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator());
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
enqueueForTellNext(tpi, tbMsg, Collections.singleton(relationType), null, onSuccess, onFailure);
}
@Override
public void enqueueForTellNext(TbMsg tbMsg, String queueName, Set<String> relationTypes, Runnable onSuccess, Consumer<Throwable> onFailure) {
TopicPartitionInfo tpi = mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator());
TopicPartitionInfo tpi = resolvePartition(tbMsg, queueName);
enqueueForTellNext(tpi, tbMsg, relationTypes, null, onSuccess, onFailure);
}
private TopicPartitionInfo resolvePartition(TbMsg tbMsg, String queueName) {
if (StringUtils.isEmpty(queueName)) {
queueName = ServiceQueue.MAIN;
}
return mainCtx.resolve(ServiceType.TB_RULE_ENGINE, queueName, getTenantId(), tbMsg.getOriginator());
}
private TopicPartitionInfo resolvePartition(TbMsg tbMsg) {
return resolvePartition(tbMsg, tbMsg.getQueueName());
}
private void enqueueForTellNext(TopicPartitionInfo tpi, TbMsg source, Set<String> relationTypes, String failureMessage, Runnable onSuccess, Consumer<Throwable> onFailure) {
RuleChainId ruleChainId = nodeCtx.getSelf().getRuleChainId();
RuleNodeId ruleNodeId = nodeCtx.getSelf().getId();
@ -456,6 +469,14 @@ class DefaultTbContext implements TbContext {
return mainCtx.getRuleNodeStateService().save(getTenantId(), state);
}
@Override
public void clearRuleNodeStates() {
if (log.isDebugEnabled()) {
log.debug("[{}][{}] Going to clear rule node states", getTenantId(), getSelfId());
}
mainCtx.getRuleNodeStateService().removeByRuleNodeId(getTenantId(), getSelfId());
}
private TbMsgMetaData getActionMetaData(RuleNodeId ruleNodeId) {
TbMsgMetaData metaData = new TbMsgMetaData();
metaData.putValue("ruleNodeId", ruleNodeId.toString());

4
application/src/main/java/org/thingsboard/server/actors/tenant/TenantActor.java

@ -47,6 +47,7 @@ import org.thingsboard.server.common.msg.queue.PartitionChangeMsg;
import org.thingsboard.server.common.msg.queue.QueueToRuleEngineMsg;
import org.thingsboard.server.common.msg.queue.RuleEngineException;
import org.thingsboard.server.common.msg.queue.ServiceType;
import org.thingsboard.server.service.transport.msg.TransportToDeviceActorMsgWrapper;
import java.util.List;
import java.util.Optional;
@ -116,6 +117,9 @@ public class TenantActor extends RuleChainManagerActor {
if (msg.getMsgType().equals(MsgType.QUEUE_TO_RULE_ENGINE_MSG)) {
QueueToRuleEngineMsg queueMsg = (QueueToRuleEngineMsg) msg;
queueMsg.getTbMsg().getCallback().onSuccess();
} else if (msg.getMsgType().equals(MsgType.TRANSPORT_TO_DEVICE_ACTOR_MSG)){
TransportToDeviceActorMsgWrapper transportMsg = (TransportToDeviceActorMsgWrapper) msg;
transportMsg.getCallback().onSuccess();
}
return true;
}

267
application/src/main/java/org/thingsboard/server/config/CustomOAuth2AuthorizationRequestResolver.java

@ -0,0 +1,267 @@
/**
* Copyright © 2016-2020 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.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator;
import org.springframework.security.crypto.keygen.StringKeyGenerator;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.utils.MiscUtils;
import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
@Service
@Slf4j
public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver {
public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization";
public static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/";
private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId";
private static final char PATH_DELIMITER = '/';
private final AntPathRequestMatcher authorizationRequestMatcher = new AntPathRequestMatcher(
DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{" + REGISTRATION_ID_URI_VARIABLE_NAME + "}");
private final StringKeyGenerator stateGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder());
private final StringKeyGenerator secureKeyGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96);
@Autowired
private ClientRegistrationRepository clientRegistrationRepository;
@Autowired(required = false)
private OAuth2Configuration oauth2Configuration;
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
String registrationId = this.resolveRegistrationId(request);
String redirectUriAction = getAction(request, "login");
return resolve(request, registrationId, redirectUriAction);
}
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId) {
if (registrationId == null) {
return null;
}
String redirectUriAction = getAction(request, "authorize");
return resolve(request, registrationId, redirectUriAction);
}
private String getAction(HttpServletRequest request, String defaultAction) {
String action = request.getParameter("action");
if (action == null) {
return defaultAction;
}
return action;
}
private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) {
if (registrationId == null) {
return null;
}
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId);
if (clientRegistration == null) {
throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId);
}
Map<String, Object> attributes = new HashMap<>();
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId());
OAuth2AuthorizationRequest.Builder builder;
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) {
builder = OAuth2AuthorizationRequest.authorizationCode();
Map<String, Object> additionalParameters = new HashMap<>();
if (!CollectionUtils.isEmpty(clientRegistration.getScopes()) &&
clientRegistration.getScopes().contains(OidcScopes.OPENID)) {
// Section 3.1.2.1 Authentication Request - https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
// scope
// REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
addNonceParameters(attributes, additionalParameters);
}
if (ClientAuthenticationMethod.NONE.equals(clientRegistration.getClientAuthenticationMethod())) {
addPkceParameters(attributes, additionalParameters);
}
builder.additionalParameters(additionalParameters);
} else if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) {
builder = OAuth2AuthorizationRequest.implicit();
} else {
throw new IllegalArgumentException("Invalid Authorization Grant Type (" +
clientRegistration.getAuthorizationGrantType().getValue() +
") for Client Registration with Id: " + clientRegistration.getRegistrationId());
}
String redirectUriStr = expandRedirectUri(request, clientRegistration, redirectUriAction);
return builder
.clientId(clientRegistration.getClientId())
.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri())
.redirectUri(redirectUriStr)
.scopes(clientRegistration.getScopes())
.state(this.stateGenerator.generateKey())
.attributes(attributes)
.build();
}
private String resolveRegistrationId(HttpServletRequest request) {
if (this.authorizationRequestMatcher.matches(request)) {
return this.authorizationRequestMatcher
.matcher(request).getVariables().get(REGISTRATION_ID_URI_VARIABLE_NAME);
}
return null;
}
/**
* Expands the {@link ClientRegistration#getRedirectUriTemplate()} with following provided variables:<br/>
* - baseUrl (e.g. https://localhost/app) <br/>
* - baseScheme (e.g. https) <br/>
* - baseHost (e.g. localhost) <br/>
* - basePort (e.g. :8080) <br/>
* - basePath (e.g. /app) <br/>
* - registrationId (e.g. google) <br/>
* - action (e.g. login) <br/>
* <p/>
* Null variables are provided as empty strings.
* <p/>
* Default redirectUriTemplate is: {@link org.springframework.security.config.oauth2.client}.CommonOAuth2Provider#DEFAULT_REDIRECT_URL
*
* @return expanded URI
*/
private String expandRedirectUri(HttpServletRequest request, ClientRegistration clientRegistration, String action) {
Map<String, String> uriVariables = new HashMap<>();
uriVariables.put("registrationId", clientRegistration.getRegistrationId());
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request))
.replacePath(request.getContextPath())
.replaceQuery(null)
.fragment(null)
.build();
String scheme = uriComponents.getScheme();
uriVariables.put("baseScheme", scheme == null ? "" : scheme);
String host = uriComponents.getHost();
uriVariables.put("baseHost", host == null ? "" : host);
// following logic is based on HierarchicalUriComponents#toUriString()
int port = uriComponents.getPort();
uriVariables.put("basePort", port == -1 ? "" : ":" + port);
String path = uriComponents.getPath();
if (StringUtils.hasLength(path)) {
if (path.charAt(0) != PATH_DELIMITER) {
path = PATH_DELIMITER + path;
}
}
uriVariables.put("basePath", path == null ? "" : path);
uriVariables.put("baseUrl", uriComponents.toUriString());
uriVariables.put("action", action == null ? "" : action);
String redirectUri = getRedirectUri(request);
log.trace("Redirect URI - {}.", redirectUri);
return UriComponentsBuilder.fromUriString(redirectUri)
.buildAndExpand(uriVariables)
.toUriString();
}
private String getRedirectUri(HttpServletRequest request) {
String loginProcessingUri = oauth2Configuration != null ? oauth2Configuration.getLoginProcessingUrl() : DEFAULT_LOGIN_PROCESSING_URI;
String scheme = MiscUtils.getScheme(request);
String domainName = MiscUtils.getDomainName(request);
int port = MiscUtils.getPort(request);
String baseUrl = scheme + "://" + domainName;
if (needsPort(scheme, port)){
baseUrl += ":" + port;
}
return baseUrl + loginProcessingUri;
}
private boolean needsPort(String scheme, int port) {
boolean isHttpDefault = "http".equals(scheme.toLowerCase()) && port == 80;
boolean isHttpsDefault = "https".equals(scheme.toLowerCase()) && port == 443;
return !isHttpDefault && !isHttpsDefault;
}
/**
* Creates nonce and its hash for use in OpenID Connect 1.0 Authentication Requests.
*
* @param attributes where the {@link OidcParameterNames#NONCE} is stored for the authentication request
* @param additionalParameters where the {@link OidcParameterNames#NONCE} hash is added for the authentication request
*
* @since 5.2
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest">3.1.2.1. Authentication Request</a>
*/
private void addNonceParameters(Map<String, Object> attributes, Map<String, Object> additionalParameters) {
try {
String nonce = this.secureKeyGenerator.generateKey();
String nonceHash = createHash(nonce);
attributes.put(OidcParameterNames.NONCE, nonce);
additionalParameters.put(OidcParameterNames.NONCE, nonceHash);
} catch (NoSuchAlgorithmException e) { }
}
/**
* Creates and adds additional PKCE parameters for use in the OAuth 2.0 Authorization and Access Token Requests
*
* @param attributes where {@link PkceParameterNames#CODE_VERIFIER} is stored for the token request
* @param additionalParameters where {@link PkceParameterNames#CODE_CHALLENGE} and, usually,
* {@link PkceParameterNames#CODE_CHALLENGE_METHOD} are added to be used in the authorization request.
*
* @since 5.2
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7636#section-1.1">1.1. Protocol Flow</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7636#section-4.1">4.1. Client Creates a Code Verifier</a>
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7636#section-4.2">4.2. Client Creates the Code Challenge</a>
*/
private void addPkceParameters(Map<String, Object> attributes, Map<String, Object> additionalParameters) {
String codeVerifier = this.secureKeyGenerator.generateKey();
attributes.put(PkceParameterNames.CODE_VERIFIER, codeVerifier);
try {
String codeChallenge = createHash(codeVerifier);
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, codeChallenge);
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256");
} catch (NoSuchAlgorithmException e) {
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, codeVerifier);
}
}
private static String createHash(String value) throws NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII));
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest);
}
}

8
application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java

@ -32,6 +32,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@ -175,6 +176,9 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
web.ignoring().antMatchers("/*.js","/*.css","/*.ico","/assets/**","/static/**");
}
@Autowired
private OAuth2AuthorizationRequestResolver oAuth2AuthorizationRequestResolver;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.headers().cacheControl().and().frameOptions().disable()
@ -207,8 +211,10 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
.addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterBefore(buildWsJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
.addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class);
if (oauth2Configuration != null && oauth2Configuration.isEnabled()) {
if (oauth2Configuration != null) {
http.oauth2Login()
.authorizationEndpoint().authorizationRequestResolver(oAuth2AuthorizationRequestResolver)
.and()
.loginPage("/oauth2Login")
.loginProcessingUrl(oauth2Configuration.getLoginProcessingUrl())
.successHandler(oauth2AuthenticationSuccessHandler)

15
application/src/main/java/org/thingsboard/server/controller/AuthController.java

@ -38,10 +38,8 @@ import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.audit.AuditLogService;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails;
@ -84,9 +82,6 @@ public class AuthController extends BaseController {
@Autowired
private AuditLogService auditLogService;
@Autowired
private OAuth2Service oauth2Service;
@PreAuthorize("isAuthenticated()")
@RequestMapping(value = "/auth/user", method = RequestMethod.GET)
public @ResponseBody User getUser() throws ThingsboardException {
@ -336,14 +331,4 @@ public class AuthController extends BaseController {
throw handleException(e);
}
}
@RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST)
@ResponseBody
public List<OAuth2ClientInfo> getOAuth2Clients() throws ThingsboardException {
try {
return oauth2Service.getOAuth2Clients();
} catch (Exception e) {
throw handleException(e);
}
}
}

26
application/src/main/java/org/thingsboard/server/controller/BaseController.java

@ -27,22 +27,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Dashboard;
import org.thingsboard.server.common.data.DashboardInfo;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.EntityViewInfo;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantInfo;
import org.thingsboard.server.common.data.TenantProfile;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.*;
import org.thingsboard.server.common.data.alarm.Alarm;
import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.asset.Asset;
@ -50,6 +35,7 @@ import org.thingsboard.server.common.data.asset.AssetInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.*;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.AssetId;
import org.thingsboard.server.common.data.id.CustomerId;
@ -93,6 +79,8 @@ import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.dao.relation.RelationService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantProfileService;
@ -175,6 +163,12 @@ public abstract class BaseController {
@Autowired
protected DashboardService dashboardService;
@Autowired
protected OAuth2Service oAuth2Service;
@Autowired
protected OAuth2ConfigTemplateService oAuth2ConfigTemplateService;
@Autowired
protected ComponentDiscoveryService componentDescriptorService;

5
application/src/main/java/org/thingsboard/server/controller/DeviceProfileController.java

@ -207,10 +207,11 @@ public class DeviceProfileController extends BaseController {
@RequestParam int page,
@RequestParam(required = false) String textSearch,
@RequestParam(required = false) String sortProperty,
@RequestParam(required = false) String sortOrder) throws ThingsboardException {
@RequestParam(required = false) String sortOrder,
@RequestParam(required = false) String transportType) throws ThingsboardException {
try {
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder);
return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink));
return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink, transportType));
} catch (Exception e) {
throw handleException(e);
}

77
application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java

@ -0,0 +1,77 @@
/**
* Copyright © 2016-2020 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 lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import java.util.List;
@RestController
@TbCoreComponent
@RequestMapping("/api/oauth2/config/template")
@Slf4j
public class OAuth2ConfigTemplateController extends BaseController {
private static final String CLIENT_REGISTRATION_TEMPLATE_ID = "clientRegistrationTemplateId";
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(@RequestBody OAuth2ClientRegistrationTemplate clientRegistrationTemplate) throws ThingsboardException {
try {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.WRITE);
return oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/{clientRegistrationTemplateId}", method = RequestMethod.DELETE)
@ResponseStatus(value = HttpStatus.OK)
public void deleteClientRegistrationTemplate(@PathVariable(CLIENT_REGISTRATION_TEMPLATE_ID) String strClientRegistrationTemplateId) throws ThingsboardException {
checkParameter(CLIENT_REGISTRATION_TEMPLATE_ID, strClientRegistrationTemplateId);
try {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.DELETE);
OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId = new OAuth2ClientRegistrationTemplateId(toUUID(strClientRegistrationTemplateId));
oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplateId);
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')")
@RequestMapping(method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public List<OAuth2ClientRegistrationTemplate> getClientRegistrationTemplates() throws ThingsboardException {
try {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.READ);
return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates();
} catch (Exception e) {
throw handleException(e);
}
}
}

73
application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java

@ -0,0 +1,73 @@
/**
* Copyright © 2016-2020 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 lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams;
import org.thingsboard.server.common.data.oauth2.SchemeType;
import org.thingsboard.server.queue.util.TbCoreComponent;
import org.thingsboard.server.service.security.permission.Operation;
import org.thingsboard.server.service.security.permission.Resource;
import org.thingsboard.server.utils.MiscUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@RestController
@TbCoreComponent
@RequestMapping("/api")
@Slf4j
public class OAuth2Controller extends BaseController {
@RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST)
@ResponseBody
public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request) throws ThingsboardException {
try {
return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainName(request));
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json")
@ResponseBody
public OAuth2ClientsParams getCurrentOAuth2Params() throws ThingsboardException {
try {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ);
return oAuth2Service.findOAuth2Params();
} catch (Exception e) {
throw handleException(e);
}
}
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')")
@RequestMapping(value = "/oauth2/config", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public OAuth2ClientsParams saveOAuth2Params(@RequestBody OAuth2ClientsParams oauth2Params) throws ThingsboardException {
try {
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE);
oAuth2Service.saveOAuth2Params(oauth2Params);
return oAuth2Service.findOAuth2Params();
} catch (Exception e) {
throw handleException(e);
}
}
}

2
application/src/main/java/org/thingsboard/server/controller/RuleChainController.java

@ -164,6 +164,8 @@ public class RuleChainController extends BaseController {
RuleChain savedRuleChain = installScripts.createDefaultRuleChain(getCurrentUser().getTenantId(), request.getName());
tbClusterService.onEntityStateChange(savedRuleChain.getTenantId(), savedRuleChain.getId(), ComponentLifecycleEvent.CREATED);
logEntityAction(savedRuleChain.getId(), savedRuleChain, null, ActionType.ADDED, null);
return savedRuleChain;

9
application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java

@ -176,15 +176,15 @@ public class ThingsboardInstallService {
log.info("Upgrading ThingsBoard from version 3.1.0 to 3.1.1 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.1.0");
case "3.1.1":
log.info("Upgrading ThingsBoard from version 3.1.1 to 3.1.2 ...");
log.info("Upgrading ThingsBoard from version 3.1.1 to 3.2.0 ...");
if (databaseTsUpgradeService != null) {
databaseTsUpgradeService.upgradeDatabase("3.1.1");
}
case "3.1.2":
log.info("Upgrading ThingsBoard from version 3.1.2 to 3.2.0 ...");
databaseEntitiesUpgradeService.upgradeDatabase("3.1.2");
databaseEntitiesUpgradeService.upgradeDatabase("3.1.1");
dataUpdateService.updateData("3.1.1");
log.info("Updating system data...");
systemDataLoaderService.updateSystemWidgets();
systemDataLoaderService.createOAuth2Templates();
break;
default:
throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion);
@ -217,6 +217,7 @@ public class ThingsboardInstallService {
systemDataLoaderService.createDefaultTenantProfiles();
systemDataLoaderService.createAdminSettings();
systemDataLoaderService.loadSystemWidgets();
systemDataLoaderService.createOAuth2Templates();
// systemDataLoaderService.loadSystemPlugins();
// systemDataLoaderService.loadSystemRules();

5
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java

@ -193,6 +193,11 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService {
adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, mailSettings);
}
@Override
public void createOAuth2Templates() throws Exception {
installScripts.createOAuth2Templates();
}
@Override
public void loadDemoData() throws Exception {
Tenant demoTenant = new Tenant();

24
application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java

@ -26,11 +26,13 @@ import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.widget.WidgetType;
import org.thingsboard.server.common.data.widget.WidgetsBundle;
import org.thingsboard.server.dao.dashboard.DashboardService;
import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.widget.WidgetTypeService;
import org.thingsboard.server.dao.widget.WidgetsBundleService;
@ -61,6 +63,7 @@ public class InstallScripts {
public static final String DEMO_DIR = "demo";
public static final String RULE_CHAINS_DIR = "rule_chains";
public static final String WIDGET_BUNDLES_DIR = "widget_bundles";
public static final String OAUTH2_CONFIG_TEMPLATES_DIR = "oauth2_config_templates";
public static final String DASHBOARDS_DIR = "dashboards";
public static final String JSON_EXT = ".json";
@ -80,6 +83,9 @@ public class InstallScripts {
@Autowired
private WidgetsBundleService widgetsBundleService;
@Autowired
private OAuth2ConfigTemplateService oAuth2TemplateService;
public Path getTenantRuleChainsDir() {
return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR);
}
@ -228,4 +234,22 @@ public class InstallScripts {
throw new RuntimeException("Unable to load dashboard from json", e);
}
}
public void createOAuth2Templates() throws Exception {
Path oauth2ConfigTemplatesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, OAUTH2_CONFIG_TEMPLATES_DIR);
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(oauth2ConfigTemplatesDir, path -> path.toString().endsWith(JSON_EXT))) {
dirStream.forEach(
path -> {
try {
JsonNode oauth2ConfigTemplateJson = objectMapper.readTree(path.toFile());
OAuth2ClientRegistrationTemplate clientRegistrationTemplate = objectMapper.treeToValue(oauth2ConfigTemplateJson, OAuth2ClientRegistrationTemplate.class);
oAuth2TemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate);
} catch (Exception e) {
log.error("Unable to load oauth2 config templates from json: [{}]", path.toString());
throw new RuntimeException("Unable to load oauth2 config templates from json", e);
}
}
);
}
}
}

6
application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java

@ -324,7 +324,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
log.info("Schema updated.");
}
break;
case "3.1.2":
case "3.1.1":
try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) {
log.info("Updating schema ...");
if (isOldSchema(conn, 3001000)) {
@ -352,7 +352,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
} catch (Exception e) {
}
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.2", "schema_update_before.sql");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update_before.sql");
loadSql(schemaUpdateFile, conn);
log.info("Creating default tenant profiles...");
@ -385,7 +385,7 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService
log.info("Updating device profiles...");
conn.createStatement().execute("call update_device_profiles()");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.2", "schema_update_after.sql");
schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update_after.sql");
loadSql(schemaUpdateFile, conn);
conn.createStatement().execute("UPDATE tb_schema_settings SET schema_version = 3002000;");

2
application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java

@ -23,6 +23,8 @@ public interface SystemDataLoaderService {
void createAdminSettings() throws Exception;
void createOAuth2Templates() throws Exception;
void loadSystemWidgets() throws Exception;
void updateSystemWidgets() throws Exception;

130
application/src/main/java/org/thingsboard/server/service/install/update/DefaultDataUpdateService.java

@ -15,6 +15,8 @@
*/
package org.thingsboard.server.service.install.update;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@ -23,9 +25,13 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.rule.engine.profile.TbDeviceProfileNode;
import org.thingsboard.rule.engine.profile.TbDeviceProfileNodeConfiguration;
import org.thingsboard.server.common.data.EntityView;
import org.thingsboard.server.common.data.SearchTextBased;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
@ -35,10 +41,13 @@ import org.thingsboard.server.common.data.kv.TsKvEntry;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.rule.RuleNode;
import org.thingsboard.server.dao.entityview.EntityViewService;
import org.thingsboard.server.dao.rule.RuleChainService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.dao.timeseries.TimeseriesService;
import org.thingsboard.server.dao.util.mapping.JacksonUtil;
import org.thingsboard.server.service.install.InstallScripts;
import javax.annotation.Nullable;
@ -49,6 +58,7 @@ import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static org.apache.commons.lang.StringUtils.isBlank;
import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper;
@Service
@Profile("install")
@ -81,6 +91,10 @@ public class DefaultDataUpdateService implements DataUpdateService {
log.info("Updating data from version 3.0.1 to 3.1.0 ...");
tenantsEntityViewsUpdater.updateEntities(null);
break;
case "3.1.1":
log.info("Updating data from version 3.1.1 to 3.2.0 ...");
tenantsRootRuleChainUpdater.updateEntities(null);
break;
default:
throw new RuntimeException("Unable to update data, unsupported fromVersion: " + fromVersion);
}
@ -107,6 +121,60 @@ public class DefaultDataUpdateService implements DataUpdateService {
}
};
private PaginatedUpdater<String, Tenant> tenantsRootRuleChainUpdater =
new PaginatedUpdater<String, Tenant>() {
@Override
protected PageData<Tenant> findEntities(String region, PageLink pageLink) {
return tenantService.findTenants(pageLink);
}
@Override
protected void updateEntity(Tenant tenant) {
try {
RuleChain ruleChain = ruleChainService.getRootTenantRuleChain(tenant.getId());
if (ruleChain == null) {
installScripts.createDefaultRuleChains(tenant.getId());
} else {
RuleChainMetaData md = ruleChainService.loadRuleChainMetaData(tenant.getId(), ruleChain.getId());
int oldIdx = md.getFirstNodeIndex();
int newIdx = md.getNodes().size();
if (md.getNodes().size() < oldIdx) {
// Skip invalid rule chains
return;
}
RuleNode oldFirstNode = md.getNodes().get(oldIdx);
if (oldFirstNode.getType().equals(TbDeviceProfileNode.class.getName())) {
// No need to update the rule node twice.
return;
}
RuleNode ruleNode = new RuleNode();
ruleNode.setRuleChainId(ruleChain.getId());
ruleNode.setName("Device Profile Node");
ruleNode.setType(TbDeviceProfileNode.class.getName());
ruleNode.setDebugMode(false);
TbDeviceProfileNodeConfiguration ruleNodeConfiguration = new TbDeviceProfileNodeConfiguration().defaultConfiguration();
ruleNode.setConfiguration(JacksonUtil.valueToTree(ruleNodeConfiguration));
ObjectNode additionalInfo = JacksonUtil.newObjectNode();
additionalInfo.put("description", "Process incoming messages from devices with the alarm rules defined in the device profile. Dispatch all incoming messages with \"Success\" relation type.");
additionalInfo.put("layoutX", 204);
additionalInfo.put("layoutY", 240);
ruleNode.setAdditionalInfo(additionalInfo);
md.getNodes().add(ruleNode);
md.setFirstNodeIndex(newIdx);
md.addConnectionInfo(newIdx, oldIdx, "Success");
ruleChainService.saveRuleChainMetaData(tenant.getId(), md);
}
} catch (Exception e) {
log.error("Unable to update Tenant", e);
}
}
};
private PaginatedUpdater<String, Tenant> tenantsEntityViewsUpdater =
new PaginatedUpdater<String, Tenant>() {
@ -121,30 +189,30 @@ public class DefaultDataUpdateService implements DataUpdateService {
}
};
private void updateTenantEntityViews(TenantId tenantId) {
PageLink pageLink = new PageLink(100);
PageData<EntityView> pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink);
boolean hasNext = true;
while (hasNext) {
List<ListenableFuture<List<Void>>> updateFutures = new ArrayList<>();
for (EntityView entityView : pageData.getData()) {
updateFutures.add(updateEntityViewLatestTelemetry(entityView));
}
try {
Futures.allAsList(updateFutures).get();
} catch (InterruptedException | ExecutionException e) {
log.error("Failed to copy latest telemetry to entity view", e);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink);
} else {
hasNext = false;
}
}
}
private void updateTenantEntityViews(TenantId tenantId) {
PageLink pageLink = new PageLink(100);
PageData<EntityView> pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink);
boolean hasNext = true;
while (hasNext) {
List<ListenableFuture<List<Void>>> updateFutures = new ArrayList<>();
for (EntityView entityView : pageData.getData()) {
updateFutures.add(updateEntityViewLatestTelemetry(entityView));
}
try {
Futures.allAsList(updateFutures).get();
} catch (InterruptedException | ExecutionException e) {
log.error("Failed to copy latest telemetry to entity view", e);
}
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
pageData = entityViewService.findEntityViewByTenantId(tenantId, pageLink);
} else {
hasNext = false;
}
}
}
private ListenableFuture<List<Void>> updateEntityViewLatestTelemetry(EntityView entityView) {
EntityViewId entityId = entityView.getId();
@ -160,13 +228,13 @@ public class DefaultDataUpdateService implements DataUpdateService {
keysFuture = Futures.immediateFuture(keys);
}
ListenableFuture<List<TsKvEntry>> latestFuture = Futures.transformAsync(keysFuture, fetchKeys -> {
List<ReadTsKvQuery> queries = fetchKeys.stream().filter(key -> !isBlank(key)).map(key -> new BaseReadTsKvQuery(key, startTs, endTs, 1, "DESC")).collect(Collectors.toList());
if (!queries.isEmpty()) {
return tsService.findAll(TenantId.SYS_TENANT_ID, entityView.getEntityId(), queries);
} else {
return Futures.immediateFuture(null);
}
}, MoreExecutors.directExecutor());
List<ReadTsKvQuery> queries = fetchKeys.stream().filter(key -> !isBlank(key)).map(key -> new BaseReadTsKvQuery(key, startTs, endTs, 1, "DESC")).collect(Collectors.toList());
if (!queries.isEmpty()) {
return tsService.findAll(TenantId.SYS_TENANT_ID, entityView.getEntityId(), queries);
} else {
return Futures.immediateFuture(null);
}
}, MoreExecutors.directExecutor());
return Futures.transformAsync(latestFuture, latestValues -> {
if (latestValues != null && !latestValues.isEmpty()) {
ListenableFuture<List<Void>> saveFuture = tsService.saveLatest(TenantId.SYS_TENANT_ID, entityId, latestValues);

4
application/src/main/java/org/thingsboard/server/service/profile/DefaultTbDeviceProfileCache.java

@ -50,9 +50,9 @@ public class DefaultTbDeviceProfileCache implements TbDeviceProfileCache {
public DeviceProfile get(TenantId tenantId, DeviceProfileId deviceProfileId) {
DeviceProfile profile = deviceProfilesMap.get(deviceProfileId);
if (profile == null) {
deviceProfileFetchLock.lock();
profile = deviceProfilesMap.get(deviceProfileId);
if (profile == null) {
deviceProfileFetchLock.lock();
try {
profile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId);
if (profile != null) {
@ -74,6 +74,8 @@ public class DefaultTbDeviceProfileCache implements TbDeviceProfileCache {
if (device != null) {
profileId = device.getDeviceProfileId();
devicesMap.put(deviceId, profileId);
} else {
return null;
}
}
return get(tenantId, profileId);

3
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java

@ -17,7 +17,6 @@ package org.thingsboard.server.service.security.auth.oauth2;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
@ -34,7 +33,6 @@ import org.thingsboard.server.common.data.id.IdBased;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.UserCredentials;
import org.thingsboard.server.dao.customer.CustomerService;
@ -49,7 +47,6 @@ import org.thingsboard.server.service.security.model.UserPrincipal;
import java.io.IOException;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

78
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicMapperUtils.java

@ -0,0 +1,78 @@
/**
* Copyright © 2016-2020 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.security.auth.oauth2;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.dao.oauth2.OAuth2User;
import java.util.Map;
@Slf4j
public class BasicMapperUtils {
private static final String START_PLACEHOLDER_PREFIX = "%{";
private static final String END_PLACEHOLDER_PREFIX = "}";
public static OAuth2User getOAuth2User(String email, Map<String, Object> attributes, OAuth2MapperConfig config) {
OAuth2User oauth2User = new OAuth2User();
oauth2User.setEmail(email);
oauth2User.setTenantName(getTenantName(email, attributes, config));
if (!StringUtils.isEmpty(config.getBasic().getLastNameAttributeKey())) {
String lastName = getStringAttributeByKey(attributes, config.getBasic().getLastNameAttributeKey());
oauth2User.setLastName(lastName);
}
if (!StringUtils.isEmpty(config.getBasic().getFirstNameAttributeKey())) {
String firstName = getStringAttributeByKey(attributes, config.getBasic().getFirstNameAttributeKey());
oauth2User.setFirstName(firstName);
}
if (!StringUtils.isEmpty(config.getBasic().getCustomerNamePattern())) {
StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX);
String customerName = sub.replace(config.getBasic().getCustomerNamePattern());
oauth2User.setCustomerName(customerName);
}
oauth2User.setAlwaysFullScreen(config.getBasic().isAlwaysFullScreen());
if (!StringUtils.isEmpty(config.getBasic().getDefaultDashboardName())) {
oauth2User.setDefaultDashboardName(config.getBasic().getDefaultDashboardName());
}
return oauth2User;
}
public static String getTenantName(String email, Map<String, Object> attributes, OAuth2MapperConfig config) {
switch (config.getBasic().getTenantNameStrategy()) {
case EMAIL:
return email;
case DOMAIN:
return email.substring(email .indexOf("@") + 1);
case CUSTOM:
StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX);
return sub.replace(config.getBasic().getTenantNamePattern());
default:
throw new RuntimeException("Tenant Name Strategy with type " + config.getBasic().getTenantNameStrategy() + " is not supported!");
}
}
public static String getStringAttributeByKey(Map<String, Object> attributes, String key) {
String result = null;
try {
result = (String) attributes.get(key);
} catch (Exception e) {
log.warn("Can't convert attribute to String by key " + key);
}
return result;
}
}

60
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java

@ -16,11 +16,9 @@
package org.thingsboard.server.service.security.auth.oauth2;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.text.StrSubstitutor;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.dao.oauth2.OAuth2ClientMapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.dao.oauth2.OAuth2User;
import org.thingsboard.server.service.security.model.SecurityUser;
@ -30,62 +28,12 @@ import java.util.Map;
@Slf4j
public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
private static final String START_PLACEHOLDER_PREFIX = "%{";
private static final String END_PLACEHOLDER_PREFIX = "}";
private static final String EMAIL_TENANT_STRATEGY = "email";
private static final String DOMAIN_TENANT_STRATEGY = "domain";
private static final String CUSTOM_TENANT_STRATEGY = "custom";
@Override
public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig config) {
OAuth2User oauth2User = new OAuth2User();
public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) {
Map<String, Object> attributes = token.getPrincipal().getAttributes();
String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey());
oauth2User.setEmail(email);
oauth2User.setTenantName(getTenantName(attributes, config));
if (!StringUtils.isEmpty(config.getBasic().getLastNameAttributeKey())) {
String lastName = getStringAttributeByKey(attributes, config.getBasic().getLastNameAttributeKey());
oauth2User.setLastName(lastName);
}
if (!StringUtils.isEmpty(config.getBasic().getFirstNameAttributeKey())) {
String firstName = getStringAttributeByKey(attributes, config.getBasic().getFirstNameAttributeKey());
oauth2User.setFirstName(firstName);
}
if (!StringUtils.isEmpty(config.getBasic().getCustomerNamePattern())) {
StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX);
String customerName = sub.replace(config.getBasic().getCustomerNamePattern());
oauth2User.setCustomerName(customerName);
}
oauth2User.setAlwaysFullScreen(config.getBasic().isAlwaysFullScreen());
if (!StringUtils.isEmpty(config.getBasic().getDefaultDashboardName())) {
oauth2User.setDefaultDashboardName(config.getBasic().getDefaultDashboardName());
}
String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey());
OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config);
return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser());
}
private String getTenantName(Map<String, Object> attributes, OAuth2ClientMapperConfig config) {
switch (config.getBasic().getTenantNameStrategy()) {
case EMAIL_TENANT_STRATEGY:
return getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey());
case DOMAIN_TENANT_STRATEGY:
String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey());
return email.substring(email .indexOf("@") + 1);
case CUSTOM_TENANT_STRATEGY:
StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX);
return sub.replace(config.getBasic().getTenantNamePattern());
default:
throw new RuntimeException("Tenant Name Strategy with type " + config.getBasic().getTenantNameStrategy() + " is not supported!");
}
}
private String getStringAttributeByKey(Map<String, Object> attributes, String key) {
String result = null;
try {
result = (String) attributes.get(key);
} catch (Exception e) {
log.warn("Can't convert attribute to String by key " + key);
}
return result;
}
}

14
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java

@ -23,28 +23,34 @@ import org.springframework.security.oauth2.client.authentication.OAuth2Authentic
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;
import org.thingsboard.server.dao.oauth2.OAuth2ClientMapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.dao.oauth2.OAuth2User;
import org.thingsboard.server.service.security.model.SecurityUser;
@Service(value = "customOAuth2ClientMapper")
@Slf4j
public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
private static final String PROVIDER_ACCESS_TOKEN = "provider-access-token";
private static final ObjectMapper json = new ObjectMapper();
private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
@Override
public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig config) {
OAuth2User oauth2User = getOAuth2User(token, config.getCustom());
public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) {
OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom());
return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser());
}
private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig.CustomOAuth2ClientMapperConfig custom) {
private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2CustomMapperConfig custom) {
if (!StringUtils.isEmpty(custom.getUsername()) && !StringUtils.isEmpty(custom.getPassword())) {
restTemplateBuilder = restTemplateBuilder.basicAuthentication(custom.getUsername(), custom.getPassword());
}
if (custom.isSendToken() && !StringUtils.isEmpty(providerAccessToken)) {
restTemplateBuilder = restTemplateBuilder.defaultHeader(PROVIDER_ACCESS_TOKEN, providerAccessToken);
}
RestTemplate restTemplate = restTemplateBuilder.build();
String request;
try {

91
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java

@ -0,0 +1,91 @@
/**
* Copyright © 2016-2020 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.security.auth.oauth2;
import lombok.Data;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.dao.oauth2.OAuth2User;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.ArrayList;
import java.util.Map;
import java.util.Optional;
@Service(value = "githubOAuth2ClientMapper")
@Slf4j
public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
private static final String EMAIL_URL_KEY = "emailUrl";
private static final String AUTHORIZATION = "Authorization";
private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
@Autowired
private OAuth2Configuration oAuth2Configuration;
@Override
public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) {
Map<String, String> githubMapperConfig = oAuth2Configuration.getGithubMapper();
String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken);
Map<String, Object> attributes = token.getPrincipal().getAttributes();
OAuth2User oAuth2User = BasicMapperUtils.getOAuth2User(email, attributes, config);
return getOrCreateSecurityUserFromOAuth2User(oAuth2User, config.isAllowUserCreation(), config.isActivateUser());
}
private synchronized String getEmail(String emailUrl, String oauth2Token) {
restTemplateBuilder = restTemplateBuilder.defaultHeader(AUTHORIZATION, "token " + oauth2Token);
RestTemplate restTemplate = restTemplateBuilder.build();
GithubEmailsResponse githubEmailsResponse;
try {
githubEmailsResponse = restTemplate.getForEntity(emailUrl, GithubEmailsResponse.class).getBody();
if (githubEmailsResponse == null){
throw new RuntimeException("Empty Github response!");
}
} catch (Exception e) {
log.error("There was an error during connection to Github API", e);
throw new RuntimeException("Unable to login. Please contact your Administrator!");
}
Optional<String> emailOpt = githubEmailsResponse.stream()
.filter(GithubEmailResponse::isPrimary)
.map(GithubEmailResponse::getEmail)
.findAny();
if (emailOpt.isPresent()){
return emailOpt.get();
} else {
log.error("Could not find primary email from {}.", githubEmailsResponse);
throw new RuntimeException("Unable to login. Please contact your Administrator!");
}
}
private static class GithubEmailsResponse extends ArrayList<GithubEmailResponse> {}
@Data
@ToString
private static class GithubEmailResponse {
private String email;
private boolean verified;
private boolean primary;
private String visibility;
}
}

4
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java

@ -16,9 +16,9 @@
package org.thingsboard.server.service.security.auth.oauth2;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.thingsboard.server.dao.oauth2.OAuth2ClientMapperConfig;
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
import org.thingsboard.server.service.security.model.SecurityUser;
public interface OAuth2ClientMapper {
SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig config);
SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config);
}

17
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java

@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.oauth2.MapperType;
@Component
@Slf4j
@ -32,14 +33,20 @@ public class OAuth2ClientMapperProvider {
@Qualifier("customOAuth2ClientMapper")
private OAuth2ClientMapper customOAuth2ClientMapper;
public OAuth2ClientMapper getOAuth2ClientMapperByType(String oauth2ClientType) {
switch (oauth2ClientType) {
case "custom":
@Autowired
@Qualifier("githubOAuth2ClientMapper")
private OAuth2ClientMapper githubOAuth2ClientMapper;
public OAuth2ClientMapper getOAuth2ClientMapperByType(MapperType oauth2MapperType) {
switch (oauth2MapperType) {
case CUSTOM:
return customOAuth2ClientMapper;
case "basic":
case BASIC:
return basicOAuth2ClientMapper;
case GITHUB:
return githubOAuth2ClientMapper;
default:
throw new RuntimeException("OAuth2ClientMapper with type " + oauth2ClientType + " is not supported!");
throw new RuntimeException("OAuth2ClientRegistrationMapper with type " + oauth2MapperType + " is not supported!");
}
}
}

28
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java

@ -16,13 +16,14 @@
package org.thingsboard.server.service.security.auth.oauth2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.security.core.Authentication;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService;
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import org.thingsboard.server.dao.oauth2.OAuth2Client;
import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.dao.oauth2.OAuth2Service;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository;
import org.thingsboard.server.service.security.model.SecurityUser;
import org.thingsboard.server.service.security.model.token.JwtToken;
@ -34,25 +35,28 @@ import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
@Component(value = "oauth2AuthenticationSuccessHandler")
@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true")
public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
private final JwtTokenFactory tokenFactory;
private final RefreshTokenRepository refreshTokenRepository;
private final OAuth2ClientMapperProvider oauth2ClientMapperProvider;
private final OAuth2Configuration oauth2Configuration;
private final OAuth2Service oAuth2Service;
private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService;
@Autowired
public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory,
final RefreshTokenRepository refreshTokenRepository,
final OAuth2ClientMapperProvider oauth2ClientMapperProvider,
final OAuth2Configuration oauth2Configuration) {
final OAuth2Service oAuth2Service,
final OAuth2AuthorizedClientService oAuth2AuthorizedClientService) {
this.tokenFactory = tokenFactory;
this.refreshTokenRepository = refreshTokenRepository;
this.oauth2ClientMapperProvider = oauth2ClientMapperProvider;
this.oauth2Configuration = oauth2Configuration;
this.oAuth2Service = oAuth2Service;
this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService;
}
@Override
@ -64,9 +68,13 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
try {
OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication;
OAuth2Client oauth2Client = oauth2Configuration.getClientByRegistrationId(token.getAuthorizedClientRegistrationId());
OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(oauth2Client.getMapperConfig().getType());
SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oauth2Client.getMapperConfig());
OAuth2ClientRegistrationInfo clientRegistration = oAuth2Service.findClientRegistrationInfo(UUID.fromString(token.getAuthorizedClientRegistrationId()));
OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient(
token.getAuthorizedClientRegistrationId(),
token.getPrincipal().getName());
OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType());
SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(),
clientRegistration.getMapperConfig());
JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser);

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

@ -32,6 +32,8 @@ public enum Resource {
USER(EntityType.USER),
WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE),
WIDGET_TYPE(EntityType.WIDGET_TYPE),
OAUTH2_CONFIGURATION_INFO(),
OAUTH2_CONFIGURATION_TEMPLATE(),
TENANT_PROFILE(EntityType.TENANT_PROFILE),
DEVICE_PROFILE(EntityType.DEVICE_PROFILE);

6
application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java

@ -19,14 +19,10 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.HashMap;
import java.util.Optional;
@Component(value="sysAdminPermissions")
public class SysAdminPermissions extends AbstractPermissions {
@ -39,6 +35,8 @@ public class SysAdminPermissions extends AbstractPermissions {
put(Resource.USER, userPermissionChecker);
put(Resource.WIDGETS_BUNDLE, systemEntityPermissionChecker);
put(Resource.WIDGET_TYPE, systemEntityPermissionChecker);
put(Resource.OAUTH2_CONFIGURATION_INFO, PermissionChecker.allowAllPermissionChecker);
put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, PermissionChecker.allowAllPermissionChecker);
put(Resource.TENANT_PROFILE, PermissionChecker.allowAllPermissionChecker);
}

3
application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java

@ -19,13 +19,10 @@ import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UserId;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.service.security.model.SecurityUser;
import java.util.HashMap;
@Component(value="tenantAdminPermissions")
public class TenantAdminPermissions extends AbstractPermissions {

3
application/src/main/java/org/thingsboard/server/service/state/DefaultDeviceStateService.java

@ -392,7 +392,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
if (stateData != null) {
DeviceState state = stateData.getState();
state.setActive(ts < state.getLastActivityTime() + state.getInactivityTimeout());
if (!state.isActive() && (state.getLastInactivityAlarmTime() == 0L || state.getLastInactivityAlarmTime() < state.getLastActivityTime())) {
if (!state.isActive() && (state.getLastInactivityAlarmTime() == 0L || state.getLastInactivityAlarmTime() < state.getLastActivityTime()) && stateData.getDeviceCreationTime() + state.getInactivityTimeout() < ts) {
state.setLastInactivityAlarmTime(ts);
pushRuleEngineMessage(stateData, INACTIVITY_EVENT);
save(deviceId, INACTIVITY_ALARM_TIME, ts);
@ -479,6 +479,7 @@ public class DefaultDeviceStateService implements DeviceStateService {
return DeviceStateData.builder()
.tenantId(device.getTenantId())
.deviceId(device.getId())
.deviceCreationTime(device.getCreatedTime())
.metaData(md)
.state(deviceState).build();
} catch (Exception e) {

4
application/src/main/java/org/thingsboard/server/service/state/DeviceStateData.java

@ -30,8 +30,8 @@ class DeviceStateData {
private final TenantId tenantId;
private final DeviceId deviceId;
private final long deviceCreationTime;
private TbMsgMetaData metaData;
private final DeviceState state;
}

24
application/src/main/java/org/thingsboard/server/utils/MiscUtils.java

@ -49,12 +49,27 @@ public class MiscUtils {
}
public static String constructBaseUrl(HttpServletRequest request) {
String scheme = request.getScheme();
return String.format("%s://%s:%d",
getScheme(request),
getDomainName(request),
getPort(request));
}
public static String getScheme(HttpServletRequest request){
String scheme = request.getScheme();
String forwardedProto = request.getHeader("x-forwarded-proto");
if (forwardedProto != null) {
scheme = forwardedProto;
}
return scheme;
}
public static String getDomainName(HttpServletRequest request){
return request.getServerName();
}
public static int getPort(HttpServletRequest request){
String forwardedProto = request.getHeader("x-forwarded-proto");
int serverPort = request.getServerPort();
if (request.getHeader("x-forwarded-port") != null) {
@ -72,11 +87,6 @@ public class MiscUtils {
break;
}
}
String baseUrl = String.format("%s://%s:%d",
scheme,
request.getServerName(),
serverPort);
return baseUrl;
return serverPort;
}
}

63
application/src/main/resources/thingsboard.yml

@ -113,62 +113,10 @@ security:
basic:
enabled: "${SECURITY_BASIC_ENABLED:false}"
oauth2:
# Enable/disable OAuth 2 login functionality
# For details please refer to https://thingsboard.io/docs/user-guide/oauth-2-support/
enabled: "${SECURITY_OAUTH2_ENABLED:false}"
# Redirect URL where access code from external user management system will be processed
loginProcessingUrl: "${SECURITY_OAUTH2_LOGIN_PROCESSING_URL:/login/oauth2/code/}"
# List of SSO clients
clients:
default:
# Label that going to be show on login button - 'Login with {loginButtonLabel}'
loginButtonLabel: "${SECURITY_OAUTH2_DEFAULT_LOGIN_BUTTON_LABEL:Default}"
# Icon that going to be show on login button. Material design icon ID (https://material.angularjs.org/latest/api/directive/mdIcon)
loginButtonIcon: "${SECURITY_OAUTH2_DEFAULT_LOGIN_BUTTON_ICON:}"
clientName: "${SECURITY_OAUTH2_DEFAULT_CLIENT_NAME:ClientName}"
clientId: "${SECURITY_OAUTH2_DEFAULT_CLIENT_ID:}"
clientSecret: "${SECURITY_OAUTH2_DEFAULT_CLIENT_SECRET:}"
accessTokenUri: "${SECURITY_OAUTH2_DEFAULT_ACCESS_TOKEN_URI:}"
authorizationUri: "${SECURITY_OAUTH2_DEFAULT_AUTHORIZATION_URI:}"
scope: "${SECURITY_OAUTH2_DEFAULT_SCOPE:}"
# Redirect URL that must be in sync with 'security.oauth2.loginProcessingUrl', but domain name added
redirectUriTemplate: "${SECURITY_OAUTH2_DEFAULT_REDIRECT_URI_TEMPLATE:http://localhost:8080/login/oauth2/code/}"
jwkSetUri: "${SECURITY_OAUTH2_DEFAULT_JWK_SET_URI:}"
# 'authorization_code', 'implicit', 'refresh_token' or 'client_credentials'
authorizationGrantType: "${SECURITY_OAUTH2_DEFAULT_AUTHORIZATION_GRANT_TYPE:authorization_code}"
clientAuthenticationMethod: "${SECURITY_OAUTH2_DEFAULT_CLIENT_AUTHENTICATION_METHOD:post}" # basic or post
userInfoUri: "${SECURITY_OAUTH2_DEFAULT_USER_INFO_URI:}"
userNameAttributeName: "${SECURITY_OAUTH2_DEFAULT_USER_NAME_ATTRIBUTE_NAME:email}"
mapperConfig:
# Allows to create user if it not exists
allowUserCreation: "${SECURITY_OAUTH2_DEFAULT_MAPPER_ALLOW_USER_CREATION:true}"
# Allows user to setup ThingsBoard internal password and login over default Login window
activateUser: "${SECURITY_OAUTH2_DEFAULT_MAPPER_ACTIVATE_USER:false}"
# Mapper type of converter from external user into internal - 'basic' or 'custom'
type: "${SECURITY_OAUTH2_DEFAULT_MAPPER_TYPE:basic}"
basic:
# Key from attributes of external user object to use as email
emailAttributeKey: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_EMAIL_ATTRIBUTE_KEY:email}"
firstNameAttributeKey: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_FIRST_NAME_ATTRIBUTE_KEY:}"
lastNameAttributeKey: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_LAST_NAME_ATTRIBUTE_KEY:}"
# Strategy for generating Tenant from external user object - 'domain', 'email' or 'custom'
# 'domain' - name of the Tenant will be extracted as domain from the email of the user
# 'email' - name of the Tenant will email of the user
# 'custom' - please configure 'tenantNamePattern' for custom mapping
tenantNameStrategy: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_TENANT_NAME_STRATEGY:domain}"
# %{attribute_key} as placeholder for attribute value of attributes of external user object
tenantNamePattern: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_TENANT_NAME_PATTERN:}"
# If this field is not empty, user will be created as a user under defined Customer
# %{attribute_key} as placeholder for attribute value of attributes of external user object
customerNamePattern: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_CUSTOMER_NAME_PATTERN:}"
# If this field is not empty, user will be created with default defined Dashboard
defaultDashboardName: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_DEFAULT_DASHBOARD_NAME:}"
# If this field is set 'true' along with non-empty 'defaultDashboardName', user will start from the defined Dashboard in fullscreen mode
alwaysFullScreen: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_ALWAYS_FULL_SCREEN:false}"
custom:
url: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_URL:}"
username: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_USERNAME:}"
password: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_PASSWORD:}"
githubMapper:
emailUrl: "${SECURITY_OAUTH2_GITHUB_MAPPER_EMAIL_URL_KEY:https://api.github.com/user/emails}"
# Dashboard parameters
dashboard:
@ -439,6 +387,9 @@ updates:
# Enable/disable updates checking.
enabled: "${UPDATES_ENABLED:true}"
# spring freemarker configuration
spring.freemarker.checkTemplateLocation: "false"
# spring CORS configuration
spring.mvc.cors:
mappings:
@ -478,7 +429,7 @@ spring:
database-platform: "${SPRING_JPA_DATABASE_PLATFORM:org.hibernate.dialect.PostgreSQLDialect}"
datasource:
driverClassName: "${SPRING_DRIVER_CLASS_NAME:org.postgresql.Driver}"
url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard_32}"
url: "${SPRING_DATASOURCE_URL:jdbc:postgresql://localhost:5432/thingsboard}"
username: "${SPRING_DATASOURCE_USERNAME:postgres}"
password: "${SPRING_DATASOURCE_PASSWORD:postgres}"
hikari:
@ -605,6 +556,8 @@ transport:
key_password: "${MQTT_SSL_KEY_PASSWORD:server_key_password}"
# Type of the key store
key_store_type: "${MQTT_SSL_KEY_STORE_TYPE:JKS}"
# Skip certificate validity check for client certificates.
skip_validity_check_for_client_cert: "${MQTT_SSL_SKIP_VALIDITY_CHECK_FOR_CLIENT_CERT:false}"
# Local CoAP transport parameters
coap:
# Enable/disable coap transport protocol.

30
application/src/test/java/org/thingsboard/server/mqtt/AbstractMqttIntegrationTest.java

@ -44,6 +44,7 @@ import org.thingsboard.server.gen.transport.TransportProtos;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -219,4 +220,33 @@ public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest
return builder.build();
}
protected <T> T doExecuteWithRetriesAndInterval(SupplierWithThrowable<T> supplier, int retries, int intervalMs) throws Exception {
int count = 0;
T result = null;
Throwable lastException = null;
while (count < retries) {
try {
result = supplier.get();
if (result != null) {
return result;
}
} catch (Throwable e) {
lastException = e;
}
count++;
if (count < retries) {
Thread.sleep(intervalMs);
}
}
if (lastException != null) {
throw new RuntimeException(lastException);
} else {
return result;
}
}
@FunctionalInterface
public interface SupplierWithThrowable<T> {
T get() throws Throwable;
}
}

8
application/src/test/java/org/thingsboard/server/mqtt/attributes/request/AbstractMqttAttributesRequestIntegrationTest.java

@ -84,10 +84,14 @@ public abstract class AbstractMqttAttributesRequestIntegrationTest extends Abstr
postGatewayDeviceClientAttributes(client);
Thread.sleep(1000);
Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + "Gateway Device Request Attributes", Device.class),
20,
100);
Device savedDevice = doGet("/api/tenant/devices?deviceName=" + "Gateway Device Request Attributes", Device.class);
assertNotNull(savedDevice);
Thread.sleep(1000);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
Thread.sleep(1000);

9
application/src/test/java/org/thingsboard/server/mqtt/attributes/updates/AbstractMqttAttributesUpdatesIntegrationTest.java

@ -84,7 +84,7 @@ public abstract class AbstractMqttAttributesUpdatesIntegrationTest extends Abstr
client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(2000);
Thread.sleep(1000);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS);
@ -127,14 +127,15 @@ public abstract class AbstractMqttAttributesUpdatesIntegrationTest extends Abstr
publishMqttMsg(client, connectPayloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
Thread.sleep(1000);
Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + "Gateway Device Subscribe to attribute updates", Device.class),
20,
100);
Device savedDevice = doGet("/api/tenant/devices?deviceName=" + "Gateway Device Subscribe to attribute updates", Device.class);
assertNotNull(savedDevice);
client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(2000);
Thread.sleep(1000);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS);

35
application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimDeviceTest.java

@ -75,25 +75,21 @@ public abstract class AbstractMqttClaimDeviceTest extends AbstractMqttIntegratio
}
@Test
@Ignore
public void testClaimingDevice() throws Exception {
processTestClaimingDevice(false);
}
@Test
@Ignore
public void testClaimingDeviceWithoutSecretAndDuration() throws Exception {
processTestClaimingDevice(true);
}
@Test
@Ignore
public void testGatewayClaimingDevice() throws Exception {
processTestGatewayClaimingDevice("Test claiming gateway device", false);
}
@Test
@Ignore
public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception {
processTestGatewayClaimingDevice("Test claiming gateway device empty payload", true);
}
@ -116,8 +112,6 @@ public abstract class AbstractMqttClaimDeviceTest extends AbstractMqttIntegratio
protected void validateClaimResponse(boolean emptyPayload, MqttAsyncClient client, byte[] payloadBytes, byte[] failurePayloadBytes) throws Exception {
client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes));
Thread.sleep(2000);
loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD);
ClaimRequest claimRequest;
if (!emptyPayload) {
@ -126,14 +120,21 @@ public abstract class AbstractMqttClaimDeviceTest extends AbstractMqttIntegratio
claimRequest = new ClaimRequest(null);
}
ClaimResponse claimResponse = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest());
ClaimResponse claimResponse = doExecuteWithRetriesAndInterval(
() -> doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()),
20,
100
);
assertEquals(claimResponse, ClaimResponse.FAILURE);
client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(payloadBytes));
Thread.sleep(2000);
ClaimResult claimResult = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResult.class, status().isOk());
ClaimResult claimResult = doExecuteWithRetriesAndInterval(
() -> doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResult.class, status().isOk()),
20,
100
);
assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS);
Device claimedDevice = claimResult.getDevice();
assertNotNull(claimedDevice);
@ -147,9 +148,12 @@ public abstract class AbstractMqttClaimDeviceTest extends AbstractMqttIntegratio
protected void validateGatewayClaimResponse(String deviceName, boolean emptyPayload, MqttAsyncClient client, byte[] failurePayloadBytes, byte[] payloadBytes) throws Exception {
client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes));
Thread.sleep(2000);
Device savedDevice = doExecuteWithRetriesAndInterval(
() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class),
20,
100
);
Device savedDevice = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
assertNotNull(savedDevice);
loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD);
@ -165,9 +169,12 @@ public abstract class AbstractMqttClaimDeviceTest extends AbstractMqttIntegratio
client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(payloadBytes));
Thread.sleep(2000);
ClaimResult claimResult = doExecuteWithRetriesAndInterval(
() -> doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResult.class, status().isOk()),
20,
100
);
ClaimResult claimResult = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResult.class, status().isOk());
assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS);
Device claimedDevice = claimResult.getDevice();
assertNotNull(claimedDevice);

1
application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimJsonDeviceTest.java

@ -52,7 +52,6 @@ public abstract class AbstractMqttClaimJsonDeviceTest extends AbstractMqttClaimD
}
@Test
@Ignore
public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception {
processTestGatewayClaimingDevice("Test claiming gateway device empty payload Json", true);
}

4
application/src/test/java/org/thingsboard/server/mqtt/claim/AbstractMqttClaimProtoDeviceTest.java

@ -37,25 +37,21 @@ public abstract class AbstractMqttClaimProtoDeviceTest extends AbstractMqttClaim
public void afterTest() throws Exception { super.afterTest(); }
@Test
@Ignore
public void testClaimingDevice() throws Exception {
processTestClaimingDevice(false);
}
@Test
@Ignore
public void testClaimingDeviceWithoutSecretAndDuration() throws Exception {
processTestClaimingDevice(true);
}
@Test
@Ignore
public void testGatewayClaimingDevice() throws Exception {
processTestGatewayClaimingDevice("Test claiming gateway device Proto", false);
}
@Test
@Ignore
public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception {
processTestGatewayClaimingDevice("Test claiming gateway device empty payload Proto", true);
}

24
application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java

@ -84,7 +84,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
client.subscribe(MqttTopics.DEVICE_RPC_REQUESTS_SUB_TOPIC, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(2000);
Thread.sleep(1000);
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
@ -109,7 +109,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
TestMqttCallback callback = new TestMqttCallback(client, latch);
client.setCallback(callback);
Thread.sleep(2000);
Thread.sleep(1000);
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"26\",\"value\": 1}}";
String deviceId = savedDevice.getId().getId().toString();
@ -132,9 +132,11 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
protected void validateOneWayRpcGatewayResponse(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception {
publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
Thread.sleep(2000);
Device savedDevice = getDeviceByName(deviceName);
Device savedDevice = doExecuteWithRetriesAndInterval(
() -> getDeviceByName(deviceName),
20,
100
);
assertNotNull(savedDevice);
CountDownLatch latch = new CountDownLatch(1);
@ -143,7 +145,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(2000);
Thread.sleep(1000);
String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
String deviceId = savedDevice.getId().getId().toString();
@ -156,9 +158,11 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
protected void validateTwoWayRpcGateway(String deviceName, MqttAsyncClient client, byte[] payloadBytes) throws Exception {
publishMqttMsg(client, payloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC);
Thread.sleep(2000);
Device savedDevice = getDeviceByName(deviceName);
Device savedDevice = doExecuteWithRetriesAndInterval(
() -> getDeviceByName(deviceName),
20,
100
);
assertNotNull(savedDevice);
CountDownLatch latch = new CountDownLatch(1);
@ -167,7 +171,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractM
client.subscribe(MqttTopics.GATEWAY_RPC_TOPIC, MqttQoS.AT_MOST_ONCE.value());
Thread.sleep(2000);
Thread.sleep(1000);
String setGpioRequest = "{\"method\": \"toggle_gpio\", \"params\": {\"pin\":1}}";
String deviceId = savedDevice.getId().getId().toString();

15
application/src/test/java/org/thingsboard/server/mqtt/telemetry/attributes/AbstractMqttAttributesIntegrationTest.java

@ -75,7 +75,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
DeviceId deviceId = savedDevice.getId();
long start = System.currentTimeMillis();
long end = System.currentTimeMillis() + 2000;
long end = System.currentTimeMillis() + 5000;
List<String> actualKeys = null;
while (start <= end) {
@ -106,13 +106,20 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
publishMqttMsg(client, payload, MqttTopics.GATEWAY_ATTRIBUTES_TOPIC);
Thread.sleep(2000);
Device firstDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class),
20,
100);
Device firstDevice = doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class);
assertNotNull(firstDevice);
Device secondDevice = doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class);
Device secondDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class),
20,
100);
assertNotNull(secondDevice);
Thread.sleep(2000);
List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/attributes/CLIENT_SCOPE", List.class);
Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys);

23
application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesIntegrationTest.java

@ -88,10 +88,12 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
publishMqttMsg(client, payload.getBytes(), MqttTopics.GATEWAY_CONNECT_TOPIC);
Thread.sleep(2000);
String deviceName = "Device A";
Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class),
20,
100);
assertNotNull(device);
}
@ -102,7 +104,7 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
String deviceId = savedDevice.getId().getId().toString();
long start = System.currentTimeMillis();
long end = System.currentTimeMillis() + 2000;
long end = System.currentTimeMillis() + 5000;
List<String> actualKeys = null;
while (start <= end) {
@ -139,13 +141,20 @@ public abstract class AbstractMqttTimeseriesIntegrationTest extends AbstractMqtt
publishMqttMsg(client, payload, topic);
Thread.sleep(2000);
Device firstDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class),
20,
100);
Device firstDevice = doGet("/api/tenant/devices?deviceName=" + firstDeviceName, Device.class);
assertNotNull(firstDevice);
Device secondDevice = doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class);
Device secondDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + secondDeviceName, Device.class),
20,
100);
assertNotNull(secondDevice);
Thread.sleep(2000);
List<String> firstDeviceActualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + firstDevice.getId() + "/keys/timeseries", List.class);
Set<String> firstDeviceActualKeySet = new HashSet<>(firstDeviceActualKeys);

6
application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesJsonIntegrationTest.java

@ -73,10 +73,10 @@ public abstract class AbstractMqttTimeseriesJsonIntegrationTest extends Abstract
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
publishMqttMsg(client, payload.getBytes(), MqttTopics.GATEWAY_CONNECT_TOPIC);
Thread.sleep(2000);
String deviceName = "Device A";
Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class),
20,
100);
assertNotNull(device);
}
}

5
application/src/test/java/org/thingsboard/server/mqtt/telemetry/timeseries/AbstractMqttTimeseriesProtoIntegrationTest.java

@ -86,9 +86,10 @@ public abstract class AbstractMqttTimeseriesProtoIntegrationTest extends Abstrac
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken);
publishMqttMsg(client, connectMsgProto.toByteArray(), MqttTopics.GATEWAY_CONNECT_TOPIC);
Thread.sleep(2000);
Device device = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class),
20,
100);
Device device = doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class);
assertNotNull(device);
}

2
common/dao-api/src/main/java/org/thingsboard/server/dao/device/DeviceProfileService.java

@ -37,7 +37,7 @@ public interface DeviceProfileService {
PageData<DeviceProfile> findDeviceProfiles(TenantId tenantId, PageLink pageLink);
PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink);
PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink, String transportType);
DeviceProfile findOrCreateDeviceProfile(TenantId tenantId, String profileName);

31
common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateService.java

@ -0,0 +1,31 @@
/**
* Copyright © 2016-2020 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 org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
import java.util.List;
public interface OAuth2ConfigTemplateService {
OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate);
OAuth2ClientRegistrationTemplate findClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId);
List<OAuth2ClientRegistrationTemplate> findAllClientRegistrationTemplates();
void deleteClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId);
}

12
common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java

@ -16,10 +16,20 @@
package org.thingsboard.server.dao.oauth2;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams;
import java.util.List;
import java.util.UUID;
public interface OAuth2Service {
List<OAuth2ClientInfo> getOAuth2Clients(String domainScheme, String domainName);
List<OAuth2ClientInfo> getOAuth2Clients();
void saveOAuth2Params(OAuth2ClientsParams oauth2Params);
OAuth2ClientsParams findOAuth2Params();
OAuth2ClientRegistrationInfo findClientRegistrationInfo(UUID id);
List<OAuth2ClientRegistrationInfo> findAllClientRegistrationInfos();
}

1
common/dao-api/src/main/java/org/thingsboard/server/dao/rule/RuleNodeStateService.java

@ -30,4 +30,5 @@ public interface RuleNodeStateService {
RuleNodeState save(TenantId tenantId, RuleNodeState ruleNodeState);
void removeByRuleNodeId(TenantId tenantId, RuleNodeId selfId);
}

4
common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java

@ -24,7 +24,7 @@ public interface AdminSettingsService {
AdminSettings findAdminSettingsById(TenantId tenantId, AdminSettingsId adminSettingsId);
AdminSettings findAdminSettingsByKey(TenantId tenantId, String key);
AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings);
}

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

@ -19,5 +19,5 @@ package org.thingsboard.server.common.data;
* @author Andrew Shvayka
*/
public enum EntityType {
TENANT, TENANT_PROFILE, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, DEVICE_PROFILE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE
TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, TENANT_PROFILE, DEVICE_PROFILE
}

10
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2IntegrationId.java → common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java

@ -20,16 +20,14 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.UUID;
public class OAuth2IntegrationId extends UUIDBased {
private static final long serialVersionUID = 1L;
public class OAuth2ClientRegistrationId extends UUIDBased {
@JsonCreator
public OAuth2IntegrationId(@JsonProperty("id") UUID id) {
public OAuth2ClientRegistrationId(@JsonProperty("id") UUID id) {
super(id);
}
public static OAuth2IntegrationId fromString(String oauth2IntegrationId) {
return new OAuth2IntegrationId(UUID.fromString(oauth2IntegrationId));
public static OAuth2ClientRegistrationId fromString(String clientRegistrationId) {
return new OAuth2ClientRegistrationId(UUID.fromString(clientRegistrationId));
}
}

33
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java

@ -0,0 +1,33 @@
/**
* Copyright © 2016-2020 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.id;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.UUID;
public class OAuth2ClientRegistrationInfoId extends UUIDBased {
@JsonCreator
public OAuth2ClientRegistrationInfoId(@JsonProperty("id") UUID id) {
super(id);
}
public static OAuth2ClientRegistrationInfoId fromString(String clientRegistrationInfoId) {
return new OAuth2ClientRegistrationInfoId(UUID.fromString(clientRegistrationInfoId));
}
}

33
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationTemplateId.java

@ -0,0 +1,33 @@
/**
* Copyright © 2016-2020 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.id;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.UUID;
public class OAuth2ClientRegistrationTemplateId extends UUIDBased {
@JsonCreator
public OAuth2ClientRegistrationTemplateId(@JsonProperty("id") UUID id) {
super(id);
}
public static OAuth2ClientRegistrationTemplateId fromString(String clientRegistrationTemplateId) {
return new OAuth2ClientRegistrationTemplateId(UUID.fromString(clientRegistrationTemplateId));
}
}

35
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Client.java → common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java

@ -13,27 +13,32 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.dao.oauth2;
package org.thingsboard.server.common.data.oauth2;
import lombok.Data;
import com.fasterxml.jackson.databind.JsonNode;
import lombok.*;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId;
@Data
public class OAuth2Client {
import java.util.List;
private String loginButtonLabel;
private String loginButtonIcon;
private String clientName;
@EqualsAndHashCode
@Data
@ToString(exclude = {"clientSecret"})
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ClientRegistrationDto {
private OAuth2MapperConfig mapperConfig;
private String clientId;
private String clientSecret;
private String accessTokenUri;
private String authorizationUri;
private String scope;
private String redirectUriTemplate;
private String jwkSetUri;
private String authorizationGrantType;
private String clientAuthenticationMethod;
private String accessTokenUri;
private List<String> scope;
private String userInfoUri;
private String userNameAttributeName;
private OAuth2ClientMapperConfig mapperConfig;
private String jwkSetUri;
private String clientAuthenticationMethod;
private String loginButtonLabel;
private String loginButtonIcon;
private JsonNode additionalInfo;
}

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

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2020 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.*;
@EqualsAndHashCode
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class DomainInfo {
private SchemeType scheme;
private String name;
}

39
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistrationInfo.java

@ -0,0 +1,39 @@
/**
* Copyright © 2016-2020 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;
@EqualsAndHashCode(callSuper = true)
@Data
public class ExtendedOAuth2ClientRegistrationInfo extends OAuth2ClientRegistrationInfo {
private String domainName;
private SchemeType domainScheme;
public ExtendedOAuth2ClientRegistrationInfo() {
super();
}
public ExtendedOAuth2ClientRegistrationInfo(OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo,
SchemeType domainScheme,
String domainName) {
super(oAuth2ClientRegistrationInfo);
this.domainScheme = domainScheme;
this.domainName = domainName;
}
}

20
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java

@ -0,0 +1,20 @@
/**
* Copyright © 2016-2020 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;
public enum MapperType {
BASIC, CUSTOM, GITHUB;
}

33
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java

@ -0,0 +1,33 @@
/**
* Copyright © 2016-2020 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.*;
@Builder(toBuilder = true)
@EqualsAndHashCode
@Data
@ToString
public class OAuth2BasicMapperConfig {
private final String emailAttributeKey;
private final String firstNameAttributeKey;
private final String lastNameAttributeKey;
private final TenantNameStrategyType tenantNameStrategy;
private final String tenantNamePattern;
private final String customerNamePattern;
private final String defaultDashboardName;
private final boolean alwaysFullScreen;
}

19
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientInfo.java

@ -17,27 +17,20 @@ package org.thingsboard.server.common.data.oauth2;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.id.OAuth2IntegrationId;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
@EqualsAndHashCode(callSuper = true)
@EqualsAndHashCode
@Data
public class OAuth2ClientInfo extends BaseData<OAuth2IntegrationId> {
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2ClientInfo {
private String name;
private String icon;
private String url;
public OAuth2ClientInfo() {
super();
}
public OAuth2ClientInfo(OAuth2IntegrationId id) {
super(id);
}
public OAuth2ClientInfo(OAuth2ClientInfo oauth2ClientInfo) {
super(oauth2ClientInfo);
this.name = oauth2ClientInfo.getName();
this.icon = oauth2ClientInfo.getIcon();
this.url = oauth2ClientInfo.getUrl();

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

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2020 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.OAuth2ClientRegistrationId;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId;
@EqualsAndHashCode(callSuper = true)
@Data
@ToString
@NoArgsConstructor
public class OAuth2ClientRegistration extends BaseData<OAuth2ClientRegistrationId> {
private OAuth2ClientRegistrationInfoId clientRegistrationId;
private String domainName;
private SchemeType domainScheme;
public OAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration) {
super(clientRegistration);
this.clientRegistrationId = clientRegistration.clientRegistrationId;
this.domainName = clientRegistration.domainName;
this.domainScheme = clientRegistration.domainScheme;
}
}

76
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationInfo.java

@ -0,0 +1,76 @@
/**
* Copyright © 2016-2020 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 com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.thingsboard.server.common.data.HasName;
import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
@ToString(exclude = {"clientSecret"})
@NoArgsConstructor
public class OAuth2ClientRegistrationInfo extends SearchTextBasedWithAdditionalInfo<OAuth2ClientRegistrationInfoId> implements HasName {
private boolean enabled;
private OAuth2MapperConfig mapperConfig;
private String clientId;
private String clientSecret;
private String authorizationUri;
private String accessTokenUri;
private List<String> scope;
private String userInfoUri;
private String userNameAttributeName;
private String jwkSetUri;
private String clientAuthenticationMethod;
private String loginButtonLabel;
private String loginButtonIcon;
public OAuth2ClientRegistrationInfo(OAuth2ClientRegistrationInfo clientRegistration) {
super(clientRegistration);
this.enabled = clientRegistration.enabled;
this.mapperConfig = clientRegistration.mapperConfig;
this.clientId = clientRegistration.clientId;
this.clientSecret = clientRegistration.clientSecret;
this.authorizationUri = clientRegistration.authorizationUri;
this.accessTokenUri = clientRegistration.accessTokenUri;
this.scope = clientRegistration.scope;
this.userInfoUri = clientRegistration.userInfoUri;
this.userNameAttributeName = clientRegistration.userNameAttributeName;
this.jwkSetUri = clientRegistration.jwkSetUri;
this.clientAuthenticationMethod = clientRegistration.clientAuthenticationMethod;
this.loginButtonLabel = clientRegistration.loginButtonLabel;
this.loginButtonIcon = clientRegistration.loginButtonIcon;
}
@Override
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
public String getName() {
return loginButtonLabel;
}
@Override
public String getSearchText() {
return getName();
}
}

78
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java

@ -0,0 +1,78 @@
/**
* Copyright © 2016-2020 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.HasName;
import org.thingsboard.server.common.data.HasTenantId;
import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.List;
@EqualsAndHashCode(callSuper = true)
@Data
@ToString
@NoArgsConstructor
public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo<OAuth2ClientRegistrationTemplateId> implements HasName {
private String providerId;
private MapperType mapperType;
private OAuth2BasicMapperConfig basic;
private String authorizationUri;
private String accessTokenUri;
private List<String> scope;
private String userInfoUri;
private String userNameAttributeName;
private String jwkSetUri;
private String clientAuthenticationMethod;
private String comment;
private String loginButtonIcon;
private String loginButtonLabel;
private String helpLink;
public OAuth2ClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) {
super(clientRegistrationTemplate);
this.providerId = clientRegistrationTemplate.providerId;
this.mapperType = clientRegistrationTemplate.mapperType;
this.basic = clientRegistrationTemplate.basic;
this.authorizationUri = clientRegistrationTemplate.authorizationUri;
this.accessTokenUri = clientRegistrationTemplate.accessTokenUri;
this.scope = clientRegistrationTemplate.scope;
this.userInfoUri = clientRegistrationTemplate.userInfoUri;
this.userNameAttributeName = clientRegistrationTemplate.userNameAttributeName;
this.jwkSetUri = clientRegistrationTemplate.jwkSetUri;
this.clientAuthenticationMethod = clientRegistrationTemplate.clientAuthenticationMethod;
this.comment = clientRegistrationTemplate.comment;
this.loginButtonIcon = clientRegistrationTemplate.loginButtonIcon;
this.loginButtonLabel = clientRegistrationTemplate.loginButtonLabel;
this.helpLink = clientRegistrationTemplate.helpLink;
}
@Override
public String getName() {
return providerId;
}
@Override
public String getSearchText() {
return getName();
}
}

32
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java

@ -0,0 +1,32 @@
/**
* Copyright © 2016-2020 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.*;
import java.util.List;
import java.util.Set;
@EqualsAndHashCode
@Data
@ToString
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2ClientsDomainParams {
private Set<DomainInfo> domainInfos;
private Set<ClientRegistrationDto> clientRegistrations;
}

30
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java

@ -0,0 +1,30 @@
/**
* Copyright © 2016-2020 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.*;
import java.util.Set;
@EqualsAndHashCode
@Data
@ToString
@Builder(toBuilder = true)
@NoArgsConstructor
@AllArgsConstructor
public class OAuth2ClientsParams {
private boolean enabled;
private Set<OAuth2ClientsDomainParams> domainsParams;
}

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

@ -0,0 +1,29 @@
/**
* Copyright © 2016-2020 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.*;
@Builder(toBuilder = true)
@EqualsAndHashCode
@Data
@ToString(exclude = {"password"})
public class OAuth2CustomMapperConfig {
private final String url;
private final String username;
private final String password;
private final boolean sendToken;
}

33
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java

@ -0,0 +1,33 @@
/**
* Copyright © 2016-2020 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.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
@Builder(toBuilder = true)
@EqualsAndHashCode
@Data
@ToString
public class OAuth2MapperConfig {
private boolean allowUserCreation;
private boolean activateUser;
private MapperType type;
private OAuth2BasicMapperConfig basic;
private OAuth2CustomMapperConfig custom;
}

20
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/SchemeType.java

@ -0,0 +1,20 @@
/**
* Copyright © 2016-2020 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;
public enum SchemeType {
HTTP, HTTPS, MIXED;
}

20
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java

@ -0,0 +1,20 @@
/**
* Copyright © 2016-2020 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;
public enum TenantNameStrategyType {
DOMAIN, EMAIL, CUSTOM;
}

3
common/data/src/main/java/org/thingsboard/server/common/data/query/DynamicValueSourceType.java

@ -18,5 +18,6 @@ package org.thingsboard.server.common.data.query;
public enum DynamicValueSourceType {
CURRENT_TENANT,
CURRENT_CUSTOMER,
CURRENT_USER
CURRENT_USER,
CURRENT_DEVICE
}

4
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportContext.java

@ -51,6 +51,10 @@ public class MqttTransportContext extends TransportContext {
@Value("${transport.mqtt.netty.max_payload_size}")
private Integer maxPayloadSize;
@Getter
@Value("${transport.mqtt.netty.skip_validity_check_for_client_cert:false}")
private boolean skipValidityCheckForClientCert;
@Getter
@Setter
private SslHandler sslHandler;

4
common/transport/mqtt/src/main/java/org/thingsboard/server/transport/mqtt/MqttTransportHandler.java

@ -68,6 +68,7 @@ import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.Date;
import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED;
import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED;
@ -386,6 +387,9 @@ public class MqttTransportHandler extends ChannelInboundHandlerAdapter implement
private void processX509CertConnect(ChannelHandlerContext ctx, X509Certificate cert) {
try {
if(!context.isSkipValidityCheckForClientCert()){
cert.checkValidity();
}
String strCert = SslUtil.getX509CertificateString(cert);
String sha3Hash = EncryptionUtil.getSha3Hash(strCert);
transportService.process(DeviceTransportType.MQTT, ValidateDeviceX509CertRequestMsg.newBuilder().setHash(sha3Hash).build(),

2
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileDao.java

@ -32,7 +32,7 @@ public interface DeviceProfileDao extends Dao<DeviceProfile> {
PageData<DeviceProfile> findDeviceProfiles(TenantId tenantId, PageLink pageLink);
PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink);
PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink, String transportType);
DeviceProfile findDefaultDeviceProfile(TenantId tenantId);

4
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileServiceImpl.java

@ -178,11 +178,11 @@ public class DeviceProfileServiceImpl extends AbstractEntityService implements D
}
@Override
public PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink) {
public PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink, String transportType) {
log.trace("Executing findDeviceProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink);
validateId(tenantId, INCORRECT_TENANT_ID + tenantId);
Validator.validatePageLink(pageLink);
return deviceProfileDao.findDeviceProfileInfos(tenantId, pageLink);
return deviceProfileDao.findDeviceProfileInfos(tenantId, pageLink, transportType);
}
@Cacheable(cacheNames = DEVICE_PROFILE_CACHE, key = "{#tenantId.id, #name}")

47
dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java

@ -389,6 +389,53 @@ public class ModelConstants {
public static final String RULE_NODE_STATE_ENTITY_ID_PROPERTY = "entity_id";
public static final String RULE_NODE_STATE_DATA_PROPERTY = "state_data";
/**
* OAuth2 client registration constants.
*/
public static final String OAUTH2_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY;
public static final String OAUTH2_CLIENT_REGISTRATION_INFO_COLUMN_FAMILY_NAME = "oauth2_client_registration_info";
public static final String OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_client_registration";
public static final String OAUTH2_CLIENT_REGISTRATION_TO_DOMAIN_COLUMN_FAMILY_NAME = "oauth2_client_registration_to_domain";
public static final String OAUTH2_CLIENT_REGISTRATION_TEMPLATE_COLUMN_FAMILY_NAME = "oauth2_client_registration_template";
public static final String OAUTH2_ENABLED_PROPERTY = "enabled";
public static final String OAUTH2_TEMPLATE_PROVIDER_ID_PROPERTY = "provider_id";
public static final String OAUTH2_CLIENT_REGISTRATION_INFO_ID_PROPERTY = "client_registration_info_id";
public static final String OAUTH2_DOMAIN_NAME_PROPERTY = "domain_name";
public static final String OAUTH2_DOMAIN_SCHEME_PROPERTY = "domain_scheme";
public static final String OAUTH2_CLIENT_ID_PROPERTY = "client_id";
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_TOKEN_URI_PROPERTY = "token_uri";
public static final String OAUTH2_REDIRECT_URI_TEMPLATE_PROPERTY = "redirect_uri_template";
public static final String OAUTH2_SCOPE_PROPERTY = "scope";
public static final String OAUTH2_USER_INFO_URI_PROPERTY = "user_info_uri";
public static final String OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY = "user_name_attribute_name";
public static final String OAUTH2_JWK_SET_URI_PROPERTY = "jwk_set_uri";
public static final String OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY = "client_authentication_method";
public static final String OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY = "login_button_label";
public static final String OAUTH2_LOGIN_BUTTON_ICON_PROPERTY = "login_button_icon";
public static final String OAUTH2_ALLOW_USER_CREATION_PROPERTY = "allow_user_creation";
public static final String OAUTH2_ACTIVATE_USER_PROPERTY = "activate_user";
public static final String OAUTH2_MAPPER_TYPE_PROPERTY = "type";
public static final String OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY = "basic_email_attribute_key";
public static final String OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY = "basic_first_name_attribute_key";
public static final String OAUTH2_LAST_NAME_ATTRIBUTE_KEY_PROPERTY = "basic_last_name_attribute_key";
public static final String OAUTH2_TENANT_NAME_STRATEGY_PROPERTY = "basic_tenant_name_strategy";
public static final String OAUTH2_TENANT_NAME_PATTERN_PROPERTY = "basic_tenant_name_pattern";
public static final String OAUTH2_CUSTOMER_NAME_PATTERN_PROPERTY = "basic_customer_name_pattern";
public static final String OAUTH2_DEFAULT_DASHBOARD_NAME_PROPERTY = "basic_default_dashboard_name";
public static final String OAUTH2_ALWAYS_FULL_SCREEN_PROPERTY = "basic_always_full_screen";
public static final String OAUTH2_MAPPER_URL_PROPERTY = "custom_url";
public static final String OAUTH2_MAPPER_USERNAME_PROPERTY = "custom_username";
public static final String OAUTH2_MAPPER_PASSWORD_PROPERTY = "custom_password";
public static final String OAUTH2_MAPPER_SEND_TOKEN_PROPERTY = "custom_send_token";
public static final String OAUTH2_TEMPLATE_COMMENT_PROPERTY = "comment";
public static final String OAUTH2_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
public static final String OAUTH2_TEMPLATE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY;
public static final String OAUTH2_TEMPLATE_LOGIN_BUTTON_ICON_PROPERTY = OAUTH2_LOGIN_BUTTON_ICON_PROPERTY;
public static final String OAUTH2_TEMPLATE_LOGIN_BUTTON_LABEL_PROPERTY = OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY;
public static final String OAUTH2_TEMPLATE_HELP_LINK_PROPERTY = "help_link";
/**
* Cassandra attributes and timeseries constants.
*/

231
dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java

@ -0,0 +1,231 @@
/**
* Copyright © 2016-2020 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 com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId;
import org.thingsboard.server.common.data.oauth2.*;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.*;
import java.util.Arrays;
@Data
@EqualsAndHashCode(callSuper = true)
@TypeDef(name = "json", typeClass = JsonStringType.class)
@MappedSuperclass
public abstract class AbstractOAuth2ClientRegistrationInfoEntity<T extends OAuth2ClientRegistrationInfo> extends BaseSqlEntity<T> {
@Column(name = ModelConstants.OAUTH2_ENABLED_PROPERTY)
private Boolean enabled;
@Column(name = ModelConstants.OAUTH2_CLIENT_ID_PROPERTY)
private String clientId;
@Column(name = ModelConstants.OAUTH2_CLIENT_SECRET_PROPERTY)
private String clientSecret;
@Column(name = ModelConstants.OAUTH2_AUTHORIZATION_URI_PROPERTY)
private String authorizationUri;
@Column(name = ModelConstants.OAUTH2_TOKEN_URI_PROPERTY)
private String tokenUri;
@Column(name = ModelConstants.OAUTH2_SCOPE_PROPERTY)
private String scope;
@Column(name = ModelConstants.OAUTH2_USER_INFO_URI_PROPERTY)
private String userInfoUri;
@Column(name = ModelConstants.OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY)
private String userNameAttributeName;
@Column(name = ModelConstants.OAUTH2_JWK_SET_URI_PROPERTY)
private String jwkSetUri;
@Column(name = ModelConstants.OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY)
private String clientAuthenticationMethod;
@Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY)
private String loginButtonLabel;
@Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_ICON_PROPERTY)
private String loginButtonIcon;
@Column(name = ModelConstants.OAUTH2_ALLOW_USER_CREATION_PROPERTY)
private Boolean allowUserCreation;
@Column(name = ModelConstants.OAUTH2_ACTIVATE_USER_PROPERTY)
private Boolean activateUser;
@Enumerated(EnumType.STRING)
@Column(name = ModelConstants.OAUTH2_MAPPER_TYPE_PROPERTY)
private MapperType type;
@Column(name = ModelConstants.OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY)
private String emailAttributeKey;
@Column(name = ModelConstants.OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY)
private String firstNameAttributeKey;
@Column(name = ModelConstants.OAUTH2_LAST_NAME_ATTRIBUTE_KEY_PROPERTY)
private String lastNameAttributeKey;
@Enumerated(EnumType.STRING)
@Column(name = ModelConstants.OAUTH2_TENANT_NAME_STRATEGY_PROPERTY)
private TenantNameStrategyType tenantNameStrategy;
@Column(name = ModelConstants.OAUTH2_TENANT_NAME_PATTERN_PROPERTY)
private String tenantNamePattern;
@Column(name = ModelConstants.OAUTH2_CUSTOMER_NAME_PATTERN_PROPERTY)
private String customerNamePattern;
@Column(name = ModelConstants.OAUTH2_DEFAULT_DASHBOARD_NAME_PROPERTY)
private String defaultDashboardName;
@Column(name = ModelConstants.OAUTH2_ALWAYS_FULL_SCREEN_PROPERTY)
private Boolean alwaysFullScreen;
@Column(name = ModelConstants.OAUTH2_MAPPER_URL_PROPERTY)
private String url;
@Column(name = ModelConstants.OAUTH2_MAPPER_USERNAME_PROPERTY)
private String username;
@Column(name = ModelConstants.OAUTH2_MAPPER_PASSWORD_PROPERTY)
private String password;
@Column(name = ModelConstants.OAUTH2_MAPPER_SEND_TOKEN_PROPERTY)
private Boolean sendToken;
@Type(type = "json")
@Column(name = ModelConstants.OAUTH2_ADDITIONAL_INFO_PROPERTY)
private JsonNode additionalInfo;
public AbstractOAuth2ClientRegistrationInfoEntity() {
super();
}
public AbstractOAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfo clientRegistrationInfo) {
if (clientRegistrationInfo.getId() != null) {
this.setUuid(clientRegistrationInfo.getId().getId());
}
this.createdTime = clientRegistrationInfo.getCreatedTime();
this.enabled = clientRegistrationInfo.isEnabled();
this.clientId = clientRegistrationInfo.getClientId();
this.clientSecret = clientRegistrationInfo.getClientSecret();
this.authorizationUri = clientRegistrationInfo.getAuthorizationUri();
this.tokenUri = clientRegistrationInfo.getAccessTokenUri();
this.scope = clientRegistrationInfo.getScope().stream().reduce((result, element) -> result + "," + element).orElse("");
this.userInfoUri = clientRegistrationInfo.getUserInfoUri();
this.userNameAttributeName = clientRegistrationInfo.getUserNameAttributeName();
this.jwkSetUri = clientRegistrationInfo.getJwkSetUri();
this.clientAuthenticationMethod = clientRegistrationInfo.getClientAuthenticationMethod();
this.loginButtonLabel = clientRegistrationInfo.getLoginButtonLabel();
this.loginButtonIcon = clientRegistrationInfo.getLoginButtonIcon();
this.additionalInfo = clientRegistrationInfo.getAdditionalInfo();
OAuth2MapperConfig mapperConfig = clientRegistrationInfo.getMapperConfig();
if (mapperConfig != null) {
this.allowUserCreation = mapperConfig.isAllowUserCreation();
this.activateUser = mapperConfig.isActivateUser();
this.type = mapperConfig.getType();
OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic();
if (basicConfig != null) {
this.emailAttributeKey = basicConfig.getEmailAttributeKey();
this.firstNameAttributeKey = basicConfig.getFirstNameAttributeKey();
this.lastNameAttributeKey = basicConfig.getLastNameAttributeKey();
this.tenantNameStrategy = basicConfig.getTenantNameStrategy();
this.tenantNamePattern = basicConfig.getTenantNamePattern();
this.customerNamePattern = basicConfig.getCustomerNamePattern();
this.defaultDashboardName = basicConfig.getDefaultDashboardName();
this.alwaysFullScreen = basicConfig.isAlwaysFullScreen();
}
OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom();
if (customConfig != null) {
this.url = customConfig.getUrl();
this.username = customConfig.getUsername();
this.password = customConfig.getPassword();
this.sendToken = customConfig.isSendToken();
}
}
}
public AbstractOAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfoEntity oAuth2ClientRegistrationInfoEntity) {
this.setId(oAuth2ClientRegistrationInfoEntity.getId());
this.setCreatedTime(oAuth2ClientRegistrationInfoEntity.getCreatedTime());
this.enabled = oAuth2ClientRegistrationInfoEntity.getEnabled();
this.clientId = oAuth2ClientRegistrationInfoEntity.getClientId();
this.clientSecret = oAuth2ClientRegistrationInfoEntity.getClientSecret();
this.authorizationUri = oAuth2ClientRegistrationInfoEntity.getAuthorizationUri();
this.tokenUri = oAuth2ClientRegistrationInfoEntity.getTokenUri();
this.scope = oAuth2ClientRegistrationInfoEntity.getScope();
this.userInfoUri = oAuth2ClientRegistrationInfoEntity.getUserInfoUri();
this.userNameAttributeName = oAuth2ClientRegistrationInfoEntity.getUserNameAttributeName();
this.jwkSetUri = oAuth2ClientRegistrationInfoEntity.getJwkSetUri();
this.clientAuthenticationMethod = oAuth2ClientRegistrationInfoEntity.getClientAuthenticationMethod();
this.loginButtonLabel = oAuth2ClientRegistrationInfoEntity.getLoginButtonLabel();
this.loginButtonIcon = oAuth2ClientRegistrationInfoEntity.getLoginButtonIcon();
this.additionalInfo = oAuth2ClientRegistrationInfoEntity.getAdditionalInfo();
this.allowUserCreation = oAuth2ClientRegistrationInfoEntity.getAllowUserCreation();
this.activateUser = oAuth2ClientRegistrationInfoEntity.getActivateUser();
this.type = oAuth2ClientRegistrationInfoEntity.getType();
this.emailAttributeKey = oAuth2ClientRegistrationInfoEntity.getEmailAttributeKey();
this.firstNameAttributeKey = oAuth2ClientRegistrationInfoEntity.getFirstNameAttributeKey();
this.lastNameAttributeKey = oAuth2ClientRegistrationInfoEntity.getLastNameAttributeKey();
this.tenantNameStrategy = oAuth2ClientRegistrationInfoEntity.getTenantNameStrategy();
this.tenantNamePattern = oAuth2ClientRegistrationInfoEntity.getTenantNamePattern();
this.customerNamePattern = oAuth2ClientRegistrationInfoEntity.getCustomerNamePattern();
this.defaultDashboardName = oAuth2ClientRegistrationInfoEntity.getDefaultDashboardName();
this.alwaysFullScreen = oAuth2ClientRegistrationInfoEntity.getAlwaysFullScreen();
this.url = oAuth2ClientRegistrationInfoEntity.getUrl();
this.username = oAuth2ClientRegistrationInfoEntity.getUsername();
this.password = oAuth2ClientRegistrationInfoEntity.getPassword();
this.sendToken = oAuth2ClientRegistrationInfoEntity.getSendToken();
}
protected OAuth2ClientRegistrationInfo toOAuth2ClientRegistrationInfo() {
OAuth2ClientRegistrationInfo clientRegistrationInfo = new OAuth2ClientRegistrationInfo();
clientRegistrationInfo.setId(new OAuth2ClientRegistrationInfoId(id));
clientRegistrationInfo.setEnabled(enabled);
clientRegistrationInfo.setCreatedTime(createdTime);
clientRegistrationInfo.setAdditionalInfo(additionalInfo);
clientRegistrationInfo.setMapperConfig(
OAuth2MapperConfig.builder()
.allowUserCreation(allowUserCreation)
.activateUser(activateUser)
.type(type)
.basic(
(type == MapperType.BASIC || type == MapperType.GITHUB) ?
OAuth2BasicMapperConfig.builder()
.emailAttributeKey(emailAttributeKey)
.firstNameAttributeKey(firstNameAttributeKey)
.lastNameAttributeKey(lastNameAttributeKey)
.tenantNameStrategy(tenantNameStrategy)
.tenantNamePattern(tenantNamePattern)
.customerNamePattern(customerNamePattern)
.defaultDashboardName(defaultDashboardName)
.alwaysFullScreen(alwaysFullScreen)
.build()
: null
)
.custom(
type == MapperType.CUSTOM ?
OAuth2CustomMapperConfig.builder()
.url(url)
.username(username)
.password(password)
.sendToken(sendToken)
.build()
: null
)
.build()
);
clientRegistrationInfo.setClientId(clientId);
clientRegistrationInfo.setClientSecret(clientSecret);
clientRegistrationInfo.setAuthorizationUri(authorizationUri);
clientRegistrationInfo.setAccessTokenUri(tokenUri);
clientRegistrationInfo.setScope(Arrays.asList(scope.split(",")));
clientRegistrationInfo.setUserInfoUri(userInfoUri);
clientRegistrationInfo.setUserNameAttributeName(userNameAttributeName);
clientRegistrationInfo.setJwkSetUri(jwkSetUri);
clientRegistrationInfo.setClientAuthenticationMethod(clientAuthenticationMethod);
clientRegistrationInfo.setLoginButtonLabel(loginButtonLabel);
clientRegistrationInfo.setLoginButtonIcon(loginButtonIcon);
return clientRegistrationInfo;
}
}

48
dao/src/main/java/org/thingsboard/server/dao/model/sql/ExtendedOAuth2ClientRegistrationInfoEntity.java

@ -0,0 +1,48 @@
/**
* Copyright © 2016-2020 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.oauth2.ExtendedOAuth2ClientRegistrationInfo;
import org.thingsboard.server.common.data.oauth2.SchemeType;
@Data
@EqualsAndHashCode(callSuper = true)
public class ExtendedOAuth2ClientRegistrationInfoEntity extends AbstractOAuth2ClientRegistrationInfoEntity<ExtendedOAuth2ClientRegistrationInfo> {
private String domainName;
private SchemeType domainScheme;
public ExtendedOAuth2ClientRegistrationInfoEntity() {
super();
}
public ExtendedOAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfoEntity oAuth2ClientRegistrationInfoEntity,
String domainName,
SchemeType domainScheme) {
super(oAuth2ClientRegistrationInfoEntity);
this.domainName = domainName;
this.domainScheme = domainScheme;
}
@Override
public ExtendedOAuth2ClientRegistrationInfo toData() {
return new ExtendedOAuth2ClientRegistrationInfo(super.toOAuth2ClientRegistrationInfo(),
domainScheme,
domainName);
}
}

75
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java

@ -0,0 +1,75 @@
/**
* Copyright © 2016-2020 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.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId;
import org.thingsboard.server.common.data.oauth2.*;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.*;
import java.util.Arrays;
import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@TypeDef(name = "json", typeClass = JsonStringType.class)
@Table(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME)
public class OAuth2ClientRegistrationEntity extends BaseSqlEntity<OAuth2ClientRegistration> {
@Column(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_INFO_ID_PROPERTY, columnDefinition = "uuid")
private UUID clientRegistrationInfoId;
@Column(name = ModelConstants.OAUTH2_DOMAIN_NAME_PROPERTY)
private String domainName;
@Enumerated(EnumType.STRING)
@Column(name = ModelConstants.OAUTH2_DOMAIN_SCHEME_PROPERTY)
private SchemeType domainScheme;
public OAuth2ClientRegistrationEntity() {
super();
}
public OAuth2ClientRegistrationEntity(OAuth2ClientRegistration clientRegistration) {
if (clientRegistration.getId() != null) {
this.setUuid(clientRegistration.getId().getId());
}
if (clientRegistration.getClientRegistrationId() != null){
this.clientRegistrationInfoId = clientRegistration.getClientRegistrationId().getId();
}
this.createdTime = clientRegistration.getCreatedTime();
this.domainName = clientRegistration.getDomainName();
this.domainScheme = clientRegistration.getDomainScheme();
}
@Override
public OAuth2ClientRegistration toData() {
OAuth2ClientRegistration clientRegistration = new OAuth2ClientRegistration();
clientRegistration.setId(new OAuth2ClientRegistrationId(id));
clientRegistration.setClientRegistrationId(new OAuth2ClientRegistrationInfoId(clientRegistrationInfoId));
clientRegistration.setCreatedTime(createdTime);
clientRegistration.setDomainName(domainName);
clientRegistration.setDomainScheme(domainScheme);
return clientRegistration;
}
}

51
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationInfoEntity.java

@ -0,0 +1,51 @@
/**
* Copyright © 2016-2020 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.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonStringType;
import javax.persistence.Entity;
import javax.persistence.Table;
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@TypeDef(name = "json", typeClass = JsonStringType.class)
@Table(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_INFO_COLUMN_FAMILY_NAME)
public class OAuth2ClientRegistrationInfoEntity extends AbstractOAuth2ClientRegistrationInfoEntity<OAuth2ClientRegistrationInfo> {
public OAuth2ClientRegistrationInfoEntity() {
super();
}
public OAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfo clientRegistration) {
super(clientRegistration);
}
public OAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfoEntity oAuth2ClientRegistrationInfoEntity) {
super(oAuth2ClientRegistrationInfoEntity);
}
@Override
public OAuth2ClientRegistrationInfo toData() {
return super.toOAuth2ClientRegistrationInfo();
}
}

160
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationTemplateEntity.java

@ -0,0 +1,160 @@
/**
* Copyright © 2016-2020 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 com.fasterxml.jackson.databind.JsonNode;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.*;
import org.thingsboard.server.dao.model.BaseSqlEntity;
import org.thingsboard.server.dao.model.ModelConstants;
import org.thingsboard.server.dao.util.mapping.JsonStringType;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
import javax.persistence.*;
import java.util.Arrays;
import java.util.UUID;
@Data
@EqualsAndHashCode(callSuper = true)
@Entity
@TypeDef(name = "json", typeClass = JsonStringType.class)
@Table(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_TEMPLATE_COLUMN_FAMILY_NAME)
public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2ClientRegistrationTemplate> {
@Column(name = ModelConstants.OAUTH2_TEMPLATE_PROVIDER_ID_PROPERTY)
private String providerId;
@Column(name = ModelConstants.OAUTH2_AUTHORIZATION_URI_PROPERTY)
private String authorizationUri;
@Column(name = ModelConstants.OAUTH2_TOKEN_URI_PROPERTY)
private String tokenUri;
@Column(name = ModelConstants.OAUTH2_SCOPE_PROPERTY)
private String scope;
@Column(name = ModelConstants.OAUTH2_USER_INFO_URI_PROPERTY)
private String userInfoUri;
@Column(name = ModelConstants.OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY)
private String userNameAttributeName;
@Column(name = ModelConstants.OAUTH2_JWK_SET_URI_PROPERTY)
private String jwkSetUri;
@Column(name = ModelConstants.OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY)
private String clientAuthenticationMethod;
@Enumerated(EnumType.STRING)
@Column(name = ModelConstants.OAUTH2_MAPPER_TYPE_PROPERTY)
private MapperType type;
@Column(name = ModelConstants.OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY)
private String emailAttributeKey;
@Column(name = ModelConstants.OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY)
private String firstNameAttributeKey;
@Column(name = ModelConstants.OAUTH2_LAST_NAME_ATTRIBUTE_KEY_PROPERTY)
private String lastNameAttributeKey;
@Enumerated(EnumType.STRING)
@Column(name = ModelConstants.OAUTH2_TENANT_NAME_STRATEGY_PROPERTY)
private TenantNameStrategyType tenantNameStrategy;
@Column(name = ModelConstants.OAUTH2_TENANT_NAME_PATTERN_PROPERTY)
private String tenantNamePattern;
@Column(name = ModelConstants.OAUTH2_CUSTOMER_NAME_PATTERN_PROPERTY)
private String customerNamePattern;
@Column(name = ModelConstants.OAUTH2_DEFAULT_DASHBOARD_NAME_PROPERTY)
private String defaultDashboardName;
@Column(name = ModelConstants.OAUTH2_ALWAYS_FULL_SCREEN_PROPERTY)
private Boolean alwaysFullScreen;
@Column(name = ModelConstants.OAUTH2_TEMPLATE_COMMENT_PROPERTY)
private String comment;
@Column(name = ModelConstants.OAUTH2_TEMPLATE_LOGIN_BUTTON_ICON_PROPERTY)
private String loginButtonIcon;
@Column(name = ModelConstants.OAUTH2_TEMPLATE_LOGIN_BUTTON_LABEL_PROPERTY)
private String loginButtonLabel;
@Column(name = ModelConstants.OAUTH2_TEMPLATE_HELP_LINK_PROPERTY)
private String helpLink;
@Type(type = "json")
@Column(name = ModelConstants.OAUTH2_TEMPLATE_ADDITIONAL_INFO_PROPERTY)
private JsonNode additionalInfo;
public OAuth2ClientRegistrationTemplateEntity() {
}
public OAuth2ClientRegistrationTemplateEntity(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) {
if (clientRegistrationTemplate.getId() != null) {
this.setUuid(clientRegistrationTemplate.getId().getId());
}
this.createdTime = clientRegistrationTemplate.getCreatedTime();
this.providerId = clientRegistrationTemplate.getProviderId();
this.authorizationUri = clientRegistrationTemplate.getAuthorizationUri();
this.tokenUri = clientRegistrationTemplate.getAccessTokenUri();
this.scope = clientRegistrationTemplate.getScope().stream().reduce((result, element) -> result + "," + element).orElse("");
this.userInfoUri = clientRegistrationTemplate.getUserInfoUri();
this.userNameAttributeName = clientRegistrationTemplate.getUserNameAttributeName();
this.jwkSetUri = clientRegistrationTemplate.getJwkSetUri();
this.clientAuthenticationMethod = clientRegistrationTemplate.getClientAuthenticationMethod();
this.comment = clientRegistrationTemplate.getComment();
this.loginButtonIcon = clientRegistrationTemplate.getLoginButtonIcon();
this.loginButtonLabel = clientRegistrationTemplate.getLoginButtonLabel();
this.helpLink = clientRegistrationTemplate.getHelpLink();
this.additionalInfo = clientRegistrationTemplate.getAdditionalInfo();
this.type = clientRegistrationTemplate.getMapperType();
OAuth2BasicMapperConfig basicConfig = clientRegistrationTemplate.getBasic();
if (basicConfig != null) {
this.emailAttributeKey = basicConfig.getEmailAttributeKey();
this.firstNameAttributeKey = basicConfig.getFirstNameAttributeKey();
this.lastNameAttributeKey = basicConfig.getLastNameAttributeKey();
this.tenantNameStrategy = basicConfig.getTenantNameStrategy();
this.tenantNamePattern = basicConfig.getTenantNamePattern();
this.customerNamePattern = basicConfig.getCustomerNamePattern();
this.defaultDashboardName = basicConfig.getDefaultDashboardName();
this.alwaysFullScreen = basicConfig.isAlwaysFullScreen();
}
}
@Override
public OAuth2ClientRegistrationTemplate toData() {
OAuth2ClientRegistrationTemplate clientRegistrationTemplate = new OAuth2ClientRegistrationTemplate();
clientRegistrationTemplate.setId(new OAuth2ClientRegistrationTemplateId(id));
clientRegistrationTemplate.setCreatedTime(createdTime);
clientRegistrationTemplate.setAdditionalInfo(additionalInfo);
clientRegistrationTemplate.setMapperType(type);
clientRegistrationTemplate.setProviderId(providerId);
clientRegistrationTemplate.setBasic(
OAuth2BasicMapperConfig.builder()
.emailAttributeKey(emailAttributeKey)
.firstNameAttributeKey(firstNameAttributeKey)
.lastNameAttributeKey(lastNameAttributeKey)
.tenantNameStrategy(tenantNameStrategy)
.tenantNamePattern(tenantNamePattern)
.customerNamePattern(customerNamePattern)
.defaultDashboardName(defaultDashboardName)
.alwaysFullScreen(alwaysFullScreen)
.build()
);
clientRegistrationTemplate.setAuthorizationUri(authorizationUri);
clientRegistrationTemplate.setAccessTokenUri(tokenUri);
clientRegistrationTemplate.setScope(Arrays.asList(scope.split(",")));
clientRegistrationTemplate.setUserInfoUri(userInfoUri);
clientRegistrationTemplate.setUserNameAttributeName(userNameAttributeName);
clientRegistrationTemplate.setJwkSetUri(jwkSetUri);
clientRegistrationTemplate.setClientAuthenticationMethod(clientAuthenticationMethod);
clientRegistrationTemplate.setComment(comment);
clientRegistrationTemplate.setLoginButtonIcon(loginButtonIcon);
clientRegistrationTemplate.setLoginButtonLabel(loginButtonLabel);
clientRegistrationTemplate.setHelpLink(helpLink);
return clientRegistrationTemplate;
}
}

59
dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java

@ -0,0 +1,59 @@
/**
* Copyright © 2016-2020 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 org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
import java.util.UUID;
@Component
public class HybridClientRegistrationRepository implements ClientRegistrationRepository {
private static final String defaultRedirectUriTemplate = "{baseUrl}/login/oauth2/code/{registrationId}";
@Autowired
private OAuth2Service oAuth2Service;
@Override
public ClientRegistration findByRegistrationId(String registrationId) {
OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo = oAuth2Service.findClientRegistrationInfo(UUID.fromString(registrationId));
return oAuth2ClientRegistrationInfo == null ?
null : toSpringClientRegistration(oAuth2ClientRegistrationInfo);
}
private ClientRegistration toSpringClientRegistration(OAuth2ClientRegistrationInfo localClientRegistration){
String registrationId = localClientRegistration.getUuidId().toString();
return ClientRegistration.withRegistrationId(registrationId)
.clientName(localClientRegistration.getName())
.clientId(localClientRegistration.getClientId())
.authorizationUri(localClientRegistration.getAuthorizationUri())
.clientSecret(localClientRegistration.getClientSecret())
.tokenUri(localClientRegistration.getAccessTokenUri())
.scope(localClientRegistration.getScope())
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.userInfoUri(localClientRegistration.getUserInfoUri())
.userNameAttributeName(localClientRegistration.getUserNameAttributeName())
.jwkSetUri(localClientRegistration.getJwkSetUri())
.clientAuthenticationMethod(new ClientAuthenticationMethod(localClientRegistration.getClientAuthenticationMethod()))
.redirectUriTemplate(defaultRedirectUriTemplate)
.build();
}
}

47
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientMapperConfig.java

@ -1,47 +0,0 @@
/**
* Copyright © 2016-2020 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.Data;
@Data
public class OAuth2ClientMapperConfig {
private boolean allowUserCreation;
private boolean activateUser;
private String type;
private BasicOAuth2ClientMapperConfig basic;
private CustomOAuth2ClientMapperConfig custom;
@Data
public static class BasicOAuth2ClientMapperConfig {
private String emailAttributeKey;
private String firstNameAttributeKey;
private String lastNameAttributeKey;
private String tenantNameStrategy;
private String tenantNamePattern;
private String customerNamePattern;
private boolean alwaysFullScreen;
private String defaultDashboardName;
}
@Data
public static class CustomOAuth2ClientMapperConfig {
private String url;
private String username;
private String password;
}
}

23
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java

@ -0,0 +1,23 @@
/**
* Copyright © 2016-2020 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 org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration;
import org.thingsboard.server.dao.Dao;
public interface OAuth2ClientRegistrationDao extends Dao<OAuth2ClientRegistration> {
void deleteAll();
}

34
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationInfoDao.java

@ -0,0 +1,34 @@
/**
* Copyright © 2016-2020 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 org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.common.data.oauth2.SchemeType;
import org.thingsboard.server.dao.Dao;
import java.util.List;
import java.util.Set;
public interface OAuth2ClientRegistrationInfoDao extends Dao<OAuth2ClientRegistrationInfo> {
List<OAuth2ClientRegistrationInfo> findAll();
List<ExtendedOAuth2ClientRegistrationInfo> findAllExtended();
List<OAuth2ClientRegistrationInfo> findByDomainSchemesAndDomainName(List<SchemeType> domainSchemes, String domainName);
void deleteAll();
}

25
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationTemplateDao.java

@ -0,0 +1,25 @@
/**
* Copyright © 2016-2020 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 org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate;
import org.thingsboard.server.dao.Dao;
import java.util.List;
public interface OAuth2ClientRegistrationTemplateDao extends Dao<OAuth2ClientRegistrationTemplate> {
List<OAuth2ClientRegistrationTemplate> findAll();
}

103
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java

@ -0,0 +1,103 @@
/**
* Copyright © 2016-2020 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.hibernate.exception.ConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.*;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.service.DataValidator;
import java.util.List;
import static org.thingsboard.server.dao.service.Validator.validateId;
import static org.thingsboard.server.dao.service.Validator.validateString;
@Slf4j
@Service
public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService implements OAuth2ConfigTemplateService {
public static final String INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID = "Incorrect clientRegistrationTemplateId ";
@Autowired
private OAuth2ClientRegistrationTemplateDao clientRegistrationTemplateDao;
@Override
public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) {
log.trace("Executing saveClientRegistrationTemplate [{}]", clientRegistrationTemplate);
clientRegistrationTemplateValidator.validate(clientRegistrationTemplate, o -> TenantId.SYS_TENANT_ID);
OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate;
try {
savedClientRegistrationTemplate = clientRegistrationTemplateDao.save(TenantId.SYS_TENANT_ID, clientRegistrationTemplate);
} catch (Exception t) {
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null);
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("oauth2_template_provider_id_unq_key")) {
throw new DataValidationException("Client registration template with such providerId already exists!");
} else {
throw t;
}
}
return savedClientRegistrationTemplate;
}
@Override
public OAuth2ClientRegistrationTemplate findClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId) {
log.trace("Executing findClientRegistrationTemplateById [{}]", templateId);
validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId);
return clientRegistrationTemplateDao.findById(TenantId.SYS_TENANT_ID, templateId.getId());
}
@Override
public List<OAuth2ClientRegistrationTemplate> findAllClientRegistrationTemplates() {
log.trace("Executing findAllClientRegistrationTemplates");
return clientRegistrationTemplateDao.findAll();
}
@Override
public void deleteClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId) {
log.trace("Executing deleteClientRegistrationTemplateById [{}]", templateId);
validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId);
clientRegistrationTemplateDao.removeById(TenantId.SYS_TENANT_ID, templateId.getId());
}
private final DataValidator<OAuth2ClientRegistrationTemplate> clientRegistrationTemplateValidator =
new DataValidator<OAuth2ClientRegistrationTemplate>() {
@Override
protected void validateCreate(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) {
}
@Override
protected void validateUpdate(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) {
}
@Override
protected void validateDataImpl(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) {
if (StringUtils.isEmpty(clientRegistrationTemplate.getProviderId())) {
throw new DataValidationException("Provider ID should be specified!");
}
if (clientRegistrationTemplate.getBasic() == null) {
throw new DataValidationException("Basic mapper config should be specified!");
}
}
};
}

53
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java

@ -17,66 +17,15 @@ package org.thingsboard.server.dao.oauth2;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true")
@ConfigurationProperties(prefix = "security.oauth2")
@Data
@Slf4j
public class OAuth2Configuration {
private boolean enabled;
private String loginProcessingUrl;
private Map<String, OAuth2Client> clients = new HashMap<>();
@Bean
public ClientRegistrationRepository clientRegistrationRepository() {
List<ClientRegistration> result = new ArrayList<>();
for (Map.Entry<String, OAuth2Client> entry : clients.entrySet()) {
OAuth2Client client = entry.getValue();
ClientRegistration registration = ClientRegistration.withRegistrationId(entry.getKey())
.clientId(client.getClientId())
.authorizationUri(client.getAuthorizationUri())
.clientSecret(client.getClientSecret())
.tokenUri(client.getAccessTokenUri())
.redirectUriTemplate(client.getRedirectUriTemplate())
.scope(client.getScope().split(","))
.clientName(client.getClientName())
.authorizationGrantType(new AuthorizationGrantType(client.getAuthorizationGrantType()))
.userInfoUri(client.getUserInfoUri())
.userNameAttributeName(client.getUserNameAttributeName())
.jwkSetUri(client.getJwkSetUri())
.clientAuthenticationMethod(new ClientAuthenticationMethod(client.getClientAuthenticationMethod()))
.build();
result.add(registration);
}
return new InMemoryClientRegistrationRepository(result);
}
public OAuth2Client getClientByRegistrationId(String registrationId) {
OAuth2Client result = null;
if (clients != null && !clients.isEmpty()) {
for (String key : clients.keySet()) {
if (key.equals(registrationId)) {
result = clients.get(key);
break;
}
}
}
return result;
}
private Map<String, String> githubMapper;
}

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

@ -18,33 +18,198 @@ package org.thingsboard.server.dao.oauth2;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
import org.springframework.util.StringUtils;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.oauth2.*;
import org.thingsboard.server.dao.entity.AbstractEntityService;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.exception.IncorrectParameterException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.transaction.Transactional;
import java.util.*;
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 implements OAuth2Service {
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(required = false)
OAuth2Configuration oauth2Configuration;
@Autowired
private OAuth2ClientRegistrationInfoDao clientRegistrationInfoDao;
@Autowired
private OAuth2ClientRegistrationDao clientRegistrationDao;
@Override
public List<OAuth2ClientInfo> getOAuth2Clients() {
if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) {
return Collections.emptyList();
public List<OAuth2ClientInfo> getOAuth2Clients(String domainSchemeStr, String domainName) {
log.trace("Executing getOAuth2Clients [{}://{}]", domainSchemeStr, domainName);
if (domainSchemeStr == null) {
throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME);
}
List<OAuth2ClientInfo> result = new ArrayList<>();
for (Map.Entry<String, OAuth2Client> entry : oauth2Configuration.getClients().entrySet()) {
OAuth2ClientInfo client = new OAuth2ClientInfo();
client.setName(entry.getValue().getLoginButtonLabel());
client.setUrl(String.format("/oauth2/authorization/%s", entry.getKey()));
client.setIcon(entry.getValue().getLoginButtonIcon());
result.add(client);
SchemeType domainScheme;
try {
domainScheme = SchemeType.valueOf(domainSchemeStr.toUpperCase());
} catch (IllegalArgumentException e){
throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME);
}
return result;
validateString(domainName, INCORRECT_DOMAIN_NAME + domainName);
return clientRegistrationInfoDao.findByDomainSchemesAndDomainName(Arrays.asList(domainScheme, SchemeType.MIXED), domainName).stream()
.filter(OAuth2ClientRegistrationInfo::isEnabled)
.map(OAuth2Utils::toClientInfo)
.collect(Collectors.toList());
}
@Override
@Transactional
public void saveOAuth2Params(OAuth2ClientsParams oauth2Params) {
log.trace("Executing saveOAuth2Params [{}]", oauth2Params);
clientParamsValidator.accept(oauth2Params);
clientRegistrationDao.deleteAll();
clientRegistrationInfoDao.deleteAll();
oauth2Params.getDomainsParams().forEach(domainParams -> {
domainParams.getClientRegistrations().forEach(clientRegistrationDto -> {
OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo = OAuth2Utils.toClientRegistrationInfo(oauth2Params.isEnabled(), clientRegistrationDto);
OAuth2ClientRegistrationInfo savedClientRegistrationInfo = clientRegistrationInfoDao.save(TenantId.SYS_TENANT_ID, oAuth2ClientRegistrationInfo);
domainParams.getDomainInfos().forEach(domainInfo -> {
OAuth2ClientRegistration oAuth2ClientRegistration = OAuth2Utils.toClientRegistration(savedClientRegistrationInfo.getId(),
domainInfo.getScheme(), domainInfo.getName());
clientRegistrationDao.save(TenantId.SYS_TENANT_ID, oAuth2ClientRegistration);
});
});
});
}
@Override
public OAuth2ClientsParams findOAuth2Params() {
log.trace("Executing findOAuth2Params");
List<ExtendedOAuth2ClientRegistrationInfo> extendedInfos = clientRegistrationInfoDao.findAllExtended();
return OAuth2Utils.toOAuth2Params(extendedInfos);
}
@Override
public OAuth2ClientRegistrationInfo findClientRegistrationInfo(UUID id) {
log.trace("Executing findClientRegistrationInfo [{}]", id);
validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id);
return clientRegistrationInfoDao.findById(null, id);
}
@Override
public List<OAuth2ClientRegistrationInfo> findAllClientRegistrationInfos() {
log.trace("Executing findAllClientRegistrationInfos");
return clientRegistrationInfoDao.findAll();
}
private final Consumer<OAuth2ClientsParams> clientParamsValidator = oauth2Params -> {
if (oauth2Params == null
|| oauth2Params.getDomainsParams() == null) {
throw new DataValidationException("Domain params should be specified!");
}
for (OAuth2ClientsDomainParams domainParams : oauth2Params.getDomainsParams()) {
if (domainParams.getDomainInfos() == null
|| domainParams.getDomainInfos().isEmpty()) {
throw new DataValidationException("List of domain configuration should be specified!");
}
for (DomainInfo domainInfo : domainParams.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!");
}
}
domainParams.getDomainInfos().stream()
.collect(Collectors.groupingBy(DomainInfo::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!");
}
});
if (domainParams.getClientRegistrations() == null || domainParams.getClientRegistrations().isEmpty()) {
throw new DataValidationException("Client registrations should be specified!");
}
for (ClientRegistrationDto clientRegistration : domainParams.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 (StringUtils.isEmpty(clientRegistration.getScope())) {
throw new DataValidationException("Scope should be specified!");
}
if (StringUtils.isEmpty(clientRegistration.getUserInfoUri())) {
throw new DataValidationException("User info uri 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!");
}
}
}
}
};
}

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

@ -0,0 +1,102 @@
/**
* Copyright © 2016-2020 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 org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId;
import org.thingsboard.server.common.data.oauth2.*;
import java.util.*;
public class OAuth2Utils {
public static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s";
public static OAuth2ClientInfo toClientInfo(OAuth2ClientRegistrationInfo clientRegistrationInfo) {
OAuth2ClientInfo client = new OAuth2ClientInfo();
client.setName(clientRegistrationInfo.getLoginButtonLabel());
client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistrationInfo.getUuidId().toString()));
client.setIcon(clientRegistrationInfo.getLoginButtonIcon());
return client;
}
public static OAuth2ClientsParams toOAuth2Params(List<ExtendedOAuth2ClientRegistrationInfo> extendedOAuth2ClientRegistrationInfos) {
Map<OAuth2ClientRegistrationInfoId, Set<DomainInfo>> domainsByInfoId = new HashMap<>();
Map<OAuth2ClientRegistrationInfoId, OAuth2ClientRegistrationInfo> infoById = new HashMap<>();
for (ExtendedOAuth2ClientRegistrationInfo extendedClientRegistrationInfo : extendedOAuth2ClientRegistrationInfos) {
String domainName = extendedClientRegistrationInfo.getDomainName();
SchemeType domainScheme = extendedClientRegistrationInfo.getDomainScheme();
domainsByInfoId.computeIfAbsent(extendedClientRegistrationInfo.getId(), key -> new HashSet<>())
.add(new DomainInfo(domainScheme, domainName));
infoById.put(extendedClientRegistrationInfo.getId(), extendedClientRegistrationInfo);
}
Map<Set<DomainInfo>, OAuth2ClientsDomainParams> domainParamsMap = new HashMap<>();
domainsByInfoId.forEach((clientRegistrationInfoId, domainInfos) -> {
domainParamsMap.computeIfAbsent(domainInfos,
key -> new OAuth2ClientsDomainParams(key, new HashSet<>())
)
.getClientRegistrations()
.add(toClientRegistrationDto(infoById.get(clientRegistrationInfoId)));
});
boolean enabled = extendedOAuth2ClientRegistrationInfos.stream()
.map(OAuth2ClientRegistrationInfo::isEnabled)
.findFirst().orElse(false);
return new OAuth2ClientsParams(enabled, new HashSet<>(domainParamsMap.values()));
}
public static ClientRegistrationDto toClientRegistrationDto(OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo) {
return ClientRegistrationDto.builder()
.mapperConfig(oAuth2ClientRegistrationInfo.getMapperConfig())
.clientId(oAuth2ClientRegistrationInfo.getClientId())
.clientSecret(oAuth2ClientRegistrationInfo.getClientSecret())
.authorizationUri(oAuth2ClientRegistrationInfo.getAuthorizationUri())
.accessTokenUri(oAuth2ClientRegistrationInfo.getAccessTokenUri())
.scope(oAuth2ClientRegistrationInfo.getScope())
.userInfoUri(oAuth2ClientRegistrationInfo.getUserInfoUri())
.userNameAttributeName(oAuth2ClientRegistrationInfo.getUserNameAttributeName())
.jwkSetUri(oAuth2ClientRegistrationInfo.getJwkSetUri())
.clientAuthenticationMethod(oAuth2ClientRegistrationInfo.getClientAuthenticationMethod())
.loginButtonLabel(oAuth2ClientRegistrationInfo.getLoginButtonLabel())
.loginButtonIcon(oAuth2ClientRegistrationInfo.getLoginButtonIcon())
.additionalInfo(oAuth2ClientRegistrationInfo.getAdditionalInfo())
.build();
}
public static OAuth2ClientRegistrationInfo toClientRegistrationInfo(boolean enabled, ClientRegistrationDto clientRegistrationDto) {
OAuth2ClientRegistrationInfo clientRegistrationInfo = new OAuth2ClientRegistrationInfo();
clientRegistrationInfo.setEnabled(enabled);
clientRegistrationInfo.setMapperConfig(clientRegistrationDto.getMapperConfig());
clientRegistrationInfo.setClientId(clientRegistrationDto.getClientId());
clientRegistrationInfo.setClientSecret(clientRegistrationDto.getClientSecret());
clientRegistrationInfo.setAuthorizationUri(clientRegistrationDto.getAuthorizationUri());
clientRegistrationInfo.setAccessTokenUri(clientRegistrationDto.getAccessTokenUri());
clientRegistrationInfo.setScope(clientRegistrationDto.getScope());
clientRegistrationInfo.setUserInfoUri(clientRegistrationDto.getUserInfoUri());
clientRegistrationInfo.setUserNameAttributeName(clientRegistrationDto.getUserNameAttributeName());
clientRegistrationInfo.setJwkSetUri(clientRegistrationDto.getJwkSetUri());
clientRegistrationInfo.setClientAuthenticationMethod(clientRegistrationDto.getClientAuthenticationMethod());
clientRegistrationInfo.setLoginButtonLabel(clientRegistrationDto.getLoginButtonLabel());
clientRegistrationInfo.setLoginButtonIcon(clientRegistrationDto.getLoginButtonIcon());
clientRegistrationInfo.setAdditionalInfo(clientRegistrationDto.getAdditionalInfo());
return clientRegistrationInfo;
}
public static OAuth2ClientRegistration toClientRegistration(OAuth2ClientRegistrationInfoId clientRegistrationInfoId, SchemeType domainScheme, String domainName) {
OAuth2ClientRegistration clientRegistration = new OAuth2ClientRegistration();
clientRegistration.setClientRegistrationId(clientRegistrationInfoId);
clientRegistration.setDomainName(domainName);
clientRegistration.setDomainScheme(domainScheme);
return clientRegistration;
}
}

11
dao/src/main/java/org/thingsboard/server/dao/rule/BaseRuleNodeStateService.java

@ -68,6 +68,17 @@ public class BaseRuleNodeStateService extends AbstractEntityService implements R
return saveOrUpdate(tenantId, ruleNodeState, false);
}
@Override
public void removeByRuleNodeId(TenantId tenantId, RuleNodeId ruleNodeId) {
if (tenantId == null) {
throw new DataValidationException("Tenant id should be specified!.");
}
if (ruleNodeId == null) {
throw new DataValidationException("Rule node id should be specified!.");
}
ruleNodeStateDao.removeByRuleNodeId(ruleNodeId.getId());
}
public RuleNodeState saveOrUpdate(TenantId tenantId, RuleNodeState ruleNodeState, boolean update) {
try {
if (update) {

4
dao/src/main/java/org/thingsboard/server/dao/rule/RuleNodeStateDao.java

@ -16,6 +16,8 @@
package org.thingsboard.server.dao.rule;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleNodeId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleNodeState;
@ -31,4 +33,6 @@ public interface RuleNodeStateDao extends Dao<RuleNodeState> {
PageData<RuleNodeState> findByRuleNodeId(UUID ruleNodeId, PageLink pageLink);
RuleNodeState findByRuleNodeIdAndEntityId(UUID ruleNodeId, UUID entityId);
void removeByRuleNodeId(UUID ruleNodeId);
}

9
dao/src/main/java/org/thingsboard/server/dao/sql/device/DeviceProfileRepository.java

@ -22,6 +22,7 @@ import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileInfo;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.dao.model.sql.DeviceProfileEntity;
import java.util.UUID;
@ -46,6 +47,14 @@ public interface DeviceProfileRepository extends PagingAndSortingRepository<Devi
@Param("textSearch") String textSearch,
Pageable pageable);
@Query("SELECT new org.thingsboard.server.common.data.DeviceProfileInfo(d.id, d.name, d.type, d.transportType) " +
"FROM DeviceProfileEntity d WHERE " +
"d.tenantId = :tenantId AND d.transportType = :transportType AND LOWER(d.searchText) LIKE LOWER(CONCAT(:textSearch, '%'))")
Page<DeviceProfileInfo> findDeviceProfileInfos(@Param("tenantId") UUID tenantId,
@Param("textSearch") String textSearch,
@Param("transportType") DeviceTransportType transportType,
Pageable pageable);
@Query("SELECT d FROM DeviceProfileEntity d " +
"WHERE d.tenantId = :tenantId AND d.isDefault = true")
DeviceProfileEntity findByDefaultTrueAndTenantId(@Param("tenantId") UUID tenantId);

23
dao/src/main/java/org/thingsboard/server/dao/sql/device/JpaDeviceProfileDao.java

@ -15,11 +15,13 @@
*/
package org.thingsboard.server.dao.sql.device;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileInfo;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
@ -62,12 +64,21 @@ public class JpaDeviceProfileDao extends JpaAbstractSearchTextDao<DeviceProfileE
}
@Override
public PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink) {
return DaoUtil.pageToPageData(
deviceProfileRepository.findDeviceProfileInfos(
tenantId.getId(),
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
public PageData<DeviceProfileInfo> findDeviceProfileInfos(TenantId tenantId, PageLink pageLink, String transportType) {
if (StringUtils.isNotEmpty(transportType)) {
return DaoUtil.pageToPageData(
deviceProfileRepository.findDeviceProfileInfos(
tenantId.getId(),
Objects.toString(pageLink.getTextSearch(), ""),
DeviceTransportType.valueOf(transportType),
DaoUtil.toPageable(pageLink)));
} else {
return DaoUtil.pageToPageData(
deviceProfileRepository.findDeviceProfileInfos(
tenantId.getId(),
Objects.toString(pageLink.getTextSearch(), ""),
DaoUtil.toPageable(pageLink)));
}
}
@Override

48
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java

@ -0,0 +1,48 @@
/**
* Copyright © 2016-2020 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 lombok.extern.slf4j.Slf4j;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration;
import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity;
import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationDao;
import org.thingsboard.server.dao.sql.JpaAbstractDao;
import java.util.UUID;
@Component
@RequiredArgsConstructor
public class JpaOAuth2ClientRegistrationDao extends JpaAbstractDao<OAuth2ClientRegistrationEntity, OAuth2ClientRegistration> implements OAuth2ClientRegistrationDao {
private final OAuth2ClientRegistrationRepository repository;
@Override
protected Class<OAuth2ClientRegistrationEntity> getEntityClass() {
return OAuth2ClientRegistrationEntity.class;
}
@Override
protected CrudRepository<OAuth2ClientRegistrationEntity, UUID> getCrudRepository() {
return repository;
}
@Override
public void deleteAll() {
repository.deleteAll();
}
}

76
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationInfoDao.java

@ -0,0 +1,76 @@
/**
* Copyright © 2016-2020 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.repository.CrudRepository;
import org.springframework.stereotype.Component;
import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo;
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo;
import org.thingsboard.server.common.data.oauth2.SchemeType;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationInfoEntity;
import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationInfoDao;
import org.thingsboard.server.dao.sql.JpaAbstractDao;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
@Component
@RequiredArgsConstructor
public class JpaOAuth2ClientRegistrationInfoDao extends JpaAbstractDao<OAuth2ClientRegistrationInfoEntity, OAuth2ClientRegistrationInfo> implements OAuth2ClientRegistrationInfoDao {
private final OAuth2ClientRegistrationInfoRepository repository;
@Override
protected Class<OAuth2ClientRegistrationInfoEntity> getEntityClass() {
return OAuth2ClientRegistrationInfoEntity.class;
}
@Override
protected CrudRepository<OAuth2ClientRegistrationInfoEntity, UUID> getCrudRepository() {
return repository;
}
@Override
public List<OAuth2ClientRegistrationInfo> findAll() {
Iterable<OAuth2ClientRegistrationInfoEntity> entities = repository.findAll();
List<OAuth2ClientRegistrationInfo> result = new ArrayList<>();
entities.forEach(entity -> {
result.add(DaoUtil.getData(entity));
});
return result;
}
@Override
public List<ExtendedOAuth2ClientRegistrationInfo> findAllExtended() {
return repository.findAllExtended().stream()
.map(DaoUtil::getData)
.collect(Collectors.toList());
}
@Override
public List<OAuth2ClientRegistrationInfo> findByDomainSchemesAndDomainName(List<SchemeType> domainSchemes, String domainName) {
List<OAuth2ClientRegistrationInfoEntity> entities = repository.findAllByDomainSchemesAndName(domainSchemes, domainName);
return entities.stream().map(DaoUtil::getData).collect(Collectors.toList());
}
@Override
public void deleteAll() {
repository.deleteAll();
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save