From d01d4ac48b0583a5655c0d76a1148e4c6bd20a66 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 18 Jun 2020 18:49:57 +0300 Subject: [PATCH 001/117] Created oauth2 client_registration and mapper_config (as separate entities) --- .../main/data/upgrade/3.1.0/schema_update.sql | 56 ++++++++++ .../server/common/data/oauth2/MapperType.java | 5 + .../data/oauth2/OAuth2BasicMapperConfig.java | 21 ++++ .../data/oauth2/OAuth2ClientRegistration.java | 57 ++++++++++ .../data/oauth2/OAuth2CustomMapperConfig.java | 16 +++ .../data/oauth2/OAuth2MapperConfig.java | 34 ++++++ .../server/dao/model/ModelConstants.java | 40 +++++++ .../sql/OAuth2ClientRegistrationEntity.java | 99 +++++++++++++++++ .../model/sql/OAuth2MapperConfigEntity.java | 101 ++++++++++++++++++ .../resources/sql/schema-entities-hsql.sql | 36 +++++++ .../main/resources/sql/schema-entities.sql | 38 +++++++ 11 files changed, 503 insertions(+) create mode 100644 application/src/main/data/upgrade/3.1.0/schema_update.sql create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MapperConfigEntity.java diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql new file mode 100644 index 0000000000..3ee67b03a4 --- /dev/null +++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql @@ -0,0 +1,56 @@ +-- +-- 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. +-- + +DROP TABLE IF EXISTS oauth2_client_registration; + +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( + registration_id varchar(255) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + mapper_config_id varchar(31), + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + redirect_uri_template varchar(255), + scope varchar(255), + authorization_grant_type varchar(255), + user_info_uri varchar(255), + user_name_attribute varchar(255), + jwk_set_uri varchar(255), + client_authentication_method varchar(255), + client_name varchar(255), + login_button_label varchar(255), + login_button_icon varchar(255) +); + +DROP TABLE IF EXISTS oauth2_mapper_config; + +CREATE TABLE IF NOT EXISTS oauth2_mapper_config ( + id varchar(31) NOT NULL CONSTRAINT oauth2_mapper_config_pkey PRIMARY KEY, + 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) +); \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java new file mode 100644 index 0000000000..d33563b668 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java @@ -0,0 +1,5 @@ +package org.thingsboard.server.common.data.oauth2; + +public enum MapperType { + BASIC, CUSTOM; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java new file mode 100644 index 0000000000..e91daf75e7 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java @@ -0,0 +1,21 @@ +package org.thingsboard.server.common.data.oauth2; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Builder(toBuilder = true) +@EqualsAndHashCode +@Getter +@ToString +public class OAuth2BasicMapperConfig { + private final String emailAttributeKey; + private final String firstNameAttributeKey; + private final String lastNameAttributeKey; + private final String tenantNameStrategy; + private final String tenantNamePattern; + private final String customerNamePattern; + private final String defaultDashboardName; + private final boolean alwaysFullScreen; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java new file mode 100644 index 0000000000..d5bd3b076e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -0,0 +1,57 @@ +package org.thingsboard.server.common.data.oauth2; + +import lombok.*; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.id.OAuth2IntegrationId; + +@EqualsAndHashCode(callSuper = true) +@Data +@ToString(exclude = {"clientSecret"}) +public class OAuth2ClientRegistration extends BaseData { + + private String registrationId; + private OAuth2IntegrationId mapperConfigId; + private String clientId; + private String clientSecret; + private String authorizationUri; + private String tokenUri; + private String redirectUriTemplate; + private String scope; + private String authorizationGrantType; + private String userInfoUri; + private String userNameAttribute; + private String jwkSetUri; + private String clientAuthenticationMethod; + private String clientName; + private String loginButtonLabel; + private String loginButtonIcon; + + public OAuth2ClientRegistration() { + super(); + } + + public OAuth2ClientRegistration(OAuth2IntegrationId id) { + super(id); + } + + @Builder(toBuilder = true) + public OAuth2ClientRegistration(OAuth2IntegrationId id, String registrationId, String clientId, String clientSecret, String authorizationUri, String tokenUri, String redirectUriTemplate, String scope, String authorizationGrantType, String userInfoUri, String userNameAttribute, String jwkSetUri, String clientAuthenticationMethod, String clientName, String loginButtonLabel, String loginButtonIcon, OAuth2IntegrationId mapperConfigId) { + super(id); + this.registrationId = registrationId; + this.clientId = clientId; + this.clientSecret = clientSecret; + this.authorizationUri = authorizationUri; + this.tokenUri = tokenUri; + this.redirectUriTemplate = redirectUriTemplate; + this.scope = scope; + this.authorizationGrantType = authorizationGrantType; + this.userInfoUri = userInfoUri; + this.userNameAttribute = userNameAttribute; + this.jwkSetUri = jwkSetUri; + this.clientAuthenticationMethod = clientAuthenticationMethod; + this.clientName = clientName; + this.loginButtonLabel = loginButtonLabel; + this.loginButtonIcon = loginButtonIcon; + this.mapperConfigId = mapperConfigId; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java new file mode 100644 index 0000000000..ea9c963721 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java @@ -0,0 +1,16 @@ +package org.thingsboard.server.common.data.oauth2; + +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.ToString; + +@Builder(toBuilder = true) +@EqualsAndHashCode +@Getter +@ToString(exclude = {"password"}) +public class OAuth2CustomMapperConfig { + private final String url; + private final String username; + private final String password; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java new file mode 100644 index 0000000000..95c1fdf4a8 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java @@ -0,0 +1,34 @@ +package org.thingsboard.server.common.data.oauth2; + +import lombok.*; +import org.thingsboard.server.common.data.BaseData; +import org.thingsboard.server.common.data.id.OAuth2IntegrationId; + +@EqualsAndHashCode(callSuper = true) +@Data +@ToString +public class OAuth2MapperConfig extends BaseData { + private boolean allowUserCreation; + private boolean activateUser; + private MapperType type; + private OAuth2BasicMapperConfig basicConfig; + private OAuth2CustomMapperConfig customConfig; + + public OAuth2MapperConfig() { + super(); + } + + public OAuth2MapperConfig(OAuth2IntegrationId id) { + super(id); + } + + @Builder(toBuilder = true) + public OAuth2MapperConfig(OAuth2IntegrationId id, boolean allowUserCreation, boolean activateUser, MapperType type, OAuth2BasicMapperConfig basicConfig, OAuth2CustomMapperConfig customConfig) { + super(id); + this.allowUserCreation = allowUserCreation; + this.activateUser = activateUser; + this.type = type; + this.basicConfig = basicConfig; + this.customConfig = customConfig; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index f52e450d8b..c551114aa5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -354,6 +354,46 @@ public class ModelConstants { public static final String RULE_NODE_NAME_PROPERTY = "name"; public static final String RULE_NODE_CONFIGURATION_PROPERTY = "configuration"; + /** + * Cassandra OAuth2 client registration constants. + */ + public static final String OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_client_registration"; + public static final String OAUTH2_CLIENT_REGISTRATION_ID_PROPERTY = "registration_id"; + public static final String OAUTH2_CLIENT_REGISTRATION_MAPPER_CONFIG_ID_PROPERTY = "mapper_config_id"; + public static final String OAUTH2_CLIENT_ID_PROPERTY = "client_id"; + public static final String OAUTH2_CLIENT_SECRET_PROPERTY = "client_secret"; + public static final String OAUTH2_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_AUTHORIZATION_GRANT_TYPE_PROPERTY = "authorization_grant_type"; + public static final String OAUTH2_USER_INFO_URI_PROPERTY = "user_info_uri"; + public static final String OAUTH2_USER_NAME_ATTRIBUTE_PROPERTY = "user_name_attribute"; + 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_CLIENT_NAME_PROPERTY = "client_name"; + public static final String OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY = "login_button_label"; + public static final String OAUTH2_LOGIN_BUTTON_ICON_PROPERTY = "login_button_icon"; + + /** + * Cassandra OAuth2 mapper config constants. + */ + public static final String OAUTH2_MAPPER_CONFIG_COLUMN_FAMILY_NAME = "oauth2_mapper_config"; + 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"; + /** * Cassandra attributes and timeseries constants. */ diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java new file mode 100644 index 0000000000..bc402a7d7d --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java @@ -0,0 +1,99 @@ +/** + * 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 lombok.NoArgsConstructor; +import org.hibernate.annotations.TypeDef; +import org.thingsboard.server.common.data.EntityView; +import org.thingsboard.server.common.data.id.OAuth2IntegrationId; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; +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.Column; +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_COLUMN_FAMILY_NAME) +public class OAuth2ClientRegistrationEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_ID_PROPERTY) + private String registrationId; + @Column(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_MAPPER_CONFIG_ID_PROPERTY) + private String mapperConfigId; + @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_REDIRECT_URI_TEMPLATE_PROPERTY) + private String redirectUriTemplate; + @Column(name = ModelConstants.OAUTH2_SCOPE_PROPERTY) + private String scope; + @Column(name = ModelConstants.OAUTH2_AUTHORIZATION_GRANT_TYPE_PROPERTY) + private String authorizationGrantType; + @Column(name = ModelConstants.OAUTH2_USER_INFO_URI_PROPERTY) + private String userInfoUri; + @Column(name = ModelConstants.OAUTH2_USER_NAME_ATTRIBUTE_PROPERTY) + private String userNameAttribute; + @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_CLIENT_NAME_PROPERTY) + private String clientName; + @Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY) + private String loginButtonLabel; + @Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_ICON_PROPERTY) + private String loginButtonIcon; + + public OAuth2ClientRegistrationEntity() { + super(); + } + + @Override + public OAuth2ClientRegistration toData() { + return OAuth2ClientRegistration.builder() + .id(new OAuth2IntegrationId(toUUID(id))) + .registrationId(registrationId) + .mapperConfigId(new OAuth2IntegrationId(toUUID(mapperConfigId))) + .clientId(clientId) + .clientSecret(clientSecret) + .authorizationUri(authorizationUri) + .tokenUri(tokenUri) + .redirectUriTemplate(redirectUriTemplate) + .scope(scope) + .authorizationGrantType(authorizationGrantType) + .userInfoUri(userInfoUri) + .userNameAttribute(userNameAttribute) + .jwkSetUri(jwkSetUri) + .clientAuthenticationMethod(clientAuthenticationMethod) + .clientName(clientName) + .loginButtonLabel(loginButtonLabel) + .loginButtonIcon(loginButtonIcon) + .build(); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MapperConfigEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MapperConfigEntity.java new file mode 100644 index 0000000000..de75c052e4 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MapperConfigEntity.java @@ -0,0 +1,101 @@ +/** + * 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.OAuth2IntegrationId; +import org.thingsboard.server.common.data.oauth2.MapperType; +import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; +import org.thingsboard.server.dao.model.BaseSqlEntity; +import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.dao.util.mapping.JsonStringType; + +import javax.persistence.*; + +@Data +@EqualsAndHashCode(callSuper = true) +@Entity +@TypeDef(name = "json", typeClass = JsonStringType.class) +@Table(name = ModelConstants.OAUTH2_MAPPER_CONFIG_COLUMN_FAMILY_NAME) +public class OAuth2MapperConfigEntity extends BaseSqlEntity { + + @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; + @Column(name = ModelConstants.OAUTH2_TENANT_NAME_STRATEGY_PROPERTY) + private String 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; + + public OAuth2MapperConfigEntity() { + super(); + } + + @Override + public OAuth2MapperConfig toData() { + return OAuth2MapperConfig.builder() + .id(new OAuth2IntegrationId(toUUID(id))) + .allowUserCreation(allowUserCreation) + .activateUser(activateUser) + .type(type) + .basicConfig( + OAuth2BasicMapperConfig.builder() + .emailAttributeKey(emailAttributeKey) + .firstNameAttributeKey(firstNameAttributeKey) + .lastNameAttributeKey(lastNameAttributeKey) + .tenantNameStrategy(tenantNameStrategy) + .tenantNamePattern(tenantNamePattern) + .customerNamePattern(customerNamePattern) + .defaultDashboardName(defaultDashboardName) + .alwaysFullScreen(alwaysFullScreen) + .build() + ) + .customConfig( + OAuth2CustomMapperConfig.builder() + .url(url) + .username(username) + .password(password) + .build() + ) + .build(); + } +} diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 697e34930a..d9dba652b1 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -253,3 +253,39 @@ CREATE TABLE IF NOT EXISTS entity_view ( additional_info varchar ); +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( + registration_id varchar(255) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + mapper_config_id varchar(31), + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + redirect_uri_template varchar(255), + scope varchar(255), + authorization_grant_type varchar(255), + user_info_uri varchar(255), + user_name_attribute varchar(255), + jwk_set_uri varchar(255), + client_authentication_method varchar(255), + client_name varchar(255), + login_button_label varchar(255), + login_button_icon varchar(255) +); + +CREATE TABLE IF NOT EXISTS oauth2_mapper_config ( + id varchar(31) NOT NULL CONSTRAINT oauth2_mapper_config_pkey PRIMARY KEY, + 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) +); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 931856e1df..820a7775c5 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -253,6 +253,44 @@ CREATE TABLE IF NOT EXISTS entity_view ( additional_info varchar ); +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( + id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + registration_id varchar(255) UNIQUE, + mapper_config_id varchar(31), + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + redirect_uri_template varchar(255), + scope varchar(255), + authorization_grant_type varchar(255), + user_info_uri varchar(255), + user_name_attribute varchar(255), + jwk_set_uri varchar(255), + client_authentication_method varchar(255), + client_name varchar(255), + login_button_label varchar(255), + login_button_icon varchar(255) +); + +CREATE TABLE IF NOT EXISTS oauth2_mapper_config ( + id varchar(31) NOT NULL CONSTRAINT oauth2_mapper_config_pkey PRIMARY KEY, + 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) +); + CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) LANGUAGE plpgsql AS $$ From da8ad473892f3618ca51b3ffaaf8384dc554d4ab Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 18 Jun 2020 18:54:44 +0300 Subject: [PATCH 002/117] Fixed schema-entities-hsql.sql and update script --- application/src/main/data/upgrade/3.1.0/schema_update.sql | 3 ++- dao/src/main/resources/sql/schema-entities-hsql.sql | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql index 3ee67b03a4..7ef91b2bec 100644 --- a/application/src/main/data/upgrade/3.1.0/schema_update.sql +++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql @@ -17,7 +17,8 @@ DROP TABLE IF EXISTS oauth2_client_registration; CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - registration_id varchar(255) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + registration_id varchar(255) UNIQUE, mapper_config_id varchar(31), client_id varchar(255), client_secret varchar(255), diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index d9dba652b1..ca14f0b0e8 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -254,7 +254,8 @@ CREATE TABLE IF NOT EXISTS entity_view ( ); CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - registration_id varchar(255) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + registration_id varchar(255) UNIQUE, mapper_config_id varchar(31), client_id varchar(255), client_secret varchar(255), From b63f34316d5a92312fe85292115621c556d1e6cb Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 18 Jun 2020 18:58:30 +0300 Subject: [PATCH 003/117] Changed @Getter to @Data for Custom and Basic mappers --- .../server/common/data/oauth2/OAuth2BasicMapperConfig.java | 7 ++----- .../common/data/oauth2/OAuth2CustomMapperConfig.java | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java index e91daf75e7..a38390ef81 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java @@ -1,13 +1,10 @@ package org.thingsboard.server.common.data.oauth2; -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.ToString; +import lombok.*; @Builder(toBuilder = true) @EqualsAndHashCode -@Getter +@Data @ToString public class OAuth2BasicMapperConfig { private final String emailAttributeKey; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java index ea9c963721..ad401e0aff 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java @@ -1,13 +1,10 @@ package org.thingsboard.server.common.data.oauth2; -import lombok.Builder; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.ToString; +import lombok.*; @Builder(toBuilder = true) @EqualsAndHashCode -@Getter +@Data @ToString(exclude = {"password"}) public class OAuth2CustomMapperConfig { private final String url; From d7ff230bbda700db3055e8ba92092d2bbeb6d6db Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 19 Jun 2020 10:41:34 +0300 Subject: [PATCH 004/117] Merged OAuth2 mapper_config with client_registration --- .../main/data/upgrade/3.1.0/schema_update.sql | 39 +++---- .../data/oauth2/OAuth2ClientRegistration.java | 6 +- .../data/oauth2/OAuth2MapperConfig.java | 30 ++---- .../server/dao/model/ModelConstants.java | 6 -- .../sql/OAuth2ClientRegistrationEntity.java | 62 +++++++++-- .../model/sql/OAuth2MapperConfigEntity.java | 101 ------------------ .../resources/sql/schema-entities-hsql.sql | 37 +++---- .../main/resources/sql/schema-entities.sql | 37 +++---- 8 files changed, 113 insertions(+), 205 deletions(-) delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2MapperConfigEntity.java diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql index 7ef91b2bec..61ba70894c 100644 --- a/application/src/main/data/upgrade/3.1.0/schema_update.sql +++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql @@ -17,29 +17,22 @@ DROP TABLE IF EXISTS oauth2_client_registration; CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, - registration_id varchar(255) UNIQUE, - mapper_config_id varchar(31), - client_id varchar(255), - client_secret varchar(255), - authorization_uri varchar(255), - token_uri varchar(255), - redirect_uri_template varchar(255), - scope varchar(255), - authorization_grant_type varchar(255), - user_info_uri varchar(255), - user_name_attribute varchar(255), - jwk_set_uri varchar(255), - client_authentication_method varchar(255), - client_name varchar(255), - login_button_label varchar(255), - login_button_icon varchar(255) -); - -DROP TABLE IF EXISTS oauth2_mapper_config; - -CREATE TABLE IF NOT EXISTS oauth2_mapper_config ( - id varchar(31) NOT NULL CONSTRAINT oauth2_mapper_config_pkey PRIMARY KEY, + id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + registration_id varchar(255) UNIQUE, + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + redirect_uri_template varchar(255), + scope varchar(255), + authorization_grant_type varchar(255), + user_info_uri varchar(255), + user_name_attribute varchar(255), + jwk_set_uri varchar(255), + client_authentication_method varchar(255), + client_name varchar(255), + login_button_label varchar(255), + login_button_icon varchar(255), allow_user_creation boolean, activate_user boolean, type varchar(31), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java index d5bd3b076e..fc855e24f1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -10,7 +10,7 @@ import org.thingsboard.server.common.data.id.OAuth2IntegrationId; public class OAuth2ClientRegistration extends BaseData { private String registrationId; - private OAuth2IntegrationId mapperConfigId; + private OAuth2MapperConfig mapperConfig; private String clientId; private String clientSecret; private String authorizationUri; @@ -35,7 +35,7 @@ public class OAuth2ClientRegistration extends BaseData { } @Builder(toBuilder = true) - public OAuth2ClientRegistration(OAuth2IntegrationId id, String registrationId, String clientId, String clientSecret, String authorizationUri, String tokenUri, String redirectUriTemplate, String scope, String authorizationGrantType, String userInfoUri, String userNameAttribute, String jwkSetUri, String clientAuthenticationMethod, String clientName, String loginButtonLabel, String loginButtonIcon, OAuth2IntegrationId mapperConfigId) { + public OAuth2ClientRegistration(OAuth2IntegrationId id, String registrationId, String clientId, String clientSecret, String authorizationUri, String tokenUri, String redirectUriTemplate, String scope, String authorizationGrantType, String userInfoUri, String userNameAttribute, String jwkSetUri, String clientAuthenticationMethod, String clientName, String loginButtonLabel, String loginButtonIcon, OAuth2MapperConfig mapperConfig) { super(id); this.registrationId = registrationId; this.clientId = clientId; @@ -52,6 +52,6 @@ public class OAuth2ClientRegistration extends BaseData { this.clientName = clientName; this.loginButtonLabel = loginButtonLabel; this.loginButtonIcon = loginButtonIcon; - this.mapperConfigId = mapperConfigId; + this.mapperConfig = mapperConfig; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java index 95c1fdf4a8..1e45edc4de 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java @@ -1,34 +1,18 @@ package org.thingsboard.server.common.data.oauth2; -import lombok.*; -import org.thingsboard.server.common.data.BaseData; -import org.thingsboard.server.common.data.id.OAuth2IntegrationId; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; -@EqualsAndHashCode(callSuper = true) +@Builder(toBuilder = true) +@EqualsAndHashCode @Data @ToString -public class OAuth2MapperConfig extends BaseData { +public class OAuth2MapperConfig { private boolean allowUserCreation; private boolean activateUser; private MapperType type; private OAuth2BasicMapperConfig basicConfig; private OAuth2CustomMapperConfig customConfig; - - public OAuth2MapperConfig() { - super(); - } - - public OAuth2MapperConfig(OAuth2IntegrationId id) { - super(id); - } - - @Builder(toBuilder = true) - public OAuth2MapperConfig(OAuth2IntegrationId id, boolean allowUserCreation, boolean activateUser, MapperType type, OAuth2BasicMapperConfig basicConfig, OAuth2CustomMapperConfig customConfig) { - super(id); - this.allowUserCreation = allowUserCreation; - this.activateUser = activateUser; - this.type = type; - this.basicConfig = basicConfig; - this.customConfig = customConfig; - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index c551114aa5..2f967b648b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -359,7 +359,6 @@ public class ModelConstants { */ public static final String OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_client_registration"; public static final String OAUTH2_CLIENT_REGISTRATION_ID_PROPERTY = "registration_id"; - public static final String OAUTH2_CLIENT_REGISTRATION_MAPPER_CONFIG_ID_PROPERTY = "mapper_config_id"; public static final String OAUTH2_CLIENT_ID_PROPERTY = "client_id"; public static final String OAUTH2_CLIENT_SECRET_PROPERTY = "client_secret"; public static final String OAUTH2_AUTHORIZATION_URI_PROPERTY = "authorization_uri"; @@ -374,11 +373,6 @@ public class ModelConstants { public static final String OAUTH2_CLIENT_NAME_PROPERTY = "client_name"; public static final String OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY = "login_button_label"; public static final String OAUTH2_LOGIN_BUTTON_ICON_PROPERTY = "login_button_icon"; - - /** - * Cassandra OAuth2 mapper config constants. - */ - public static final String OAUTH2_MAPPER_CONFIG_COLUMN_FAMILY_NAME = "oauth2_mapper_config"; 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"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java index bc402a7d7d..7189b315c2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java @@ -21,14 +21,12 @@ import lombok.NoArgsConstructor; import org.hibernate.annotations.TypeDef; import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.id.OAuth2IntegrationId; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; +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.Column; -import javax.persistence.Entity; -import javax.persistence.Table; +import javax.persistence.*; @Data @EqualsAndHashCode(callSuper = true) @@ -39,8 +37,6 @@ public class OAuth2ClientRegistrationEntity extends BaseSqlEntity - * 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.OAuth2IntegrationId; -import org.thingsboard.server.common.data.oauth2.MapperType; -import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; -import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; -import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; -import org.thingsboard.server.dao.model.BaseSqlEntity; -import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.dao.util.mapping.JsonStringType; - -import javax.persistence.*; - -@Data -@EqualsAndHashCode(callSuper = true) -@Entity -@TypeDef(name = "json", typeClass = JsonStringType.class) -@Table(name = ModelConstants.OAUTH2_MAPPER_CONFIG_COLUMN_FAMILY_NAME) -public class OAuth2MapperConfigEntity extends BaseSqlEntity { - - @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; - @Column(name = ModelConstants.OAUTH2_TENANT_NAME_STRATEGY_PROPERTY) - private String 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; - - public OAuth2MapperConfigEntity() { - super(); - } - - @Override - public OAuth2MapperConfig toData() { - return OAuth2MapperConfig.builder() - .id(new OAuth2IntegrationId(toUUID(id))) - .allowUserCreation(allowUserCreation) - .activateUser(activateUser) - .type(type) - .basicConfig( - OAuth2BasicMapperConfig.builder() - .emailAttributeKey(emailAttributeKey) - .firstNameAttributeKey(firstNameAttributeKey) - .lastNameAttributeKey(lastNameAttributeKey) - .tenantNameStrategy(tenantNameStrategy) - .tenantNamePattern(tenantNamePattern) - .customerNamePattern(customerNamePattern) - .defaultDashboardName(defaultDashboardName) - .alwaysFullScreen(alwaysFullScreen) - .build() - ) - .customConfig( - OAuth2CustomMapperConfig.builder() - .url(url) - .username(username) - .password(password) - .build() - ) - .build(); - } -} diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index ca14f0b0e8..b76665df3f 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -254,27 +254,22 @@ CREATE TABLE IF NOT EXISTS entity_view ( ); CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, - registration_id varchar(255) UNIQUE, - mapper_config_id varchar(31), - client_id varchar(255), - client_secret varchar(255), - authorization_uri varchar(255), - token_uri varchar(255), - redirect_uri_template varchar(255), - scope varchar(255), - authorization_grant_type varchar(255), - user_info_uri varchar(255), - user_name_attribute varchar(255), - jwk_set_uri varchar(255), - client_authentication_method varchar(255), - client_name varchar(255), - login_button_label varchar(255), - login_button_icon varchar(255) -); - -CREATE TABLE IF NOT EXISTS oauth2_mapper_config ( - id varchar(31) NOT NULL CONSTRAINT oauth2_mapper_config_pkey PRIMARY KEY, + id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + registration_id varchar(255) UNIQUE, + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + redirect_uri_template varchar(255), + scope varchar(255), + authorization_grant_type varchar(255), + user_info_uri varchar(255), + user_name_attribute varchar(255), + jwk_set_uri varchar(255), + client_authentication_method varchar(255), + client_name varchar(255), + login_button_label varchar(255), + login_button_icon varchar(255), allow_user_creation boolean, activate_user boolean, type varchar(31), diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 820a7775c5..a27907fc45 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -254,27 +254,22 @@ CREATE TABLE IF NOT EXISTS entity_view ( ); CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, - registration_id varchar(255) UNIQUE, - mapper_config_id varchar(31), - client_id varchar(255), - client_secret varchar(255), - authorization_uri varchar(255), - token_uri varchar(255), - redirect_uri_template varchar(255), - scope varchar(255), - authorization_grant_type varchar(255), - user_info_uri varchar(255), - user_name_attribute varchar(255), - jwk_set_uri varchar(255), - client_authentication_method varchar(255), - client_name varchar(255), - login_button_label varchar(255), - login_button_icon varchar(255) -); - -CREATE TABLE IF NOT EXISTS oauth2_mapper_config ( - id varchar(31) NOT NULL CONSTRAINT oauth2_mapper_config_pkey PRIMARY KEY, + id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + registration_id varchar(255) UNIQUE, + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + redirect_uri_template varchar(255), + scope varchar(255), + authorization_grant_type varchar(255), + user_info_uri varchar(255), + user_name_attribute varchar(255), + jwk_set_uri varchar(255), + client_authentication_method varchar(255), + client_name varchar(255), + login_button_label varchar(255), + login_button_icon varchar(255), allow_user_creation boolean, activate_user boolean, type varchar(31), From e8bf9b45efb8aa08831f35467c3dc70125d457fa Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 19 Jun 2020 11:42:45 +0300 Subject: [PATCH 005/117] Added repository and dao for OAuth2ClientRegistration --- .../sql/OAuth2ClientRegistrationEntity.java | 44 ++++++++++ .../oauth2/OAuth2ClientRegistrationDao.java | 15 ++++ .../JpaOAuth2ClientRegistrationDao.java | 83 +++++++++++++++++++ .../OAuth2ClientRegistrationRepository.java | 30 +++++++ 4 files changed, 172 insertions(+) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java index 7189b315c2..66e57faaf4 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java @@ -99,6 +99,50 @@ public class OAuth2ClientRegistrationEntity extends BaseSqlEntity find(); + + OAuth2ClientRegistration findById(String registrationId); + + OAuth2ClientRegistration save(OAuth2ClientRegistration clientRegistration); + + boolean removeById(String registrationId); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java new file mode 100644 index 0000000000..d2b951a072 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java @@ -0,0 +1,83 @@ +/** + * 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 com.google.common.collect.Lists; +import com.google.common.util.concurrent.ListenableFuture; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.repository.CrudRepository; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.page.PageData; +import org.thingsboard.server.common.data.page.PageLink; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; +import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; + +@Slf4j +@Component +@SqlDao +@RequiredArgsConstructor +public class JpaOAuth2ClientRegistrationDao implements OAuth2ClientRegistrationDao { + private final OAuth2ClientRegistrationRepository repository; + + @Override + @Transactional + public OAuth2ClientRegistration save(OAuth2ClientRegistration clientRegistration) { + OAuth2ClientRegistrationEntity entity; + try { + entity = new OAuth2ClientRegistrationEntity(clientRegistration); + } catch (Exception e) { + log.error("Can't create entity for domain object {}", clientRegistration, e); + throw new IllegalArgumentException("Can't create entity for domain object {" + clientRegistration + "}", e); + } + log.debug("Saving entity {}", entity); + entity = repository.save(entity); + return DaoUtil.getData(entity); + } + + @Override + public List find() { + List entities = Lists.newArrayList(repository.findAll()); + return DaoUtil.convertDataList(entities); + } + + @Override + public OAuth2ClientRegistration findById(String registrationId) { + log.debug("Get entity by registration id {}", registrationId); + Optional entity = repository.findByRegistrationId(registrationId); + return DaoUtil.getData(entity); + } + + @Override + public boolean removeById(String registrationId) { + repository.deleteByRegistrationId(registrationId); + log.debug("Remove request: {}", registrationId); + return !repository.existsByRegistrationId(registrationId); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java new file mode 100644 index 0000000000..2e6eb57209 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.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.dao.sql.oauth2; + +import org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; +import org.thingsboard.server.dao.util.SqlDao; + +import java.util.Optional; + +@SqlDao +public interface OAuth2ClientRegistrationRepository extends CrudRepository { + Optional findByRegistrationId(String registrationId); + int deleteByRegistrationId(String registrationId); + boolean existsByRegistrationId(String registrationId); + +} From cdf0e78176e4a50d17108a7a7694821cdd593db1 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 19 Jun 2020 14:05:58 +0300 Subject: [PATCH 006/117] Added named UNIQUE constraint --- application/src/main/data/upgrade/3.1.0/schema_update.sql | 5 +++-- dao/src/main/resources/sql/schema-entities-hsql.sql | 5 +++-- dao/src/main/resources/sql/schema-entities.sql | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql index 61ba70894c..fc334929b4 100644 --- a/application/src/main/data/upgrade/3.1.0/schema_update.sql +++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql @@ -18,7 +18,7 @@ DROP TABLE IF EXISTS oauth2_client_registration; CREATE TABLE IF NOT EXISTS oauth2_client_registration ( id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, - registration_id varchar(255) UNIQUE, + registration_id varchar(255), client_id varchar(255), client_secret varchar(255), authorization_uri varchar(255), @@ -46,5 +46,6 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( basic_always_full_screen boolean, custom_url varchar(255), custom_username varchar(255), - custom_password varchar(255) + custom_password varchar(255), + CONSTRAINT oauth2_registration_id_unq_key UNIQUE (registration_id) ); \ No newline at end of file diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index b76665df3f..425d303f2d 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -255,7 +255,7 @@ CREATE TABLE IF NOT EXISTS entity_view ( CREATE TABLE IF NOT EXISTS oauth2_client_registration ( id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, - registration_id varchar(255) UNIQUE, + registration_id varchar(255), client_id varchar(255), client_secret varchar(255), authorization_uri varchar(255), @@ -283,5 +283,6 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( basic_always_full_screen boolean, custom_url varchar(255), custom_username varchar(255), - custom_password varchar(255) + custom_password varchar(255), + CONSTRAINT oauth2_registration_id_unq_key UNIQUE (registration_id) ); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index a27907fc45..42aabf72db 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -255,7 +255,7 @@ CREATE TABLE IF NOT EXISTS entity_view ( CREATE TABLE IF NOT EXISTS oauth2_client_registration ( id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, - registration_id varchar(255) UNIQUE, + registration_id varchar(255), client_id varchar(255), client_secret varchar(255), authorization_uri varchar(255), @@ -283,7 +283,8 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( basic_always_full_screen boolean, custom_url varchar(255), custom_username varchar(255), - custom_password varchar(255) + custom_password varchar(255), + CONSTRAINT oauth2_registration_id_unq_key UNIQUE (registration_id) ); CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) From b08d9cc935023d977ed69a8a763c843ec188bb7d Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 19 Jun 2020 15:56:35 +0300 Subject: [PATCH 007/117] Made TenantNameStrategyType as Enum --- .../server/common/data/oauth2/OAuth2BasicMapperConfig.java | 2 +- .../server/common/data/oauth2/TenantNameStrategyType.java | 5 +++++ .../server/dao/model/sql/OAuth2ClientRegistrationEntity.java | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java index a38390ef81..18bab333fb 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java @@ -10,7 +10,7 @@ public class OAuth2BasicMapperConfig { private final String emailAttributeKey; private final String firstNameAttributeKey; private final String lastNameAttributeKey; - private final String tenantNameStrategy; + private final TenantNameStrategyType tenantNameStrategy; private final String tenantNamePattern; private final String customerNamePattern; private final String defaultDashboardName; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java new file mode 100644 index 0000000000..e38c7e37dc --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java @@ -0,0 +1,5 @@ +package org.thingsboard.server.common.data.oauth2; + +public enum TenantNameStrategyType { + DOMAIN, EMAIL, CUSTOM; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java index 66e57faaf4..70e9c034dc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java @@ -78,8 +78,9 @@ public class OAuth2ClientRegistrationEntity extends BaseSqlEntity Date: Fri, 19 Jun 2020 16:03:36 +0300 Subject: [PATCH 008/117] Refactored OAuth2Dao --- .../dao/oauth2/OAuth2ClientRegistrationDao.java | 4 ++-- .../sql/oauth2/JpaOAuth2ClientRegistrationDao.java | 14 ++------------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java index 25c68d58e4..dbdcd8f68b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java @@ -7,9 +7,9 @@ import java.util.List; public interface OAuth2ClientRegistrationDao { List find(); - OAuth2ClientRegistration findById(String registrationId); + OAuth2ClientRegistration findByRegistrationId(String registrationId); OAuth2ClientRegistration save(OAuth2ClientRegistration clientRegistration); - boolean removeById(String registrationId); + boolean removeByRegistrationId(String registrationId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java index d2b951a072..98e1b8431a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java @@ -16,29 +16,19 @@ package org.thingsboard.server.dao.sql.oauth2; import com.google.common.collect.Lists; -import com.google.common.util.concurrent.ListenableFuture; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.common.data.EntityType; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; -import org.thingsboard.server.common.data.page.PageData; -import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationDao; -import org.thingsboard.server.dao.sql.JpaAbstractDao; import org.thingsboard.server.dao.util.SqlDao; import java.util.List; -import java.util.Objects; import java.util.Optional; -import static org.thingsboard.server.common.data.UUIDConverter.fromTimeUUID; - @Slf4j @Component @SqlDao @@ -68,14 +58,14 @@ public class JpaOAuth2ClientRegistrationDao implements OAuth2ClientRegistrationD } @Override - public OAuth2ClientRegistration findById(String registrationId) { + public OAuth2ClientRegistration findByRegistrationId(String registrationId) { log.debug("Get entity by registration id {}", registrationId); Optional entity = repository.findByRegistrationId(registrationId); return DaoUtil.getData(entity); } @Override - public boolean removeById(String registrationId) { + public boolean removeByRegistrationId(String registrationId) { repository.deleteByRegistrationId(registrationId); log.debug("Remove request: {}", registrationId); return !repository.existsByRegistrationId(registrationId); From d5abe337be44763efa85129a47da3f0f9edcfc60 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 19 Jun 2020 16:04:10 +0300 Subject: [PATCH 009/117] Added OAuth2ClientRegistrationService --- .../OAuth2ClientRegistrationService.java | 30 ++++ .../OAuth2ClientRegistrationServiceImpl.java | 151 ++++++++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationServiceImpl.java diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationService.java new file mode 100644 index 0000000000..a38fb78eb9 --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationService.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.dao.oauth2; + +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; + +import java.util.List; + +public interface OAuth2ClientRegistrationService { + OAuth2ClientRegistration saveClientRegistration(OAuth2ClientRegistration clientRegistration); + + List findClientRegistrations(); + + OAuth2ClientRegistration findClientRegistrationsByRegistrationId(String registrationId); + + void deleteClientRegistrationsByRegistrationId(String registrationId); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationServiceImpl.java new file mode 100644 index 0000000000..699f7acd81 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationServiceImpl.java @@ -0,0 +1,151 @@ +/** + * 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.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.Customer; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.dao.exception.DataValidationException; + +import java.util.List; +import java.util.function.Consumer; + +import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; +import static org.thingsboard.server.dao.service.Validator.validateId; +import static org.thingsboard.server.dao.service.Validator.validateString; + +@Slf4j +@Service +public class OAuth2ClientRegistrationServiceImpl implements OAuth2ClientRegistrationService { + public static final String INCORRECT_REGISTRATION_ID = "Incorrect registrationId "; + + @Autowired + private OAuth2ClientRegistrationDao clientRegistrationDao; + + @Override + public OAuth2ClientRegistration saveClientRegistration(OAuth2ClientRegistration clientRegistration) { + log.trace("Executing saveClientRegistration [{}]", clientRegistration); + return null; + } + + @Override + public List findClientRegistrations() { + log.trace("Executing findClientRegistrations []"); + return clientRegistrationDao.find(); + } + + @Override + public OAuth2ClientRegistration findClientRegistrationsByRegistrationId(String registrationId) { + log.trace("Executing findClientRegistrationsByRegistrationId [{}]", registrationId); + validateString(registrationId, INCORRECT_REGISTRATION_ID + registrationId); + return clientRegistrationDao.findByRegistrationId(registrationId); + } + + @Override + public void deleteClientRegistrationsByRegistrationId(String registrationId) { + log.trace("Executing deleteClientRegistrationsByRegistrationId [{}]", registrationId); + validateString(registrationId, INCORRECT_REGISTRATION_ID + registrationId); + clientRegistrationDao.removeByRegistrationId(registrationId); + } + + private Consumer validator = clientRegistration -> { + if (StringUtils.isEmpty(clientRegistration.getRegistrationId())) { + throw new DataValidationException("Registration ID should be specified!"); + } + 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.getTokenUri())) { + throw new DataValidationException("Token uri should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getRedirectUriTemplate())) { + throw new DataValidationException("Redirect uri template should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getScope())) { + throw new DataValidationException("Scope should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getAuthorizationGrantType())) { + throw new DataValidationException("Authorization grant type should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getUserInfoUri())) { + throw new DataValidationException("User info uri should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getUserNameAttribute())) { + throw new DataValidationException("User name attribute should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getJwkSetUri())) { + throw new DataValidationException("Jwk set uri should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getClientAuthenticationMethod())) { + throw new DataValidationException("Client authentication method should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getClientName())) { + throw new DataValidationException("Client name 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.getBasicConfig(); + 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.CUSTOM) { + OAuth2CustomMapperConfig customConfig = mapperConfig.getCustomConfig(); + 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!"); + } + if (StringUtils.isEmpty(customConfig.getUsername())) { + throw new DataValidationException("Custom mapper username should be specified!"); + } + if (StringUtils.isEmpty(customConfig.getPassword())) { + throw new DataValidationException("Custom mapper password should be specified!"); + } + } + }; +} From a4a05fcf3058dabb14e1e73b0a3b068462fc405b Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 19 Jun 2020 16:36:22 +0300 Subject: [PATCH 010/117] Refactored 'getClientByRegistrationId' --- .../server/dao/oauth2/OAuth2Configuration.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java index e54f9a5b3e..b2acc524b1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java @@ -68,15 +68,6 @@ public class OAuth2Configuration { } 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; + return clients != null ? clients.get(registrationId) : null; } } From 6ad3de83b7193c03c15522353a6280639d80f46d Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 19 Jun 2020 17:35:31 +0300 Subject: [PATCH 011/117] Moved getting ClientInfo to OAuth2Service --- .../auth/oauth2/BasicOAuth2ClientMapper.java | 45 ++++++------- .../auth/oauth2/CustomOAuth2ClientMapper.java | 9 +-- .../auth/oauth2/OAuth2ClientMapper.java | 4 +- .../oauth2/OAuth2ClientMapperProvider.java | 11 +-- .../Oauth2AuthenticationSuccessHandler.java | 16 ++--- .../server/dao/oauth2/OAuth2Service.java | 2 + .../dao/oauth2/OAuth2Configuration.java | 4 -- .../server/dao/oauth2/OAuth2ServiceImpl.java | 67 ++++++++++++++++++- 8 files changed, 110 insertions(+), 48 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java index 2170479f92..7d671767bb 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java @@ -20,7 +20,7 @@ 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; @@ -32,50 +32,47 @@ public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implemen 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) { + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2MapperConfig config) { OAuth2User oauth2User = new OAuth2User(); Map attributes = token.getPrincipal().getAttributes(); - String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); + String email = getStringAttributeByKey(attributes, config.getBasicConfig().getEmailAttributeKey()); oauth2User.setEmail(email); oauth2User.setTenantName(getTenantName(attributes, config)); - if (!StringUtils.isEmpty(config.getBasic().getLastNameAttributeKey())) { - String lastName = getStringAttributeByKey(attributes, config.getBasic().getLastNameAttributeKey()); + if (!StringUtils.isEmpty(config.getBasicConfig().getLastNameAttributeKey())) { + String lastName = getStringAttributeByKey(attributes, config.getBasicConfig().getLastNameAttributeKey()); oauth2User.setLastName(lastName); } - if (!StringUtils.isEmpty(config.getBasic().getFirstNameAttributeKey())) { - String firstName = getStringAttributeByKey(attributes, config.getBasic().getFirstNameAttributeKey()); + if (!StringUtils.isEmpty(config.getBasicConfig().getFirstNameAttributeKey())) { + String firstName = getStringAttributeByKey(attributes, config.getBasicConfig().getFirstNameAttributeKey()); oauth2User.setFirstName(firstName); } - if (!StringUtils.isEmpty(config.getBasic().getCustomerNamePattern())) { + if (!StringUtils.isEmpty(config.getBasicConfig().getCustomerNamePattern())) { StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); - String customerName = sub.replace(config.getBasic().getCustomerNamePattern()); + String customerName = sub.replace(config.getBasicConfig().getCustomerNamePattern()); oauth2User.setCustomerName(customerName); } - oauth2User.setAlwaysFullScreen(config.getBasic().isAlwaysFullScreen()); - if (!StringUtils.isEmpty(config.getBasic().getDefaultDashboardName())) { - oauth2User.setDefaultDashboardName(config.getBasic().getDefaultDashboardName()); + oauth2User.setAlwaysFullScreen(config.getBasicConfig().isAlwaysFullScreen()); + if (!StringUtils.isEmpty(config.getBasicConfig().getDefaultDashboardName())) { + oauth2User.setDefaultDashboardName(config.getBasicConfig().getDefaultDashboardName()); } return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser()); } - private String getTenantName(Map 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()); + private String getTenantName(Map attributes, OAuth2MapperConfig config) { + switch (config.getBasicConfig().getTenantNameStrategy()) { + case EMAIL: + return getStringAttributeByKey(attributes, config.getBasicConfig().getEmailAttributeKey()); + case DOMAIN: + String email = getStringAttributeByKey(attributes, config.getBasicConfig().getEmailAttributeKey()); return email.substring(email .indexOf("@") + 1); - case CUSTOM_TENANT_STRATEGY: + case CUSTOM: StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); - return sub.replace(config.getBasic().getTenantNamePattern()); + return sub.replace(config.getBasicConfig().getTenantNamePattern()); default: - throw new RuntimeException("Tenant Name Strategy with type " + config.getBasic().getTenantNameStrategy() + " is not supported!"); + throw new RuntimeException("Tenant Name Strategy with type " + config.getBasicConfig().getTenantNameStrategy() + " is not supported!"); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java index 42ba95b4ee..9d4ac4d2e2 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java @@ -23,7 +23,8 @@ 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; @@ -36,12 +37,12 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); @Override - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig config) { - OAuth2User oauth2User = getOAuth2User(token, config.getCustom()); + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2MapperConfig config) { + OAuth2User oauth2User = getOAuth2User(token, config.getCustomConfig()); return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser()); } - private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig.CustomOAuth2ClientMapperConfig custom) { + private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, OAuth2CustomMapperConfig custom) { if (!StringUtils.isEmpty(custom.getUsername()) && !StringUtils.isEmpty(custom.getPassword())) { restTemplateBuilder = restTemplateBuilder.basicAuthentication(custom.getUsername(), custom.getPassword()); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java index 196bfe7b50..444332d74b 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java +++ b/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, OAuth2MapperConfig config); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java index e1c5b694bb..64bdf40970 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java +++ b/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,14 @@ public class OAuth2ClientMapperProvider { @Qualifier("customOAuth2ClientMapper") private OAuth2ClientMapper customOAuth2ClientMapper; - public OAuth2ClientMapper getOAuth2ClientMapperByType(String oauth2ClientType) { - switch (oauth2ClientType) { - case "custom": + public OAuth2ClientMapper getOAuth2ClientMapperByType(MapperType oauth2MapperType) { + switch (oauth2MapperType) { + case CUSTOM: return customOAuth2ClientMapper; - case "basic": + case BASIC: return basicOAuth2ClientMapper; default: - throw new RuntimeException("OAuth2ClientMapper with type " + oauth2ClientType + " is not supported!"); + throw new RuntimeException("OAuth2ClientRegistrationMapper with type " + oauth2MapperType + " is not supported!"); } } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 3375d046e2..08ea4dedca 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -21,8 +21,8 @@ import org.springframework.security.core.Authentication; 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.OAuth2ClientRegistration; +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; @@ -42,17 +42,17 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS private final JwtTokenFactory tokenFactory; private final RefreshTokenRepository refreshTokenRepository; private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; - private final OAuth2Configuration oauth2Configuration; + private final OAuth2Service oAuth2Service; @Autowired public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory, final RefreshTokenRepository refreshTokenRepository, final OAuth2ClientMapperProvider oauth2ClientMapperProvider, - final OAuth2Configuration oauth2Configuration) { + final OAuth2Service oAuth2Service) { this.tokenFactory = tokenFactory; this.refreshTokenRepository = refreshTokenRepository; this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; - this.oauth2Configuration = oauth2Configuration; + this.oAuth2Service = oAuth2Service; } @Override @@ -64,9 +64,9 @@ 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()); + OAuth2ClientRegistration clientRegistration = oAuth2Service.getClientRegistrationByRegistrationId(token.getAuthorizedClientRegistrationId()); + OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); + SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, clientRegistration.getMapperConfig()); JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index d72b6ef98c..efea46a416 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -16,10 +16,12 @@ package org.thingsboard.server.dao.oauth2; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; import java.util.List; public interface OAuth2Service { + OAuth2ClientRegistration getClientRegistrationByRegistrationId(String registrationId); List getOAuth2Clients(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java index b2acc524b1..114a96a61b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java @@ -66,8 +66,4 @@ public class OAuth2Configuration { } return new InMemoryClientRegistrationRepository(result); } - - public OAuth2Client getClientByRegistrationId(String registrationId) { - return clients != null ? clients.get(registrationId) : null; - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 43e11244b0..d0dcbae4d5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -18,8 +18,10 @@ 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.thingsboard.server.common.data.id.OAuth2IntegrationId; +import org.thingsboard.server.common.data.oauth2.*; +import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -32,6 +34,14 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Autowired(required = false) OAuth2Configuration oauth2Configuration; + @Autowired + private OAuth2ClientRegistrationDao clientRegistrationDao; + + @PostConstruct + public void init() { + + } + @Override public List getOAuth2Clients() { if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) { @@ -47,4 +57,59 @@ public class OAuth2ServiceImpl implements OAuth2Service { } return result; } + + @Override + public OAuth2ClientRegistration getClientRegistrationByRegistrationId(String registrationId) { + if (oauth2Configuration == null || oauth2Configuration.getClients() == null) return null; + OAuth2Client oAuth2Client = oauth2Configuration.getClients().get(registrationId); + OAuth2ClientMapperConfig mapperConfig = oAuth2Client.getMapperConfig(); + OAuth2ClientMapperConfig.BasicOAuth2ClientMapperConfig basicConfig = mapperConfig.getBasic(); + OAuth2ClientMapperConfig.CustomOAuth2ClientMapperConfig customConfig = mapperConfig.getCustom(); + + return OAuth2ClientRegistration.builder() + .registrationId(registrationId) + .mapperConfig(OAuth2MapperConfig.builder() + .allowUserCreation(mapperConfig.isAllowUserCreation()) + .activateUser(mapperConfig.isActivateUser()) + .type(MapperType.valueOf( + mapperConfig.getType().toUpperCase() + )) + .basicConfig( + OAuth2BasicMapperConfig.builder() + .emailAttributeKey(basicConfig.getEmailAttributeKey()) + .firstNameAttributeKey(basicConfig.getFirstNameAttributeKey()) + .lastNameAttributeKey(basicConfig.getLastNameAttributeKey()) + .tenantNameStrategy(TenantNameStrategyType.valueOf( + basicConfig.getTenantNameStrategy().toUpperCase() + )) + .tenantNamePattern(basicConfig.getTenantNamePattern()) + .customerNamePattern(basicConfig.getCustomerNamePattern()) + .defaultDashboardName(basicConfig.getDefaultDashboardName()) + .alwaysFullScreen(basicConfig.isAlwaysFullScreen()) + .build() + ) + .customConfig( + OAuth2CustomMapperConfig.builder() + .url(customConfig.getUrl()) + .username(customConfig.getUsername()) + .password(customConfig.getPassword()) + .build() + ) + .build()) + .clientId(oAuth2Client.getClientId()) + .clientSecret(oAuth2Client.getClientSecret()) + .authorizationUri(oAuth2Client.getAuthorizationUri()) + .tokenUri(oAuth2Client.getAccessTokenUri()) + .redirectUriTemplate(oAuth2Client.getRedirectUriTemplate()) + .scope(oAuth2Client.getScope()) + .authorizationGrantType(oAuth2Client.getAuthorizationGrantType()) + .userInfoUri(oAuth2Client.getUserInfoUri()) + .userNameAttribute(oAuth2Client.getUserNameAttributeName()) + .jwkSetUri(oAuth2Client.getJwkSetUri()) + .clientAuthenticationMethod(oAuth2Client.getClientAuthenticationMethod()) + .clientName(oAuth2Client.getClientName()) + .loginButtonLabel(oAuth2Client.getLoginButtonLabel()) + .loginButtonIcon(oAuth2Client.getLoginButtonIcon()) + .build(); + } } From f0a68ff9b636dfc717c419b025af0dbb5e68ae44 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 19 Jun 2020 18:12:50 +0300 Subject: [PATCH 012/117] Use ClientRegistration dao to load oauth2 configs --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 66 ++++++++++++++----- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index d0dcbae4d5..760f2554fe 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.oauth2; +import com.google.common.collect.Sets; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -22,24 +23,35 @@ import org.thingsboard.server.common.data.id.OAuth2IntegrationId; import org.thingsboard.server.common.data.oauth2.*; import javax.annotation.PostConstruct; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; @Slf4j @Service public class OAuth2ServiceImpl implements OAuth2Service { + private static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; + @Autowired(required = false) OAuth2Configuration oauth2Configuration; @Autowired - private OAuth2ClientRegistrationDao clientRegistrationDao; + private OAuth2ClientRegistrationService clientRegistrationService; @PostConstruct public void init() { - + if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) { + return; + } + Set dbClientRegistration = clientRegistrationService.findClientRegistrations().stream() + .map(OAuth2ClientRegistration::getRegistrationId) + .collect(Collectors.toSet()); + // TODO decide what to do with same registrationIds in DB + Sets.SetView intersection = Sets.intersection(dbClientRegistration, oauth2Configuration.getClients().keySet()); + if (!intersection.isEmpty()) { + throw new RuntimeException("OAuth2 configurations " + intersection + " are already stored in DB."); + } } @Override @@ -47,21 +59,41 @@ public class OAuth2ServiceImpl implements OAuth2Service { if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) { return Collections.emptyList(); } - List result = new ArrayList<>(); - for (Map.Entry 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); - } - return result; + + Stream startUpConfiguration = oauth2Configuration.getClients().entrySet().stream() + .map(entry -> { + OAuth2ClientInfo client = new OAuth2ClientInfo(); + client.setName(entry.getValue().getLoginButtonLabel()); + client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, entry.getKey())); + client.setIcon(entry.getValue().getLoginButtonIcon()); + return client; + }); + + Stream dbConfiguration = clientRegistrationService.findClientRegistrations().stream() + .map(clientRegistration -> { + OAuth2ClientInfo client = new OAuth2ClientInfo(); + client.setName(clientRegistration.getLoginButtonLabel()); + client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getRegistrationId())); + client.setIcon(clientRegistration.getLoginButtonIcon()); + return client; + }); + + return Stream.concat(startUpConfiguration, dbConfiguration) + .collect(Collectors.toList()); } @Override public OAuth2ClientRegistration getClientRegistrationByRegistrationId(String registrationId) { - if (oauth2Configuration == null || oauth2Configuration.getClients() == null) return null; - OAuth2Client oAuth2Client = oauth2Configuration.getClients().get(registrationId); + if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) return null; + OAuth2Client oAuth2Client = oauth2Configuration.getClients() == null ? null : oauth2Configuration.getClients().get(registrationId); + if (oAuth2Client != null){ + return toClientRegistration(registrationId, oAuth2Client); + } + + return clientRegistrationService.findClientRegistrationsByRegistrationId(registrationId); + } + + private OAuth2ClientRegistration toClientRegistration(String registrationId, OAuth2Client oAuth2Client) { OAuth2ClientMapperConfig mapperConfig = oAuth2Client.getMapperConfig(); OAuth2ClientMapperConfig.BasicOAuth2ClientMapperConfig basicConfig = mapperConfig.getBasic(); OAuth2ClientMapperConfig.CustomOAuth2ClientMapperConfig customConfig = mapperConfig.getCustom(); From 5586eed4b7ad77846a90bde1a7bdd0796d03da10 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 19 Jun 2020 18:19:24 +0300 Subject: [PATCH 013/117] Created HybridClientRegistrationRepository --- .../HybridClientRegistrationRepository.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java new file mode 100644 index 0000000000..72088e8151 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java @@ -0,0 +1,42 @@ +package org.thingsboard.server.dao.oauth2; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +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.OAuth2ClientRegistration; + +@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") +@Component +public class HybridClientRegistrationRepository implements ClientRegistrationRepository { + + @Autowired + private OAuth2Service oAuth2Service; + + @Override + public ClientRegistration findByRegistrationId(String registrationId) { + OAuth2ClientRegistration localClientRegistration = oAuth2Service.getClientRegistrationByRegistrationId(registrationId); + return localClientRegistration == null ? + null : toSpringClientRegistration(localClientRegistration); + } + + private ClientRegistration toSpringClientRegistration(OAuth2ClientRegistration localClientRegistration){ + return ClientRegistration.withRegistrationId(localClientRegistration.getRegistrationId()) + .clientId(localClientRegistration.getClientId()) + .authorizationUri(localClientRegistration.getAuthorizationUri()) + .clientSecret(localClientRegistration.getClientSecret()) + .tokenUri(localClientRegistration.getTokenUri()) + .redirectUriTemplate(localClientRegistration.getRedirectUriTemplate()) + .scope(localClientRegistration.getScope().split(",")) + .clientName(localClientRegistration.getClientName()) + .authorizationGrantType(new AuthorizationGrantType(localClientRegistration.getAuthorizationGrantType())) + .userInfoUri(localClientRegistration.getUserInfoUri()) + .userNameAttributeName(localClientRegistration.getUserNameAttribute()) + .jwkSetUri(localClientRegistration.getJwkSetUri()) + .clientAuthenticationMethod(new ClientAuthenticationMethod(localClientRegistration.getClientAuthenticationMethod())) + .build(); + } +} From de7c43d92b3a103086d07d6954f773b3ad6aa146 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 19 Jun 2020 18:25:01 +0300 Subject: [PATCH 014/117] Renamed 'user_name_attribute' to 'user_name_attribute_name' --- .../src/main/data/upgrade/3.1.0/schema_update.sql | 2 +- .../common/data/oauth2/OAuth2ClientRegistration.java | 6 +++--- .../thingsboard/server/dao/model/ModelConstants.java | 2 +- .../dao/model/sql/OAuth2ClientRegistrationEntity.java | 10 ++++------ .../dao/oauth2/HybridClientRegistrationRepository.java | 2 +- .../oauth2/OAuth2ClientRegistrationServiceImpl.java | 8 ++------ .../server/dao/oauth2/OAuth2ServiceImpl.java | 2 +- dao/src/main/resources/sql/schema-entities-hsql.sql | 2 +- dao/src/main/resources/sql/schema-entities.sql | 2 +- 9 files changed, 15 insertions(+), 21 deletions(-) diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql index fc334929b4..645cc7a6ad 100644 --- a/application/src/main/data/upgrade/3.1.0/schema_update.sql +++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql @@ -27,7 +27,7 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( scope varchar(255), authorization_grant_type varchar(255), user_info_uri varchar(255), - user_name_attribute varchar(255), + user_name_attribute_name varchar(255), jwk_set_uri varchar(255), client_authentication_method varchar(255), client_name varchar(255), diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java index fc855e24f1..84c95df84c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -19,7 +19,7 @@ public class OAuth2ClientRegistration extends BaseData { private String scope; private String authorizationGrantType; private String userInfoUri; - private String userNameAttribute; + private String userNameAttributeName; private String jwkSetUri; private String clientAuthenticationMethod; private String clientName; @@ -35,7 +35,7 @@ public class OAuth2ClientRegistration extends BaseData { } @Builder(toBuilder = true) - public OAuth2ClientRegistration(OAuth2IntegrationId id, String registrationId, String clientId, String clientSecret, String authorizationUri, String tokenUri, String redirectUriTemplate, String scope, String authorizationGrantType, String userInfoUri, String userNameAttribute, String jwkSetUri, String clientAuthenticationMethod, String clientName, String loginButtonLabel, String loginButtonIcon, OAuth2MapperConfig mapperConfig) { + public OAuth2ClientRegistration(OAuth2IntegrationId id, String registrationId, String clientId, String clientSecret, String authorizationUri, String tokenUri, String redirectUriTemplate, String scope, String authorizationGrantType, String userInfoUri, String userNameAttributeName, String jwkSetUri, String clientAuthenticationMethod, String clientName, String loginButtonLabel, String loginButtonIcon, OAuth2MapperConfig mapperConfig) { super(id); this.registrationId = registrationId; this.clientId = clientId; @@ -46,7 +46,7 @@ public class OAuth2ClientRegistration extends BaseData { this.scope = scope; this.authorizationGrantType = authorizationGrantType; this.userInfoUri = userInfoUri; - this.userNameAttribute = userNameAttribute; + this.userNameAttributeName = userNameAttributeName; this.jwkSetUri = jwkSetUri; this.clientAuthenticationMethod = clientAuthenticationMethod; this.clientName = clientName; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 2f967b648b..4deee9fe78 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -367,7 +367,7 @@ public class ModelConstants { public static final String OAUTH2_SCOPE_PROPERTY = "scope"; public static final String OAUTH2_AUTHORIZATION_GRANT_TYPE_PROPERTY = "authorization_grant_type"; public static final String OAUTH2_USER_INFO_URI_PROPERTY = "user_info_uri"; - public static final String OAUTH2_USER_NAME_ATTRIBUTE_PROPERTY = "user_name_attribute"; + 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_CLIENT_NAME_PROPERTY = "client_name"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java index 70e9c034dc..c3c96c1838 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java @@ -17,9 +17,7 @@ package org.thingsboard.server.dao.model.sql; import lombok.Data; import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; import org.hibernate.annotations.TypeDef; -import org.thingsboard.server.common.data.EntityView; import org.thingsboard.server.common.data.id.OAuth2IntegrationId; import org.thingsboard.server.common.data.oauth2.*; import org.thingsboard.server.dao.model.BaseSqlEntity; @@ -53,8 +51,8 @@ public class OAuth2ClientRegistrationEntity extends BaseSqlEntity Date: Fri, 19 Jun 2020 18:25:18 +0300 Subject: [PATCH 015/117] Removed unused ClientRegistrationRepository bean creation --- .../dao/oauth2/OAuth2Configuration.java | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java index 114a96a61b..7618e6a776 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java @@ -42,28 +42,4 @@ public class OAuth2Configuration { private boolean enabled; private String loginProcessingUrl; private Map clients = new HashMap<>(); - - @Bean - public ClientRegistrationRepository clientRegistrationRepository() { - List result = new ArrayList<>(); - for (Map.Entry 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); - } } From 4d2813be8533027c4a8deb4c8db8c6d16a4d898f Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Mon, 22 Jun 2020 13:14:01 +0300 Subject: [PATCH 016/117] Added controller + fixes [almost working version] --- .../server/controller/OAuth2Controller.java | 61 +++++++++++++++++++ .../sql/OAuth2ClientRegistrationEntity.java | 36 ++++++----- .../OAuth2ClientRegistrationServiceImpl.java | 3 +- .../JpaOAuth2ClientRegistrationDao.java | 4 ++ 4 files changed, 87 insertions(+), 17 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java new file mode 100644 index 0000000000..07a0523223 --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -0,0 +1,61 @@ +/** + * 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.beans.factory.annotation.Autowired; +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.OAuth2ClientRegistration; +import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationService; +import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.queue.util.TbCoreComponent; + +@RestController +@TbCoreComponent +@RequestMapping("/api") +@Slf4j +public class OAuth2Controller extends BaseController { + private static final String REGISTRATION_ID = "registrationId"; + + @Autowired + private OAuth2Service oauth2Service; + @Autowired + private OAuth2ClientRegistrationService oAuth2ClientRegistrationService; + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") + @RequestMapping(value = "/oauth2/config/{" + REGISTRATION_ID + "}", method = RequestMethod.GET) + @ResponseBody + public OAuth2ClientRegistration getClientRegistrationById(@PathVariable(REGISTRATION_ID) String registrationId) throws ThingsboardException { + try { + return oauth2Service.getClientRegistrationByRegistrationId(registrationId); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") + @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) + @ResponseBody + public OAuth2ClientRegistration saveClientRegistration(@RequestBody OAuth2ClientRegistration clientRegistration) throws ThingsboardException { + try { + return oAuth2ClientRegistrationService.saveClientRegistration(clientRegistration); + } catch (Exception e) { + throw handleException(e); + } + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java index c3c96c1838..dfca90359f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java @@ -134,7 +134,7 @@ public class OAuth2ClientRegistrationEntity extends BaseSqlEntity Date: Mon, 22 Jun 2020 16:00:52 +0300 Subject: [PATCH 017/117] Removed oauth2 entities from DB --- .../main/data/upgrade/3.1.0/schema_update.sql | 51 ----- .../server/controller/OAuth2Controller.java | 14 -- .../OAuth2ClientRegistrationService.java | 30 --- .../sql/OAuth2ClientRegistrationEntity.java | 194 ------------------ .../oauth2/OAuth2ClientRegistrationDao.java | 15 -- .../OAuth2ClientRegistrationServiceImpl.java | 148 ------------- .../server/dao/oauth2/OAuth2ServiceImpl.java | 40 +--- .../JpaOAuth2ClientRegistrationDao.java | 77 ------- .../OAuth2ClientRegistrationRepository.java | 30 --- .../resources/sql/schema-entities-hsql.sql | 36 +--- .../main/resources/sql/schema-entities.sql | 34 --- 11 files changed, 6 insertions(+), 663 deletions(-) delete mode 100644 application/src/main/data/upgrade/3.1.0/schema_update.sql delete mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationService.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationServiceImpl.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql deleted file mode 100644 index 645cc7a6ad..0000000000 --- a/application/src/main/data/upgrade/3.1.0/schema_update.sql +++ /dev/null @@ -1,51 +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. --- - -DROP TABLE IF EXISTS oauth2_client_registration; - -CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, - registration_id varchar(255), - client_id varchar(255), - client_secret varchar(255), - authorization_uri varchar(255), - token_uri varchar(255), - redirect_uri_template varchar(255), - scope varchar(255), - authorization_grant_type varchar(255), - user_info_uri varchar(255), - user_name_attribute_name varchar(255), - jwk_set_uri varchar(255), - client_authentication_method varchar(255), - client_name 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), - CONSTRAINT oauth2_registration_id_unq_key UNIQUE (registration_id) -); \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index 07a0523223..ca3d51587b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -21,7 +21,6 @@ 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.OAuth2ClientRegistration; -import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationService; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -34,8 +33,6 @@ public class OAuth2Controller extends BaseController { @Autowired private OAuth2Service oauth2Service; - @Autowired - private OAuth2ClientRegistrationService oAuth2ClientRegistrationService; @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/oauth2/config/{" + REGISTRATION_ID + "}", method = RequestMethod.GET) @@ -47,15 +44,4 @@ public class OAuth2Controller extends BaseController { throw handleException(e); } } - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") - @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) - @ResponseBody - public OAuth2ClientRegistration saveClientRegistration(@RequestBody OAuth2ClientRegistration clientRegistration) throws ThingsboardException { - try { - return oAuth2ClientRegistrationService.saveClientRegistration(clientRegistration); - } catch (Exception e) { - throw handleException(e); - } - } } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationService.java deleted file mode 100644 index a38fb78eb9..0000000000 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationService.java +++ /dev/null @@ -1,30 +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 org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; - -import java.util.List; - -public interface OAuth2ClientRegistrationService { - OAuth2ClientRegistration saveClientRegistration(OAuth2ClientRegistration clientRegistration); - - List findClientRegistrations(); - - OAuth2ClientRegistration findClientRegistrationsByRegistrationId(String registrationId); - - void deleteClientRegistrationsByRegistrationId(String registrationId); -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java deleted file mode 100644 index dfca90359f..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java +++ /dev/null @@ -1,194 +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.model.sql; - -import lombok.Data; -import lombok.EqualsAndHashCode; -import org.hibernate.annotations.TypeDef; -import org.thingsboard.server.common.data.id.OAuth2IntegrationId; -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.*; - -@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 { - - @Column(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_ID_PROPERTY) - private String registrationId; - @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_REDIRECT_URI_TEMPLATE_PROPERTY) - private String redirectUriTemplate; - @Column(name = ModelConstants.OAUTH2_SCOPE_PROPERTY) - private String scope; - @Column(name = ModelConstants.OAUTH2_AUTHORIZATION_GRANT_TYPE_PROPERTY) - private String authorizationGrantType; - @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_CLIENT_NAME_PROPERTY) - private String clientName; - @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; - - public OAuth2ClientRegistrationEntity() { - super(); - } - - public OAuth2ClientRegistrationEntity(OAuth2ClientRegistration clientRegistration) { - if (clientRegistration.getId() != null) { - this.setUuid(clientRegistration.getId().getId()); - } - this.registrationId = clientRegistration.getRegistrationId(); - this.clientId = clientRegistration.getClientId(); - this.clientSecret = clientRegistration.getClientSecret(); - this.authorizationUri = clientRegistration.getAuthorizationUri(); - this.tokenUri = clientRegistration.getTokenUri(); - this.redirectUriTemplate = clientRegistration.getRedirectUriTemplate(); - this.scope = clientRegistration.getScope(); - this.authorizationGrantType = clientRegistration.getAuthorizationGrantType(); - this.userInfoUri = clientRegistration.getUserInfoUri(); - this.userNameAttributeName = clientRegistration.getUserNameAttributeName(); - this.jwkSetUri = clientRegistration.getJwkSetUri(); - this.clientAuthenticationMethod = clientRegistration.getClientAuthenticationMethod(); - this.clientName = clientRegistration.getClientName(); - this.loginButtonLabel = clientRegistration.getLoginButtonLabel(); - this.loginButtonIcon = clientRegistration.getLoginButtonIcon(); - OAuth2MapperConfig mapperConfig = clientRegistration.getMapperConfig(); - if (mapperConfig != null) { - this.allowUserCreation = mapperConfig.isAllowUserCreation(); - this.activateUser = mapperConfig.isActivateUser(); - this.type = mapperConfig.getType(); - OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasicConfig(); - 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.getCustomConfig(); - if (customConfig != null) { - this.url = customConfig.getUrl(); - this.username = customConfig.getUsername(); - this.password = customConfig.getPassword(); - } - } - } - - @Override - public OAuth2ClientRegistration toData() { - return OAuth2ClientRegistration.builder() - .id(new OAuth2IntegrationId(toUUID(id))) - .registrationId(registrationId) - .mapperConfig(OAuth2MapperConfig.builder() - .allowUserCreation(allowUserCreation) - .activateUser(activateUser) - .type(type) - .basicConfig( - type == MapperType.BASIC ? - OAuth2BasicMapperConfig.builder() - .emailAttributeKey(emailAttributeKey) - .firstNameAttributeKey(firstNameAttributeKey) - .lastNameAttributeKey(lastNameAttributeKey) - .tenantNameStrategy(tenantNameStrategy) - .tenantNamePattern(tenantNamePattern) - .customerNamePattern(customerNamePattern) - .defaultDashboardName(defaultDashboardName) - .alwaysFullScreen(alwaysFullScreen) - .build() - : null - ) - .customConfig( - type == MapperType.CUSTOM ? - OAuth2CustomMapperConfig.builder() - .url(url) - .username(username) - .password(password) - .build() - : null - ) - .build()) - .clientId(clientId) - .clientSecret(clientSecret) - .authorizationUri(authorizationUri) - .tokenUri(tokenUri) - .redirectUriTemplate(redirectUriTemplate) - .scope(scope) - .authorizationGrantType(authorizationGrantType) - .userInfoUri(userInfoUri) - .userNameAttributeName(userNameAttributeName) - .jwkSetUri(jwkSetUri) - .clientAuthenticationMethod(clientAuthenticationMethod) - .clientName(clientName) - .loginButtonLabel(loginButtonLabel) - .loginButtonIcon(loginButtonIcon) - .build(); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java deleted file mode 100644 index dbdcd8f68b..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java +++ /dev/null @@ -1,15 +0,0 @@ -package org.thingsboard.server.dao.oauth2; - -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; - -import java.util.List; - -public interface OAuth2ClientRegistrationDao { - List find(); - - OAuth2ClientRegistration findByRegistrationId(String registrationId); - - OAuth2ClientRegistration save(OAuth2ClientRegistration clientRegistration); - - boolean removeByRegistrationId(String registrationId); -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationServiceImpl.java deleted file mode 100644 index f19e819550..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationServiceImpl.java +++ /dev/null @@ -1,148 +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.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.oauth2.*; -import org.thingsboard.server.dao.exception.DataValidationException; - -import java.util.List; -import java.util.function.Consumer; - -import static org.thingsboard.server.dao.service.Validator.validateId; -import static org.thingsboard.server.dao.service.Validator.validateString; - -@Slf4j -@Service -public class OAuth2ClientRegistrationServiceImpl implements OAuth2ClientRegistrationService { - public static final String INCORRECT_REGISTRATION_ID = "Incorrect registrationId "; - - @Autowired - private OAuth2ClientRegistrationDao clientRegistrationDao; - - @Override - public OAuth2ClientRegistration saveClientRegistration(OAuth2ClientRegistration clientRegistration) { - log.trace("Executing saveClientRegistration [{}]", clientRegistration); - // TODO add checking for duplicates and other validations - return clientRegistrationDao.save(clientRegistration); - } - - @Override - public List findClientRegistrations() { - log.trace("Executing findClientRegistrations []"); - return clientRegistrationDao.find(); - } - - @Override - public OAuth2ClientRegistration findClientRegistrationsByRegistrationId(String registrationId) { - log.trace("Executing findClientRegistrationsByRegistrationId [{}]", registrationId); - validateString(registrationId, INCORRECT_REGISTRATION_ID + registrationId); - return clientRegistrationDao.findByRegistrationId(registrationId); - } - - @Override - public void deleteClientRegistrationsByRegistrationId(String registrationId) { - log.trace("Executing deleteClientRegistrationsByRegistrationId [{}]", registrationId); - validateString(registrationId, INCORRECT_REGISTRATION_ID + registrationId); - clientRegistrationDao.removeByRegistrationId(registrationId); - } - - private Consumer validator = clientRegistration -> { - if (StringUtils.isEmpty(clientRegistration.getRegistrationId())) { - throw new DataValidationException("Registration ID should be specified!"); - } - 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.getTokenUri())) { - throw new DataValidationException("Token uri should be specified!"); - } - if (StringUtils.isEmpty(clientRegistration.getRedirectUriTemplate())) { - throw new DataValidationException("Redirect uri template should be specified!"); - } - if (StringUtils.isEmpty(clientRegistration.getScope())) { - throw new DataValidationException("Scope should be specified!"); - } - if (StringUtils.isEmpty(clientRegistration.getAuthorizationGrantType())) { - throw new DataValidationException("Authorization grant type 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.getJwkSetUri())) { - throw new DataValidationException("Jwk set uri should be specified!"); - } - if (StringUtils.isEmpty(clientRegistration.getClientAuthenticationMethod())) { - throw new DataValidationException("Client authentication method should be specified!"); - } - if (StringUtils.isEmpty(clientRegistration.getClientName())) { - throw new DataValidationException("Client name 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.getBasicConfig(); - 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.CUSTOM) { - OAuth2CustomMapperConfig customConfig = mapperConfig.getCustomConfig(); - 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!"); - } - if (StringUtils.isEmpty(customConfig.getUsername())) { - throw new DataValidationException("Custom mapper username should be specified!"); - } - if (StringUtils.isEmpty(customConfig.getPassword())) { - throw new DataValidationException("Custom mapper password should be specified!"); - } - } - }; -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 3e128a0a52..30fe4d85e6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -15,15 +15,13 @@ */ package org.thingsboard.server.dao.oauth2; -import com.google.common.collect.Sets; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.thingsboard.server.common.data.id.OAuth2IntegrationId; import org.thingsboard.server.common.data.oauth2.*; -import javax.annotation.PostConstruct; -import java.util.*; +import java.util.Collections; +import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -36,24 +34,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Autowired(required = false) OAuth2Configuration oauth2Configuration; - @Autowired - private OAuth2ClientRegistrationService clientRegistrationService; - - @PostConstruct - public void init() { - if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) { - return; - } - Set dbClientRegistration = clientRegistrationService.findClientRegistrations().stream() - .map(OAuth2ClientRegistration::getRegistrationId) - .collect(Collectors.toSet()); - // TODO decide what to do with same registrationIds in DB - Sets.SetView intersection = Sets.intersection(dbClientRegistration, oauth2Configuration.getClients().keySet()); - if (!intersection.isEmpty()) { - throw new RuntimeException("OAuth2 configurations " + intersection + " are already stored in DB."); - } - } - @Override public List getOAuth2Clients() { if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) { @@ -69,17 +49,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { return client; }); - Stream dbConfiguration = clientRegistrationService.findClientRegistrations().stream() - .map(clientRegistration -> { - OAuth2ClientInfo client = new OAuth2ClientInfo(); - client.setName(clientRegistration.getLoginButtonLabel()); - client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getRegistrationId())); - client.setIcon(clientRegistration.getLoginButtonIcon()); - return client; - }); - - return Stream.concat(startUpConfiguration, dbConfiguration) - .collect(Collectors.toList()); + return startUpConfiguration.collect(Collectors.toList()); } @Override @@ -88,9 +58,9 @@ public class OAuth2ServiceImpl implements OAuth2Service { OAuth2Client oAuth2Client = oauth2Configuration.getClients() == null ? null : oauth2Configuration.getClients().get(registrationId); if (oAuth2Client != null){ return toClientRegistration(registrationId, oAuth2Client); + } else { + return null; } - - return clientRegistrationService.findClientRegistrationsByRegistrationId(registrationId); } private OAuth2ClientRegistration toClientRegistration(String registrationId, OAuth2Client oAuth2Client) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java deleted file mode 100644 index 63068d80fb..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java +++ /dev/null @@ -1,77 +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.sql.oauth2; - -import com.datastax.oss.driver.api.core.uuid.Uuids; -import com.google.common.collect.Lists; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; -import org.thingsboard.server.dao.DaoUtil; -import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; -import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationDao; -import org.thingsboard.server.dao.util.SqlDao; - -import java.util.List; -import java.util.Optional; - -@Slf4j -@Component -@SqlDao -@RequiredArgsConstructor -public class JpaOAuth2ClientRegistrationDao implements OAuth2ClientRegistrationDao { - private final OAuth2ClientRegistrationRepository repository; - - @Override - @Transactional - public OAuth2ClientRegistration save(OAuth2ClientRegistration clientRegistration) { - OAuth2ClientRegistrationEntity entity; - try { - entity = new OAuth2ClientRegistrationEntity(clientRegistration); - } catch (Exception e) { - log.error("Can't create entity for domain object {}", clientRegistration, e); - throw new IllegalArgumentException("Can't create entity for domain object {" + clientRegistration + "}", e); - } - log.debug("Saving entity {}", entity); - if (entity.getUuid() == null) { - entity.setUuid(Uuids.timeBased()); - } - entity = repository.save(entity); - return DaoUtil.getData(entity); - } - - @Override - public List find() { - List entities = Lists.newArrayList(repository.findAll()); - return DaoUtil.convertDataList(entities); - } - - @Override - public OAuth2ClientRegistration findByRegistrationId(String registrationId) { - log.debug("Get entity by registration id {}", registrationId); - Optional entity = repository.findByRegistrationId(registrationId); - return DaoUtil.getData(entity); - } - - @Override - public boolean removeByRegistrationId(String registrationId) { - repository.deleteByRegistrationId(registrationId); - log.debug("Remove request: {}", registrationId); - return !repository.existsByRegistrationId(registrationId); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java deleted file mode 100644 index 2e6eb57209..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java +++ /dev/null @@ -1,30 +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.sql.oauth2; - -import org.springframework.data.repository.CrudRepository; -import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; -import org.thingsboard.server.dao.util.SqlDao; - -import java.util.Optional; - -@SqlDao -public interface OAuth2ClientRegistrationRepository extends CrudRepository { - Optional findByRegistrationId(String registrationId); - int deleteByRegistrationId(String registrationId); - boolean existsByRegistrationId(String registrationId); - -} diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 72785c1bb0..1e379afc6f 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -251,38 +251,4 @@ CREATE TABLE IF NOT EXISTS entity_view ( end_ts bigint, search_text varchar(255), additional_info varchar -); - -CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, - registration_id varchar(255), - client_id varchar(255), - client_secret varchar(255), - authorization_uri varchar(255), - token_uri varchar(255), - redirect_uri_template varchar(255), - scope varchar(255), - authorization_grant_type varchar(255), - user_info_uri varchar(255), - user_name_attribute_name varchar(255), - jwk_set_uri varchar(255), - client_authentication_method varchar(255), - client_name 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), - CONSTRAINT oauth2_registration_id_unq_key UNIQUE (registration_id) -); +); \ No newline at end of file diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index e83e4720ca..931856e1df 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -253,40 +253,6 @@ CREATE TABLE IF NOT EXISTS entity_view ( additional_info varchar ); -CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - id varchar(31) NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, - registration_id varchar(255), - client_id varchar(255), - client_secret varchar(255), - authorization_uri varchar(255), - token_uri varchar(255), - redirect_uri_template varchar(255), - scope varchar(255), - authorization_grant_type varchar(255), - user_info_uri varchar(255), - user_name_attribute_name varchar(255), - jwk_set_uri varchar(255), - client_authentication_method varchar(255), - client_name 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), - CONSTRAINT oauth2_registration_id_unq_key UNIQUE (registration_id) -); - CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) LANGUAGE plpgsql AS $$ From 6e7d0c633f6fee6203f18408fea4f0eafd01aa37 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Mon, 22 Jun 2020 16:19:03 +0300 Subject: [PATCH 018/117] Small refactoring --- .../org/thingsboard/server/controller/OAuth2Controller.java | 2 +- .../auth/oauth2/Oauth2AuthenticationSuccessHandler.java | 2 +- .../java/org/thingsboard/server/dao/oauth2/OAuth2Service.java | 2 +- .../server/dao/oauth2/HybridClientRegistrationRepository.java | 2 +- .../org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index ca3d51587b..c0c3034c7b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -39,7 +39,7 @@ public class OAuth2Controller extends BaseController { @ResponseBody public OAuth2ClientRegistration getClientRegistrationById(@PathVariable(REGISTRATION_ID) String registrationId) throws ThingsboardException { try { - return oauth2Service.getClientRegistrationByRegistrationId(registrationId); + return oauth2Service.getClientRegistration(registrationId); } catch (Exception e) { throw handleException(e); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 08ea4dedca..4dda36a0fb 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -64,7 +64,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS try { OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; - OAuth2ClientRegistration clientRegistration = oAuth2Service.getClientRegistrationByRegistrationId(token.getAuthorizedClientRegistrationId()); + OAuth2ClientRegistration clientRegistration = oAuth2Service.getClientRegistration(token.getAuthorizedClientRegistrationId()); OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, clientRegistration.getMapperConfig()); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index efea46a416..49b6c6e98b 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -21,7 +21,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; import java.util.List; public interface OAuth2Service { - OAuth2ClientRegistration getClientRegistrationByRegistrationId(String registrationId); + OAuth2ClientRegistration getClientRegistration(String registrationId); List getOAuth2Clients(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java index e2e38a01e6..f04c739ac1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java @@ -18,7 +18,7 @@ public class HybridClientRegistrationRepository implements ClientRegistrationRep @Override public ClientRegistration findByRegistrationId(String registrationId) { - OAuth2ClientRegistration localClientRegistration = oAuth2Service.getClientRegistrationByRegistrationId(registrationId); + OAuth2ClientRegistration localClientRegistration = oAuth2Service.getClientRegistration(registrationId); return localClientRegistration == null ? null : toSpringClientRegistration(localClientRegistration); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 30fe4d85e6..ebc66b9453 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -53,7 +53,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { } @Override - public OAuth2ClientRegistration getClientRegistrationByRegistrationId(String registrationId) { + public OAuth2ClientRegistration getClientRegistration(String registrationId) { if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) return null; OAuth2Client oAuth2Client = oauth2Configuration.getClients() == null ? null : oauth2Configuration.getClients().get(registrationId); if (oAuth2Client != null){ From a6eefa903e9a386deb289a64a2b86d17cb17b99f Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Mon, 22 Jun 2020 17:37:35 +0300 Subject: [PATCH 019/117] Added required methods to OAuth2Service --- .../server/dao/oauth2/OAuth2Service.java | 31 ++++++++++-- .../server/dao/oauth2/OAuth2ServiceImpl.java | 48 +++++++++++++++++++ 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 49b6c6e98b..1eef5fb948 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -15,6 +15,9 @@ */ package org.thingsboard.server.dao.oauth2; +import org.thingsboard.server.common.data.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; @@ -24,4 +27,24 @@ public interface OAuth2Service { OAuth2ClientRegistration getClientRegistration(String registrationId); List getOAuth2Clients(); + + List getSystemOAuth2ClientRegistrations(TenantId tenantId); + + List getTenantOAuth2ClientRegistrations(TenantId tenantId); + + List getCustomerOAuth2ClientRegistrations(TenantId tenantId, CustomerId customerId); + + OAuth2ClientRegistration saveSystemOAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration); + + OAuth2ClientRegistration saveTenantOAuth2ClientRegistration(TenantId tenantId, OAuth2ClientRegistration clientRegistration); + + OAuth2ClientRegistration saveCustomerOAuth2ClientRegistration(TenantId tenantId, CustomerId customerId, OAuth2ClientRegistration clientRegistration); + + void deleteDomainOAuth2ClientRegistrationByEntityId(TenantId tenantId, EntityId entityId); + + boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId, EntityId entityId); + + boolean isCustomerOAuth2ClientRegistrationAllowed(TenantId tenantId); + + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index ebc66b9453..8ec883d55a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -18,6 +18,9 @@ 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.id.CustomerId; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.*; import java.util.Collections; @@ -52,6 +55,51 @@ public class OAuth2ServiceImpl implements OAuth2Service { return startUpConfiguration.collect(Collectors.toList()); } + @Override + public List getSystemOAuth2ClientRegistrations(TenantId tenantId) { + return null; + } + + @Override + public List getTenantOAuth2ClientRegistrations(TenantId tenantId) { + return null; + } + + @Override + public List getCustomerOAuth2ClientRegistrations(TenantId tenantId, CustomerId customerId) { + return null; + } + + @Override + public OAuth2ClientRegistration saveSystemOAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration) { + return null; + } + + @Override + public OAuth2ClientRegistration saveTenantOAuth2ClientRegistration(TenantId tenantId, OAuth2ClientRegistration clientRegistration) { + return null; + } + + @Override + public OAuth2ClientRegistration saveCustomerOAuth2ClientRegistration(TenantId tenantId, CustomerId customerId, OAuth2ClientRegistration clientRegistration) { + return null; + } + + @Override + public void deleteDomainOAuth2ClientRegistrationByEntityId(TenantId tenantId, EntityId entityId) { + + } + + @Override + public boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId, EntityId entityId) { + return false; + } + + @Override + public boolean isCustomerOAuth2ClientRegistrationAllowed(TenantId tenantId) { + return false; + } + @Override public OAuth2ClientRegistration getClientRegistration(String registrationId) { if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) return null; From ac8cfebe31e91fc8758df03b213bf16af25b156a Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Mon, 22 Jun 2020 18:20:51 +0300 Subject: [PATCH 020/117] Removed .yml oauth2 configs --- .../ThingsboardSecurityConfiguration.java | 2 +- .../src/main/resources/thingsboard.yml | 54 ------------- .../server/dao/oauth2/OAuth2Client.java | 39 ---------- .../dao/oauth2/OAuth2ClientMapperConfig.java | 47 ------------ .../dao/oauth2/OAuth2Configuration.java | 16 ---- .../server/dao/oauth2/OAuth2ServiceImpl.java | 75 +------------------ 6 files changed, 3 insertions(+), 230 deletions(-) delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Client.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientMapperConfig.java diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java index a96843ce2c..fb71bcb994 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java @@ -207,7 +207,7 @@ 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() .loginPage("/oauth2Login") .loginProcessingUrl(oauth2Configuration.getLoginProcessingUrl()) diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index 90b84dfbad..53fc316e56 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -107,62 +107,8 @@ 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:}" # Dashboard parameters dashboard: diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Client.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Client.java deleted file mode 100644 index 9676d55f5f..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Client.java +++ /dev/null @@ -1,39 +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 OAuth2Client { - - private String loginButtonLabel; - private String loginButtonIcon; - private String clientName; - 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 userInfoUri; - private String userNameAttributeName; - private OAuth2ClientMapperConfig mapperConfig; - -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientMapperConfig.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientMapperConfig.java deleted file mode 100644 index f2f23843f2..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientMapperConfig.java +++ /dev/null @@ -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; - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java index 7618e6a776..d78ec66273 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Configuration.java @@ -17,29 +17,13 @@ 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 clients = new HashMap<>(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 8ec883d55a..88167bb019 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -39,20 +39,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public List getOAuth2Clients() { - if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) { - return Collections.emptyList(); - } - - Stream startUpConfiguration = oauth2Configuration.getClients().entrySet().stream() - .map(entry -> { - OAuth2ClientInfo client = new OAuth2ClientInfo(); - client.setName(entry.getValue().getLoginButtonLabel()); - client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, entry.getKey())); - client.setIcon(entry.getValue().getLoginButtonIcon()); - return client; - }); - - return startUpConfiguration.collect(Collectors.toList()); + return Collections.emptyList(); } @Override @@ -102,64 +89,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public OAuth2ClientRegistration getClientRegistration(String registrationId) { - if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) return null; - OAuth2Client oAuth2Client = oauth2Configuration.getClients() == null ? null : oauth2Configuration.getClients().get(registrationId); - if (oAuth2Client != null){ - return toClientRegistration(registrationId, oAuth2Client); - } else { - return null; - } - } - - private OAuth2ClientRegistration toClientRegistration(String registrationId, OAuth2Client oAuth2Client) { - OAuth2ClientMapperConfig mapperConfig = oAuth2Client.getMapperConfig(); - OAuth2ClientMapperConfig.BasicOAuth2ClientMapperConfig basicConfig = mapperConfig.getBasic(); - OAuth2ClientMapperConfig.CustomOAuth2ClientMapperConfig customConfig = mapperConfig.getCustom(); - - return OAuth2ClientRegistration.builder() - .registrationId(registrationId) - .mapperConfig(OAuth2MapperConfig.builder() - .allowUserCreation(mapperConfig.isAllowUserCreation()) - .activateUser(mapperConfig.isActivateUser()) - .type(MapperType.valueOf( - mapperConfig.getType().toUpperCase() - )) - .basicConfig( - OAuth2BasicMapperConfig.builder() - .emailAttributeKey(basicConfig.getEmailAttributeKey()) - .firstNameAttributeKey(basicConfig.getFirstNameAttributeKey()) - .lastNameAttributeKey(basicConfig.getLastNameAttributeKey()) - .tenantNameStrategy(TenantNameStrategyType.valueOf( - basicConfig.getTenantNameStrategy().toUpperCase() - )) - .tenantNamePattern(basicConfig.getTenantNamePattern()) - .customerNamePattern(basicConfig.getCustomerNamePattern()) - .defaultDashboardName(basicConfig.getDefaultDashboardName()) - .alwaysFullScreen(basicConfig.isAlwaysFullScreen()) - .build() - ) - .customConfig( - OAuth2CustomMapperConfig.builder() - .url(customConfig.getUrl()) - .username(customConfig.getUsername()) - .password(customConfig.getPassword()) - .build() - ) - .build()) - .clientId(oAuth2Client.getClientId()) - .clientSecret(oAuth2Client.getClientSecret()) - .authorizationUri(oAuth2Client.getAuthorizationUri()) - .tokenUri(oAuth2Client.getAccessTokenUri()) - .redirectUriTemplate(oAuth2Client.getRedirectUriTemplate()) - .scope(oAuth2Client.getScope()) - .authorizationGrantType(oAuth2Client.getAuthorizationGrantType()) - .userInfoUri(oAuth2Client.getUserInfoUri()) - .userNameAttributeName(oAuth2Client.getUserNameAttributeName()) - .jwkSetUri(oAuth2Client.getJwkSetUri()) - .clientAuthenticationMethod(oAuth2Client.getClientAuthenticationMethod()) - .clientName(oAuth2Client.getClientName()) - .loginButtonLabel(oAuth2Client.getLoginButtonLabel()) - .loginButtonIcon(oAuth2Client.getLoginButtonIcon()) - .build(); + return null; } } From b20b032f0b1f61345b19ff132b78e18ffedb5d86 Mon Sep 17 00:00:00 2001 From: viktor Date: Tue, 23 Jun 2020 11:05:46 +0300 Subject: [PATCH 021/117] Added saveSystemOAuth2ClientRegistration method --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 88167bb019..18a390e783 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -15,13 +15,20 @@ */ package org.thingsboard.server.dao.oauth2; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.dao.exception.IncorrectParameterException; +import org.thingsboard.server.dao.settings.AdminSettingsService; import java.util.Collections; import java.util.List; @@ -32,10 +39,14 @@ import java.util.stream.Stream; @Service public class OAuth2ServiceImpl implements OAuth2Service { + private static final ObjectMapper mapper = new ObjectMapper(); + + private static final String OAUTH2_CLIENT_REGISTRATIONS_PARAMS = "oauth2ClientRegistrationsParams"; + private static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; - @Autowired(required = false) - OAuth2Configuration oauth2Configuration; + @Autowired + private AdminSettingsService adminSettingsService; @Override public List getOAuth2Clients() { @@ -59,11 +70,31 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public OAuth2ClientRegistration saveSystemOAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration) { - return null; + // TODO check by registration ID in entities + AdminSettings clientRegistrationParamsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + if (clientRegistrationParamsSettings == null) { + clientRegistrationParamsSettings = new AdminSettings(); + clientRegistrationParamsSettings.setKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + ObjectNode node = mapper.createObjectNode(); + clientRegistrationParamsSettings.setJsonValue(node); + } + String json; + try { + json = mapper.writeValueAsString(clientRegistration); + } catch (JsonProcessingException e) { + log.error("Unable to convert OAuth2 Client Registration Params to JSON!", e); + throw new IncorrectParameterException("Unable to convert OAuth2 Client Registration Params to JSON!"); + } + ObjectNode oldClientRegistrations = (ObjectNode) clientRegistrationParamsSettings.getJsonValue(); + oldClientRegistrations.put(clientRegistration.getRegistrationId(), json); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, clientRegistrationParamsSettings); + // TODO ask if that's worth it + return getClientRegistration(clientRegistration.getRegistrationId()); } @Override public OAuth2ClientRegistration saveTenantOAuth2ClientRegistration(TenantId tenantId, OAuth2ClientRegistration clientRegistration) { + // TODO check by registration ID in system return null; } From 334361577f84fdf5a29ccf846fbfcff10684d953 Mon Sep 17 00:00:00 2001 From: viktor Date: Tue, 23 Jun 2020 12:39:30 +0300 Subject: [PATCH 022/117] Changed OAuth2Service, clear Tenant oauth2 config on deletion --- .../server/controller/AuthController.java | 5 ++- .../server/dao/oauth2/OAuth2Service.java | 15 ++----- .../data/oauth2/OAuth2ClientRegistration.java | 36 +++------------- .../data/oauth2/OAuth2ClientsParams.java | 18 ++++++++ .../server/dao/oauth2/OAuth2ServiceImpl.java | 41 +++++++++---------- .../server/dao/tenant/TenantServiceImpl.java | 5 +++ 6 files changed, 54 insertions(+), 66 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java index 9f6c6f3b44..2cf9e5d86b 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java @@ -337,11 +337,12 @@ public class AuthController extends BaseController { } } + // TODO ask why POST @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) @ResponseBody - public List getOAuth2Clients() throws ThingsboardException { + public List getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { try { - return oauth2Service.getOAuth2Clients(); + return oauth2Service.getOAuth2Clients(request.getServerName()); } catch (Exception e) { throw handleException(e); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 1eef5fb948..81a1abbc47 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -26,25 +26,18 @@ import java.util.List; public interface OAuth2Service { OAuth2ClientRegistration getClientRegistration(String registrationId); - List getOAuth2Clients(); + List getOAuth2Clients(String domainName); List getSystemOAuth2ClientRegistrations(TenantId tenantId); List getTenantOAuth2ClientRegistrations(TenantId tenantId); - List getCustomerOAuth2ClientRegistrations(TenantId tenantId, CustomerId customerId); - OAuth2ClientRegistration saveSystemOAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration); - OAuth2ClientRegistration saveTenantOAuth2ClientRegistration(TenantId tenantId, OAuth2ClientRegistration clientRegistration); - - OAuth2ClientRegistration saveCustomerOAuth2ClientRegistration(TenantId tenantId, CustomerId customerId, OAuth2ClientRegistration clientRegistration); - - void deleteDomainOAuth2ClientRegistrationByEntityId(TenantId tenantId, EntityId entityId); - - boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId, EntityId entityId); + OAuth2ClientRegistration saveTenantOAuth2ClientRegistration(TenantId tenantId, String domainName, OAuth2ClientRegistration clientRegistration); - boolean isCustomerOAuth2ClientRegistrationAllowed(TenantId tenantId); + void deleteDomainOAuth2ClientRegistrationByTenant(TenantId tenantId); + boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java index 84c95df84c..a9fe5ed060 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -4,10 +4,13 @@ import lombok.*; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.id.OAuth2IntegrationId; -@EqualsAndHashCode(callSuper = true) +@EqualsAndHashCode @Data @ToString(exclude = {"clientSecret"}) -public class OAuth2ClientRegistration extends BaseData { +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2ClientRegistration { private String registrationId; private OAuth2MapperConfig mapperConfig; @@ -25,33 +28,4 @@ public class OAuth2ClientRegistration extends BaseData { private String clientName; private String loginButtonLabel; private String loginButtonIcon; - - public OAuth2ClientRegistration() { - super(); - } - - public OAuth2ClientRegistration(OAuth2IntegrationId id) { - super(id); - } - - @Builder(toBuilder = true) - public OAuth2ClientRegistration(OAuth2IntegrationId id, String registrationId, String clientId, String clientSecret, String authorizationUri, String tokenUri, String redirectUriTemplate, String scope, String authorizationGrantType, String userInfoUri, String userNameAttributeName, String jwkSetUri, String clientAuthenticationMethod, String clientName, String loginButtonLabel, String loginButtonIcon, OAuth2MapperConfig mapperConfig) { - super(id); - this.registrationId = registrationId; - this.clientId = clientId; - this.clientSecret = clientSecret; - this.authorizationUri = authorizationUri; - this.tokenUri = tokenUri; - this.redirectUriTemplate = redirectUriTemplate; - this.scope = scope; - this.authorizationGrantType = authorizationGrantType; - this.userInfoUri = userInfoUri; - this.userNameAttributeName = userNameAttributeName; - this.jwkSetUri = jwkSetUri; - this.clientAuthenticationMethod = clientAuthenticationMethod; - this.clientName = clientName; - this.loginButtonLabel = loginButtonLabel; - this.loginButtonIcon = loginButtonIcon; - this.mapperConfig = mapperConfig; - } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java new file mode 100644 index 0000000000..1adcd51f3b --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java @@ -0,0 +1,18 @@ +package org.thingsboard.server.common.data.oauth2; + +import lombok.*; + +import java.util.List; + +@EqualsAndHashCode +@Data +@ToString +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2ClientsParams { + private String domainName; + private String adminSettingsId; + + private List clientRegistrations; +} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 18a390e783..6ce4ae1599 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -17,12 +17,13 @@ package org.thingsboard.server.dao.oauth2; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.id.AdminSettingsId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -32,8 +33,7 @@ import org.thingsboard.server.dao.settings.AdminSettingsService; import java.util.Collections; import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import java.util.UUID; @Slf4j @Service @@ -42,6 +42,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { private static final ObjectMapper mapper = new ObjectMapper(); private static final String OAUTH2_CLIENT_REGISTRATIONS_PARAMS = "oauth2ClientRegistrationsParams"; + private static final String OAUTH2_CLIENT_REGISTRATIONS_DOMAIN_NAME_PREFIX = "oauth2ClientRegistrationsDomainNamePrefix"; private static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; @@ -49,7 +50,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { private AdminSettingsService adminSettingsService; @Override - public List getOAuth2Clients() { + public List getOAuth2Clients(String domainName) { return Collections.emptyList(); } @@ -63,11 +64,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { return null; } - @Override - public List getCustomerOAuth2ClientRegistrations(TenantId tenantId, CustomerId customerId) { - return null; - } - @Override public OAuth2ClientRegistration saveSystemOAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration) { // TODO check by registration ID in entities @@ -93,28 +89,19 @@ public class OAuth2ServiceImpl implements OAuth2Service { } @Override - public OAuth2ClientRegistration saveTenantOAuth2ClientRegistration(TenantId tenantId, OAuth2ClientRegistration clientRegistration) { + public OAuth2ClientRegistration saveTenantOAuth2ClientRegistration(TenantId tenantId, String domainName, OAuth2ClientRegistration clientRegistration) { + // TODO ask what if tenant saves config for several different domain names, do we need to check it // TODO check by registration ID in system return null; } @Override - public OAuth2ClientRegistration saveCustomerOAuth2ClientRegistration(TenantId tenantId, CustomerId customerId, OAuth2ClientRegistration clientRegistration) { - return null; - } - - @Override - public void deleteDomainOAuth2ClientRegistrationByEntityId(TenantId tenantId, EntityId entityId) { + public void deleteDomainOAuth2ClientRegistrationByTenant(TenantId tenantId) { } @Override - public boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId, EntityId entityId) { - return false; - } - - @Override - public boolean isCustomerOAuth2ClientRegistrationAllowed(TenantId tenantId) { + public boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId) { return false; } @@ -122,4 +109,14 @@ public class OAuth2ServiceImpl implements OAuth2Service { public OAuth2ClientRegistration getClientRegistration(String registrationId) { return null; } + + private String constructClientRegistrationsKey(String domainName) { + String clientRegistrationsKey; + if (StringUtils.isEmpty(domainName)) { + clientRegistrationsKey = OAUTH2_CLIENT_REGISTRATIONS_PARAMS; + } else { + clientRegistrationsKey = OAUTH2_CLIENT_REGISTRATIONS_DOMAIN_NAME_PREFIX + "_" + domainName; + } + return clientRegistrationsKey; + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 33407db373..a6cfdee8c2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -33,6 +33,7 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -78,6 +79,9 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe @Autowired private RuleChainService ruleChainService; + @Autowired + private OAuth2Service oAuth2Service; + @Override public Tenant findTenantById(TenantId tenantId) { log.trace("Executing findTenantById [{}]", tenantId); @@ -104,6 +108,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe public void deleteTenant(TenantId tenantId) { log.trace("Executing deleteTenant [{}]", tenantId); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + oAuth2Service.deleteDomainOAuth2ClientRegistrationByTenant(tenantId); customerService.deleteCustomersByTenantId(tenantId); widgetsBundleService.deleteWidgetsBundlesByTenantId(tenantId); dashboardService.deleteDashboardsByTenantId(tenantId); From c3c889bbbf843c7fe18d10f28ff6f307475fd2eb Mon Sep 17 00:00:00 2001 From: viktor Date: Tue, 23 Jun 2020 14:37:56 +0300 Subject: [PATCH 023/117] Added 'deleteAdminSettingByKey' method to AdminSettingsService --- .../server/dao/settings/AdminSettingsService.java | 4 +++- .../server/dao/settings/AdminSettingsServiceImpl.java | 9 +++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java index ee263c2564..58b25f2dee 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java @@ -24,7 +24,9 @@ public interface AdminSettingsService { AdminSettings findAdminSettingsById(TenantId tenantId, AdminSettingsId adminSettingsId); AdminSettings findAdminSettingsByKey(TenantId tenantId, String key); - + + void deleteAdminSettingsByKey(TenantId tenantId, String key); + AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java index 6c7495cc19..5e5e5e2751 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java @@ -48,6 +48,15 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { return adminSettingsDao.findByKey(tenantId, key); } + @Override + public void deleteAdminSettingsByKey(TenantId tenantId, String key) { + log.trace("Executing deleteAdminSettingsByKey [{}]", key); + AdminSettings adminSettings = findAdminSettingsByKey(tenantId, key); + if (adminSettings != null) { + adminSettingsDao.removeById(tenantId, adminSettings.getId().getId()); + } + } + @Override public AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings) { log.trace("Executing saveAdminSettings [{}]", adminSettings); From 5a00973b47eb2d807a014d8fd0f279e6e37814a2 Mon Sep 17 00:00:00 2001 From: viktor Date: Tue, 23 Jun 2020 14:38:50 +0300 Subject: [PATCH 024/117] Implemented part of OAuth2Service --- .../server/dao/oauth2/OAuth2Service.java | 9 +- .../server/dao/oauth2/OAuth2ServiceImpl.java | 292 ++++++++++++++++-- 2 files changed, 267 insertions(+), 34 deletions(-) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 81a1abbc47..da289bfd54 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -20,6 +20,7 @@ import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; import java.util.List; @@ -28,13 +29,13 @@ public interface OAuth2Service { List getOAuth2Clients(String domainName); - List getSystemOAuth2ClientRegistrations(TenantId tenantId); + OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams); - List getTenantOAuth2ClientRegistrations(TenantId tenantId); + OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams); - OAuth2ClientRegistration saveSystemOAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration); + OAuth2ClientsParams getSystemOAuth2ClientsParams(TenantId tenantId); - OAuth2ClientRegistration saveTenantOAuth2ClientRegistration(TenantId tenantId, String domainName, OAuth2ClientRegistration clientRegistration); + OAuth2ClientsParams getTenantOAuth2ClientsParams(TenantId tenantId); void deleteDomainOAuth2ClientRegistrationByTenant(TenantId tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 6ce4ae1599..3dd3c7d42a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -16,24 +16,35 @@ package org.thingsboard.server.dao.oauth2; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.AdminSettings; +import org.thingsboard.server.common.data.*; import org.thingsboard.server.common.data.id.AdminSettingsId; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.settings.AdminSettingsService; +import org.thingsboard.server.dao.tenant.TenantService; -import java.util.Collections; -import java.util.List; -import java.util.UUID; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; @Slf4j @Service @@ -44,65 +55,158 @@ public class OAuth2ServiceImpl implements OAuth2Service { private static final String OAUTH2_CLIENT_REGISTRATIONS_PARAMS = "oauth2ClientRegistrationsParams"; private static final String OAUTH2_CLIENT_REGISTRATIONS_DOMAIN_NAME_PREFIX = "oauth2ClientRegistrationsDomainNamePrefix"; + private static final String ALLOW_OAUTH2_CONFIGURATION = "allowOAuth2Configuration"; + + + private static final String SYSTEM_SETTINGS_OAUTH2_VALUE = "value"; + private static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; @Autowired private AdminSettingsService adminSettingsService; - @Override - public List getOAuth2Clients(String domainName) { - return Collections.emptyList(); - } + @Autowired + private AttributesService attributesService; - @Override - public List getSystemOAuth2ClientRegistrations(TenantId tenantId) { - return null; - } + @Autowired + private TenantService tenantService; @Override - public List getTenantOAuth2ClientRegistrations(TenantId tenantId) { - return null; + public List getOAuth2Clients(String domainName) { + return Collections.emptyList(); } @Override - public OAuth2ClientRegistration saveSystemOAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration) { + public OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) { // TODO check by registration ID in entities - AdminSettings clientRegistrationParamsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); - if (clientRegistrationParamsSettings == null) { - clientRegistrationParamsSettings = new AdminSettings(); - clientRegistrationParamsSettings.setKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); - ObjectNode node = mapper.createObjectNode(); - clientRegistrationParamsSettings.setJsonValue(node); + for (OAuth2ClientRegistration clientRegistration : oAuth2ClientsParams.getClientRegistrations()) { + validator.accept(clientRegistration); } + AdminSettings clientRegistrationParamsSettings = new AdminSettings(); + clientRegistrationParamsSettings.setKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + ObjectNode clientRegistrationsNode = mapper.createObjectNode(); + + oAuth2ClientsParams.setDomainName(""); String json; try { - json = mapper.writeValueAsString(clientRegistration); + json = mapper.writeValueAsString(oAuth2ClientsParams); } catch (JsonProcessingException e) { log.error("Unable to convert OAuth2 Client Registration Params to JSON!", e); throw new IncorrectParameterException("Unable to convert OAuth2 Client Registration Params to JSON!"); } - ObjectNode oldClientRegistrations = (ObjectNode) clientRegistrationParamsSettings.getJsonValue(); - oldClientRegistrations.put(clientRegistration.getRegistrationId(), json); + clientRegistrationsNode.put(SYSTEM_SETTINGS_OAUTH2_VALUE, json); + + clientRegistrationParamsSettings.setJsonValue(clientRegistrationsNode); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, clientRegistrationParamsSettings); - // TODO ask if that's worth it - return getClientRegistration(clientRegistration.getRegistrationId()); + + return getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); } @Override - public OAuth2ClientRegistration saveTenantOAuth2ClientRegistration(TenantId tenantId, String domainName, OAuth2ClientRegistration clientRegistration) { + public OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) { // TODO ask what if tenant saves config for several different domain names, do we need to check it // TODO check by registration ID in system - return null; + for (OAuth2ClientRegistration clientRegistration : oAuth2ClientsParams.getClientRegistrations()) { + validator.accept(clientRegistration); + } + String clientRegistrationsKey = constructClientRegistrationsKey(oAuth2ClientsParams.getDomainName()); + AdminSettings existentAdminSettingsByKey = adminSettingsService.findAdminSettingsByKey(tenantId, clientRegistrationsKey); + if (StringUtils.isEmpty(oAuth2ClientsParams.getAdminSettingsId())) { + if (existentAdminSettingsByKey == null) { + existentAdminSettingsByKey = saveOAuth2ClientSettings(tenantId, clientRegistrationsKey); + oAuth2ClientsParams.setAdminSettingsId(existentAdminSettingsByKey.getId().getId().toString()); + } else { + log.error("Current domain name [{}] already registered in the system!", oAuth2ClientsParams.getDomainName()); + throw new IncorrectParameterException("Current domain name [" + oAuth2ClientsParams.getDomainName() + "] already registered in the system!"); + } + } else { + AdminSettings existentOAuth2ClientsSettingsById = adminSettingsService.findAdminSettingsById( + tenantId, + new AdminSettingsId(UUID.fromString(oAuth2ClientsParams.getAdminSettingsId())) + ); + + if (existentOAuth2ClientsSettingsById == null) { + log.error("Admin setting ID is already set in login white labeling object, but doesn't exist in the database"); + throw new IllegalStateException("Admin setting ID is already set in login white labeling object, but doesn't exist in the database"); + } + + if (!existentOAuth2ClientsSettingsById.getKey().equals(clientRegistrationsKey)) { + if (existentAdminSettingsByKey == null) { + adminSettingsService.deleteAdminSettingsByKey(tenantId, existentOAuth2ClientsSettingsById.getKey()); + AdminSettings newOAuth2ClientsSettings = saveOAuth2ClientSettings(tenantId, clientRegistrationsKey); + oAuth2ClientsParams.setAdminSettingsId(newOAuth2ClientsSettings.getId().getId().toString()); + } else { + log.error("Current domain name [{}] already registered in the system!", oAuth2ClientsParams.getDomainName()); + throw new IncorrectParameterException("Current domain name [" + oAuth2ClientsParams.getDomainName() + "] already registered in the system!"); + } + } + } + String json; + try { + json = mapper.writeValueAsString(oAuth2ClientsParams); + } catch (JsonProcessingException e) { + log.error("Unable to convert OAuth2 Client Registration Params to JSON!", e); + throw new IncorrectParameterException("Unable to convert OAuth2 Client Registration Params to JSON!"); + } + List attributes = new ArrayList<>(); + long ts = System.currentTimeMillis(); + attributes.add(new BaseAttributeKvEntry(new StringDataEntry(OAUTH2_CLIENT_REGISTRATIONS_PARAMS, json), ts)); + try { + // TODO ask if I need here .get() + attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, attributes).get(); + } catch (Exception e) { + log.error("Unable to save OAuth2 Client Registration Params to attributes!", e); + throw new IncorrectParameterException("Unable to save OAuth2 Client Registration Params to attributes!"); + } + return getTenantOAuth2ClientsParams(tenantId); } @Override - public void deleteDomainOAuth2ClientRegistrationByTenant(TenantId tenantId) { + public OAuth2ClientsParams getSystemOAuth2ClientsParams(TenantId tenantId) { + AdminSettings oauth2ClientsParamsSettings = adminSettingsService.findAdminSettingsByKey(tenantId, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + String json = null; + if (oauth2ClientsParamsSettings != null) { + json = oauth2ClientsParamsSettings.getJsonValue().get(SYSTEM_SETTINGS_OAUTH2_VALUE).asText(); + } + return constructOAuth2ClientsParams(json); + } + @Override + public OAuth2ClientsParams getTenantOAuth2ClientsParams(TenantId tenantId) { + ListenableFuture jsonFuture; + if (isOAuth2ClientRegistrationAllowed(tenantId)) { + jsonFuture = getOAuth2ClientsParamsAttribute(tenantId); + } else { + jsonFuture = Futures.immediateFuture(""); + } + try { + return Futures.transform(jsonFuture, this::constructOAuth2ClientsParams, MoreExecutors.directExecutor()).get(); + } catch (InterruptedException | ExecutionException e) { + log.error("Failed to read OAuth2 Clients Params from attributes!", e); + throw new RuntimeException("Failed to read OAuth2 Clients Params from attributes!", e); + } + } + + @Override + public void deleteDomainOAuth2ClientRegistrationByTenant(TenantId tenantId) { + OAuth2ClientsParams params = getTenantOAuth2ClientsParams(tenantId); + if (!StringUtils.isEmpty(params.getDomainName())) { + // TODO don't we need to delete from attributes? + String oauth2ClientsParamsKey = constructClientRegistrationsKey(params.getDomainName()); + adminSettingsService.deleteAdminSettingsByKey(tenantId, oauth2ClientsParamsKey); + } } @Override public boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId) { - return false; + Tenant tenant = tenantService.findTenantById(tenantId); + JsonNode allowOAuth2ConfigurationJsonNode = tenant.getAdditionalInfo() != null ? tenant.getAdditionalInfo().get(ALLOW_OAUTH2_CONFIGURATION) : null; + if (allowOAuth2ConfigurationJsonNode == null) { + return true; + } else { + return allowOAuth2ConfigurationJsonNode.asBoolean(); + } } @Override @@ -110,6 +214,35 @@ public class OAuth2ServiceImpl implements OAuth2Service { return null; } + private ListenableFuture getOAuth2ClientsParamsAttribute(TenantId tenantId) { + ListenableFuture> attributeKvEntriesFuture; + try { + attributeKvEntriesFuture = attributesService.find(tenantId, tenantId, DataConstants.SERVER_SCOPE, + Collections.singletonList(OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + } catch (Exception e) { + log.error("Unable to read OAuth2 Clients Params from attributes!", e); + throw new IncorrectParameterException("Unable to read OAuth2 Clients Params from attributes!"); + } + return Futures.transform(attributeKvEntriesFuture, attributeKvEntries -> { + if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { + AttributeKvEntry kvEntry = attributeKvEntries.get(0); + return kvEntry.getValueAsString(); + } else { + return ""; + } + }, MoreExecutors.directExecutor()); + } + + private AdminSettings saveOAuth2ClientSettings(TenantId tenantId, String clientRegistrationsKey) { + AdminSettings oauth2ClientsSettings = new AdminSettings(); + oauth2ClientsSettings.setKey(clientRegistrationsKey); + ObjectNode node = mapper.createObjectNode(); + node.put("entityType", EntityType.TENANT.name()); + node.put("entityId", tenantId.toString()); + oauth2ClientsSettings.setJsonValue(node); + return adminSettingsService.saveAdminSettings(tenantId, oauth2ClientsSettings); + } + private String constructClientRegistrationsKey(String domainName) { String clientRegistrationsKey; if (StringUtils.isEmpty(domainName)) { @@ -119,4 +252,103 @@ public class OAuth2ServiceImpl implements OAuth2Service { } return clientRegistrationsKey; } + + private OAuth2ClientsParams constructOAuth2ClientsParams(String json) { + OAuth2ClientsParams result = null; + if (!StringUtils.isEmpty(json)) { + try { + result = mapper.readValue(json, OAuth2ClientsParams.class); + } catch (IOException e) { + log.error("Unable to read OAuth2 Clients Params from JSON!", e); + throw new IncorrectParameterException("Unable to read OAuth2 Clients Params from JSON!"); + } + } + if (result == null) { + result = new OAuth2ClientsParams(); + } + return result; + } + + private final Consumer validator = clientRegistration -> { + if (StringUtils.isEmpty(clientRegistration.getRegistrationId())) { + throw new DataValidationException("Registration ID should be specified!"); + } + 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.getTokenUri())) { + throw new DataValidationException("Token uri should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getRedirectUriTemplate())) { + throw new DataValidationException("Redirect uri template should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getScope())) { + throw new DataValidationException("Scope should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getAuthorizationGrantType())) { + throw new DataValidationException("Authorization grant type 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.getJwkSetUri())) { + throw new DataValidationException("Jwk set uri should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getClientAuthenticationMethod())) { + throw new DataValidationException("Client authentication method should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getClientName())) { + throw new DataValidationException("Client name 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.getBasicConfig(); + 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.CUSTOM) { + OAuth2CustomMapperConfig customConfig = mapperConfig.getCustomConfig(); + 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!"); + } + if (StringUtils.isEmpty(customConfig.getUsername())) { + throw new DataValidationException("Custom mapper username should be specified!"); + } + if (StringUtils.isEmpty(customConfig.getPassword())) { + throw new DataValidationException("Custom mapper password should be specified!"); + } + } + }; } From 6a2bc5a9ffcd0a309b87d2bfbd6db48885b3846e Mon Sep 17 00:00:00 2001 From: viktor Date: Tue, 23 Jun 2020 15:03:24 +0300 Subject: [PATCH 025/117] Implemented 'getOAuth2Clients' method --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 59 +++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 3dd3c7d42a..e9d3b76395 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -19,18 +19,17 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.ListUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.*; -import org.thingsboard.server.common.data.id.AdminSettingsId; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; import org.thingsboard.server.common.data.kv.StringDataEntry; @@ -41,10 +40,13 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.tenant.TenantService; +import javax.annotation.PostConstruct; import java.io.IOException; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; +import java.util.stream.Collectors; @Slf4j @Service @@ -71,9 +73,30 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Autowired private TenantService tenantService; + private final Map clientRegistrationsByRegistrationId = new ConcurrentHashMap<>(); + + @PostConstruct + public void init(){ + + } + + @Override + public OAuth2ClientRegistration getClientRegistration(String registrationId) { + return null; + } + @Override public List getOAuth2Clients(String domainName) { - return Collections.emptyList(); + OAuth2ClientsParams oAuth2ClientsParams = getMergedOAuth2ClientsParams(domainName); + return oAuth2ClientsParams.getClientRegistrations().stream() + .map(clientRegistration -> { + OAuth2ClientInfo client = new OAuth2ClientInfo(); + client.setName(clientRegistration.getLoginButtonLabel()); + client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getRegistrationId())); + client.setIcon(clientRegistration.getLoginButtonIcon()); + return client; + }) + .collect(Collectors.toList()); } @Override @@ -209,11 +232,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { } } - @Override - public OAuth2ClientRegistration getClientRegistration(String registrationId) { - return null; - } - private ListenableFuture getOAuth2ClientsParamsAttribute(TenantId tenantId) { ListenableFuture> attributeKvEntriesFuture; try { @@ -269,6 +287,27 @@ public class OAuth2ServiceImpl implements OAuth2Service { return result; } + private OAuth2ClientsParams getMergedOAuth2ClientsParams(String domainName) { + AdminSettings oauth2ClientsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, constructClientRegistrationsKey(domainName)); + OAuth2ClientsParams result; + if (oauth2ClientsSettings != null) { + String strEntityType = oauth2ClientsSettings.getJsonValue().get("entityType").asText(); + String strEntityId = oauth2ClientsSettings.getJsonValue().get("entityId").asText(); + EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); + if (!entityId.getEntityType().equals(EntityType.TENANT)) { + log.error("Only tenant can configure OAuth2 for certain domain!"); + throw new IllegalStateException("Only tenant can configure OAuth2 for certain domain!"); + } + TenantId tenantId = (TenantId) entityId; + result = getTenantOAuth2ClientsParams(tenantId); + OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + result.getClientRegistrations().addAll(systemOAuth2ClientsParams.getClientRegistrations()); + } else { + result = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + } + return result; + } + private final Consumer validator = clientRegistration -> { if (StringUtils.isEmpty(clientRegistration.getRegistrationId())) { throw new DataValidationException("Registration ID should be specified!"); From 79cd0c273eda51029749e9f7783cd03763a3a068 Mon Sep 17 00:00:00 2001 From: viktor Date: Tue, 23 Jun 2020 15:39:19 +0300 Subject: [PATCH 026/117] Implemented OAuth2Controller --- .../server/controller/AuthController.java | 14 ---- .../server/controller/OAuth2Controller.java | 73 ++++++++++++++++++- .../service/security/permission/Resource.java | 4 +- .../server/dao/oauth2/OAuth2ServiceImpl.java | 7 +- 4 files changed, 77 insertions(+), 21 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java index 2cf9e5d86b..adffcba3d6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java +++ b/application/src/main/java/org/thingsboard/server/controller/AuthController.java @@ -84,9 +84,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,15 +333,4 @@ public class AuthController extends BaseController { throw handleException(e); } } - - // TODO ask why POST - @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) - @ResponseBody - public List getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { - try { - return oauth2Service.getOAuth2Clients(request.getServerName()); - } catch (Exception e) { - throw handleException(e); - } - } } diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index c0c3034c7b..31bc3e9fff 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -17,12 +17,23 @@ package org.thingsboard.server.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; +import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.queue.util.TbCoreComponent; +import org.thingsboard.server.service.security.permission.Operation; +import org.thingsboard.server.service.security.permission.Resource; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; @RestController @TbCoreComponent @@ -34,14 +45,68 @@ public class OAuth2Controller extends BaseController { @Autowired private OAuth2Service oauth2Service; - @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") - @RequestMapping(value = "/oauth2/config/{" + REGISTRATION_ID + "}", method = RequestMethod.GET) + // TODO ask why POST + @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) + @ResponseBody + public List getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { + try { + return oauth2Service.getOAuth2Clients(request.getServerName()); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/oauth2/currentOAuth2Configuration", method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public OAuth2ClientsParams getCurrentOAuth2ClientsParams() throws ThingsboardException { + try { + Authority authority = getCurrentUser().getAuthority(); + checkOAuth2ConfigPermissions(Operation.READ); + OAuth2ClientsParams oAuth2ClientsParams = null; + if (Authority.SYS_ADMIN.equals(authority)) { + oAuth2ClientsParams = oauth2Service.getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + } else if (Authority.TENANT_ADMIN.equals(authority)) { + oAuth2ClientsParams = oauth2Service.getTenantOAuth2ClientsParams(getCurrentUser().getTenantId()); + } + return oAuth2ClientsParams; + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/oauth2/oAuth2Configuration", method = RequestMethod.POST) + @ResponseStatus(value = HttpStatus.OK) + public OAuth2ClientsParams saveLoginWhiteLabelParams(@RequestBody OAuth2ClientsParams oAuth2ClientsParams) throws ThingsboardException { + try { + Authority authority = getCurrentUser().getAuthority(); + checkOAuth2ConfigPermissions(Operation.WRITE); + OAuth2ClientsParams savedOAuth2ClientsParams = null; + if (Authority.SYS_ADMIN.equals(authority)) { + savedOAuth2ClientsParams = oauth2Service.saveSystemOAuth2ClientsParams(oAuth2ClientsParams); + } else if (Authority.TENANT_ADMIN.equals(authority)) { + savedOAuth2ClientsParams = oauth2Service.saveTenantOAuth2ClientsParams(getCurrentUser().getTenantId(), oAuth2ClientsParams); + } + return savedOAuth2ClientsParams; + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") + @RequestMapping(value = "/oauth2/isOAuth2ConfigurationAllowed", method = RequestMethod.GET) @ResponseBody - public OAuth2ClientRegistration getClientRegistrationById(@PathVariable(REGISTRATION_ID) String registrationId) throws ThingsboardException { + public Boolean isOAuth2ConfigurationAllowed() throws ThingsboardException { try { - return oauth2Service.getClientRegistration(registrationId); + return oauth2Service.isOAuth2ClientRegistrationAllowed(getTenantId()); } catch (Exception e) { throw handleException(e); } } + + + private void checkOAuth2ConfigPermissions(Operation operation) throws ThingsboardException { + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION, operation); + } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index a66b822fca..dfdea59e05 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -31,7 +31,9 @@ public enum Resource { RULE_CHAIN(EntityType.RULE_CHAIN), USER(EntityType.USER), WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE), - WIDGET_TYPE(EntityType.WIDGET_TYPE); + WIDGET_TYPE(EntityType.WIDGET_TYPE), + OAUTH2_CONFIGURATION(), + ; private final EntityType entityType; diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index e9d3b76395..da783ea297 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -75,14 +75,17 @@ public class OAuth2ServiceImpl implements OAuth2Service { private final Map clientRegistrationsByRegistrationId = new ConcurrentHashMap<>(); + + // TODO add field that invalidates cache in case write to cache fails after successful saving in DB @PostConstruct public void init(){ - + OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + // TODO get all attributes with key OAUTH2_CLIENT_REGISTRATIONS_PARAMS and put into the map } @Override public OAuth2ClientRegistration getClientRegistration(String registrationId) { - return null; + return clientRegistrationsByRegistrationId.get(registrationId); } @Override From 1d56a1937bf971a1bc5f0c1928b9fafa74e5bd85 Mon Sep 17 00:00:00 2001 From: viktor Date: Tue, 23 Jun 2020 16:21:24 +0300 Subject: [PATCH 027/117] Added license headers to new classes --- .../server/controller/OAuth2Controller.java | 8 ++++---- .../server/dao/oauth2/OAuth2Service.java | 8 ++++---- .../server/common/data/oauth2/MapperType.java | 15 +++++++++++++++ .../data/oauth2/OAuth2BasicMapperConfig.java | 15 +++++++++++++++ .../data/oauth2/OAuth2ClientRegistration.java | 15 +++++++++++++++ .../common/data/oauth2/OAuth2ClientsParams.java | 15 +++++++++++++++ .../data/oauth2/OAuth2CustomMapperConfig.java | 15 +++++++++++++++ .../common/data/oauth2/OAuth2MapperConfig.java | 15 +++++++++++++++ .../data/oauth2/TenantNameStrategyType.java | 15 +++++++++++++++ .../HybridClientRegistrationRepository.java | 15 +++++++++++++++ 10 files changed, 128 insertions(+), 8 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index 31bc3e9fff..18e7a4eacb 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index da289bfd54..05e06cc3ae 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java index d33563b668..48f0a77a41 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java @@ -1,3 +1,18 @@ +/** + * 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 { diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java index 18bab333fb..3f9f1c6dc2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.java @@ -1,3 +1,18 @@ +/** + * 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.*; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java index a9fe5ed060..1fc2743e73 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -1,3 +1,18 @@ +/** + * 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.*; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java index 1adcd51f3b..3c0da5a9a0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java @@ -1,3 +1,18 @@ +/** + * 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.*; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java index ad401e0aff..8ac72e9d8a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java @@ -1,3 +1,18 @@ +/** + * 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.*; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java index 1e45edc4de..e0aaf72ee3 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java @@ -1,3 +1,18 @@ +/** + * 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; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java index e38c7e37dc..91562a883a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.java @@ -1,3 +1,18 @@ +/** + * 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 { diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java index f04c739ac1..3c9743e4d3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java @@ -1,3 +1,18 @@ +/** + * 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; From e650cb0c243a4046c2031f56cff9d4835a8fb6d7 Mon Sep 17 00:00:00 2001 From: viktor Date: Tue, 23 Jun 2020 19:20:00 +0300 Subject: [PATCH 028/117] Added reading attributes by attribute_key --- .../org/thingsboard/server/controller/AuthController.java | 2 -- .../server/dao/attributes/AttributesService.java | 2 ++ .../thingsboard/server/dao/attributes/AttributesDao.java | 2 ++ .../server/dao/attributes/BaseAttributesService.java | 6 ++++++ .../server/dao/sql/attributes/AttributeKvRepository.java | 2 ++ .../server/dao/sql/attributes/JpaAttributeDao.java | 6 ++++++ 6 files changed, 18 insertions(+), 2 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/AuthController.java b/application/src/main/java/org/thingsboard/server/controller/AuthController.java index adffcba3d6..798b71e114 100644 --- a/application/src/main/java/org/thingsboard/server/controller/AuthController.java +++ b/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; diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java index 3af4737f06..cb7cbd09c6 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java @@ -35,6 +35,8 @@ public interface AttributesService { ListenableFuture> findAll(TenantId tenantId, EntityId entityId, String scope); + ListenableFuture> findAllByAttributeKey(String attributeKey); + ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes); ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, String scope, List attributeKeys); diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java index 8ec43f49a5..6101a70b54 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java @@ -35,6 +35,8 @@ public interface AttributesDao { ListenableFuture> findAll(TenantId tenantId, EntityId entityId, String attributeType); + ListenableFuture> findAllByAttributeKey(String attributeKey); + ListenableFuture save(TenantId tenantId, EntityId entityId, String attributeType, AttributeKvEntry attribute); ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, String attributeType, List keys); diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java index a6924960ca..fbb44ac8c6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java @@ -59,6 +59,12 @@ public class BaseAttributesService implements AttributesService { return attributesDao.findAll(tenantId, entityId, scope); } + @Override + public ListenableFuture> findAllByAttributeKey(String attributeKey) { + Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); + return attributesDao.findAllByAttributeKey(attributeKey); + } + @Override public ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes) { validate(entityId, scope); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java index 0bd667b790..37b30607ab 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java @@ -47,5 +47,7 @@ public interface AttributeKvRepository extends CrudRepository findAllByAttributeKey(String attributeKey); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java index c14e2dd7d0..420ab0a42d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java @@ -119,6 +119,12 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl attributeType)))); } + @Override + public ListenableFuture> findAllByAttributeKey(String attributeKey) { + return Futures.immediateFuture( + DaoUtil.convertDataList(attributeKvRepository.findAllByAttributeKey(attributeKey))); + } + @Override public ListenableFuture save(TenantId tenantId, EntityId entityId, String attributeType, AttributeKvEntry attribute) { AttributeKvEntity entity = new AttributeKvEntity(); From db617b404e04c45f5567943e345ba71b2b365a10 Mon Sep 17 00:00:00 2001 From: viktor Date: Tue, 23 Jun 2020 19:20:30 +0300 Subject: [PATCH 029/117] Configured permissions for oauth feature --- .../security/permission/SysAdminPermissions.java | 1 + .../security/permission/TenantAdminPermissions.java | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java index cd79a29f0b..869217e4cf 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java @@ -39,6 +39,7 @@ public class SysAdminPermissions extends AbstractPermissions { put(Resource.USER, userPermissionChecker); put(Resource.WIDGETS_BUNDLE, systemEntityPermissionChecker); put(Resource.WIDGET_TYPE, systemEntityPermissionChecker); + put(Resource.OAUTH2_CONFIGURATION, PermissionChecker.allowAllPermissionChecker); } private static final PermissionChecker systemEntityPermissionChecker = new PermissionChecker() { diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index 794fb72398..703c238c64 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.security.permission; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.HasTenantId; import org.thingsboard.server.common.data.User; @@ -22,6 +23,7 @@ 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.dao.oauth2.OAuth2Service; import org.thingsboard.server.service.security.model.SecurityUser; import java.util.HashMap; @@ -29,6 +31,9 @@ import java.util.HashMap; @Component(value="tenantAdminPermissions") public class TenantAdminPermissions extends AbstractPermissions { + @Autowired + private OAuth2Service oAuth2Service; + public TenantAdminPermissions() { super(); put(Resource.ALARM, tenantEntityPermissionChecker); @@ -42,6 +47,7 @@ public class TenantAdminPermissions extends AbstractPermissions { put(Resource.USER, userPermissionChecker); put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); put(Resource.WIDGET_TYPE, widgetsPermissionChecker); + put(Resource.OAUTH2_CONFIGURATION, tenantOAuth2ConfigPermissionChecker); } public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { @@ -101,4 +107,11 @@ public class TenantAdminPermissions extends AbstractPermissions { } }; + + private final PermissionChecker tenantOAuth2ConfigPermissionChecker = new PermissionChecker() { + @Override + public boolean hasPermission(SecurityUser user, Operation operation) { + return oAuth2Service.isOAuth2ClientRegistrationAllowed(user.getTenantId()); + } + }; } From b3050b9aa55145208099ae84515738dcc953e91e Mon Sep 17 00:00:00 2001 From: viktor Date: Tue, 23 Jun 2020 19:21:09 +0300 Subject: [PATCH 030/117] Init cache on start-up --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 73 ++++++++++++++++--- 1 file changed, 62 insertions(+), 11 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index da783ea297..32221b2b73 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -26,6 +26,7 @@ import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.ListUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.*; @@ -47,6 +48,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.stream.Collectors; +import java.util.stream.Stream; @Slf4j @Service @@ -64,6 +66,9 @@ public class OAuth2ServiceImpl implements OAuth2Service { private static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; + @Autowired + private Environment environment; + @Autowired private AdminSettingsService adminSettingsService; @@ -76,11 +81,25 @@ public class OAuth2ServiceImpl implements OAuth2Service { private final Map clientRegistrationsByRegistrationId = new ConcurrentHashMap<>(); + private boolean isInstall() { + return environment.acceptsProfiles("install"); + } + // TODO add field that invalidates cache in case write to cache fails after successful saving in DB @PostConstruct - public void init(){ + public void init() { + if (isInstall()) return; + OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); - // TODO get all attributes with key OAUTH2_CLIENT_REGISTRATIONS_PARAMS and put into the map + OAuth2ClientsParams tenantsOAuth2ClientsParams = getAllOAuth2ClientsParams(); + + Stream.concat( + systemOAuth2ClientsParams.getClientRegistrations().stream(), + tenantsOAuth2ClientsParams.getClientRegistrations().stream() + ) + .forEach(clientRegistration -> { + clientRegistrationsByRegistrationId.put(clientRegistration.getRegistrationId(), clientRegistration); + }); } @Override @@ -91,15 +110,18 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public List getOAuth2Clients(String domainName) { OAuth2ClientsParams oAuth2ClientsParams = getMergedOAuth2ClientsParams(domainName); - return oAuth2ClientsParams.getClientRegistrations().stream() - .map(clientRegistration -> { - OAuth2ClientInfo client = new OAuth2ClientInfo(); - client.setName(clientRegistration.getLoginButtonLabel()); - client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getRegistrationId())); - client.setIcon(clientRegistration.getLoginButtonIcon()); - return client; - }) - .collect(Collectors.toList()); + return oAuth2ClientsParams != null && oAuth2ClientsParams.getClientRegistrations() != null ? + oAuth2ClientsParams.getClientRegistrations().stream() + .map(clientRegistration -> { + OAuth2ClientInfo client = new OAuth2ClientInfo(); + client.setName(clientRegistration.getLoginButtonLabel()); + client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getRegistrationId())); + client.setIcon(clientRegistration.getLoginButtonIcon()); + return client; + }) + .collect(Collectors.toList()) + : Collections.emptyList() + ; } @Override @@ -168,6 +190,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { } } } + // TODO refactor String json; try { json = mapper.writeValueAsString(oAuth2ClientsParams); @@ -214,6 +237,16 @@ public class OAuth2ServiceImpl implements OAuth2Service { } } + private OAuth2ClientsParams getAllOAuth2ClientsParams() { + ListenableFuture jsonFuture = getOAuth2ClientsParamsAttribute(); + try { + return Futures.transform(jsonFuture, this::constructOAuth2ClientsParams, MoreExecutors.directExecutor()).get(); + } catch (InterruptedException | ExecutionException e) { + log.error("Failed to read OAuth2 Clients Params from attributes!", e); + throw new RuntimeException("Failed to read OAuth2 Clients Params from attributes!", e); + } + } + @Override public void deleteDomainOAuth2ClientRegistrationByTenant(TenantId tenantId) { OAuth2ClientsParams params = getTenantOAuth2ClientsParams(tenantId); @@ -254,6 +287,24 @@ public class OAuth2ServiceImpl implements OAuth2Service { }, MoreExecutors.directExecutor()); } + private ListenableFuture getOAuth2ClientsParamsAttribute() { + ListenableFuture> attributeKvEntriesFuture; + try { + attributeKvEntriesFuture = attributesService.findAllByAttributeKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + } catch (Exception e) { + log.error("Unable to read OAuth2 Clients Params from attributes!", e); + throw new IncorrectParameterException("Unable to read OAuth2 Clients Params from attributes!"); + } + return Futures.transform(attributeKvEntriesFuture, attributeKvEntries -> { + if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { + AttributeKvEntry kvEntry = attributeKvEntries.get(0); + return kvEntry.getValueAsString(); + } else { + return ""; + } + }, MoreExecutors.directExecutor()); + } + private AdminSettings saveOAuth2ClientSettings(TenantId tenantId, String clientRegistrationsKey) { AdminSettings oauth2ClientsSettings = new AdminSettings(); oauth2ClientsSettings.setKey(clientRegistrationsKey); From ba0f8a8ea5e507bd840f11b4d3183e93c3c32a63 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 24 Jun 2020 12:41:19 +0300 Subject: [PATCH 031/117] Updated loading AttributeKvEntity by attribute_key --- .../dao/attributes/AttributesService.java | 3 +- .../data/kv/BaseEntityAttributeKvEntry.java | 44 +++++++++++++++++++ .../data/kv/EntityAttributeKvEntry.java | 24 ++++++++++ .../server/dao/attributes/AttributesDao.java | 3 +- .../dao/attributes/BaseAttributesService.java | 3 +- .../dao/model/sql/AttributeKvEntity.java | 33 +++++++++----- .../sql/attributes/AttributeKvRepository.java | 6 ++- .../dao/sql/attributes/JpaAttributeDao.java | 13 ++++-- 8 files changed, 109 insertions(+), 20 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseEntityAttributeKvEntry.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/kv/EntityAttributeKvEntry.java diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java index cb7cbd09c6..75122aba57 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.EntityAttributeKvEntry; import java.util.Collection; import java.util.List; @@ -35,7 +36,7 @@ public interface AttributesService { ListenableFuture> findAll(TenantId tenantId, EntityId entityId, String scope); - ListenableFuture> findAllByAttributeKey(String attributeKey); + ListenableFuture> findAllByAttributeKey(String attributeKey); ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseEntityAttributeKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseEntityAttributeKvEntry.java new file mode 100644 index 0000000000..aac91f5613 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseEntityAttributeKvEntry.java @@ -0,0 +1,44 @@ +/** + * 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.kv; + +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.thingsboard.server.common.data.EntityType; + +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BaseEntityAttributeKvEntry extends BaseAttributeKvEntry implements EntityAttributeKvEntry { + + private final EntityType entityType; + private final String entityId; + + public BaseEntityAttributeKvEntry(EntityType entityType, String entityId, long lastUpdateTs, KvEntry kv) { + super(kv, lastUpdateTs); + this.entityType = entityType; + this.entityId = entityId; + } + + @Override + public EntityType getEntityType() { + return entityType; + } + + @Override + public String getEntityId() { + return entityId; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/EntityAttributeKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/EntityAttributeKvEntry.java new file mode 100644 index 0000000000..b62576b8e0 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/EntityAttributeKvEntry.java @@ -0,0 +1,24 @@ +/** + * 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.kv; + +import org.thingsboard.server.common.data.EntityType; + +public interface EntityAttributeKvEntry extends AttributeKvEntry { + + EntityType getEntityType(); + String getEntityId(); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java index 6101a70b54..85f6b2de44 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java @@ -19,6 +19,7 @@ import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.EntityAttributeKvEntry; import java.util.Collection; import java.util.List; @@ -35,7 +36,7 @@ public interface AttributesDao { ListenableFuture> findAll(TenantId tenantId, EntityId entityId, String attributeType); - ListenableFuture> findAllByAttributeKey(String attributeKey); + ListenableFuture> findAllByAttributeKey(String attributeKey); ListenableFuture save(TenantId tenantId, EntityId entityId, String attributeType, AttributeKvEntry attribute); diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java index fbb44ac8c6..9956da347d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java @@ -23,6 +23,7 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.EntityAttributeKvEntry; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.Validator; @@ -60,7 +61,7 @@ public class BaseAttributesService implements AttributesService { } @Override - public ListenableFuture> findAllByAttributeKey(String attributeKey) { + public ListenableFuture> findAllByAttributeKey(String attributeKey) { Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); return attributesDao.findAllByAttributeKey(attributeKey); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java index f0de269cea..7787f21830 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java @@ -16,20 +16,10 @@ package org.thingsboard.server.dao.model.sql; import lombok.Data; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.kv.BooleanDataEntry; -import org.thingsboard.server.common.data.kv.DoubleDataEntry; -import org.thingsboard.server.common.data.kv.JsonDataEntry; -import org.thingsboard.server.common.data.kv.KvEntry; -import org.thingsboard.server.common.data.kv.LongDataEntry; -import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.*; import org.thingsboard.server.dao.model.ToData; -import javax.persistence.Column; -import javax.persistence.EmbeddedId; -import javax.persistence.Entity; -import javax.persistence.Table; +import javax.persistence.*; import java.io.Serializable; import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN; @@ -42,6 +32,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUM @Data @Entity @Table(name = "attribute_kv") +// TODO maybe move ToData to local field as well (or implement ToData differently) public class AttributeKvEntity implements ToData, Serializable { @EmbeddedId @@ -82,4 +73,22 @@ public class AttributeKvEntity implements ToData, Serializable return new BaseAttributeKvEntry(kvEntry, lastUpdateTs); } + + @Transient + public final ToData toEntityAttributeKvEntry = () -> { + KvEntry kvEntry = null; + if (strValue != null) { + kvEntry = new StringDataEntry(id.getAttributeKey(), strValue); + } else if (booleanValue != null) { + kvEntry = new BooleanDataEntry(id.getAttributeKey(), booleanValue); + } else if (doubleValue != null) { + kvEntry = new DoubleDataEntry(id.getAttributeKey(), doubleValue); + } else if (longValue != null) { + kvEntry = new LongDataEntry(id.getAttributeKey(), longValue); + } else if (jsonValue != null) { + kvEntry = new JsonDataEntry(id.getAttributeKey(), jsonValue); + } + + return new BaseEntityAttributeKvEntry(id.getEntityType(), id.getEntityId(), lastUpdateTs, kvEntry); + }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java index 37b30607ab..86616d6d8f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java @@ -37,6 +37,10 @@ public interface AttributeKvRepository extends CrudRepository findAllByAttributeKey(@Param("attributeKey") String attributeKey); + @Transactional @Modifying @Query("DELETE FROM AttributeKvEntity a WHERE a.id.entityType = :entityType " + @@ -47,7 +51,5 @@ public interface AttributeKvRepository extends CrudRepository findAllByAttributeKey(String attributeKey); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java index 420ab0a42d..80119f23bf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java @@ -25,9 +25,10 @@ import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.UUIDConverter; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.*; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.attributes.AttributesDao; +import org.thingsboard.server.dao.model.ToData; import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; import org.thingsboard.server.dao.model.sql.AttributeKvEntity; import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; @@ -120,9 +121,15 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl } @Override - public ListenableFuture> findAllByAttributeKey(String attributeKey) { + public ListenableFuture> findAllByAttributeKey(String attributeKey) { return Futures.immediateFuture( - DaoUtil.convertDataList(attributeKvRepository.findAllByAttributeKey(attributeKey))); + DaoUtil.convertDataList( + attributeKvRepository.findAllByAttributeKey(attributeKey).stream() + .map(attributeKvEntity -> attributeKvEntity.toEntityAttributeKvEntry) + .collect(Collectors.toList()) + ) + + ); } @Override From 7bedf10bd22f1827a23b3b1692679d74ec6d5db1 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 24 Jun 2020 12:41:58 +0300 Subject: [PATCH 032/117] Load all OAuth2 config in cache on startup --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 92 +++++++++++-------- 1 file changed, 52 insertions(+), 40 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 32221b2b73..e06fe523ee 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -19,21 +19,17 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.collect.Lists; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections.ListUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.*; import org.thingsboard.server.common.data.id.*; -import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; -import org.thingsboard.server.common.data.kv.StringDataEntry; +import org.thingsboard.server.common.data.kv.*; import org.thingsboard.server.common.data.oauth2.*; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.exception.DataValidationException; @@ -47,6 +43,7 @@ import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; +import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -78,7 +75,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Autowired private TenantService tenantService; - private final Map clientRegistrationsByRegistrationId = new ConcurrentHashMap<>(); + private final Map clientsParams = new ConcurrentHashMap<>(); private boolean isInstall() { @@ -90,21 +87,18 @@ public class OAuth2ServiceImpl implements OAuth2Service { public void init() { if (isInstall()) return; - OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); - OAuth2ClientsParams tenantsOAuth2ClientsParams = getAllOAuth2ClientsParams(); - - Stream.concat( - systemOAuth2ClientsParams.getClientRegistrations().stream(), - tenantsOAuth2ClientsParams.getClientRegistrations().stream() - ) - .forEach(clientRegistration -> { - clientRegistrationsByRegistrationId.put(clientRegistration.getRegistrationId(), clientRegistration); - }); + Map allOAuth2ClientsParams = getAllOAuth2ClientsParams(); + + allOAuth2ClientsParams.forEach(clientsParams::put); } @Override public OAuth2ClientRegistration getClientRegistration(String registrationId) { - return clientRegistrationsByRegistrationId.get(registrationId); + return clientsParams.values().stream() + .flatMap(oAuth2ClientsParams -> oAuth2ClientsParams.getClientRegistrations().stream()) + .filter(clientRegistration -> registrationId.equals(clientRegistration.getRegistrationId())) + .findFirst() + .orElse(null); } @Override @@ -112,24 +106,26 @@ public class OAuth2ServiceImpl implements OAuth2Service { OAuth2ClientsParams oAuth2ClientsParams = getMergedOAuth2ClientsParams(domainName); return oAuth2ClientsParams != null && oAuth2ClientsParams.getClientRegistrations() != null ? oAuth2ClientsParams.getClientRegistrations().stream() - .map(clientRegistration -> { - OAuth2ClientInfo client = new OAuth2ClientInfo(); - client.setName(clientRegistration.getLoginButtonLabel()); - client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getRegistrationId())); - client.setIcon(clientRegistration.getLoginButtonIcon()); - return client; - }) + .map(this::toClientInfo) .collect(Collectors.toList()) : Collections.emptyList() ; } + private OAuth2ClientInfo toClientInfo(OAuth2ClientRegistration clientRegistration) { + OAuth2ClientInfo client = new OAuth2ClientInfo(); + client.setName(clientRegistration.getLoginButtonLabel()); + client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getRegistrationId())); + client.setIcon(clientRegistration.getLoginButtonIcon()); + return client; + } + @Override public OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) { // TODO check by registration ID in entities - for (OAuth2ClientRegistration clientRegistration : oAuth2ClientsParams.getClientRegistrations()) { - validator.accept(clientRegistration); - } + validate(oAuth2ClientsParams); + + AdminSettings clientRegistrationParamsSettings = new AdminSettings(); clientRegistrationParamsSettings.setKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); ObjectNode clientRegistrationsNode = mapper.createObjectNode(); @@ -155,9 +151,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { public OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) { // TODO ask what if tenant saves config for several different domain names, do we need to check it // TODO check by registration ID in system - for (OAuth2ClientRegistration clientRegistration : oAuth2ClientsParams.getClientRegistrations()) { - validator.accept(clientRegistration); - } + validate(oAuth2ClientsParams); String clientRegistrationsKey = constructClientRegistrationsKey(oAuth2ClientsParams.getDomainName()); AdminSettings existentAdminSettingsByKey = adminSettingsService.findAdminSettingsByKey(tenantId, clientRegistrationsKey); if (StringUtils.isEmpty(oAuth2ClientsParams.getAdminSettingsId())) { @@ -211,6 +205,12 @@ public class OAuth2ServiceImpl implements OAuth2Service { return getTenantOAuth2ClientsParams(tenantId); } + private void validate(OAuth2ClientsParams oAuth2ClientsParams) { + for (OAuth2ClientRegistration clientRegistration : oAuth2ClientsParams.getClientRegistrations()) { + validator.accept(clientRegistration); + } + } + @Override public OAuth2ClientsParams getSystemOAuth2ClientsParams(TenantId tenantId) { AdminSettings oauth2ClientsParamsSettings = adminSettingsService.findAdminSettingsByKey(tenantId, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); @@ -237,10 +237,22 @@ public class OAuth2ServiceImpl implements OAuth2Service { } } - private OAuth2ClientsParams getAllOAuth2ClientsParams() { - ListenableFuture jsonFuture = getOAuth2ClientsParamsAttribute(); + private Map getAllOAuth2ClientsParams() { + OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + ListenableFuture> jsonFuture = getAllOAuth2ClientsParamsAttribute(); try { - return Futures.transform(jsonFuture, this::constructOAuth2ClientsParams, MoreExecutors.directExecutor()).get(); + return Futures.transform(jsonFuture, + clientsParamsByKvEntryKey -> { + Map tenantClientParams = clientsParamsByKvEntryKey.entrySet().stream() + .collect(Collectors.toMap( + entry -> new TenantId(UUIDConverter.fromString(entry.getKey())), + entry -> constructOAuth2ClientsParams(entry.getValue()) + )); + tenantClientParams.put(TenantId.SYS_TENANT_ID, systemOAuth2ClientsParams); + return tenantClientParams; + }, + MoreExecutors.directExecutor() + ).get(); } catch (InterruptedException | ExecutionException e) { log.error("Failed to read OAuth2 Clients Params from attributes!", e); throw new RuntimeException("Failed to read OAuth2 Clients Params from attributes!", e); @@ -287,20 +299,20 @@ public class OAuth2ServiceImpl implements OAuth2Service { }, MoreExecutors.directExecutor()); } - private ListenableFuture getOAuth2ClientsParamsAttribute() { - ListenableFuture> attributeKvEntriesFuture; + private ListenableFuture> getAllOAuth2ClientsParamsAttribute() { + ListenableFuture> entityAttributeKvEntriesFuture; try { - attributeKvEntriesFuture = attributesService.findAllByAttributeKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + entityAttributeKvEntriesFuture = attributesService.findAllByAttributeKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); } catch (Exception e) { log.error("Unable to read OAuth2 Clients Params from attributes!", e); throw new IncorrectParameterException("Unable to read OAuth2 Clients Params from attributes!"); } - return Futures.transform(attributeKvEntriesFuture, attributeKvEntries -> { + return Futures.transform(entityAttributeKvEntriesFuture, attributeKvEntries -> { if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { - AttributeKvEntry kvEntry = attributeKvEntries.get(0); - return kvEntry.getValueAsString(); + return attributeKvEntries.stream() + .collect(Collectors.toMap(EntityAttributeKvEntry::getEntityId, EntityAttributeKvEntry::getValueAsString)); } else { - return ""; + return Collections.emptyMap(); } }, MoreExecutors.directExecutor()); } From 8e1e1ec3eb1bd1952aa2a84028ad68c111ea550d Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 24 Jun 2020 12:49:53 +0300 Subject: [PATCH 033/117] Get OAuth2Params from cache (not DB) --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 39 +------------------ 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index e06fe523ee..b800426b9d 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -213,28 +213,12 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public OAuth2ClientsParams getSystemOAuth2ClientsParams(TenantId tenantId) { - AdminSettings oauth2ClientsParamsSettings = adminSettingsService.findAdminSettingsByKey(tenantId, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); - String json = null; - if (oauth2ClientsParamsSettings != null) { - json = oauth2ClientsParamsSettings.getJsonValue().get(SYSTEM_SETTINGS_OAUTH2_VALUE).asText(); - } - return constructOAuth2ClientsParams(json); + return clientsParams.get(tenantId); } @Override public OAuth2ClientsParams getTenantOAuth2ClientsParams(TenantId tenantId) { - ListenableFuture jsonFuture; - if (isOAuth2ClientRegistrationAllowed(tenantId)) { - jsonFuture = getOAuth2ClientsParamsAttribute(tenantId); - } else { - jsonFuture = Futures.immediateFuture(""); - } - try { - return Futures.transform(jsonFuture, this::constructOAuth2ClientsParams, MoreExecutors.directExecutor()).get(); - } catch (InterruptedException | ExecutionException e) { - log.error("Failed to read OAuth2 Clients Params from attributes!", e); - throw new RuntimeException("Failed to read OAuth2 Clients Params from attributes!", e); - } + return clientsParams.get(tenantId); } private Map getAllOAuth2ClientsParams() { @@ -280,25 +264,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { } } - private ListenableFuture getOAuth2ClientsParamsAttribute(TenantId tenantId) { - ListenableFuture> attributeKvEntriesFuture; - try { - attributeKvEntriesFuture = attributesService.find(tenantId, tenantId, DataConstants.SERVER_SCOPE, - Collections.singletonList(OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); - } catch (Exception e) { - log.error("Unable to read OAuth2 Clients Params from attributes!", e); - throw new IncorrectParameterException("Unable to read OAuth2 Clients Params from attributes!"); - } - return Futures.transform(attributeKvEntriesFuture, attributeKvEntries -> { - if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { - AttributeKvEntry kvEntry = attributeKvEntries.get(0); - return kvEntry.getValueAsString(); - } else { - return ""; - } - }, MoreExecutors.directExecutor()); - } - private ListenableFuture> getAllOAuth2ClientsParamsAttribute() { ListenableFuture> entityAttributeKvEntriesFuture; try { From 2d42d4e3fd538bd59a64c1d17d690834cb67b768 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 24 Jun 2020 13:21:01 +0300 Subject: [PATCH 034/117] Added saving SystemOAuth2 config in cache --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 40 ++++++++++++++++--- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index b800426b9d..2b8e5d88d2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -42,6 +42,7 @@ import java.io.IOException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; +import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; @@ -75,6 +76,8 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Autowired private TenantService tenantService; + private final ReentrantLock lock = new ReentrantLock(); + private final Map clientsParams = new ConcurrentHashMap<>(); @@ -122,15 +125,43 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) { - // TODO check by registration ID in entities validate(oAuth2ClientsParams); + validateUniqueRegistrationId(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); + lock.lock(); + try { + validateUniqueRegistrationId(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); + AdminSettings clientRegistrationParamsSettings = createSystemOAuth2Settings(oAuth2ClientsParams); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, clientRegistrationParamsSettings); + clientsParams.put(TenantId.SYS_TENANT_ID, oAuth2ClientsParams); + } finally { + lock.unlock(); + } + + return getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + } + private void validateUniqueRegistrationId(OAuth2ClientsParams inputOAuth2ClientsParams, TenantId tenantId) { + inputOAuth2ClientsParams.getClientRegistrations().stream() + .map(OAuth2ClientRegistration::getRegistrationId) + .forEach(registrationId -> { + clientsParams.forEach((paramsTenantId, oAuth2ClientsParams) -> { + boolean registrationExists = oAuth2ClientsParams.getClientRegistrations().stream() + .map(OAuth2ClientRegistration::getRegistrationId) + .anyMatch(registrationId::equals); + if (registrationExists && !tenantId.equals(paramsTenantId)) { + log.error("Current registrationId [{}] already registered in the system!", registrationId); + throw new IncorrectParameterException("Current registrationId [" + registrationId + "] already registered in the system!"); + } + }); + }); + } + + private AdminSettings createSystemOAuth2Settings(OAuth2ClientsParams oAuth2ClientsParams) { AdminSettings clientRegistrationParamsSettings = new AdminSettings(); clientRegistrationParamsSettings.setKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); ObjectNode clientRegistrationsNode = mapper.createObjectNode(); - oAuth2ClientsParams.setDomainName(""); String json; try { json = mapper.writeValueAsString(oAuth2ClientsParams); @@ -139,12 +170,9 @@ public class OAuth2ServiceImpl implements OAuth2Service { throw new IncorrectParameterException("Unable to convert OAuth2 Client Registration Params to JSON!"); } clientRegistrationsNode.put(SYSTEM_SETTINGS_OAUTH2_VALUE, json); - clientRegistrationParamsSettings.setJsonValue(clientRegistrationsNode); - adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, clientRegistrationParamsSettings); - - return getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + return clientRegistrationParamsSettings; } @Override From 5f1de39fddbb5cc341e91917046fe3f76e93895f Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 24 Jun 2020 14:59:30 +0300 Subject: [PATCH 035/117] Refactored saving system and tenant OAuth2 config --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 179 ++++++++++-------- 1 file changed, 97 insertions(+), 82 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 2b8e5d88d2..6228f0d8d8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -44,9 +44,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; -import java.util.function.Function; import java.util.stream.Collectors; -import java.util.stream.Stream; @Slf4j @Service @@ -131,7 +129,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { lock.lock(); try { validateUniqueRegistrationId(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); - AdminSettings clientRegistrationParamsSettings = createSystemOAuth2Settings(oAuth2ClientsParams); + AdminSettings clientRegistrationParamsSettings = createSystemAdminSettings(oAuth2ClientsParams); adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, clientRegistrationParamsSettings); clientsParams.put(TenantId.SYS_TENANT_ID, oAuth2ClientsParams); } finally { @@ -141,59 +139,50 @@ public class OAuth2ServiceImpl implements OAuth2Service { return getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); } - private void validateUniqueRegistrationId(OAuth2ClientsParams inputOAuth2ClientsParams, TenantId tenantId) { - inputOAuth2ClientsParams.getClientRegistrations().stream() - .map(OAuth2ClientRegistration::getRegistrationId) - .forEach(registrationId -> { - clientsParams.forEach((paramsTenantId, oAuth2ClientsParams) -> { - boolean registrationExists = oAuth2ClientsParams.getClientRegistrations().stream() - .map(OAuth2ClientRegistration::getRegistrationId) - .anyMatch(registrationId::equals); - if (registrationExists && !tenantId.equals(paramsTenantId)) { - log.error("Current registrationId [{}] already registered in the system!", registrationId); - throw new IncorrectParameterException("Current registrationId [" + registrationId + "] already registered in the system!"); - } - }); - }); - } + @Override + public OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) { + // TODO what if tenant saves config for several different domain names, do we need to check it + validate(oAuth2ClientsParams); + // TODO check by registration ID in system - private AdminSettings createSystemOAuth2Settings(OAuth2ClientsParams oAuth2ClientsParams) { - AdminSettings clientRegistrationParamsSettings = new AdminSettings(); - clientRegistrationParamsSettings.setKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); - ObjectNode clientRegistrationsNode = mapper.createObjectNode(); + String adminSettingsId = processTenantAdminSettings(tenantId, oAuth2ClientsParams.getDomainName(), oAuth2ClientsParams.getAdminSettingsId()); + oAuth2ClientsParams.setAdminSettingsId(adminSettingsId); - String json; + List attributes = createOAuth2ClientsParamsAttributes(oAuth2ClientsParams); try { - json = mapper.writeValueAsString(oAuth2ClientsParams); - } catch (JsonProcessingException e) { - log.error("Unable to convert OAuth2 Client Registration Params to JSON!", e); - throw new IncorrectParameterException("Unable to convert OAuth2 Client Registration Params to JSON!"); + // TODO ask if I need .get() here + attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, attributes).get(); + } catch (Exception e) { + log.error("Unable to save OAuth2 Client Registration Params to attributes!", e); + throw new IncorrectParameterException("Unable to save OAuth2 Client Registration Params to attributes!"); } - clientRegistrationsNode.put(SYSTEM_SETTINGS_OAUTH2_VALUE, json); - clientRegistrationParamsSettings.setJsonValue(clientRegistrationsNode); + return getTenantOAuth2ClientsParams(tenantId); + } - return clientRegistrationParamsSettings; + private List createOAuth2ClientsParamsAttributes(OAuth2ClientsParams oAuth2ClientsParams) { + String json = toJson(oAuth2ClientsParams); + List attributes = new ArrayList<>(); + long ts = System.currentTimeMillis(); + attributes.add(new BaseAttributeKvEntry(new StringDataEntry(OAUTH2_CLIENT_REGISTRATIONS_PARAMS, json), ts)); + return attributes; } - @Override - public OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) { - // TODO ask what if tenant saves config for several different domain names, do we need to check it - // TODO check by registration ID in system - validate(oAuth2ClientsParams); - String clientRegistrationsKey = constructClientRegistrationsKey(oAuth2ClientsParams.getDomainName()); - AdminSettings existentAdminSettingsByKey = adminSettingsService.findAdminSettingsByKey(tenantId, clientRegistrationsKey); - if (StringUtils.isEmpty(oAuth2ClientsParams.getAdminSettingsId())) { + private String processTenantAdminSettings(TenantId tenantId, String domainName, String prevAdminSettingsId) { + String selectedDomainSettingsKey = constructAdminSettingsDomainKey(domainName); + AdminSettings existentAdminSettingsByKey = adminSettingsService.findAdminSettingsByKey(tenantId, selectedDomainSettingsKey); + if (StringUtils.isEmpty(prevAdminSettingsId)) { if (existentAdminSettingsByKey == null) { - existentAdminSettingsByKey = saveOAuth2ClientSettings(tenantId, clientRegistrationsKey); - oAuth2ClientsParams.setAdminSettingsId(existentAdminSettingsByKey.getId().getId().toString()); + AdminSettings tenantAdminSettings = createTenantAdminSettings(tenantId, selectedDomainSettingsKey); + existentAdminSettingsByKey = adminSettingsService.saveAdminSettings(tenantId, tenantAdminSettings); + return existentAdminSettingsByKey.getId().getId().toString(); } else { - log.error("Current domain name [{}] already registered in the system!", oAuth2ClientsParams.getDomainName()); - throw new IncorrectParameterException("Current domain name [" + oAuth2ClientsParams.getDomainName() + "] already registered in the system!"); + log.error("Current domain name [{}] already registered in the system!", domainName); + throw new IncorrectParameterException("Current domain name [" + domainName + "] already registered in the system!"); } } else { AdminSettings existentOAuth2ClientsSettingsById = adminSettingsService.findAdminSettingsById( tenantId, - new AdminSettingsId(UUID.fromString(oAuth2ClientsParams.getAdminSettingsId())) + new AdminSettingsId(UUID.fromString(prevAdminSettingsId)) ); if (existentOAuth2ClientsSettingsById == null) { @@ -201,36 +190,61 @@ public class OAuth2ServiceImpl implements OAuth2Service { throw new IllegalStateException("Admin setting ID is already set in login white labeling object, but doesn't exist in the database"); } - if (!existentOAuth2ClientsSettingsById.getKey().equals(clientRegistrationsKey)) { + if (!existentOAuth2ClientsSettingsById.getKey().equals(selectedDomainSettingsKey)) { if (existentAdminSettingsByKey == null) { - adminSettingsService.deleteAdminSettingsByKey(tenantId, existentOAuth2ClientsSettingsById.getKey()); - AdminSettings newOAuth2ClientsSettings = saveOAuth2ClientSettings(tenantId, clientRegistrationsKey); - oAuth2ClientsParams.setAdminSettingsId(newOAuth2ClientsSettings.getId().getId().toString()); + AdminSettings newOAuth2ClientsSettings = replaceExistentAdminSettings(tenantId, selectedDomainSettingsKey, existentOAuth2ClientsSettingsById.getKey()); + return newOAuth2ClientsSettings.getId().getId().toString(); } else { - log.error("Current domain name [{}] already registered in the system!", oAuth2ClientsParams.getDomainName()); - throw new IncorrectParameterException("Current domain name [" + oAuth2ClientsParams.getDomainName() + "] already registered in the system!"); + log.error("Current domain name [{}] already registered in the system!", domainName); + throw new IncorrectParameterException("Current domain name [" + domainName + "] already registered in the system!"); } } + return prevAdminSettingsId; } - // TODO refactor - String json; - try { - json = mapper.writeValueAsString(oAuth2ClientsParams); - } catch (JsonProcessingException e) { - log.error("Unable to convert OAuth2 Client Registration Params to JSON!", e); - throw new IncorrectParameterException("Unable to convert OAuth2 Client Registration Params to JSON!"); - } - List attributes = new ArrayList<>(); - long ts = System.currentTimeMillis(); - attributes.add(new BaseAttributeKvEntry(new StringDataEntry(OAUTH2_CLIENT_REGISTRATIONS_PARAMS, json), ts)); - try { - // TODO ask if I need here .get() - attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, attributes).get(); - } catch (Exception e) { - log.error("Unable to save OAuth2 Client Registration Params to attributes!", e); - throw new IncorrectParameterException("Unable to save OAuth2 Client Registration Params to attributes!"); - } - return getTenantOAuth2ClientsParams(tenantId); + } + + private AdminSettings replaceExistentAdminSettings(TenantId tenantId, String newKey, String oldKey) { + adminSettingsService.deleteAdminSettingsByKey(tenantId, oldKey); + AdminSettings tenantAdminSettings = createTenantAdminSettings(tenantId, newKey); + return adminSettingsService.saveAdminSettings(tenantId, tenantAdminSettings); + } + + private AdminSettings createTenantAdminSettings(TenantId tenantId, String clientRegistrationsKey) { + AdminSettings clientRegistrationParamsSettings = new AdminSettings(); + clientRegistrationParamsSettings.setKey(clientRegistrationsKey); + ObjectNode node = mapper.createObjectNode(); + node.put("entityType", EntityType.TENANT.name()); + node.put("entityId", tenantId.toString()); + clientRegistrationParamsSettings.setJsonValue(node); + return clientRegistrationParamsSettings; + } + + private AdminSettings createSystemAdminSettings(OAuth2ClientsParams oAuth2ClientsParams) { + AdminSettings clientRegistrationParamsSettings = new AdminSettings(); + clientRegistrationParamsSettings.setKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + ObjectNode clientRegistrationsNode = mapper.createObjectNode(); + + String json = toJson(oAuth2ClientsParams); + clientRegistrationsNode.put(SYSTEM_SETTINGS_OAUTH2_VALUE, json); + clientRegistrationParamsSettings.setJsonValue(clientRegistrationsNode); + + return clientRegistrationParamsSettings; + } + + private void validateUniqueRegistrationId(OAuth2ClientsParams inputOAuth2ClientsParams, TenantId tenantId) { + inputOAuth2ClientsParams.getClientRegistrations().stream() + .map(OAuth2ClientRegistration::getRegistrationId) + .forEach(registrationId -> { + clientsParams.forEach((paramsTenantId, oAuth2ClientsParams) -> { + boolean registrationExists = oAuth2ClientsParams.getClientRegistrations().stream() + .map(OAuth2ClientRegistration::getRegistrationId) + .anyMatch(registrationId::equals); + if (registrationExists && !tenantId.equals(paramsTenantId)) { + log.error("Current registrationId [{}] already registered in the system!", registrationId); + throw new IncorrectParameterException("Current registrationId [" + registrationId + "] already registered in the system!"); + } + }); + }); } private void validate(OAuth2ClientsParams oAuth2ClientsParams) { @@ -276,8 +290,8 @@ public class OAuth2ServiceImpl implements OAuth2Service { OAuth2ClientsParams params = getTenantOAuth2ClientsParams(tenantId); if (!StringUtils.isEmpty(params.getDomainName())) { // TODO don't we need to delete from attributes? - String oauth2ClientsParamsKey = constructClientRegistrationsKey(params.getDomainName()); - adminSettingsService.deleteAdminSettingsByKey(tenantId, oauth2ClientsParamsKey); + String settingsKey = constructAdminSettingsDomainKey(params.getDomainName()); + adminSettingsService.deleteAdminSettingsByKey(tenantId, settingsKey); } } @@ -310,17 +324,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { }, MoreExecutors.directExecutor()); } - private AdminSettings saveOAuth2ClientSettings(TenantId tenantId, String clientRegistrationsKey) { - AdminSettings oauth2ClientsSettings = new AdminSettings(); - oauth2ClientsSettings.setKey(clientRegistrationsKey); - ObjectNode node = mapper.createObjectNode(); - node.put("entityType", EntityType.TENANT.name()); - node.put("entityId", tenantId.toString()); - oauth2ClientsSettings.setJsonValue(node); - return adminSettingsService.saveAdminSettings(tenantId, oauth2ClientsSettings); - } - - private String constructClientRegistrationsKey(String domainName) { + private String constructAdminSettingsDomainKey(String domainName) { String clientRegistrationsKey; if (StringUtils.isEmpty(domainName)) { clientRegistrationsKey = OAUTH2_CLIENT_REGISTRATIONS_PARAMS; @@ -347,7 +351,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { } private OAuth2ClientsParams getMergedOAuth2ClientsParams(String domainName) { - AdminSettings oauth2ClientsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, constructClientRegistrationsKey(domainName)); + AdminSettings oauth2ClientsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, constructAdminSettingsDomainKey(domainName)); OAuth2ClientsParams result; if (oauth2ClientsSettings != null) { String strEntityType = oauth2ClientsSettings.getJsonValue().get("entityType").asText(); @@ -367,6 +371,17 @@ public class OAuth2ServiceImpl implements OAuth2Service { return result; } + private String toJson(OAuth2ClientsParams oAuth2ClientsParams) { + String json; + try { + json = mapper.writeValueAsString(oAuth2ClientsParams); + } catch (JsonProcessingException e) { + log.error("Unable to convert OAuth2 Client Registration Params to JSON!", e); + throw new IncorrectParameterException("Unable to convert OAuth2 Client Registration Params to JSON!"); + } + return json; + } + private final Consumer validator = clientRegistration -> { if (StringUtils.isEmpty(clientRegistration.getRegistrationId())) { throw new DataValidationException("Registration ID should be specified!"); From 3a21235593443a96bebf12957cef15d898bba2bf Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 24 Jun 2020 15:05:56 +0300 Subject: [PATCH 036/117] Added saving TenantOAuth2 config in cache --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 6228f0d8d8..61c55c9dad 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -143,19 +143,27 @@ public class OAuth2ServiceImpl implements OAuth2Service { public OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) { // TODO what if tenant saves config for several different domain names, do we need to check it validate(oAuth2ClientsParams); - // TODO check by registration ID in system - String adminSettingsId = processTenantAdminSettings(tenantId, oAuth2ClientsParams.getDomainName(), oAuth2ClientsParams.getAdminSettingsId()); - oAuth2ClientsParams.setAdminSettingsId(adminSettingsId); - - List attributes = createOAuth2ClientsParamsAttributes(oAuth2ClientsParams); + validateUniqueRegistrationId(oAuth2ClientsParams, tenantId); + lock.lock(); try { - // TODO ask if I need .get() here - attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, attributes).get(); - } catch (Exception e) { - log.error("Unable to save OAuth2 Client Registration Params to attributes!", e); - throw new IncorrectParameterException("Unable to save OAuth2 Client Registration Params to attributes!"); + String adminSettingsId = processTenantAdminSettings(tenantId, oAuth2ClientsParams.getDomainName(), oAuth2ClientsParams.getAdminSettingsId()); + oAuth2ClientsParams.setAdminSettingsId(adminSettingsId); + + List attributes = createOAuth2ClientsParamsAttributes(oAuth2ClientsParams); + try { + // TODO ask if I need .get() here + attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, attributes).get(); + } catch (Exception e) { + log.error("Unable to save OAuth2 Client Registration Params to attributes!", e); + throw new IncorrectParameterException("Unable to save OAuth2 Client Registration Params to attributes!"); + } + + clientsParams.put(tenantId, oAuth2ClientsParams); + } finally { + lock.unlock(); } + return getTenantOAuth2ClientsParams(tenantId); } From 1628f79873849c6cba0cef6b6a0e05fb28255f24 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 24 Jun 2020 15:27:46 +0300 Subject: [PATCH 037/117] Returned reading system OAuth2 config from DB (for startup cache filling) --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 61c55c9dad..5a8b907c93 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -266,13 +266,22 @@ public class OAuth2ServiceImpl implements OAuth2Service { return clientsParams.get(tenantId); } + private OAuth2ClientsParams getSystemOAuth2ClientsParamsFromDb(TenantId tenantId) { + AdminSettings oauth2ClientsParamsSettings = adminSettingsService.findAdminSettingsByKey(tenantId, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + String json = null; + if (oauth2ClientsParamsSettings != null) { + json = oauth2ClientsParamsSettings.getJsonValue().get(SYSTEM_SETTINGS_OAUTH2_VALUE).asText(); + } + return constructOAuth2ClientsParams(json); + } + @Override public OAuth2ClientsParams getTenantOAuth2ClientsParams(TenantId tenantId) { return clientsParams.get(tenantId); } private Map getAllOAuth2ClientsParams() { - OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParamsFromDb(TenantId.SYS_TENANT_ID); ListenableFuture> jsonFuture = getAllOAuth2ClientsParamsAttribute(); try { return Futures.transform(jsonFuture, From 32b9f589979ae8e6df50eea0ec7bc0253b79e62b Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 24 Jun 2020 15:29:48 +0300 Subject: [PATCH 038/117] Refactored --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 88 +++++++++---------- 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 5a8b907c93..5cb2e90ba1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -54,14 +54,13 @@ public class OAuth2ServiceImpl implements OAuth2Service { private static final String OAUTH2_CLIENT_REGISTRATIONS_PARAMS = "oauth2ClientRegistrationsParams"; private static final String OAUTH2_CLIENT_REGISTRATIONS_DOMAIN_NAME_PREFIX = "oauth2ClientRegistrationsDomainNamePrefix"; - private static final String ALLOW_OAUTH2_CONFIGURATION = "allowOAuth2Configuration"; - - private static final String SYSTEM_SETTINGS_OAUTH2_VALUE = "value"; - private static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; + private final ReentrantLock lock = new ReentrantLock(); + private final Map clientsParams = new ConcurrentHashMap<>(); + @Autowired private Environment environment; @@ -74,16 +73,11 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Autowired private TenantService tenantService; - private final ReentrantLock lock = new ReentrantLock(); - - private final Map clientsParams = new ConcurrentHashMap<>(); - - private boolean isInstall() { return environment.acceptsProfiles("install"); } - // TODO add field that invalidates cache in case write to cache fails after successful saving in DB + // TODO do I need to add a field that invalidates cache in case write to cache fails after successful saving in DB? @PostConstruct public void init() { if (isInstall()) return; @@ -95,7 +89,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public OAuth2ClientRegistration getClientRegistration(String registrationId) { - return clientsParams.values().stream() + return clientsParams.values().stream() .flatMap(oAuth2ClientsParams -> oAuth2ClientsParams.getClientRegistrations().stream()) .filter(clientRegistration -> registrationId.equals(clientRegistration.getRegistrationId())) .findFirst() @@ -113,14 +107,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { ; } - private OAuth2ClientInfo toClientInfo(OAuth2ClientRegistration clientRegistration) { - OAuth2ClientInfo client = new OAuth2ClientInfo(); - client.setName(clientRegistration.getLoginButtonLabel()); - client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getRegistrationId())); - client.setIcon(clientRegistration.getLoginButtonIcon()); - return client; - } - @Override public OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) { validate(oAuth2ClientsParams); @@ -286,11 +272,13 @@ public class OAuth2ServiceImpl implements OAuth2Service { try { return Futures.transform(jsonFuture, clientsParamsByKvEntryKey -> { - Map tenantClientParams = clientsParamsByKvEntryKey.entrySet().stream() - .collect(Collectors.toMap( - entry -> new TenantId(UUIDConverter.fromString(entry.getKey())), - entry -> constructOAuth2ClientsParams(entry.getValue()) - )); + Map tenantClientParams = clientsParamsByKvEntryKey != null ? + clientsParamsByKvEntryKey.entrySet().stream() + .collect(Collectors.toMap( + entry -> new TenantId(UUIDConverter.fromString(entry.getKey())), + entry -> constructOAuth2ClientsParams(entry.getValue()) + )) + : new HashMap<>(); tenantClientParams.put(TenantId.SYS_TENANT_ID, systemOAuth2ClientsParams); return tenantClientParams; }, @@ -341,6 +329,29 @@ public class OAuth2ServiceImpl implements OAuth2Service { }, MoreExecutors.directExecutor()); } + private OAuth2ClientsParams getMergedOAuth2ClientsParams(String domainName) { + AdminSettings oauth2ClientsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, constructAdminSettingsDomainKey(domainName)); + OAuth2ClientsParams result; + if (oauth2ClientsSettings != null) { + String strEntityType = oauth2ClientsSettings.getJsonValue().get("entityType").asText(); + String strEntityId = oauth2ClientsSettings.getJsonValue().get("entityId").asText(); + EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); + if (!entityId.getEntityType().equals(EntityType.TENANT)) { + log.error("Only tenant can configure OAuth2 for certain domain!"); + throw new IllegalStateException("Only tenant can configure OAuth2 for certain domain!"); + } + TenantId tenantId = (TenantId) entityId; + result = getTenantOAuth2ClientsParams(tenantId); + OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + if (systemOAuth2ClientsParams != null) { + result.getClientRegistrations().addAll(systemOAuth2ClientsParams.getClientRegistrations()); + } + } else { + result = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + } + return result; + } + private String constructAdminSettingsDomainKey(String domainName) { String clientRegistrationsKey; if (StringUtils.isEmpty(domainName)) { @@ -367,27 +378,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { return result; } - private OAuth2ClientsParams getMergedOAuth2ClientsParams(String domainName) { - AdminSettings oauth2ClientsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, constructAdminSettingsDomainKey(domainName)); - OAuth2ClientsParams result; - if (oauth2ClientsSettings != null) { - String strEntityType = oauth2ClientsSettings.getJsonValue().get("entityType").asText(); - String strEntityId = oauth2ClientsSettings.getJsonValue().get("entityId").asText(); - EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); - if (!entityId.getEntityType().equals(EntityType.TENANT)) { - log.error("Only tenant can configure OAuth2 for certain domain!"); - throw new IllegalStateException("Only tenant can configure OAuth2 for certain domain!"); - } - TenantId tenantId = (TenantId) entityId; - result = getTenantOAuth2ClientsParams(tenantId); - OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); - result.getClientRegistrations().addAll(systemOAuth2ClientsParams.getClientRegistrations()); - } else { - result = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); - } - return result; - } - private String toJson(OAuth2ClientsParams oAuth2ClientsParams) { String json; try { @@ -399,6 +389,14 @@ public class OAuth2ServiceImpl implements OAuth2Service { return json; } + private OAuth2ClientInfo toClientInfo(OAuth2ClientRegistration clientRegistration) { + OAuth2ClientInfo client = new OAuth2ClientInfo(); + client.setName(clientRegistration.getLoginButtonLabel()); + client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getRegistrationId())); + client.setIcon(clientRegistration.getLoginButtonIcon()); + return client; + } + private final Consumer validator = clientRegistration -> { if (StringUtils.isEmpty(clientRegistration.getRegistrationId())) { throw new DataValidationException("Registration ID should be specified!"); From 84a06e65fa33bb92e5d5d73dac8d973c8b080a47 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 24 Jun 2020 15:58:08 +0300 Subject: [PATCH 039/117] Added removing full OAuth2 config on tenant deletion --- .../org/thingsboard/server/dao/oauth2/OAuth2Service.java | 2 +- .../org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java | 5 +++-- .../org/thingsboard/server/dao/tenant/TenantServiceImpl.java | 5 +---- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 05e06cc3ae..b02a48650f 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -37,7 +37,7 @@ public interface OAuth2Service { OAuth2ClientsParams getTenantOAuth2ClientsParams(TenantId tenantId); - void deleteDomainOAuth2ClientRegistrationByTenant(TenantId tenantId); + void deleteTenantOAuth2ClientsParams(TenantId tenantId); boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 5cb2e90ba1..910c043faf 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -291,12 +291,13 @@ public class OAuth2ServiceImpl implements OAuth2Service { } @Override - public void deleteDomainOAuth2ClientRegistrationByTenant(TenantId tenantId) { + public void deleteTenantOAuth2ClientsParams(TenantId tenantId) { OAuth2ClientsParams params = getTenantOAuth2ClientsParams(tenantId); if (!StringUtils.isEmpty(params.getDomainName())) { - // TODO don't we need to delete from attributes? String settingsKey = constructAdminSettingsDomainKey(params.getDomainName()); adminSettingsService.deleteAdminSettingsByKey(tenantId, settingsKey); + attributesService.removeAll(tenantId, tenantId, DataConstants.SERVER_SCOPE, Collections.singletonList(OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + clientsParams.remove(tenantId); } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index a6cfdee8c2..4708694cee 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -21,7 +21,6 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.asset.Asset; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.page.PageData; @@ -41,8 +40,6 @@ import org.thingsboard.server.dao.service.Validator; import org.thingsboard.server.dao.user.UserService; import org.thingsboard.server.dao.widget.WidgetsBundleService; -import java.util.List; - import static org.thingsboard.server.dao.service.Validator.validateId; @Service @@ -108,7 +105,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe public void deleteTenant(TenantId tenantId) { log.trace("Executing deleteTenant [{}]", tenantId); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - oAuth2Service.deleteDomainOAuth2ClientRegistrationByTenant(tenantId); + oAuth2Service.deleteTenantOAuth2ClientsParams(tenantId); customerService.deleteCustomersByTenantId(tenantId); widgetsBundleService.deleteWidgetsBundlesByTenantId(tenantId); dashboardService.deleteDashboardsByTenantId(tenantId); From f2a9939e88953e6c6e0c6669da885f5b498b086b Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 24 Jun 2020 16:57:40 +0300 Subject: [PATCH 040/117] Fixed bug --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 910c043faf..b2824fdf4e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -23,6 +23,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections.ListUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; @@ -111,10 +112,10 @@ public class OAuth2ServiceImpl implements OAuth2Service { public OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) { validate(oAuth2ClientsParams); - validateUniqueRegistrationId(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); + validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); lock.lock(); try { - validateUniqueRegistrationId(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); + validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); AdminSettings clientRegistrationParamsSettings = createSystemAdminSettings(oAuth2ClientsParams); adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, clientRegistrationParamsSettings); clientsParams.put(TenantId.SYS_TENANT_ID, oAuth2ClientsParams); @@ -130,7 +131,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { // TODO what if tenant saves config for several different domain names, do we need to check it validate(oAuth2ClientsParams); - validateUniqueRegistrationId(oAuth2ClientsParams, tenantId); + validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); lock.lock(); try { String adminSettingsId = processTenantAdminSettings(tenantId, oAuth2ClientsParams.getDomainName(), oAuth2ClientsParams.getAdminSettingsId()); @@ -225,7 +226,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { return clientRegistrationParamsSettings; } - private void validateUniqueRegistrationId(OAuth2ClientsParams inputOAuth2ClientsParams, TenantId tenantId) { + private void validateRegistrationIdUniqueness(OAuth2ClientsParams inputOAuth2ClientsParams, TenantId tenantId) { inputOAuth2ClientsParams.getClientRegistrations().stream() .map(OAuth2ClientRegistration::getRegistrationId) .forEach(registrationId -> { @@ -345,7 +346,11 @@ public class OAuth2ServiceImpl implements OAuth2Service { result = getTenantOAuth2ClientsParams(tenantId); OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); if (systemOAuth2ClientsParams != null) { - result.getClientRegistrations().addAll(systemOAuth2ClientsParams.getClientRegistrations()); + ArrayList tenantClientRegistrations = new ArrayList<>(result.getClientRegistrations()); + tenantClientRegistrations.addAll(systemOAuth2ClientsParams.getClientRegistrations()); + result = result.toBuilder() + .clientRegistrations(tenantClientRegistrations) + .build(); } } else { result = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); From 55a2fe890e3ef2090179812f015f83d380ffd968 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 24 Jun 2020 17:36:26 +0300 Subject: [PATCH 041/117] Added method to load oauth2 config with tenantId --- .../Oauth2AuthenticationSuccessHandler.java | 2 +- .../server/dao/oauth2/OAuth2Service.java | 3 ++ .../server/dao/oauth2/OAuth2ServiceImpl.java | 35 ++++++++++++++----- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 4dda36a0fb..03c9db757f 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -64,7 +64,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS try { OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; - OAuth2ClientRegistration clientRegistration = oAuth2Service.getClientRegistration(token.getAuthorizedClientRegistrationId()); + OAuth2ClientRegistration clientRegistration = oAuth2Service.getClientRegistrationWithTenant(token.getAuthorizedClientRegistrationId()).getRight(); OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, clientRegistration.getMapperConfig()); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index b02a48650f..17dc6d5f61 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.dao.oauth2; +import org.apache.commons.lang3.tuple.Pair; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; @@ -25,6 +26,8 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; import java.util.List; public interface OAuth2Service { + Pair getClientRegistrationWithTenant(String registrationId); + OAuth2ClientRegistration getClientRegistration(String registrationId); List getOAuth2Clients(String domainName); diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index b2824fdf4e..e23dbf4a20 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -24,6 +24,8 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections.ListUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; @@ -89,12 +91,27 @@ public class OAuth2ServiceImpl implements OAuth2Service { } @Override - public OAuth2ClientRegistration getClientRegistration(String registrationId) { - return clientsParams.values().stream() - .flatMap(oAuth2ClientsParams -> oAuth2ClientsParams.getClientRegistrations().stream()) - .filter(clientRegistration -> registrationId.equals(clientRegistration.getRegistrationId())) + public Pair getClientRegistrationWithTenant(String registrationId) { + return clientsParams.entrySet().stream() + .map(entry -> { + TenantId tenantId = entry.getKey(); + OAuth2ClientRegistration clientRegistration = entry.getValue().getClientRegistrations().stream() + .filter(registration -> registrationId.equals(registration.getRegistrationId())) + .findFirst() + .orElse(null); + return clientRegistration != null ? + ImmutablePair.of(tenantId, clientRegistration) : null; + }) + .filter(Objects::nonNull) .findFirst() - .orElse(null); + .orElse(null) + ; + } + + @Override + public OAuth2ClientRegistration getClientRegistration(String registrationId) { + Pair clientRegistrationPair = getClientRegistrationWithTenant(registrationId); + return clientRegistrationPair != null ? clientRegistrationPair.getRight() : null; } @Override From 235dc0292c733bed4297d9bbe6f53efd4cf6015d Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 24 Jun 2020 17:40:21 +0300 Subject: [PATCH 042/117] Small refactoring --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index e23dbf4a20..ca85f79511 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -23,7 +23,6 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.collections.ListUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; @@ -61,7 +60,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { private static final String SYSTEM_SETTINGS_OAUTH2_VALUE = "value"; private static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; - private final ReentrantLock lock = new ReentrantLock(); + private final ReentrantLock cacheWriteLock = new ReentrantLock(); private final Map clientsParams = new ConcurrentHashMap<>(); @Autowired @@ -130,14 +129,14 @@ public class OAuth2ServiceImpl implements OAuth2Service { validate(oAuth2ClientsParams); validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); - lock.lock(); + cacheWriteLock.lock(); try { validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); AdminSettings clientRegistrationParamsSettings = createSystemAdminSettings(oAuth2ClientsParams); adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, clientRegistrationParamsSettings); clientsParams.put(TenantId.SYS_TENANT_ID, oAuth2ClientsParams); } finally { - lock.unlock(); + cacheWriteLock.unlock(); } return getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); @@ -149,7 +148,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { validate(oAuth2ClientsParams); validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); - lock.lock(); + cacheWriteLock.lock(); try { String adminSettingsId = processTenantAdminSettings(tenantId, oAuth2ClientsParams.getDomainName(), oAuth2ClientsParams.getAdminSettingsId()); oAuth2ClientsParams.setAdminSettingsId(adminSettingsId); @@ -165,7 +164,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { clientsParams.put(tenantId, oAuth2ClientsParams); } finally { - lock.unlock(); + cacheWriteLock.unlock(); } return getTenantOAuth2ClientsParams(tenantId); From a4355ace2840163833947acf2669af8e8b67316f Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 25 Jun 2020 10:52:49 +0300 Subject: [PATCH 043/117] Added reading old system settings before saving new --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index ca85f79511..da649404e1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -129,11 +129,17 @@ public class OAuth2ServiceImpl implements OAuth2Service { validate(oAuth2ClientsParams); validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); + cacheWriteLock.lock(); try { validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); - AdminSettings clientRegistrationParamsSettings = createSystemAdminSettings(oAuth2ClientsParams); - adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, clientRegistrationParamsSettings); + AdminSettings oauth2SystemAdminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + if (oauth2SystemAdminSettings == null) { + oauth2SystemAdminSettings = createSystemAdminSettings(); + } + String json = toJson(oAuth2ClientsParams); + ((ObjectNode) oauth2SystemAdminSettings.getJsonValue()).put(SYSTEM_SETTINGS_OAUTH2_VALUE, json); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, oauth2SystemAdminSettings); clientsParams.put(TenantId.SYS_TENANT_ID, oAuth2ClientsParams); } finally { cacheWriteLock.unlock(); @@ -155,7 +161,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { List attributes = createOAuth2ClientsParamsAttributes(oAuth2ClientsParams); try { - // TODO ask if I need .get() here attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, attributes).get(); } catch (Exception e) { log.error("Unable to save OAuth2 Client Registration Params to attributes!", e); @@ -230,13 +235,11 @@ public class OAuth2ServiceImpl implements OAuth2Service { return clientRegistrationParamsSettings; } - private AdminSettings createSystemAdminSettings(OAuth2ClientsParams oAuth2ClientsParams) { + private AdminSettings createSystemAdminSettings() { AdminSettings clientRegistrationParamsSettings = new AdminSettings(); clientRegistrationParamsSettings.setKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); ObjectNode clientRegistrationsNode = mapper.createObjectNode(); - String json = toJson(oAuth2ClientsParams); - clientRegistrationsNode.put(SYSTEM_SETTINGS_OAUTH2_VALUE, json); clientRegistrationParamsSettings.setJsonValue(clientRegistrationsNode); return clientRegistrationParamsSettings; From 9c7f82e91e1c4bae966dfac238be3bb479eb82fd Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 25 Jun 2020 10:53:55 +0300 Subject: [PATCH 044/117] Set parent tenant id in OAuth2ClientMapper --- .../auth/oauth2/AbstractOAuth2ClientMapper.java | 11 ++++++++--- .../security/auth/oauth2/BasicOAuth2ClientMapper.java | 5 +++-- .../auth/oauth2/CustomOAuth2ClientMapper.java | 5 +++-- .../security/auth/oauth2/OAuth2ClientMapper.java | 3 ++- .../oauth2/Oauth2AuthenticationSuccessHandler.java | 9 ++++++--- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java index c69721542f..61d288cdff 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java @@ -79,7 +79,7 @@ public abstract class AbstractOAuth2ClientMapper { private final Lock userCreationLock = new ReentrantLock(); - protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, boolean allowUserCreation, boolean activateUser) { + protected SecurityUser getOrCreateSecurityUserFromOAuth2User(TenantId parentTenantId, OAuth2User oauth2User, boolean allowUserCreation, boolean activateUser) { UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail()); User user = userService.findUserByEmail(TenantId.SYS_TENANT_ID, oauth2User.getEmail()); @@ -99,8 +99,13 @@ public abstract class AbstractOAuth2ClientMapper { } else { user.setAuthority(Authority.CUSTOMER_USER); } - TenantId tenantId = oauth2User.getTenantId() != null ? - oauth2User.getTenantId() : getTenantId(oauth2User.getTenantName()); + TenantId tenantId; + if (TenantId.SYS_TENANT_ID.equals(parentTenantId)) { + tenantId = oauth2User.getTenantId() != null ? + oauth2User.getTenantId() : getTenantId(oauth2User.getTenantName()); + } else { + tenantId = parentTenantId; + } user.setTenantId(tenantId); CustomerId customerId = oauth2User.getCustomerId() != null ? oauth2User.getCustomerId() : getCustomerId(user.getTenantId(), oauth2User.getCustomerName()); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java index 7d671767bb..19ed540543 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java @@ -20,6 +20,7 @@ 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.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.service.security.model.SecurityUser; @@ -34,7 +35,7 @@ public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implemen private static final String END_PLACEHOLDER_PREFIX = "}"; @Override - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2MapperConfig config) { + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, TenantId parentTenantId, OAuth2MapperConfig config) { OAuth2User oauth2User = new OAuth2User(); Map attributes = token.getPrincipal().getAttributes(); String email = getStringAttributeByKey(attributes, config.getBasicConfig().getEmailAttributeKey()); @@ -58,7 +59,7 @@ public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implemen oauth2User.setDefaultDashboardName(config.getBasicConfig().getDefaultDashboardName()); } - return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser()); + return getOrCreateSecurityUserFromOAuth2User(parentTenantId, oauth2User, config.isAllowUserCreation(), config.isActivateUser()); } private String getTenantName(Map attributes, OAuth2MapperConfig config) { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java index 9d4ac4d2e2..27286e454a 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java @@ -23,6 +23,7 @@ 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.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.dao.oauth2.OAuth2User; @@ -37,9 +38,9 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); @Override - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2MapperConfig config) { + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, TenantId parentTenantId, OAuth2MapperConfig config) { OAuth2User oauth2User = getOAuth2User(token, config.getCustomConfig()); - return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser()); + return getOrCreateSecurityUserFromOAuth2User(parentTenantId, oauth2User, config.isAllowUserCreation(), config.isActivateUser()); } private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, OAuth2CustomMapperConfig custom) { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java index 444332d74b..6490322b0b 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java @@ -16,9 +16,10 @@ package org.thingsboard.server.service.security.auth.oauth2; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.service.security.model.SecurityUser; public interface OAuth2ClientMapper { - SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2MapperConfig config); + SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, TenantId parentTenantId, OAuth2MapperConfig config); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 03c9db757f..0cff27ae2e 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -15,12 +15,14 @@ */ package org.thingsboard.server.service.security.auth.oauth2; +import org.apache.commons.lang3.tuple.Pair; 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.authentication.OAuth2AuthenticationToken; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; @@ -36,7 +38,6 @@ import java.net.URLEncoder; import java.nio.charset.StandardCharsets; @Component(value = "oauth2AuthenticationSuccessHandler") -@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { private final JwtTokenFactory tokenFactory; @@ -64,9 +65,11 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS try { OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; - OAuth2ClientRegistration clientRegistration = oAuth2Service.getClientRegistrationWithTenant(token.getAuthorizedClientRegistrationId()).getRight(); + Pair clientRegistrationPair = oAuth2Service.getClientRegistrationWithTenant(token.getAuthorizedClientRegistrationId()); + TenantId tenantId = clientRegistrationPair.getKey(); + OAuth2ClientRegistration clientRegistration = clientRegistrationPair.getValue(); OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); - SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, clientRegistration.getMapperConfig()); + SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, tenantId, clientRegistration.getMapperConfig()); JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); From 997821591a5c1fcf15ac2dc59b2a82a901b629ff Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 25 Jun 2020 13:53:25 +0300 Subject: [PATCH 045/117] Fix work with cacheWriteLock in saveTenantOAuth2 method --- .../org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index da649404e1..62436bab96 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -156,6 +156,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); cacheWriteLock.lock(); try { + validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); String adminSettingsId = processTenantAdminSettings(tenantId, oAuth2ClientsParams.getDomainName(), oAuth2ClientsParams.getAdminSettingsId()); oAuth2ClientsParams.setAdminSettingsId(adminSettingsId); From d22237796ea1e1a6aa8f7446d1bfafb9d56f6882 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 25 Jun 2020 16:03:22 +0300 Subject: [PATCH 046/117] Allow to pass several domains for oauth2 config (for sys_admin only) --- .../oauth2/OAuth2ClientsDomainParams.java | 33 +++++++ .../data/oauth2/OAuth2ClientsParams.java | 5 +- .../server/dao/oauth2/OAuth2ServiceImpl.java | 98 ++++++++++++++----- 3 files changed, 106 insertions(+), 30 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java new file mode 100644 index 0000000000..d9dc07d8ea --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.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.*; + +import java.util.List; + +@EqualsAndHashCode +@Data +@ToString +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class OAuth2ClientsDomainParams { + private String domainName; + private String adminSettingsId; + + private List clientRegistrations; +} \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java index 3c0da5a9a0..ef08a892a1 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java @@ -26,8 +26,5 @@ import java.util.List; @NoArgsConstructor @AllArgsConstructor public class OAuth2ClientsParams { - private String domainName; - private String adminSettingsId; - - private List clientRegistrations; + private List clientsDomainsParams; } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 62436bab96..a3bd1c90b8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -47,6 +47,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.stream.Collectors; +import java.util.stream.Stream; @Slf4j @Service @@ -79,7 +80,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { return environment.acceptsProfiles("install"); } - // TODO do I need to add a field that invalidates cache in case write to cache fails after successful saving in DB? @PostConstruct public void init() { if (isInstall()) return; @@ -94,7 +94,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { return clientsParams.entrySet().stream() .map(entry -> { TenantId tenantId = entry.getKey(); - OAuth2ClientRegistration clientRegistration = entry.getValue().getClientRegistrations().stream() + OAuth2ClientRegistration clientRegistration = toClientRegistrationStream(entry.getValue()) .filter(registration -> registrationId.equals(registration.getRegistrationId())) .findFirst() .orElse(null); @@ -115,9 +115,9 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public List getOAuth2Clients(String domainName) { - OAuth2ClientsParams oAuth2ClientsParams = getMergedOAuth2ClientsParams(domainName); - return oAuth2ClientsParams != null && oAuth2ClientsParams.getClientRegistrations() != null ? - oAuth2ClientsParams.getClientRegistrations().stream() + OAuth2ClientsDomainParams oAuth2ClientsDomainParams = getMergedOAuth2ClientsParams(domainName); + return oAuth2ClientsDomainParams != null && oAuth2ClientsDomainParams.getClientRegistrations() != null ? + oAuth2ClientsDomainParams.getClientRegistrations().stream() .map(this::toClientInfo) .collect(Collectors.toList()) : Collections.emptyList() @@ -150,15 +150,19 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) { - // TODO what if tenant saves config for several different domain names, do we need to check it + if (oAuth2ClientsParams.getClientsDomainsParams().size() != 1) { + throw new DataValidationException("Tenant can configure OAuth2 only for one domain!"); + } validate(oAuth2ClientsParams); validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); cacheWriteLock.lock(); try { validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); - String adminSettingsId = processTenantAdminSettings(tenantId, oAuth2ClientsParams.getDomainName(), oAuth2ClientsParams.getAdminSettingsId()); - oAuth2ClientsParams.setAdminSettingsId(adminSettingsId); + + OAuth2ClientsDomainParams oAuth2ClientsDomainParams = oAuth2ClientsParams.getClientsDomainsParams().get(0); + String adminSettingsId = processTenantAdminSettings(tenantId, oAuth2ClientsDomainParams.getDomainName(), oAuth2ClientsDomainParams.getAdminSettingsId()); + oAuth2ClientsDomainParams.setAdminSettingsId(adminSettingsId); List attributes = createOAuth2ClientsParamsAttributes(oAuth2ClientsParams); try { @@ -247,11 +251,20 @@ public class OAuth2ServiceImpl implements OAuth2Service { } private void validateRegistrationIdUniqueness(OAuth2ClientsParams inputOAuth2ClientsParams, TenantId tenantId) { - inputOAuth2ClientsParams.getClientRegistrations().stream() + long distinctRegistrationIds = toClientRegistrationStream(inputOAuth2ClientsParams) + .map(OAuth2ClientRegistration::getRegistrationId) + .distinct() + .count(); + long actualRegistrationIds = toClientRegistrationStream(inputOAuth2ClientsParams).count(); + if (distinctRegistrationIds != actualRegistrationIds) { + throw new DataValidationException("All registration IDs should be unique!"); + } + + toClientRegistrationStream(inputOAuth2ClientsParams) .map(OAuth2ClientRegistration::getRegistrationId) .forEach(registrationId -> { clientsParams.forEach((paramsTenantId, oAuth2ClientsParams) -> { - boolean registrationExists = oAuth2ClientsParams.getClientRegistrations().stream() + boolean registrationExists = toClientRegistrationStream(oAuth2ClientsParams) .map(OAuth2ClientRegistration::getRegistrationId) .anyMatch(registrationId::equals); if (registrationExists && !tenantId.equals(paramsTenantId)) { @@ -263,8 +276,27 @@ public class OAuth2ServiceImpl implements OAuth2Service { } private void validate(OAuth2ClientsParams oAuth2ClientsParams) { - for (OAuth2ClientRegistration clientRegistration : oAuth2ClientsParams.getClientRegistrations()) { - validator.accept(clientRegistration); + validateDomainNames(oAuth2ClientsParams); + + toClientRegistrationStream(oAuth2ClientsParams) + .forEach(validator); + } + + private void validateDomainNames(OAuth2ClientsParams oAuth2ClientsParams) { + oAuth2ClientsParams.getClientsDomainsParams() + .forEach(oAuth2ClientsDomainParams -> { + if (StringUtils.isEmpty(oAuth2ClientsDomainParams.getDomainName())) { + throw new DataValidationException("Domain name should be specified!"); + } + }); + + long distinctDomainNames = oAuth2ClientsParams.getClientsDomainsParams().stream() + .map(OAuth2ClientsDomainParams::getDomainName) + .distinct() + .count(); + long actualDomainNames = oAuth2ClientsParams.getClientsDomainsParams().size(); + if (distinctDomainNames != actualDomainNames) { + throw new DataValidationException("All domain names should be unique!"); } } @@ -300,7 +332,9 @@ public class OAuth2ServiceImpl implements OAuth2Service { entry -> constructOAuth2ClientsParams(entry.getValue()) )) : new HashMap<>(); - tenantClientParams.put(TenantId.SYS_TENANT_ID, systemOAuth2ClientsParams); + if (systemOAuth2ClientsParams.getClientsDomainsParams() != null) { + tenantClientParams.put(TenantId.SYS_TENANT_ID, systemOAuth2ClientsParams); + } return tenantClientParams; }, MoreExecutors.directExecutor() @@ -314,12 +348,12 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public void deleteTenantOAuth2ClientsParams(TenantId tenantId) { OAuth2ClientsParams params = getTenantOAuth2ClientsParams(tenantId); - if (!StringUtils.isEmpty(params.getDomainName())) { - String settingsKey = constructAdminSettingsDomainKey(params.getDomainName()); - adminSettingsService.deleteAdminSettingsByKey(tenantId, settingsKey); - attributesService.removeAll(tenantId, tenantId, DataConstants.SERVER_SCOPE, Collections.singletonList(OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); - clientsParams.remove(tenantId); - } + if (params == null) return; + OAuth2ClientsDomainParams domainParams = params.getClientsDomainsParams().get(0); + String settingsKey = constructAdminSettingsDomainKey(domainParams.getDomainName()); + adminSettingsService.deleteAdminSettingsByKey(tenantId, settingsKey); + attributesService.removeAll(tenantId, tenantId, DataConstants.SERVER_SCOPE, Collections.singletonList(OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + clientsParams.remove(tenantId); } @Override @@ -351,9 +385,17 @@ public class OAuth2ServiceImpl implements OAuth2Service { }, MoreExecutors.directExecutor()); } - private OAuth2ClientsParams getMergedOAuth2ClientsParams(String domainName) { + private OAuth2ClientsDomainParams getMergedOAuth2ClientsParams(String domainName) { AdminSettings oauth2ClientsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, constructAdminSettingsDomainKey(domainName)); - OAuth2ClientsParams result; + OAuth2ClientsDomainParams result; + + OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + OAuth2ClientsDomainParams systemOAuth2ClientsDomainParams = systemOAuth2ClientsParams != null ? + systemOAuth2ClientsParams.getClientsDomainsParams().stream() + .filter(oAuth2ClientsDomainParams -> domainName.equals(oAuth2ClientsDomainParams.getDomainName())) + .findFirst() + .orElse(null) + : null; if (oauth2ClientsSettings != null) { String strEntityType = oauth2ClientsSettings.getJsonValue().get("entityType").asText(); String strEntityId = oauth2ClientsSettings.getJsonValue().get("entityId").asText(); @@ -363,17 +405,16 @@ public class OAuth2ServiceImpl implements OAuth2Service { throw new IllegalStateException("Only tenant can configure OAuth2 for certain domain!"); } TenantId tenantId = (TenantId) entityId; - result = getTenantOAuth2ClientsParams(tenantId); - OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); - if (systemOAuth2ClientsParams != null) { + result = getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams().get(0); + if (systemOAuth2ClientsDomainParams != null) { ArrayList tenantClientRegistrations = new ArrayList<>(result.getClientRegistrations()); - tenantClientRegistrations.addAll(systemOAuth2ClientsParams.getClientRegistrations()); + tenantClientRegistrations.addAll(systemOAuth2ClientsDomainParams.getClientRegistrations()); result = result.toBuilder() .clientRegistrations(tenantClientRegistrations) .build(); } } else { - result = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + result = systemOAuth2ClientsDomainParams; } return result; } @@ -423,6 +464,11 @@ public class OAuth2ServiceImpl implements OAuth2Service { return client; } + private Stream toClientRegistrationStream(OAuth2ClientsParams oAuth2ClientsParams) { + return oAuth2ClientsParams.getClientsDomainsParams().stream() + .flatMap(oAuth2ClientsDomainParams -> oAuth2ClientsDomainParams.getClientRegistrations().stream()); + } + private final Consumer validator = clientRegistration -> { if (StringUtils.isEmpty(clientRegistration.getRegistrationId())) { throw new DataValidationException("Registration ID should be specified!"); From 0149c4b6524e9e3c039cba8210bd03489ec718dd Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 25 Jun 2020 16:45:50 +0300 Subject: [PATCH 047/117] Get System OAuth2 config without tenantId --- .../server/controller/OAuth2Controller.java | 2 +- .../server/dao/oauth2/OAuth2Service.java | 2 +- .../server/dao/oauth2/OAuth2ServiceImpl.java | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index 18e7a4eacb..8d94cee08e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -65,7 +65,7 @@ public class OAuth2Controller extends BaseController { checkOAuth2ConfigPermissions(Operation.READ); OAuth2ClientsParams oAuth2ClientsParams = null; if (Authority.SYS_ADMIN.equals(authority)) { - oAuth2ClientsParams = oauth2Service.getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + oAuth2ClientsParams = oauth2Service.getSystemOAuth2ClientsParams(); } else if (Authority.TENANT_ADMIN.equals(authority)) { oAuth2ClientsParams = oauth2Service.getTenantOAuth2ClientsParams(getCurrentUser().getTenantId()); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 17dc6d5f61..a575142499 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -36,7 +36,7 @@ public interface OAuth2Service { OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams); - OAuth2ClientsParams getSystemOAuth2ClientsParams(TenantId tenantId); + OAuth2ClientsParams getSystemOAuth2ClientsParams(); OAuth2ClientsParams getTenantOAuth2ClientsParams(TenantId tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index a3bd1c90b8..7d28af9fc0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -145,7 +145,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { cacheWriteLock.unlock(); } - return getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + return getSystemOAuth2ClientsParams(); } @Override @@ -301,12 +301,12 @@ public class OAuth2ServiceImpl implements OAuth2Service { } @Override - public OAuth2ClientsParams getSystemOAuth2ClientsParams(TenantId tenantId) { - return clientsParams.get(tenantId); + public OAuth2ClientsParams getSystemOAuth2ClientsParams() { + return clientsParams.get(TenantId.SYS_TENANT_ID); } - private OAuth2ClientsParams getSystemOAuth2ClientsParamsFromDb(TenantId tenantId) { - AdminSettings oauth2ClientsParamsSettings = adminSettingsService.findAdminSettingsByKey(tenantId, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + private OAuth2ClientsParams getSystemOAuth2ClientsParamsFromDb() { + AdminSettings oauth2ClientsParamsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); String json = null; if (oauth2ClientsParamsSettings != null) { json = oauth2ClientsParamsSettings.getJsonValue().get(SYSTEM_SETTINGS_OAUTH2_VALUE).asText(); @@ -320,7 +320,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { } private Map getAllOAuth2ClientsParams() { - OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParamsFromDb(TenantId.SYS_TENANT_ID); + OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParamsFromDb(); ListenableFuture> jsonFuture = getAllOAuth2ClientsParamsAttribute(); try { return Futures.transform(jsonFuture, @@ -389,7 +389,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { AdminSettings oauth2ClientsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, constructAdminSettingsDomainKey(domainName)); OAuth2ClientsDomainParams result; - OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(TenantId.SYS_TENANT_ID); + OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(); OAuth2ClientsDomainParams systemOAuth2ClientsDomainParams = systemOAuth2ClientsParams != null ? systemOAuth2ClientsParams.getClientsDomainsParams().stream() .filter(oAuth2ClientsDomainParams -> domainName.equals(oAuth2ClientsDomainParams.getDomainName())) From 0c7cd1297c45e67f3f06ad861ea19e2fb2a13b18 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 25 Jun 2020 17:06:55 +0300 Subject: [PATCH 048/117] Moved static fields and methods to Util class --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 33 ++-------------- .../server/dao/oauth2/OAuth2Utils.java | 39 +++++++++++++++++++ 2 files changed, 42 insertions(+), 30 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 7d28af9fc0..0bcc67e85a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -49,18 +49,14 @@ import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.thingsboard.server.dao.oauth2.OAuth2Utils.*; + @Slf4j @Service public class OAuth2ServiceImpl implements OAuth2Service { private static final ObjectMapper mapper = new ObjectMapper(); - private static final String OAUTH2_CLIENT_REGISTRATIONS_PARAMS = "oauth2ClientRegistrationsParams"; - private static final String OAUTH2_CLIENT_REGISTRATIONS_DOMAIN_NAME_PREFIX = "oauth2ClientRegistrationsDomainNamePrefix"; - private static final String ALLOW_OAUTH2_CONFIGURATION = "allowOAuth2Configuration"; - private static final String SYSTEM_SETTINGS_OAUTH2_VALUE = "value"; - private static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; - private final ReentrantLock cacheWriteLock = new ReentrantLock(); private final Map clientsParams = new ConcurrentHashMap<>(); @@ -118,7 +114,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { OAuth2ClientsDomainParams oAuth2ClientsDomainParams = getMergedOAuth2ClientsParams(domainName); return oAuth2ClientsDomainParams != null && oAuth2ClientsDomainParams.getClientRegistrations() != null ? oAuth2ClientsDomainParams.getClientRegistrations().stream() - .map(this::toClientInfo) + .map(OAuth2Utils::toClientInfo) .collect(Collectors.toList()) : Collections.emptyList() ; @@ -419,16 +415,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { return result; } - private String constructAdminSettingsDomainKey(String domainName) { - String clientRegistrationsKey; - if (StringUtils.isEmpty(domainName)) { - clientRegistrationsKey = OAUTH2_CLIENT_REGISTRATIONS_PARAMS; - } else { - clientRegistrationsKey = OAUTH2_CLIENT_REGISTRATIONS_DOMAIN_NAME_PREFIX + "_" + domainName; - } - return clientRegistrationsKey; - } - private OAuth2ClientsParams constructOAuth2ClientsParams(String json) { OAuth2ClientsParams result = null; if (!StringUtils.isEmpty(json)) { @@ -456,19 +442,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { return json; } - private OAuth2ClientInfo toClientInfo(OAuth2ClientRegistration clientRegistration) { - OAuth2ClientInfo client = new OAuth2ClientInfo(); - client.setName(clientRegistration.getLoginButtonLabel()); - client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getRegistrationId())); - client.setIcon(clientRegistration.getLoginButtonIcon()); - return client; - } - - private Stream toClientRegistrationStream(OAuth2ClientsParams oAuth2ClientsParams) { - return oAuth2ClientsParams.getClientsDomainsParams().stream() - .flatMap(oAuth2ClientsDomainParams -> oAuth2ClientsDomainParams.getClientRegistrations().stream()); - } - private final Consumer validator = clientRegistration -> { if (StringUtils.isEmpty(clientRegistration.getRegistrationId())) { throw new DataValidationException("Registration ID should be specified!"); diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java new file mode 100644 index 0000000000..faa96e1d4f --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -0,0 +1,39 @@ +package org.thingsboard.server.dao.oauth2; + +import org.springframework.util.StringUtils; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; + +import java.util.stream.Stream; + +public class OAuth2Utils { + public static final String OAUTH2_CLIENT_REGISTRATIONS_PARAMS = "oauth2ClientRegistrationsParams"; + public static final String OAUTH2_CLIENT_REGISTRATIONS_DOMAIN_NAME_PREFIX = "oauth2ClientRegistrationsDomainNamePrefix"; + public static final String ALLOW_OAUTH2_CONFIGURATION = "allowOAuth2Configuration"; + public static final String SYSTEM_SETTINGS_OAUTH2_VALUE = "value"; + public static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; + + public static String constructAdminSettingsDomainKey(String domainName) { + String clientRegistrationsKey; + if (StringUtils.isEmpty(domainName)) { + clientRegistrationsKey = OAUTH2_CLIENT_REGISTRATIONS_PARAMS; + } else { + clientRegistrationsKey = OAUTH2_CLIENT_REGISTRATIONS_DOMAIN_NAME_PREFIX + "_" + domainName; + } + return clientRegistrationsKey; + } + + public static OAuth2ClientInfo toClientInfo(OAuth2ClientRegistration clientRegistration) { + OAuth2ClientInfo client = new OAuth2ClientInfo(); + client.setName(clientRegistration.getLoginButtonLabel()); + client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getRegistrationId())); + client.setIcon(clientRegistration.getLoginButtonIcon()); + return client; + } + + public static Stream toClientRegistrationStream(OAuth2ClientsParams oAuth2ClientsParams) { + return oAuth2ClientsParams.getClientsDomainsParams().stream() + .flatMap(oAuth2ClientsDomainParams -> oAuth2ClientsDomainParams.getClientRegistrations().stream()); + } +} From c30e8b3a2d8cfdf3afdf40028422e48916d2c115 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 25 Jun 2020 18:04:39 +0300 Subject: [PATCH 049/117] Added method to delete system oauth2 config --- .../org/thingsboard/server/dao/oauth2/OAuth2Service.java | 2 ++ .../thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index a575142499..320a2e650a 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -42,6 +42,8 @@ public interface OAuth2Service { void deleteTenantOAuth2ClientsParams(TenantId tenantId); + void deleteSystemOAuth2ClientsParams(); + boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 0bcc67e85a..6d344d9640 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -352,6 +352,12 @@ public class OAuth2ServiceImpl implements OAuth2Service { clientsParams.remove(tenantId); } + @Override + public void deleteSystemOAuth2ClientsParams() { + adminSettingsService.deleteAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + clientsParams.remove(TenantId.SYS_TENANT_ID); + } + @Override public boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId) { Tenant tenant = tenantService.findTenantById(tenantId); From 328c827744ac897729b30fc67ed085a2e6250c2b Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 25 Jun 2020 18:05:21 +0300 Subject: [PATCH 050/117] Made method 'getAllOAuth2Params' public --- .../java/org/thingsboard/server/dao/oauth2/OAuth2Service.java | 3 +++ .../org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 320a2e650a..380738583f 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -24,6 +24,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; import java.util.List; +import java.util.Map; public interface OAuth2Service { Pair getClientRegistrationWithTenant(String registrationId); @@ -46,4 +47,6 @@ public interface OAuth2Service { boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId); + Map getAllOAuth2ClientsParams(); + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 6d344d9640..c91739c9e6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -315,7 +315,9 @@ public class OAuth2ServiceImpl implements OAuth2Service { return clientsParams.get(tenantId); } - private Map getAllOAuth2ClientsParams() { + // TODO this is just for test, maybe there's a better way to test it without exporting to interface + @Override + public Map getAllOAuth2ClientsParams() { OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParamsFromDb(); ListenableFuture> jsonFuture = getAllOAuth2ClientsParamsAttribute(); try { From a2bf3d687f70c87e4d0df7361b570d03070e960c Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 25 Jun 2020 18:22:28 +0300 Subject: [PATCH 051/117] Added tests for OAuth2Service --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 1 + .../dao/service/BaseOAuth2ServiceTest.java | 415 ++++++++++++++++++ .../dao/service/sql/OAuth2ServiceSqlTest.java | 23 + 3 files changed, 439 insertions(+) create mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java create mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/sql/OAuth2ServiceSqlTest.java diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index c91739c9e6..d6210df266 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -371,6 +371,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { } } + // TODO maybe it's better to load all tenants and get attribute for each one private ListenableFuture> getAllOAuth2ClientsParamsAttribute() { ListenableFuture> entityAttributeKvEntriesFuture; try { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java new file mode 100644 index 0000000000..86fac00f1f --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -0,0 +1,415 @@ +/** + * 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.service; + +import org.apache.commons.lang3.tuple.Pair; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.common.data.DataConstants; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.oauth2.OAuth2Service; +import org.thingsboard.server.dao.oauth2.OAuth2Utils; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static org.thingsboard.server.dao.oauth2.OAuth2Utils.ALLOW_OAUTH2_CONFIGURATION; + +public class BaseOAuth2ServiceTest extends AbstractServiceTest { + + @Autowired + protected OAuth2Service oAuth2Service; + + @Autowired + protected AttributesService attributesService; + + private TenantId tenantId; + + @Before + public void beforeRun() throws Exception { + Tenant tenant = new Tenant(); + tenant.setTitle("My tenant"); + Tenant savedTenant = tenantService.saveTenant(tenant); + Assert.assertNotNull(savedTenant); + tenantId = savedTenant.getId(); + + Assert.assertNull(oAuth2Service.getSystemOAuth2ClientsParams()); + Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId)); + + Assert.assertTrue(attributesService.findAll(tenantId, tenantId, DataConstants.SERVER_SCOPE).get().isEmpty()); + Assert.assertNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + } + + @After + public void after() throws Exception { + clearSysAdmin(); + + tenantService.deleteTenant(tenantId); + + Assert.assertNull(oAuth2Service.getSystemOAuth2ClientsParams()); + Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId)); + + Assert.assertTrue(attributesService.findAll(tenantId, tenantId, DataConstants.SERVER_SCOPE).get().isEmpty()); + Assert.assertNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + } + + @Test + public void testIsOAuth2Allowed_null() throws IOException { + updateTenantAllowOAuth2Setting(null); + Assert.assertTrue(oAuth2Service.isOAuth2ClientRegistrationAllowed(tenantId)); + } + + @Test + public void testIsOAuth2Allowed_false() throws IOException { + updateTenantAllowOAuth2Setting(false); + Assert.assertFalse(oAuth2Service.isOAuth2ClientRegistrationAllowed(tenantId)); + } + + @Test + public void testIsOAuth2Allowed_true() throws IOException { + updateTenantAllowOAuth2Setting(true); + Assert.assertTrue(oAuth2Service.isOAuth2ClientRegistrationAllowed(tenantId)); + } + + @Test + public void testSaveSystemOAuth2() throws IOException { + updateTenantAllowOAuth2Setting(true); + Assert.assertTrue(oAuth2Service.isOAuth2ClientRegistrationAllowed(tenantId)); + } + + @Test(expected = DataValidationException.class) + public void testSaveSystemParamsWithDuplicateDomains() { + oAuth2Service.saveSystemOAuth2ClientsParams(clientsParamsWithDuplicateDomains()); + } + + @Test(expected = DataValidationException.class) + public void testSaveSystemParamsWithDuplicateRegistrationIds() { + oAuth2Service.saveSystemOAuth2ClientsParams(clientsParamsWithDuplicateRegistrationIds()); + } + + @Test(expected = DataValidationException.class) + public void testSaveTenantParamsWithDuplicateRegistrationIds() { + oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParamsWithDuplicateRegistrationIds()); + } + + @Test(expected = DataValidationException.class) + public void testSaveTenantParamsWithMultipleDomains() { + oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, validClientsParamsWithMultipleDomains()); + } + + @Test + public void testSaveSystemParams() { + OAuth2ClientsParams clientsParams = validClientsParams(); + OAuth2ClientsParams savedClientParams = oAuth2Service.saveSystemOAuth2ClientsParams(clientsParams); + + Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + Assert.assertEquals(clientsParams, savedClientParams); + } + + @Test + public void testSaveSystemParamsWithMultipleDomains() { + OAuth2ClientsParams clientsParams = validClientsParamsWithMultipleDomains(); + OAuth2ClientsParams savedClientParams = oAuth2Service.saveSystemOAuth2ClientsParams(clientsParams); + + Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + Assert.assertEquals(clientsParams, savedClientParams); + } + + @Test + public void testFindSystemParams() { + OAuth2ClientsParams clientsParams = validClientsParams(); + oAuth2Service.saveSystemOAuth2ClientsParams(clientsParams); + + Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + + OAuth2ClientsParams foundClientParams = oAuth2Service.getSystemOAuth2ClientsParams(); + Assert.assertNotNull(foundClientParams); + Assert.assertEquals(clientsParams, foundClientParams); + } + + @Test + public void testSaveTenantParams() { + OAuth2ClientsParams clientsParams = validClientsParams(); + OAuth2ClientsDomainParams domainParams = clientsParams.getClientsDomainsParams().get(0); + String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainParams.getDomainName()); + + Assert.assertNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + + OAuth2ClientsParams savedClientParams = oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParams); + + Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + Assert.assertNotNull(savedClientParams); + + OAuth2ClientsDomainParams savedDomainParams = savedClientParams.getClientsDomainsParams().get(0); + Assert.assertNotNull(savedDomainParams.getAdminSettingsId()); + Assert.assertEquals(domainParams.getDomainName(), savedDomainParams.getDomainName()); + Assert.assertEquals(domainParams.getClientRegistrations(), savedDomainParams.getClientRegistrations()); + } + + @Test + public void testFindTenantParams() { + OAuth2ClientsParams clientsParams = validClientsParams(); + OAuth2ClientsDomainParams domainParams = clientsParams.getClientsDomainsParams().get(0); + String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainParams.getDomainName()); + + Assert.assertNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + + OAuth2ClientsParams savedClientsParams = oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParams); + + Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + + OAuth2ClientsParams foundClientsParams = oAuth2Service.getTenantOAuth2ClientsParams(tenantId); + Assert.assertEquals(savedClientsParams, foundClientsParams); + } + + @Test + public void testGetClientRegistrationWithTenant() { + OAuth2ClientsParams tenantClientsParams = validClientsParams(); + OAuth2ClientsParams sysAdminClientsParams = validClientsParams(); + + oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, tenantClientsParams); + oAuth2Service.saveSystemOAuth2ClientsParams(sysAdminClientsParams); + + OAuth2Utils.toClientRegistrationStream(tenantClientsParams) + .forEach(clientRegistration -> { + Pair pair = oAuth2Service.getClientRegistrationWithTenant(clientRegistration.getRegistrationId()); + Assert.assertEquals(tenantId, pair.getKey()); + Assert.assertEquals(clientRegistration.getRegistrationId(), pair.getValue().getRegistrationId()); + }); + OAuth2Utils.toClientRegistrationStream(sysAdminClientsParams) + .forEach(clientRegistration -> { + Pair pair = oAuth2Service.getClientRegistrationWithTenant(clientRegistration.getRegistrationId()); + Assert.assertNotNull(pair); + Assert.assertEquals(TenantId.SYS_TENANT_ID, pair.getKey()); + Assert.assertEquals(clientRegistration.getRegistrationId(), pair.getValue().getRegistrationId()); + }); + } + + @Test + public void testGetClientRegistration() { + OAuth2ClientsParams tenantClientsParams = validClientsParams(); + OAuth2ClientsParams sysAdminClientsParams = validClientsParams(); + + oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, tenantClientsParams); + oAuth2Service.saveSystemOAuth2ClientsParams(sysAdminClientsParams); + + Stream.concat( + OAuth2Utils.toClientRegistrationStream(tenantClientsParams), + OAuth2Utils.toClientRegistrationStream(sysAdminClientsParams) + ) + .forEach(clientRegistration -> { + OAuth2ClientRegistration foundClientRegistration = oAuth2Service.getClientRegistration(clientRegistration.getRegistrationId()); + Assert.assertNotNull(foundClientRegistration); + Assert.assertEquals(clientRegistration.getRegistrationId(), foundClientRegistration.getRegistrationId()); + }); + + } + + @Test + public void testGetOAuth2Clients() { + OAuth2ClientsParams tenantClientsParams = validClientsParams(); + OAuth2ClientsParams sysAdminClientsParams = validClientsParams(); + + OAuth2ClientsDomainParams tenantDomainParams = tenantClientsParams.getClientsDomainsParams().get(0); + OAuth2ClientsDomainParams systemDomainParams = sysAdminClientsParams.getClientsDomainsParams().get(0); + systemDomainParams.setDomainName(tenantDomainParams.getDomainName()); + + oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, tenantClientsParams); + oAuth2Service.saveSystemOAuth2ClientsParams(sysAdminClientsParams); + + List oAuth2Clients = oAuth2Service.getOAuth2Clients(tenantDomainParams.getDomainName()); + + Set actualLabels = Stream.concat( + tenantDomainParams.getClientRegistrations().stream() + .map(OAuth2ClientRegistration::getLoginButtonLabel), + systemDomainParams.getClientRegistrations().stream() + .map(OAuth2ClientRegistration::getLoginButtonLabel) + ).collect(Collectors.toSet()); + + + Set foundLabels = oAuth2Clients.stream().map(OAuth2ClientInfo::getName).collect(Collectors.toSet()); + Assert.assertEquals(actualLabels, foundLabels); + } + + @Test + public void testGetAllOAuth2ClientsParams() { + OAuth2ClientsParams tenantClientsParams = validClientsParams(); + OAuth2ClientsParams sysAdminClientsParams = validClientsParams(); + + Map emptyParams = oAuth2Service.getAllOAuth2ClientsParams(); + Assert.assertTrue(emptyParams.isEmpty()); + + OAuth2ClientsParams savedTenantParams = oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, tenantClientsParams); + OAuth2ClientsParams savedSystemParams = oAuth2Service.saveSystemOAuth2ClientsParams(sysAdminClientsParams); + + Map clientsParams = oAuth2Service.getAllOAuth2ClientsParams(); + + OAuth2ClientsParams foundTenantParams = clientsParams.get(tenantId); + Assert.assertEquals(savedTenantParams, foundTenantParams); + + OAuth2ClientsParams foundSystemParams = clientsParams.get(TenantId.SYS_TENANT_ID); + Assert.assertEquals(savedSystemParams, foundSystemParams); + } + + @Test + public void testDeleteSystemOAuth2ClientsParams() { + OAuth2ClientsParams sysAdminClientsParams = validClientsParams(); + + Assert.assertNull(oAuth2Service.getSystemOAuth2ClientsParams()); + + oAuth2Service.saveSystemOAuth2ClientsParams(sysAdminClientsParams); + + Assert.assertNotNull(oAuth2Service.getSystemOAuth2ClientsParams()); + } + + @Test + public void testDeleteTenantOAuth2ClientsParams() { + OAuth2ClientsParams tenantClientsParams = validClientsParams(); + + Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId)); + + oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, tenantClientsParams); + + Assert.assertNotNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId)); + } + + + private void clearSysAdmin() { + oAuth2Service.deleteSystemOAuth2ClientsParams(); + Assert.assertNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + } + + private void updateTenantAllowOAuth2Setting(Boolean allowOAuth2) throws IOException { + Tenant tenant = tenantService.findTenantById(tenantId); + if (allowOAuth2 == null) { + tenant.setAdditionalInfo(mapper.readTree("{}")); + } else { + String additionalInfo = "{\"" + ALLOW_OAUTH2_CONFIGURATION + "\":" + allowOAuth2 + "}"; + tenant.setAdditionalInfo(mapper.readTree(additionalInfo)); + tenantService.saveTenant(tenant); + } + } + + private OAuth2ClientsParams validClientsParams() { + OAuth2ClientRegistration first = validClientRegistration(); + OAuth2ClientRegistration second = validClientRegistration(); + return OAuth2ClientsParams.builder() + .clientsDomainsParams(Collections.singletonList( + OAuth2ClientsDomainParams.builder() + .domainName(UUID.randomUUID().toString()) + .clientRegistrations(Arrays.asList(first, second)) + .build() + )) + .build(); + } + + private OAuth2ClientsParams validClientsParamsWithMultipleDomains() { + OAuth2ClientRegistration first = validClientRegistration(); + OAuth2ClientRegistration second = validClientRegistration(); + OAuth2ClientRegistration third = validClientRegistration(); + return OAuth2ClientsParams.builder() + .clientsDomainsParams(Arrays.asList( + OAuth2ClientsDomainParams.builder() + .domainName(UUID.randomUUID().toString()) + .clientRegistrations(Arrays.asList(first, second)) + .build(), + OAuth2ClientsDomainParams.builder() + .domainName(UUID.randomUUID().toString()) + .clientRegistrations(Arrays.asList(third)) + .build() + )) + .build(); + } + + + private OAuth2ClientsParams clientsParamsWithDuplicateDomains() { + OAuth2ClientRegistration first = validClientRegistration(); + OAuth2ClientRegistration second = validClientRegistration(); + OAuth2ClientRegistration third = validClientRegistration(); + return OAuth2ClientsParams.builder() + .clientsDomainsParams(Arrays.asList( + OAuth2ClientsDomainParams.builder() + .domainName("domain") + .clientRegistrations(Collections.singletonList(first)) + .build(), + OAuth2ClientsDomainParams.builder() + .domainName("domain") + .clientRegistrations(Collections.singletonList(second)) + .build(), + OAuth2ClientsDomainParams.builder() + .domainName(UUID.randomUUID().toString()) + .clientRegistrations(Collections.singletonList(third)) + .build() + )) + .build(); + } + + private OAuth2ClientsParams clientsParamsWithDuplicateRegistrationIds() { + OAuth2ClientRegistration first = validClientRegistration(); + first.setRegistrationId("registrationId"); + OAuth2ClientRegistration second = validClientRegistration(); + OAuth2ClientRegistration third = validClientRegistration(); + third.setRegistrationId("registrationId"); + return OAuth2ClientsParams.builder() + .clientsDomainsParams(Arrays.asList( + OAuth2ClientsDomainParams.builder() + .domainName(UUID.randomUUID().toString()) + .clientRegistrations(Arrays.asList(first, second, third)) + .build() + )) + .build(); + } + + private OAuth2ClientRegistration validClientRegistration() { + return OAuth2ClientRegistration.builder() + .registrationId(UUID.randomUUID().toString()) + .mapperConfig(OAuth2MapperConfig.builder() + .allowUserCreation(true) + .activateUser(true) + .type(MapperType.CUSTOM) + .customConfig( + OAuth2CustomMapperConfig.builder() + .url("localhost:8082") + .username("test") + .password("test") + .build() + ) + .build()) + .clientId("clientId") + .clientSecret("clientSecret") + .authorizationUri("authorizationUri") + .tokenUri("tokenUri") + .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") + .scope("scope") + .authorizationGrantType("authorizationGrantType") + .userInfoUri("userInfoUri") + .userNameAttributeName("userNameAttributeName") + .jwkSetUri("jwkSetUri") + .clientAuthenticationMethod("clientAuthenticationMethod") + .clientName("clientName") + .loginButtonLabel("loginButtonLabel") + .build(); + } +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/OAuth2ServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/OAuth2ServiceSqlTest.java new file mode 100644 index 0000000000..64eba8f89a --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/OAuth2ServiceSqlTest.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.service.sql; + +import org.thingsboard.server.dao.service.BaseOAuth2ServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class OAuth2ServiceSqlTest extends BaseOAuth2ServiceTest { +} From 909a989e14d238c571f034277db43a9fd468686d Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 26 Jun 2020 17:41:00 +0300 Subject: [PATCH 052/117] Removed authorizationGrantType from ClientRegistration --- .../server/common/data/oauth2/OAuth2ClientRegistration.java | 1 - .../server/dao/oauth2/HybridClientRegistrationRepository.java | 2 +- .../org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java | 3 --- .../thingsboard/server/dao/service/BaseOAuth2ServiceTest.java | 1 - 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java index 1fc2743e73..231a8167f0 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -35,7 +35,6 @@ public class OAuth2ClientRegistration { private String tokenUri; private String redirectUriTemplate; private String scope; - private String authorizationGrantType; private String userInfoUri; private String userNameAttributeName; private String jwkSetUri; diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java index 3c9743e4d3..d581899fae 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java @@ -47,7 +47,7 @@ public class HybridClientRegistrationRepository implements ClientRegistrationRep .redirectUriTemplate(localClientRegistration.getRedirectUriTemplate()) .scope(localClientRegistration.getScope().split(",")) .clientName(localClientRegistration.getClientName()) - .authorizationGrantType(new AuthorizationGrantType(localClientRegistration.getAuthorizationGrantType())) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .userInfoUri(localClientRegistration.getUserInfoUri()) .userNameAttributeName(localClientRegistration.getUserNameAttributeName()) .jwkSetUri(localClientRegistration.getJwkSetUri()) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index d6210df266..e82eca3a71 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -473,9 +473,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { if (StringUtils.isEmpty(clientRegistration.getScope())) { throw new DataValidationException("Scope should be specified!"); } - if (StringUtils.isEmpty(clientRegistration.getAuthorizationGrantType())) { - throw new DataValidationException("Authorization grant type should be specified!"); - } if (StringUtils.isEmpty(clientRegistration.getUserInfoUri())) { throw new DataValidationException("User info uri should be specified!"); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index 86fac00f1f..30cf12c2a8 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -403,7 +403,6 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { .tokenUri("tokenUri") .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") .scope("scope") - .authorizationGrantType("authorizationGrantType") .userInfoUri("userInfoUri") .userNameAttributeName("userNameAttributeName") .jwkSetUri("jwkSetUri") From 7d5ceaf3112176ed62a32aa3a1a2a9fb2e1b858c Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Tue, 30 Jun 2020 14:58:34 +0300 Subject: [PATCH 053/117] Removed condition on creating ClientRegistrationRepo --- .../server/dao/oauth2/HybridClientRegistrationRepository.java | 1 - 1 file changed, 1 deletion(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java index d581899fae..26a3312376 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java @@ -24,7 +24,6 @@ import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; -@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") @Component public class HybridClientRegistrationRepository implements ClientRegistrationRepository { From 201ffebc5af2853885b9750cf5c14c1d0bbe1c75 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Tue, 30 Jun 2020 15:10:55 +0300 Subject: [PATCH 054/117] Renamed lock in OAuth2Service --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index e82eca3a71..659601deaa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -47,7 +47,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.stream.Collectors; -import java.util.stream.Stream; import static org.thingsboard.server.dao.oauth2.OAuth2Utils.*; @@ -57,7 +56,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { private static final ObjectMapper mapper = new ObjectMapper(); - private final ReentrantLock cacheWriteLock = new ReentrantLock(); + private final ReentrantLock clientRegistrationSaveLock = new ReentrantLock(); private final Map clientsParams = new ConcurrentHashMap<>(); @Autowired @@ -126,7 +125,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); - cacheWriteLock.lock(); + clientRegistrationSaveLock.lock(); try { validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); AdminSettings oauth2SystemAdminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); @@ -138,7 +137,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, oauth2SystemAdminSettings); clientsParams.put(TenantId.SYS_TENANT_ID, oAuth2ClientsParams); } finally { - cacheWriteLock.unlock(); + clientRegistrationSaveLock.unlock(); } return getSystemOAuth2ClientsParams(); @@ -152,7 +151,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { validate(oAuth2ClientsParams); validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); - cacheWriteLock.lock(); + clientRegistrationSaveLock.lock(); try { validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); @@ -170,7 +169,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { clientsParams.put(tenantId, oAuth2ClientsParams); } finally { - cacheWriteLock.unlock(); + clientRegistrationSaveLock.unlock(); } return getTenantOAuth2ClientsParams(tenantId); From b9b8b49aefe122f6cb6370d5505a3346ec165eff Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Tue, 30 Jun 2020 15:42:37 +0300 Subject: [PATCH 055/117] Get OAuth2Params from DB (not cache) --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 38 ++++++++++++++++--- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 659601deaa..ea8cd21557 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -297,10 +297,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public OAuth2ClientsParams getSystemOAuth2ClientsParams() { - return clientsParams.get(TenantId.SYS_TENANT_ID); - } - - private OAuth2ClientsParams getSystemOAuth2ClientsParamsFromDb() { AdminSettings oauth2ClientsParamsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); String json = null; if (oauth2ClientsParamsSettings != null) { @@ -311,13 +307,24 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public OAuth2ClientsParams getTenantOAuth2ClientsParams(TenantId tenantId) { - return clientsParams.get(tenantId); + ListenableFuture jsonFuture; + if (isOAuth2ClientRegistrationAllowed(tenantId)) { + jsonFuture = getOAuth2ClientsParamsAttribute(tenantId); + } else { + jsonFuture = Futures.immediateFuture(""); + } + try { + return Futures.transform(jsonFuture, this::constructOAuth2ClientsParams, MoreExecutors.directExecutor()).get(); + } catch (InterruptedException | ExecutionException e) { + log.error("Failed to read OAuth2 Clients Params from attributes!", e); + throw new RuntimeException("Failed to read OAuth2 Clients Params from attributes!", e); + } } // TODO this is just for test, maybe there's a better way to test it without exporting to interface @Override public Map getAllOAuth2ClientsParams() { - OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParamsFromDb(); + OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(); ListenableFuture> jsonFuture = getAllOAuth2ClientsParamsAttribute(); try { return Futures.transform(jsonFuture, @@ -370,6 +377,25 @@ public class OAuth2ServiceImpl implements OAuth2Service { } } + private ListenableFuture getOAuth2ClientsParamsAttribute(TenantId tenantId) { + ListenableFuture> attributeKvEntriesFuture; + try { + attributeKvEntriesFuture = attributesService.find(tenantId, tenantId, DataConstants.SERVER_SCOPE, + Collections.singletonList(OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + } catch (Exception e) { + log.error("Unable to read OAuth2 Clients Params from attributes!", e); + throw new IncorrectParameterException("Unable to read OAuth2 Clients Params from attributes!"); + } + return Futures.transform(attributeKvEntriesFuture, attributeKvEntries -> { + if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { + AttributeKvEntry kvEntry = attributeKvEntries.get(0); + return kvEntry.getValueAsString(); + } else { + return ""; + } + }, MoreExecutors.directExecutor()); + } + // TODO maybe it's better to load all tenants and get attribute for each one private ListenableFuture> getAllOAuth2ClientsParamsAttribute() { ListenableFuture> entityAttributeKvEntriesFuture; From b7321716117a411d63a71290502fc356b34aee38 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Tue, 30 Jun 2020 16:05:10 +0300 Subject: [PATCH 056/117] Removed cache from OAuth2Service --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 21 ++++--------------- .../dao/service/BaseOAuth2ServiceTest.java | 16 +++++++------- 2 files changed, 12 insertions(+), 25 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index ea8cd21557..0e9c7f8f7c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -57,7 +57,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { private static final ObjectMapper mapper = new ObjectMapper(); private final ReentrantLock clientRegistrationSaveLock = new ReentrantLock(); - private final Map clientsParams = new ConcurrentHashMap<>(); @Autowired private Environment environment; @@ -75,18 +74,9 @@ public class OAuth2ServiceImpl implements OAuth2Service { return environment.acceptsProfiles("install"); } - @PostConstruct - public void init() { - if (isInstall()) return; - - Map allOAuth2ClientsParams = getAllOAuth2ClientsParams(); - - allOAuth2ClientsParams.forEach(clientsParams::put); - } - @Override public Pair getClientRegistrationWithTenant(String registrationId) { - return clientsParams.entrySet().stream() + return getAllOAuth2ClientsParams().entrySet().stream() .map(entry -> { TenantId tenantId = entry.getKey(); OAuth2ClientRegistration clientRegistration = toClientRegistrationStream(entry.getValue()) @@ -135,7 +125,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { String json = toJson(oAuth2ClientsParams); ((ObjectNode) oauth2SystemAdminSettings.getJsonValue()).put(SYSTEM_SETTINGS_OAUTH2_VALUE, json); adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, oauth2SystemAdminSettings); - clientsParams.put(TenantId.SYS_TENANT_ID, oAuth2ClientsParams); } finally { clientRegistrationSaveLock.unlock(); } @@ -167,7 +156,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { throw new IncorrectParameterException("Unable to save OAuth2 Client Registration Params to attributes!"); } - clientsParams.put(tenantId, oAuth2ClientsParams); } finally { clientRegistrationSaveLock.unlock(); } @@ -258,7 +246,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { toClientRegistrationStream(inputOAuth2ClientsParams) .map(OAuth2ClientRegistration::getRegistrationId) .forEach(registrationId -> { - clientsParams.forEach((paramsTenantId, oAuth2ClientsParams) -> { + getAllOAuth2ClientsParams().forEach((paramsTenantId, oAuth2ClientsParams) -> { boolean registrationExists = toClientRegistrationStream(oAuth2ClientsParams) .map(OAuth2ClientRegistration::getRegistrationId) .anyMatch(registrationId::equals); @@ -352,23 +340,22 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public void deleteTenantOAuth2ClientsParams(TenantId tenantId) { OAuth2ClientsParams params = getTenantOAuth2ClientsParams(tenantId); - if (params == null) return; + if (params == null || params.getClientsDomainsParams() == null) return; OAuth2ClientsDomainParams domainParams = params.getClientsDomainsParams().get(0); String settingsKey = constructAdminSettingsDomainKey(domainParams.getDomainName()); adminSettingsService.deleteAdminSettingsByKey(tenantId, settingsKey); attributesService.removeAll(tenantId, tenantId, DataConstants.SERVER_SCOPE, Collections.singletonList(OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); - clientsParams.remove(tenantId); } @Override public void deleteSystemOAuth2ClientsParams() { adminSettingsService.deleteAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS); - clientsParams.remove(TenantId.SYS_TENANT_ID); } @Override public boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId) { Tenant tenant = tenantService.findTenantById(tenantId); + if (tenant == null) return false; JsonNode allowOAuth2ConfigurationJsonNode = tenant.getAdditionalInfo() != null ? tenant.getAdditionalInfo().get(ALLOW_OAUTH2_CONFIGURATION) : null; if (allowOAuth2ConfigurationJsonNode == null) { return true; diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index 30cf12c2a8..09fec5af4a 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -55,8 +55,8 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { Assert.assertNotNull(savedTenant); tenantId = savedTenant.getId(); - Assert.assertNull(oAuth2Service.getSystemOAuth2ClientsParams()); - Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId)); + Assert.assertNull(oAuth2Service.getSystemOAuth2ClientsParams().getClientsDomainsParams()); + Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams()); Assert.assertTrue(attributesService.findAll(tenantId, tenantId, DataConstants.SERVER_SCOPE).get().isEmpty()); Assert.assertNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); @@ -68,8 +68,8 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { tenantService.deleteTenant(tenantId); - Assert.assertNull(oAuth2Service.getSystemOAuth2ClientsParams()); - Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId)); + Assert.assertNull(oAuth2Service.getSystemOAuth2ClientsParams().getClientsDomainsParams()); + Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams()); Assert.assertTrue(attributesService.findAll(tenantId, tenantId, DataConstants.SERVER_SCOPE).get().isEmpty()); Assert.assertNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); @@ -277,22 +277,22 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { public void testDeleteSystemOAuth2ClientsParams() { OAuth2ClientsParams sysAdminClientsParams = validClientsParams(); - Assert.assertNull(oAuth2Service.getSystemOAuth2ClientsParams()); + Assert.assertNull(oAuth2Service.getSystemOAuth2ClientsParams().getClientsDomainsParams()); oAuth2Service.saveSystemOAuth2ClientsParams(sysAdminClientsParams); - Assert.assertNotNull(oAuth2Service.getSystemOAuth2ClientsParams()); + Assert.assertNotNull(oAuth2Service.getSystemOAuth2ClientsParams().getClientsDomainsParams()); } @Test public void testDeleteTenantOAuth2ClientsParams() { OAuth2ClientsParams tenantClientsParams = validClientsParams(); - Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId)); + Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams()); oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, tenantClientsParams); - Assert.assertNotNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId)); + Assert.assertNotNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams()); } From 64a7b160fc7fd4d051ff6efed099eeceea30fe1e Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 6 Jul 2020 11:28:43 +0300 Subject: [PATCH 057/117] Add license header --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 8 ++++---- .../server/dao/oauth2/OAuth2Utils.java | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 0e9c7f8f7c..7e681e4f6e 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java index faa96e1d4f..003f87cc73 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -1,3 +1,18 @@ +/** + * 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.util.StringUtils; From e4777d9267ea5f7fe752176d54bf28880a7b2a08 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Mon, 6 Jul 2020 12:26:38 +0300 Subject: [PATCH 058/117] Fixed nullpointers, refactored, added test --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 45 +++++++++++++------ .../dao/service/BaseOAuth2ServiceTest.java | 6 +++ 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 0e9c7f8f7c..f7485461f7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -403,16 +403,22 @@ public class OAuth2ServiceImpl implements OAuth2Service { } private OAuth2ClientsDomainParams getMergedOAuth2ClientsParams(String domainName) { - AdminSettings oauth2ClientsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, constructAdminSettingsDomainKey(domainName)); - OAuth2ClientsDomainParams result; + OAuth2ClientsDomainParams result = OAuth2ClientsDomainParams.builder() + .domainName(domainName) + .clientRegistrations(new ArrayList<>()) + .build(); OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(); - OAuth2ClientsDomainParams systemOAuth2ClientsDomainParams = systemOAuth2ClientsParams != null ? + OAuth2ClientsDomainParams systemOAuth2ClientsDomainParams = systemOAuth2ClientsParams != null && systemOAuth2ClientsParams.getClientsDomainsParams() != null ? systemOAuth2ClientsParams.getClientsDomainsParams().stream() .filter(oAuth2ClientsDomainParams -> domainName.equals(oAuth2ClientsDomainParams.getDomainName())) .findFirst() .orElse(null) : null; + + result = mergeDomainParams(result, systemOAuth2ClientsDomainParams); + + AdminSettings oauth2ClientsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, constructAdminSettingsDomainKey(domainName)); if (oauth2ClientsSettings != null) { String strEntityType = oauth2ClientsSettings.getJsonValue().get("entityType").asText(); String strEntityId = oauth2ClientsSettings.getJsonValue().get("entityId").asText(); @@ -422,20 +428,33 @@ public class OAuth2ServiceImpl implements OAuth2Service { throw new IllegalStateException("Only tenant can configure OAuth2 for certain domain!"); } TenantId tenantId = (TenantId) entityId; - result = getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams().get(0); - if (systemOAuth2ClientsDomainParams != null) { - ArrayList tenantClientRegistrations = new ArrayList<>(result.getClientRegistrations()); - tenantClientRegistrations.addAll(systemOAuth2ClientsDomainParams.getClientRegistrations()); - result = result.toBuilder() - .clientRegistrations(tenantClientRegistrations) - .build(); - } - } else { - result = systemOAuth2ClientsDomainParams; + OAuth2ClientsParams tenantOAuth2ClientsParams = getTenantOAuth2ClientsParams(tenantId); + OAuth2ClientsDomainParams tenantDomainsParams = tenantOAuth2ClientsParams != null && tenantOAuth2ClientsParams.getClientsDomainsParams() != null ? + tenantOAuth2ClientsParams.getClientsDomainsParams().stream().findFirst().orElse(null) : null; + result = mergeDomainParams(result, tenantDomainsParams); } return result; } + private OAuth2ClientsDomainParams mergeDomainParams(OAuth2ClientsDomainParams sourceParams, OAuth2ClientsDomainParams newParams){ + if (newParams == null) return sourceParams; + + OAuth2ClientsDomainParams.OAuth2ClientsDomainParamsBuilder mergedParamsBuilder = sourceParams.toBuilder(); + + if (newParams.getClientRegistrations() != null){ + List mergedClientRegistrations = sourceParams.getClientRegistrations() != null ? + sourceParams.getClientRegistrations() : new ArrayList<>(); + mergedClientRegistrations.addAll(newParams.getClientRegistrations()); + mergedParamsBuilder.clientRegistrations(mergedClientRegistrations); + } + + if (newParams.getAdminSettingsId() != null){ + mergedParamsBuilder.adminSettingsId(newParams.getAdminSettingsId()); + } + + return mergedParamsBuilder.build(); + } + private OAuth2ClientsParams constructOAuth2ClientsParams(String json) { OAuth2ClientsParams result = null; if (!StringUtils.isEmpty(json)) { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index 09fec5af4a..b5e8280eee 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -253,6 +253,12 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { Assert.assertEquals(actualLabels, foundLabels); } + @Test + public void testGetEmptyOAuth2Clients() { + List oAuth2Clients = oAuth2Service.getOAuth2Clients("random-domain"); + Assert.assertTrue(oAuth2Clients.isEmpty()); + } + @Test public void testGetAllOAuth2ClientsParams() { OAuth2ClientsParams tenantClientsParams = validClientsParams(); From 60857dd7336a8d9485a80c0119b0610b3013f1fb Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Tue, 7 Jul 2020 10:59:44 +0300 Subject: [PATCH 059/117] Refactored OAuth2Controller --- .../server/controller/OAuth2Controller.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index 8d94cee08e..b3b2b1eb83 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -40,8 +40,6 @@ import java.util.List; @RequestMapping("/api") @Slf4j public class OAuth2Controller extends BaseController { - private static final String REGISTRATION_ID = "registrationId"; - @Autowired private OAuth2Service oauth2Service; @@ -57,9 +55,9 @@ public class OAuth2Controller extends BaseController { } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/oauth2/currentOAuth2Configuration", method = RequestMethod.GET, produces = "application/json") + @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") @ResponseBody - public OAuth2ClientsParams getCurrentOAuth2ClientsParams() throws ThingsboardException { + public OAuth2ClientsParams getCurrentClientsParams() throws ThingsboardException { try { Authority authority = getCurrentUser().getAuthority(); checkOAuth2ConfigPermissions(Operation.READ); @@ -76,9 +74,9 @@ public class OAuth2Controller extends BaseController { } @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/oauth2/oAuth2Configuration", method = RequestMethod.POST) + @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) - public OAuth2ClientsParams saveLoginWhiteLabelParams(@RequestBody OAuth2ClientsParams oAuth2ClientsParams) throws ThingsboardException { + public OAuth2ClientsParams saveClientParams(@RequestBody OAuth2ClientsParams oAuth2ClientsParams) throws ThingsboardException { try { Authority authority = getCurrentUser().getAuthority(); checkOAuth2ConfigPermissions(Operation.WRITE); @@ -95,7 +93,7 @@ public class OAuth2Controller extends BaseController { } @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/oauth2/isOAuth2ConfigurationAllowed", method = RequestMethod.GET) + @RequestMapping(value = "/oauth2/config/isAllowed", method = RequestMethod.GET) @ResponseBody public Boolean isOAuth2ConfigurationAllowed() throws ThingsboardException { try { @@ -105,7 +103,6 @@ public class OAuth2Controller extends BaseController { } } - private void checkOAuth2ConfigPermissions(Operation operation) throws ThingsboardException { accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION, operation); } From 8590bcb7093da60d8696e92afdedb3d3587ae107 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Tue, 7 Jul 2020 12:10:35 +0300 Subject: [PATCH 060/117] Refactored OAuth2Service --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 64 +++++++++---------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 0d8af953df..e915dc8bb8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -114,7 +114,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { validate(oAuth2ClientsParams); validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); - clientRegistrationSaveLock.lock(); try { validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); @@ -190,8 +189,8 @@ public class OAuth2ServiceImpl implements OAuth2Service { ); if (existentOAuth2ClientsSettingsById == null) { - log.error("Admin setting ID is already set in login white labeling object, but doesn't exist in the database"); - throw new IllegalStateException("Admin setting ID is already set in login white labeling object, but doesn't exist in the database"); + log.error("Admin setting ID is already set in OAuth2 Client Params object, but doesn't exist in the database"); + throw new IllegalStateException("Admin setting ID is already set in OAuth2 Client Params object, but doesn't exist in the database"); } if (!existentOAuth2ClientsSettingsById.getKey().equals(selectedDomainSettingsKey)) { @@ -234,28 +233,27 @@ public class OAuth2ServiceImpl implements OAuth2Service { } private void validateRegistrationIdUniqueness(OAuth2ClientsParams inputOAuth2ClientsParams, TenantId tenantId) { - long distinctRegistrationIds = toClientRegistrationStream(inputOAuth2ClientsParams) + List registrationIds = toClientRegistrationStream(inputOAuth2ClientsParams) .map(OAuth2ClientRegistration::getRegistrationId) - .distinct() - .count(); - long actualRegistrationIds = toClientRegistrationStream(inputOAuth2ClientsParams).count(); - if (distinctRegistrationIds != actualRegistrationIds) { + .collect(Collectors.toList()); + + boolean regIdDuplicates = registrationIds.stream() + .anyMatch(registrationId -> Collections.frequency(registrationIds, registrationId) > 1); + if (regIdDuplicates) { throw new DataValidationException("All registration IDs should be unique!"); } - toClientRegistrationStream(inputOAuth2ClientsParams) - .map(OAuth2ClientRegistration::getRegistrationId) - .forEach(registrationId -> { - getAllOAuth2ClientsParams().forEach((paramsTenantId, oAuth2ClientsParams) -> { - boolean registrationExists = toClientRegistrationStream(oAuth2ClientsParams) - .map(OAuth2ClientRegistration::getRegistrationId) - .anyMatch(registrationId::equals); - if (registrationExists && !tenantId.equals(paramsTenantId)) { - log.error("Current registrationId [{}] already registered in the system!", registrationId); - throw new IncorrectParameterException("Current registrationId [" + registrationId + "] already registered in the system!"); - } - }); - }); + getAllOAuth2ClientsParams().forEach((paramsTenantId, oAuth2ClientsParams) -> { + if (tenantId.equals(paramsTenantId)) return; + Set duplicatedRegistrationIds = toClientRegistrationStream(oAuth2ClientsParams) + .map(OAuth2ClientRegistration::getRegistrationId) + .filter(registrationIds::contains) + .collect(Collectors.toSet()); + if (!duplicatedRegistrationIds.isEmpty()) { + log.error("RegistrationIds [{}] are already registered in the system!", duplicatedRegistrationIds); + throw new IncorrectParameterException("RegistrationIds [" + duplicatedRegistrationIds + "] are already registered in the system!"); + } + }); } private void validate(OAuth2ClientsParams oAuth2ClientsParams) { @@ -266,19 +264,19 @@ public class OAuth2ServiceImpl implements OAuth2Service { } private void validateDomainNames(OAuth2ClientsParams oAuth2ClientsParams) { - oAuth2ClientsParams.getClientsDomainsParams() - .forEach(oAuth2ClientsDomainParams -> { - if (StringUtils.isEmpty(oAuth2ClientsDomainParams.getDomainName())) { - throw new DataValidationException("Domain name should be specified!"); - } - }); - - long distinctDomainNames = oAuth2ClientsParams.getClientsDomainsParams().stream() + List domainNames = oAuth2ClientsParams.getClientsDomainsParams().stream() .map(OAuth2ClientsDomainParams::getDomainName) - .distinct() - .count(); - long actualDomainNames = oAuth2ClientsParams.getClientsDomainsParams().size(); - if (distinctDomainNames != actualDomainNames) { + .collect(Collectors.toList()); + + domainNames.forEach(domainName -> { + if (StringUtils.isEmpty(domainName)) { + throw new DataValidationException("Domain name should be specified!"); + } + }); + + boolean duplicateDomainNames = domainNames.stream() + .anyMatch(domainName -> Collections.frequency(domainNames, domainName) > 1); + if (duplicateDomainNames) { throw new DataValidationException("All domain names should be unique!"); } } From 7468f4b713bc1563d86d515cbf09746649c582df Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Tue, 7 Jul 2020 13:03:11 +0300 Subject: [PATCH 061/117] Added code for validating AdminSettingsId --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index e915dc8bb8..5ce6256816 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -258,6 +258,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { private void validate(OAuth2ClientsParams oAuth2ClientsParams) { validateDomainNames(oAuth2ClientsParams); + validateAdminSettingsIds(oAuth2ClientsParams); toClientRegistrationStream(oAuth2ClientsParams) .forEach(validator); @@ -281,6 +282,19 @@ public class OAuth2ServiceImpl implements OAuth2Service { } } + private void validateAdminSettingsIds(OAuth2ClientsParams oAuth2ClientsParams) { + List adminSettingsIds = oAuth2ClientsParams.getClientsDomainsParams().stream() + .map(OAuth2ClientsDomainParams::getAdminSettingsId) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + boolean duplicateAdminSettingsIds = adminSettingsIds.stream() + .anyMatch(adminSettingsId -> Collections.frequency(adminSettingsIds, adminSettingsId) > 1); + if (duplicateAdminSettingsIds) { + throw new DataValidationException("All admin settings ids should be unique!"); + } + } + @Override public OAuth2ClientsParams getSystemOAuth2ClientsParams() { AdminSettings oauth2ClientsParamsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); From 65d075035f19ec65f700e5b27530dec362281f85 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Tue, 7 Jul 2020 15:07:20 +0300 Subject: [PATCH 062/117] Removed AdminSettingsId from ClientParams --- .../oauth2/OAuth2ClientsDomainParams.java | 2 - .../server/dao/oauth2/OAuth2ServiceImpl.java | 103 +++++++----------- .../dao/service/BaseOAuth2ServiceTest.java | 92 ++++++++++++++-- 3 files changed, 123 insertions(+), 74 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java index d9dc07d8ea..3ebfa783c2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java @@ -27,7 +27,5 @@ import java.util.List; @AllArgsConstructor public class OAuth2ClientsDomainParams { private String domainName; - private String adminSettingsId; - private List clientRegistrations; } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 5ce6256816..8857e6ceb9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -133,9 +133,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) { - if (oAuth2ClientsParams.getClientsDomainsParams().size() != 1) { - throw new DataValidationException("Tenant can configure OAuth2 only for one domain!"); - } validate(oAuth2ClientsParams); validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); @@ -143,9 +140,10 @@ public class OAuth2ServiceImpl implements OAuth2Service { try { validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); - OAuth2ClientsDomainParams oAuth2ClientsDomainParams = oAuth2ClientsParams.getClientsDomainsParams().get(0); - String adminSettingsId = processTenantAdminSettings(tenantId, oAuth2ClientsDomainParams.getDomainName(), oAuth2ClientsDomainParams.getAdminSettingsId()); - oAuth2ClientsDomainParams.setAdminSettingsId(adminSettingsId); + Set domainNames = oAuth2ClientsParams.getClientsDomainsParams().stream() + .map(OAuth2ClientsDomainParams::getDomainName) + .collect(Collectors.toSet()); + processTenantAdminSettings(tenantId, domainNames); List attributes = createOAuth2ClientsParamsAttributes(oAuth2ClientsParams); try { @@ -170,46 +168,40 @@ public class OAuth2ServiceImpl implements OAuth2Service { return attributes; } - private String processTenantAdminSettings(TenantId tenantId, String domainName, String prevAdminSettingsId) { - String selectedDomainSettingsKey = constructAdminSettingsDomainKey(domainName); - AdminSettings existentAdminSettingsByKey = adminSettingsService.findAdminSettingsByKey(tenantId, selectedDomainSettingsKey); - if (StringUtils.isEmpty(prevAdminSettingsId)) { - if (existentAdminSettingsByKey == null) { - AdminSettings tenantAdminSettings = createTenantAdminSettings(tenantId, selectedDomainSettingsKey); - existentAdminSettingsByKey = adminSettingsService.saveAdminSettings(tenantId, tenantAdminSettings); - return existentAdminSettingsByKey.getId().getId().toString(); - } else { + private void processTenantAdminSettings(TenantId tenantId, Set domainNames) { + OAuth2ClientsParams existentClientsParams = getTenantOAuth2ClientsParams(tenantId); + + Set existentDomainNames = existentClientsParams != null && existentClientsParams.getClientsDomainsParams() != null ? + existentClientsParams.getClientsDomainsParams().stream() + .map(OAuth2ClientsDomainParams::getDomainName) + .collect(Collectors.toSet()) + : Collections.emptySet(); + + Set domainNamesToAdd = domainNames.stream() + .filter(domainName -> !existentDomainNames.contains(domainName)) + .collect(Collectors.toSet()); + Set domainNamesToDelete = existentDomainNames.stream() + .filter(domainName -> !domainNames.contains(domainName)) + .collect(Collectors.toSet()); + + domainNamesToAdd.forEach(domainName -> { + String domainSettingsKey = constructAdminSettingsDomainKey(domainName); + if (adminSettingsService.findAdminSettingsByKey(tenantId, domainSettingsKey) != null) { log.error("Current domain name [{}] already registered in the system!", domainName); throw new IncorrectParameterException("Current domain name [" + domainName + "] already registered in the system!"); } - } else { - AdminSettings existentOAuth2ClientsSettingsById = adminSettingsService.findAdminSettingsById( - tenantId, - new AdminSettingsId(UUID.fromString(prevAdminSettingsId)) - ); - - if (existentOAuth2ClientsSettingsById == null) { - log.error("Admin setting ID is already set in OAuth2 Client Params object, but doesn't exist in the database"); - throw new IllegalStateException("Admin setting ID is already set in OAuth2 Client Params object, but doesn't exist in the database"); - } + }); - if (!existentOAuth2ClientsSettingsById.getKey().equals(selectedDomainSettingsKey)) { - if (existentAdminSettingsByKey == null) { - AdminSettings newOAuth2ClientsSettings = replaceExistentAdminSettings(tenantId, selectedDomainSettingsKey, existentOAuth2ClientsSettingsById.getKey()); - return newOAuth2ClientsSettings.getId().getId().toString(); - } else { - log.error("Current domain name [{}] already registered in the system!", domainName); - throw new IncorrectParameterException("Current domain name [" + domainName + "] already registered in the system!"); - } - } - return prevAdminSettingsId; - } - } + domainNamesToAdd.forEach(domainName -> { + String domainSettingsKey = constructAdminSettingsDomainKey(domainName); + AdminSettings tenantAdminSettings = createTenantAdminSettings(tenantId, domainSettingsKey); + adminSettingsService.saveAdminSettings(tenantId, tenantAdminSettings); + }); - private AdminSettings replaceExistentAdminSettings(TenantId tenantId, String newKey, String oldKey) { - adminSettingsService.deleteAdminSettingsByKey(tenantId, oldKey); - AdminSettings tenantAdminSettings = createTenantAdminSettings(tenantId, newKey); - return adminSettingsService.saveAdminSettings(tenantId, tenantAdminSettings); + domainNamesToDelete.forEach(domainName -> { + String domainSettingsKey = constructAdminSettingsDomainKey(domainName); + adminSettingsService.deleteAdminSettingsByKey(tenantId, domainSettingsKey); + }); } private AdminSettings createTenantAdminSettings(TenantId tenantId, String clientRegistrationsKey) { @@ -258,7 +250,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { private void validate(OAuth2ClientsParams oAuth2ClientsParams) { validateDomainNames(oAuth2ClientsParams); - validateAdminSettingsIds(oAuth2ClientsParams); toClientRegistrationStream(oAuth2ClientsParams) .forEach(validator); @@ -282,19 +273,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { } } - private void validateAdminSettingsIds(OAuth2ClientsParams oAuth2ClientsParams) { - List adminSettingsIds = oAuth2ClientsParams.getClientsDomainsParams().stream() - .map(OAuth2ClientsDomainParams::getAdminSettingsId) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - - boolean duplicateAdminSettingsIds = adminSettingsIds.stream() - .anyMatch(adminSettingsId -> Collections.frequency(adminSettingsIds, adminSettingsId) > 1); - if (duplicateAdminSettingsIds) { - throw new DataValidationException("All admin settings ids should be unique!"); - } - } - @Override public OAuth2ClientsParams getSystemOAuth2ClientsParams() { AdminSettings oauth2ClientsParamsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); @@ -353,9 +331,10 @@ public class OAuth2ServiceImpl implements OAuth2Service { public void deleteTenantOAuth2ClientsParams(TenantId tenantId) { OAuth2ClientsParams params = getTenantOAuth2ClientsParams(tenantId); if (params == null || params.getClientsDomainsParams() == null) return; - OAuth2ClientsDomainParams domainParams = params.getClientsDomainsParams().get(0); - String settingsKey = constructAdminSettingsDomainKey(domainParams.getDomainName()); - adminSettingsService.deleteAdminSettingsByKey(tenantId, settingsKey); + params.getClientsDomainsParams().forEach(domainParams -> { + String settingsKey = constructAdminSettingsDomainKey(domainParams.getDomainName()); + adminSettingsService.deleteAdminSettingsByKey(tenantId, settingsKey); + }); attributesService.removeAll(tenantId, tenantId, DataConstants.SERVER_SCOPE, Collections.singletonList(OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); } @@ -448,22 +427,18 @@ public class OAuth2ServiceImpl implements OAuth2Service { return result; } - private OAuth2ClientsDomainParams mergeDomainParams(OAuth2ClientsDomainParams sourceParams, OAuth2ClientsDomainParams newParams){ + private OAuth2ClientsDomainParams mergeDomainParams(OAuth2ClientsDomainParams sourceParams, OAuth2ClientsDomainParams newParams) { if (newParams == null) return sourceParams; OAuth2ClientsDomainParams.OAuth2ClientsDomainParamsBuilder mergedParamsBuilder = sourceParams.toBuilder(); - if (newParams.getClientRegistrations() != null){ + if (newParams.getClientRegistrations() != null) { List mergedClientRegistrations = sourceParams.getClientRegistrations() != null ? sourceParams.getClientRegistrations() : new ArrayList<>(); mergedClientRegistrations.addAll(newParams.getClientRegistrations()); mergedParamsBuilder.clientRegistrations(mergedClientRegistrations); } - if (newParams.getAdminSettingsId() != null){ - mergedParamsBuilder.adminSettingsId(newParams.getAdminSettingsId()); - } - return mergedParamsBuilder.build(); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index b5e8280eee..94b58fb5ce 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -114,11 +114,6 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParamsWithDuplicateRegistrationIds()); } - @Test(expected = DataValidationException.class) - public void testSaveTenantParamsWithMultipleDomains() { - oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, validClientsParamsWithMultipleDomains()); - } - @Test public void testSaveSystemParams() { OAuth2ClientsParams clientsParams = validClientsParams(); @@ -130,7 +125,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testSaveSystemParamsWithMultipleDomains() { - OAuth2ClientsParams clientsParams = validClientsParamsWithMultipleDomains(); + OAuth2ClientsParams clientsParams = validClientsParamsWithThreeDomains(); OAuth2ClientsParams savedClientParams = oAuth2Service.saveSystemOAuth2ClientsParams(clientsParams); Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); @@ -163,11 +158,81 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { Assert.assertNotNull(savedClientParams); OAuth2ClientsDomainParams savedDomainParams = savedClientParams.getClientsDomainsParams().get(0); - Assert.assertNotNull(savedDomainParams.getAdminSettingsId()); Assert.assertEquals(domainParams.getDomainName(), savedDomainParams.getDomainName()); Assert.assertEquals(domainParams.getClientRegistrations(), savedDomainParams.getClientRegistrations()); } + @Test + public void testSaveTenantMultipleParams() { + OAuth2ClientsParams clientsParams = validClientsParamsWithThreeDomains(); + + clientsParams.getClientsDomainsParams().forEach(oAuth2ClientsDomainParams -> { + String domainName = oAuth2ClientsDomainParams.getDomainName(); + String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainName); + Assert.assertNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + }); + + OAuth2ClientsParams savedClientParams = oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParams); + Assert.assertNotNull(savedClientParams); + + clientsParams.getClientsDomainsParams().forEach(oAuth2ClientsDomainParams -> { + String domainName = oAuth2ClientsDomainParams.getDomainName(); + String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainName); + Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + }); + + Assert.assertEquals(clientsParams, savedClientParams); + } + + @Test + public void testRewriteSameDomainTenantParams() { + OAuth2ClientsParams clientsParams = validClientsParamsWithThreeDomains(); + oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParams); + + List clientsDomainsParams = clientsParams.getClientsDomainsParams(); + OAuth2ClientsParams updatedClientsParams = validClientsParamsWithThreeDomains(); + String sameDomainName = clientsDomainsParams.get(0).getDomainName(); + updatedClientsParams.getClientsDomainsParams().get(0).setDomainName(sameDomainName); + OAuth2ClientsParams rewrittenClientParams = oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, updatedClientsParams); + Assert.assertEquals(updatedClientsParams, rewrittenClientParams); + + clientsParams.getClientsDomainsParams().forEach(oAuth2ClientsDomainParams -> { + String domainName = oAuth2ClientsDomainParams.getDomainName(); + String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainName); + if (domainName.equals(sameDomainName)) { + Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + } else { + Assert.assertNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + } + }); + updatedClientsParams.getClientsDomainsParams().forEach(oAuth2ClientsDomainParams -> { + String domainName = oAuth2ClientsDomainParams.getDomainName(); + String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainName); + Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + }); + } + + @Test + public void testAddDeleteTenantDomainParams() { + OAuth2ClientsParams clientsParams = validClientsParamsWithThreeDomains(); + oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParams); + + List clientsDomainsParams = clientsParams.getClientsDomainsParams(); + OAuth2ClientsParams updatedClientsParams = validClientsParamsWithThreeDomains(); + for (int i = 0; i < updatedClientsParams.getClientsDomainsParams().size(); i++) { + String domainName = clientsDomainsParams.get(i).getDomainName(); + updatedClientsParams.getClientsDomainsParams().get(i).setDomainName(domainName); + } + OAuth2ClientsParams rewrittenClientParams = oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, updatedClientsParams); + Assert.assertEquals(updatedClientsParams, rewrittenClientParams); + + clientsParams.getClientsDomainsParams().forEach(oAuth2ClientsDomainParams -> { + String domainName = oAuth2ClientsDomainParams.getDomainName(); + String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainName); + Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + }); + } + @Test public void testFindTenantParams() { OAuth2ClientsParams clientsParams = validClientsParams(); @@ -288,6 +353,9 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { oAuth2Service.saveSystemOAuth2ClientsParams(sysAdminClientsParams); Assert.assertNotNull(oAuth2Service.getSystemOAuth2ClientsParams().getClientsDomainsParams()); + + oAuth2Service.deleteSystemOAuth2ClientsParams(); + Assert.assertNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); } @Test @@ -299,6 +367,14 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, tenantClientsParams); Assert.assertNotNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams()); + + oAuth2Service.deleteTenantOAuth2ClientsParams(tenantId); + Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams()); + tenantClientsParams.getClientsDomainsParams().forEach(oAuth2ClientsDomainParams -> { + String domainName = oAuth2ClientsDomainParams.getDomainName(); + String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainName); + Assert.assertNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + }); } @@ -331,7 +407,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { .build(); } - private OAuth2ClientsParams validClientsParamsWithMultipleDomains() { + private OAuth2ClientsParams validClientsParamsWithThreeDomains() { OAuth2ClientRegistration first = validClientRegistration(); OAuth2ClientRegistration second = validClientRegistration(); OAuth2ClientRegistration third = validClientRegistration(); From 5aba253928a216a2c8952c7f26989801d46378e3 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 9 Jul 2020 12:11:49 +0300 Subject: [PATCH 063/117] Changed OAuth2ClientsDomainParams protocol --- .../auth/oauth2/BasicOAuth2ClientMapper.java | 30 ++++---- .../auth/oauth2/CustomOAuth2ClientMapper.java | 2 +- .../server/dao/oauth2/OAuth2Service.java | 3 +- .../ExtendedOAuth2ClientRegistration.java | 28 ++++++++ .../data/oauth2/OAuth2ClientRegistration.java | 7 +- .../oauth2/OAuth2ClientsDomainParams.java | 1 + .../data/oauth2/OAuth2MapperConfig.java | 4 +- .../HybridClientRegistrationRepository.java | 16 ++--- .../server/dao/oauth2/OAuth2ServiceImpl.java | 68 +++++++++++-------- .../server/dao/SqlDaoServiceTestSuite.java | 2 +- .../dao/service/BaseOAuth2ServiceTest.java | 24 ++++--- 11 files changed, 116 insertions(+), 69 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistration.java diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java index 19ed540543..8c7c4d9bb4 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java @@ -38,42 +38,42 @@ public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implemen public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, TenantId parentTenantId, OAuth2MapperConfig config) { OAuth2User oauth2User = new OAuth2User(); Map attributes = token.getPrincipal().getAttributes(); - String email = getStringAttributeByKey(attributes, config.getBasicConfig().getEmailAttributeKey()); + String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); oauth2User.setEmail(email); oauth2User.setTenantName(getTenantName(attributes, config)); - if (!StringUtils.isEmpty(config.getBasicConfig().getLastNameAttributeKey())) { - String lastName = getStringAttributeByKey(attributes, config.getBasicConfig().getLastNameAttributeKey()); + if (!StringUtils.isEmpty(config.getBasic().getLastNameAttributeKey())) { + String lastName = getStringAttributeByKey(attributes, config.getBasic().getLastNameAttributeKey()); oauth2User.setLastName(lastName); } - if (!StringUtils.isEmpty(config.getBasicConfig().getFirstNameAttributeKey())) { - String firstName = getStringAttributeByKey(attributes, config.getBasicConfig().getFirstNameAttributeKey()); + if (!StringUtils.isEmpty(config.getBasic().getFirstNameAttributeKey())) { + String firstName = getStringAttributeByKey(attributes, config.getBasic().getFirstNameAttributeKey()); oauth2User.setFirstName(firstName); } - if (!StringUtils.isEmpty(config.getBasicConfig().getCustomerNamePattern())) { + if (!StringUtils.isEmpty(config.getBasic().getCustomerNamePattern())) { StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); - String customerName = sub.replace(config.getBasicConfig().getCustomerNamePattern()); + String customerName = sub.replace(config.getBasic().getCustomerNamePattern()); oauth2User.setCustomerName(customerName); } - oauth2User.setAlwaysFullScreen(config.getBasicConfig().isAlwaysFullScreen()); - if (!StringUtils.isEmpty(config.getBasicConfig().getDefaultDashboardName())) { - oauth2User.setDefaultDashboardName(config.getBasicConfig().getDefaultDashboardName()); + oauth2User.setAlwaysFullScreen(config.getBasic().isAlwaysFullScreen()); + if (!StringUtils.isEmpty(config.getBasic().getDefaultDashboardName())) { + oauth2User.setDefaultDashboardName(config.getBasic().getDefaultDashboardName()); } return getOrCreateSecurityUserFromOAuth2User(parentTenantId, oauth2User, config.isAllowUserCreation(), config.isActivateUser()); } private String getTenantName(Map attributes, OAuth2MapperConfig config) { - switch (config.getBasicConfig().getTenantNameStrategy()) { + switch (config.getBasic().getTenantNameStrategy()) { case EMAIL: - return getStringAttributeByKey(attributes, config.getBasicConfig().getEmailAttributeKey()); + return getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); case DOMAIN: - String email = getStringAttributeByKey(attributes, config.getBasicConfig().getEmailAttributeKey()); + String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); return email.substring(email .indexOf("@") + 1); case CUSTOM: StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); - return sub.replace(config.getBasicConfig().getTenantNamePattern()); + return sub.replace(config.getBasic().getTenantNamePattern()); default: - throw new RuntimeException("Tenant Name Strategy with type " + config.getBasicConfig().getTenantNameStrategy() + " is not supported!"); + throw new RuntimeException("Tenant Name Strategy with type " + config.getBasic().getTenantNameStrategy() + " is not supported!"); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java index 27286e454a..0dce6f1e98 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java @@ -39,7 +39,7 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme @Override public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, TenantId parentTenantId, OAuth2MapperConfig config) { - OAuth2User oauth2User = getOAuth2User(token, config.getCustomConfig()); + OAuth2User oauth2User = getOAuth2User(token, config.getCustom()); return getOrCreateSecurityUserFromOAuth2User(parentTenantId, oauth2User, config.isAllowUserCreation(), config.isActivateUser()); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 380738583f..bfeb8f52a1 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -19,6 +19,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.thingsboard.server.common.data.id.CustomerId; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistration; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; @@ -29,7 +30,7 @@ import java.util.Map; public interface OAuth2Service { Pair getClientRegistrationWithTenant(String registrationId); - OAuth2ClientRegistration getClientRegistration(String registrationId); + ExtendedOAuth2ClientRegistration getExtendedClientRegistration(String registrationId); List getOAuth2Clients(String domainName); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistration.java new file mode 100644 index 0000000000..18d5337132 --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ExtendedOAuth2ClientRegistration.java @@ -0,0 +1,28 @@ +/** + * 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 +@ToString +@Builder(toBuilder = true) +@NoArgsConstructor +@AllArgsConstructor +public class ExtendedOAuth2ClientRegistration { + private String redirectUriTemplate; + private OAuth2ClientRegistration clientRegistration; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java index 231a8167f0..2051e4b850 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -19,6 +19,8 @@ import lombok.*; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.id.OAuth2IntegrationId; +import java.util.List; + @EqualsAndHashCode @Data @ToString(exclude = {"clientSecret"}) @@ -32,9 +34,8 @@ public class OAuth2ClientRegistration { private String clientId; private String clientSecret; private String authorizationUri; - private String tokenUri; - private String redirectUriTemplate; - private String scope; + private String accessTokenUri; + private List scope; private String userInfoUri; private String userNameAttributeName; private String jwkSetUri; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java index 3ebfa783c2..699bf90c37 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java @@ -27,5 +27,6 @@ import java.util.List; @AllArgsConstructor public class OAuth2ClientsDomainParams { private String domainName; + private String redirectUriTemplate; private List clientRegistrations; } \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java index e0aaf72ee3..15b3067cda 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java @@ -28,6 +28,6 @@ public class OAuth2MapperConfig { private boolean allowUserCreation; private boolean activateUser; private MapperType type; - private OAuth2BasicMapperConfig basicConfig; - private OAuth2CustomMapperConfig customConfig; + private OAuth2BasicMapperConfig basic; + private OAuth2CustomMapperConfig custom; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java index 26a3312376..ab8a14e078 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java @@ -16,12 +16,12 @@ package org.thingsboard.server.dao.oauth2; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 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.ExtendedOAuth2ClientRegistration; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; @Component @@ -32,19 +32,19 @@ public class HybridClientRegistrationRepository implements ClientRegistrationRep @Override public ClientRegistration findByRegistrationId(String registrationId) { - OAuth2ClientRegistration localClientRegistration = oAuth2Service.getClientRegistration(registrationId); - return localClientRegistration == null ? - null : toSpringClientRegistration(localClientRegistration); + ExtendedOAuth2ClientRegistration localExtendedClientRegistration = oAuth2Service.getExtendedClientRegistration(registrationId); + return localExtendedClientRegistration == null ? + null : toSpringClientRegistration(localExtendedClientRegistration.getRedirectUriTemplate(), localExtendedClientRegistration.getClientRegistration()); } - private ClientRegistration toSpringClientRegistration(OAuth2ClientRegistration localClientRegistration){ + private ClientRegistration toSpringClientRegistration(String redirectUriTemplate, OAuth2ClientRegistration localClientRegistration){ return ClientRegistration.withRegistrationId(localClientRegistration.getRegistrationId()) .clientId(localClientRegistration.getClientId()) .authorizationUri(localClientRegistration.getAuthorizationUri()) .clientSecret(localClientRegistration.getClientSecret()) - .tokenUri(localClientRegistration.getTokenUri()) - .redirectUriTemplate(localClientRegistration.getRedirectUriTemplate()) - .scope(localClientRegistration.getScope().split(",")) + .tokenUri(localClientRegistration.getAccessTokenUri()) + .redirectUriTemplate(redirectUriTemplate) + .scope(localClientRegistration.getScope()) .clientName(localClientRegistration.getClientName()) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .userInfoUri(localClientRegistration.getUserInfoUri()) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 8857e6ceb9..25dec8e696 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -1,12 +1,12 @@ /** * 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 - * + *

+ * 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. @@ -39,10 +39,8 @@ import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.tenant.TenantService; -import javax.annotation.PostConstruct; import java.io.IOException; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; @@ -76,27 +74,40 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public Pair getClientRegistrationWithTenant(String registrationId) { + return getExtendedOAuth2ClientRegistrationWithTenant(registrationId) + .map(pair -> ImmutablePair.of(pair.getLeft(), pair.getRight().getClientRegistration())) + .orElse(null); + } + + @Override + public ExtendedOAuth2ClientRegistration getExtendedClientRegistration(String registrationId) { + return getExtendedOAuth2ClientRegistrationWithTenant(registrationId) + .map(Pair::getValue) + .orElse(null); + + } + + private Optional> getExtendedOAuth2ClientRegistrationWithTenant(String registrationId) { return getAllOAuth2ClientsParams().entrySet().stream() .map(entry -> { TenantId tenantId = entry.getKey(); - OAuth2ClientRegistration clientRegistration = toClientRegistrationStream(entry.getValue()) - .filter(registration -> registrationId.equals(registration.getRegistrationId())) + return entry.getValue().getClientsDomainsParams().stream() + .flatMap(domainParams -> + domainParams.getClientRegistrations().stream() + .map(clientRegistration -> new ExtendedOAuth2ClientRegistration(domainParams.getRedirectUriTemplate(), clientRegistration)) + ) + .filter(registration -> registrationId.equals(registration.getClientRegistration().getRegistrationId())) .findFirst() + .map(extendedClientRegistration -> ImmutablePair.of(tenantId, extendedClientRegistration)) .orElse(null); - return clientRegistration != null ? - ImmutablePair.of(tenantId, clientRegistration) : null; + }) .filter(Objects::nonNull) .findFirst() - .orElse(null) + .map(entry -> ImmutablePair.of(entry.getKey(), entry.getValue())) ; } - @Override - public OAuth2ClientRegistration getClientRegistration(String registrationId) { - Pair clientRegistrationPair = getClientRegistrationWithTenant(registrationId); - return clientRegistrationPair != null ? clientRegistrationPair.getRight() : null; - } @Override public List getOAuth2Clients(String domainName) { @@ -249,6 +260,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { } private void validate(OAuth2ClientsParams oAuth2ClientsParams) { + validateRedirectUris(oAuth2ClientsParams); validateDomainNames(oAuth2ClientsParams); toClientRegistrationStream(oAuth2ClientsParams) @@ -273,6 +285,15 @@ public class OAuth2ServiceImpl implements OAuth2Service { } } + private void validateRedirectUris(OAuth2ClientsParams oAuth2ClientsParams) { + oAuth2ClientsParams.getClientsDomainsParams().stream() + .forEach(oAuth2ClientsDomainParams -> { + if (StringUtils.isEmpty(oAuth2ClientsDomainParams.getRedirectUriTemplate())) { + throw new DataValidationException("Redirect uri template should be specified!"); + } + }); + } + @Override public OAuth2ClientsParams getSystemOAuth2ClientsParams() { AdminSettings oauth2ClientsParamsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); @@ -482,12 +503,9 @@ public class OAuth2ServiceImpl implements OAuth2Service { if (StringUtils.isEmpty(clientRegistration.getAuthorizationUri())) { throw new DataValidationException("Authorization uri should be specified!"); } - if (StringUtils.isEmpty(clientRegistration.getTokenUri())) { + if (StringUtils.isEmpty(clientRegistration.getAccessTokenUri())) { throw new DataValidationException("Token uri should be specified!"); } - if (StringUtils.isEmpty(clientRegistration.getRedirectUriTemplate())) { - throw new DataValidationException("Redirect uri template should be specified!"); - } if (StringUtils.isEmpty(clientRegistration.getScope())) { throw new DataValidationException("Scope should be specified!"); } @@ -517,7 +535,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { throw new DataValidationException("Mapper config type should be specified!"); } if (mapperConfig.getType() == MapperType.BASIC) { - OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasicConfig(); + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); if (basicConfig == null) { throw new DataValidationException("Basic config should be specified!"); } @@ -533,19 +551,13 @@ public class OAuth2ServiceImpl implements OAuth2Service { } } if (mapperConfig.getType() == MapperType.CUSTOM) { - OAuth2CustomMapperConfig customConfig = mapperConfig.getCustomConfig(); + 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!"); } - if (StringUtils.isEmpty(customConfig.getUsername())) { - throw new DataValidationException("Custom mapper username should be specified!"); - } - if (StringUtils.isEmpty(customConfig.getPassword())) { - throw new DataValidationException("Custom mapper password should be specified!"); - } } }; } diff --git a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java index 32fd45c188..3f182339d0 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java @@ -24,7 +24,7 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClassnameFilters({ - "org.thingsboard.server.dao.service.*ServiceSqlTest" + "org.thingsboard.server.dao.service.*2ServiceSqlTest" }) public class SqlDaoServiceTestSuite { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index 94b58fb5ce..e502b660ec 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -273,7 +273,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { } @Test - public void testGetClientRegistration() { + public void testGetExtendedClientRegistration() { OAuth2ClientsParams tenantClientsParams = validClientsParams(); OAuth2ClientsParams sysAdminClientsParams = validClientsParams(); @@ -285,9 +285,9 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { OAuth2Utils.toClientRegistrationStream(sysAdminClientsParams) ) .forEach(clientRegistration -> { - OAuth2ClientRegistration foundClientRegistration = oAuth2Service.getClientRegistration(clientRegistration.getRegistrationId()); - Assert.assertNotNull(foundClientRegistration); - Assert.assertEquals(clientRegistration.getRegistrationId(), foundClientRegistration.getRegistrationId()); + ExtendedOAuth2ClientRegistration foundExtendedClientRegistration = oAuth2Service.getExtendedClientRegistration(clientRegistration.getRegistrationId()); + Assert.assertNotNull(foundExtendedClientRegistration); + Assert.assertEquals(clientRegistration, foundExtendedClientRegistration.getClientRegistration()); }); } @@ -401,6 +401,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { .clientsDomainsParams(Collections.singletonList( OAuth2ClientsDomainParams.builder() .domainName(UUID.randomUUID().toString()) + .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") .clientRegistrations(Arrays.asList(first, second)) .build() )) @@ -415,10 +416,12 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { .clientsDomainsParams(Arrays.asList( OAuth2ClientsDomainParams.builder() .domainName(UUID.randomUUID().toString()) + .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") .clientRegistrations(Arrays.asList(first, second)) .build(), OAuth2ClientsDomainParams.builder() .domainName(UUID.randomUUID().toString()) + .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") .clientRegistrations(Arrays.asList(third)) .build() )) @@ -434,14 +437,17 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { .clientsDomainsParams(Arrays.asList( OAuth2ClientsDomainParams.builder() .domainName("domain") + .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") .clientRegistrations(Collections.singletonList(first)) .build(), OAuth2ClientsDomainParams.builder() .domainName("domain") + .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") .clientRegistrations(Collections.singletonList(second)) .build(), OAuth2ClientsDomainParams.builder() .domainName(UUID.randomUUID().toString()) + .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") .clientRegistrations(Collections.singletonList(third)) .build() )) @@ -458,6 +464,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { .clientsDomainsParams(Arrays.asList( OAuth2ClientsDomainParams.builder() .domainName(UUID.randomUUID().toString()) + .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") .clientRegistrations(Arrays.asList(first, second, third)) .build() )) @@ -471,20 +478,17 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { .allowUserCreation(true) .activateUser(true) .type(MapperType.CUSTOM) - .customConfig( + .custom( OAuth2CustomMapperConfig.builder() .url("localhost:8082") - .username("test") - .password("test") .build() ) .build()) .clientId("clientId") .clientSecret("clientSecret") .authorizationUri("authorizationUri") - .tokenUri("tokenUri") - .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") - .scope("scope") + .accessTokenUri("tokenUri") + .scope(Arrays.asList("scope1", "scope2")) .userInfoUri("userInfoUri") .userNameAttributeName("userNameAttributeName") .jwkSetUri("jwkSetUri") From 981ff1fda7049c80e34767ccf1b0d869457b652e Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 9 Jul 2020 12:19:23 +0300 Subject: [PATCH 064/117] Fixed test suite --- .../java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java index 3f182339d0..32fd45c188 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java +++ b/dao/src/test/java/org/thingsboard/server/dao/SqlDaoServiceTestSuite.java @@ -24,7 +24,7 @@ import java.util.Arrays; @RunWith(ClasspathSuite.class) @ClassnameFilters({ - "org.thingsboard.server.dao.service.*2ServiceSqlTest" + "org.thingsboard.server.dao.service.*ServiceSqlTest" }) public class SqlDaoServiceTestSuite { From 7af3fe0050a674b46888cd72fc28c88622e398c7 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 9 Jul 2020 13:47:35 +0300 Subject: [PATCH 065/117] Add system admin OAuth2 settings --- ui-ngx/src/app/core/http/admin.service.ts | 18 +- ui-ngx/src/app/core/services/menu.service.ts | 8 +- .../home/pages/admin/admin-routing.module.ts | 14 + .../modules/home/pages/admin/admin.module.ts | 4 +- .../admin/oauth2-settings.component.html | 364 ++++++++++++++++++ .../admin/oauth2-settings.component.scss | 28 ++ .../pages/admin/oauth2-settings.component.ts | 284 ++++++++++++++ .../home/pages/tenant/tenant.component.html | 3 + .../home/pages/tenant/tenant.component.ts | 11 +- ui-ngx/src/app/shared/models/constants.ts | 3 +- .../src/app/shared/models/settings.models.ts | 56 +++ .../assets/locale/locale.constant-en_US.json | 61 ++- 12 files changed, 845 insertions(+), 9 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss create mode 100644 ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts diff --git a/ui-ngx/src/app/core/http/admin.service.ts b/ui-ngx/src/app/core/http/admin.service.ts index f575f56906..ddc2069d4a 100644 --- a/ui-ngx/src/app/core/http/admin.service.ts +++ b/ui-ngx/src/app/core/http/admin.service.ts @@ -18,7 +18,13 @@ import { Injectable } from '@angular/core'; import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; -import { AdminSettings, MailServerSettings, SecuritySettings, UpdateMessage } from '@shared/models/settings.models'; +import { + AdminSettings, + MailServerSettings, + OAuth2Settings, + SecuritySettings, + UpdateMessage +} from '@shared/models/settings.models'; @Injectable({ providedIn: 'root' @@ -53,6 +59,16 @@ export class AdminService { defaultHttpOptionsFromConfig(config)); } + public getOAuth2Settings(config?: RequestConfig): Observable { + return this.http.get(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config)); + } + + public saveOAuth2Settings(OAuth2Setting: OAuth2Settings, + config?: RequestConfig): Observable { + return this.http.post('/api/oauth2/config', OAuth2Setting, + defaultHttpOptionsFromConfig(config)); + } + public checkUpdates(config?: RequestConfig): Observable { return this.http.get(`/api/admin/updates`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index 6032bf24f3..ea04968f71 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -95,7 +95,7 @@ export class MenuService { name: 'admin.system-settings', type: 'toggle', path: '/settings', - height: '120px', + height: '160px', icon: 'settings', pages: [ { @@ -115,6 +115,12 @@ export class MenuService { type: 'link', path: '/settings/security-settings', icon: 'security' + }, + { + name: 'admin.oauth2.settings', + type: 'link', + path: '/settings/oauth2-settings', + icon: 'security' } ] } diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index 47f1a5261b..32c25c6e7d 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -22,6 +22,7 @@ import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; import { Authority } from '@shared/models/authority.enum'; import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component'; import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; +import { OAuth2SettingsComponent } from '@home/pages/admin/oauth2-settings.component'; const routes: Routes = [ { @@ -77,6 +78,19 @@ const routes: Routes = [ icon: 'security' } } + }, + { + path: 'oauth2-settings', + component: OAuth2SettingsComponent, + canDeactivate: [ConfirmOnExitGuard], + data: { + auth: [Authority.SYS_ADMIN], + title: 'admin.oauth2.settings', + breadcrumb: { + label: 'admin.oauth2.settings', + icon: 'security' + } + } } ] } diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts index 48bcf56fd5..73fef6a32f 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts @@ -23,13 +23,15 @@ import { MailServerComponent } from '@modules/home/pages/admin/mail-server.compo import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component'; import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; import { HomeComponentsModule } from '@modules/home/components/home-components.module'; +import { OAuth2SettingsComponent } from '@modules/home/pages/admin/oauth2-settings.component'; @NgModule({ declarations: [ GeneralSettingsComponent, MailServerComponent, - SecuritySettingsComponent + SecuritySettingsComponent, + OAuth2SettingsComponent ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html new file mode 100644 index 0000000000..87f1871ec2 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -0,0 +1,364 @@ + +

+ + +
+ admin.oauth2.settings + +
+
+
+ + +
+ +
+
+ +
+ + + + + + {{ domain.get('domainName').value ? domain.get('domainName').value : ("admin.new-domain" | translate) }} + + + + + + + + admin.domain-name + + + {{ 'admin.error-verification-url' | translate }} + + + + + admin.oauth2.redirect-uri-template + + + {{ 'admin.oauth2.redirect-uri-template-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + + + +
+ +
+
+ +
+
+ + admin.oauth2.registration-id + + + {{ 'admin.oauth2.registration-id-required' | translate }} + + + + + admin.oauth2.client-name + + + {{ 'admin.oauth2.client-name-required' | translate }} + + +
+ +
+ + admin.oauth2.client-id + + + {{ 'admin.oauth2.client-id-required' | translate }} + + + + + admin.oauth2.client-secret + + + {{ 'admin.oauth2.client-secret-required' | translate }} + + +
+ +
+ + admin.oauth2.access-token-uri + + + {{ 'admin.oauth2.access-token-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + + + + admin.oauth2.authorization-uri + + + {{ 'admin.oauth2.authorization-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + +
+ + + admin.oauth2.scope + + + {{scope}} + cancel + + + + + +
+ + admin.oauth2.jwk-set-uri + + + {{ 'admin.oauth2.jwk-set-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + + + + admin.oauth2.user-info-uri + + + {{ 'admin.oauth2.user-info-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + +
+ + + admin.oauth2.client-authentication-method + + + {{ clientAuthenticationMethod | uppercase }} + + + + + + admin.oauth2.user-name-attribute-name + + + {{ 'admin.oauth2.user-name-attribute-name-required' | translate }} + + + +
+
+ + {{ 'admin.oauth2.allow-user-creation' | translate }} + + + {{ 'admin.oauth2.activate-user' | translate }} + +
+ + + admin.oauth2.type + + + {{ converterTypeExternalUser }} + + + + +
+ + admin.oauth2.email-attribute-key + + + {{ 'admin.oauth2.email-attribute-key-required' | translate }} + + + +
+ + admin.oauth2.first-name-attribute-key + + + + + admin.oauth2.last-name-attribute-key + + +
+ +
+ + admin.oauth2.tenant-name-strategy + + + {{ tenantNameStrategy }} + + + + + + admin.oauth2.tenant-name-pattern + + + {{ 'admin.oauth2.tenant-name-pattern-required' | translate }} + + +
+ + + admin.oauth2.customer-name-pattern + + + +
+ + admin.oauth2.default-dashboard-name + + + + + {{ 'admin.oauth2.always-fullscreen' | translate}} + +
+ +
+ +
+ + admin.oauth2.url + + + {{ 'admin.oauth2.url-required' | translate }} + + + {{ 'admin.oauth2.url-pattern' | translate }} + + + +
+ + common.username + + + + + common.password + + +
+
+
+ +
+ + admin.oauth2.login-button-label + + + {{ 'admin.oauth2.login-button-label-required' | translate }} + + + + + admin.oauth2.login-button-icon + + +
+
+
+
+
+ +
+ +
+ +
+
+
+
+
+ +
+ + +
+
+
+
+
+
diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss new file mode 100644 index 0000000000..95f47b7023 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss @@ -0,0 +1,28 @@ +/** + * 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. + */ +:host{ + .checkbox-row { + margin-top: 16px; + } + + .registration-card{ + margin-bottom: 8px; + } + + .container{ + margin-bottom: 16px; + } +} diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts new file mode 100644 index 0000000000..d128ede633 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -0,0 +1,284 @@ +/// +/// 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. +/// + +import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; +import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; +import { ClientRegistration, DomainParams, OAuth2Settings } from '@shared/models/settings.models'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { AdminService } from '@core/http/admin.service'; +import { PageComponent } from '@shared/components/page.component'; +import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; +import { COMMA, ENTER } from '@angular/cdk/keycodes'; +import { MatChipInputEvent } from '@angular/material/chips'; +import { WINDOW } from '@core/services/window.service'; +import { Subscription } from 'rxjs'; +import { DialogService } from '@core/services/dialog.service'; +import { TranslateService } from '@ngx-translate/core'; + +@Component({ + selector: 'tb-oauth2-settings', + templateUrl: './oauth2-settings.component.html', + styleUrls: ['./oauth2-settings.component.scss', './settings-card.scss'] +}) +export class OAuth2SettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy { + + private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/; + private subscriptions: Subscription[] = []; + + readonly separatorKeysCodes: number[] = [ENTER, COMMA]; + + oauth2SettingsForm: FormGroup; + oauth2Settings: OAuth2Settings; + + clientAuthenticationMethods = ['basic', 'post']; + converterTypesExternalUser = ['BASIC', 'CUSTOM']; + tenantNameStrategies = ['DOMAIN', 'EMAIL', 'CUSTOM']; + + constructor(protected store: Store, + private adminService: AdminService, + private fb: FormBuilder, + private dialogService: DialogService, + private translate: TranslateService, + @Inject(WINDOW) private window: Window) { + super(store); + } + + ngOnInit(): void { + this.buildOAuth2SettingsForm(); + this.adminService.getOAuth2Settings().subscribe( + (oauth2Settings) => { + this.oauth2Settings = oauth2Settings; + this.initOAuth2Settings(this.oauth2Settings); + this.oauth2SettingsForm.reset(this.oauth2Settings); + } + ); + } + + ngOnDestroy() { + super.ngOnDestroy(); + this.subscriptions.forEach((subscription) => { + subscription.unsubscribe(); + }) + } + + get clientsDomainsParams(): FormArray { + return this.oauth2SettingsForm.get('clientsDomainsParams') as FormArray; + } + + private get formBasicGroup(): FormGroup { + const basicGroup = this.fb.group({ + emailAttributeKey: ['email', [Validators.required]], + firstNameAttributeKey: [''], + lastNameAttributeKey: [''], + tenantNameStrategy: ['DOMAIN'], + tenantNamePattern: [null], + customerNamePattern: [null], + defaultDashboardName: [null], + alwaysFullScreen: [false], + }); + + this.subscriptions.push(basicGroup.get('tenantNameStrategy').valueChanges.subscribe((domain) => { + if (domain === 'CUSTOM') { + basicGroup.get('tenantNamePattern').setValidators(Validators.required); + } else { + basicGroup.get('tenantNamePattern').clearValidators(); + } + })); + + return basicGroup; + } + + get formCustomGroup(): FormGroup { + return this.fb.group({ + url: [null, [Validators.required, Validators.pattern(this.URL_REGEXP)]], + username: [null], + password: [null] + }) + } + + private buildOAuth2SettingsForm(): void { + this.oauth2SettingsForm = this.fb.group({ + clientsDomainsParams: this.fb.array([]) + }); + } + + private initOAuth2Settings(oauth2Settings: OAuth2Settings): void { + oauth2Settings.clientsDomainsParams.forEach((domaindomain) => { + this.clientsDomainsParams.push(this.buildSettingsDomain(domaindomain)); + }); + } + + private buildSettingsDomain(domainParams?: DomainParams): FormGroup { + let url = this.window.location.protocol + '//' + this.window.location.hostname; + const port = this.window.location.port; + if (port !== '80' && port !== '443') { + url += ':' + port; + } + url += '/login/oauth2/code/'; + const formDomain = this.fb.group({ + domainName: ['', [Validators.required, Validators.pattern('((?![:/]).)*$')]], + redirectUriTemplate: [url, [Validators.required, Validators.pattern(this.URL_REGEXP)]], + clientRegistrations: this.fb.array([]) + }); + + this.subscriptions.push(formDomain.get('domainName').valueChanges.subscribe((domain) => { + if (!domain) { + domain = this.window.location.hostname + } + const uri = this.window.location.protocol + `//${domain}/login/oauth2/code/`; + formDomain.get('redirectUriTemplate').patchValue(uri); + })); + + if(domainParams){ + domainParams.clientRegistrations.forEach((registration) => { + this.clientDomainRegistrations(formDomain).push(this.buildSettingsRegistration(registration)); + }) + } else { + this.clientDomainRegistrations(formDomain).push(this.buildSettingsRegistration()); + } + + return formDomain; + } + + private buildSettingsRegistration(registrationData?: ClientRegistration): FormGroup { + const clientRegistration = this.fb.group({ + registrationId: [null, [Validators.required]], + clientName: [null, [Validators.required]], + loginButtonLabel: [null, [Validators.required]], + loginButtonIcon: [null], + clientId: ['', [Validators.required]], + clientSecret: ['', [Validators.required]], + accessTokenUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], + authorizationUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], + scope: this.fb.array([], [Validators.required]), + jwkSetUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], + userInfoUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], + clientAuthenticationMethod: ['post', [Validators.required]], + userNameAttributeName: ['email', [Validators.required]], + mapperConfig: this.fb.group({ + allowUserCreation: [true], + activateUser: [false], + type: ['BASIC', [Validators.required]], + basic: this.formBasicGroup + } + ) + }); + + this.subscriptions.push(clientRegistration.get('mapperConfig.type').valueChanges.subscribe((value) => { + const mapperConfig = clientRegistration.get('mapperConfig') as FormGroup; + if (value === 'BASIC') { + mapperConfig.removeControl('custom'); + mapperConfig.addControl('basic', this.formBasicGroup); + } else { + mapperConfig.removeControl('basic'); + mapperConfig.addControl('custom', this.formCustomGroup); + } + })); + + if(registrationData){ + registrationData.scope.forEach(() => { + (clientRegistration.get('scope') as FormArray).push(this.fb.control('')) + }) + if(registrationData.mapperConfig.type !== 'BASIC'){ + clientRegistration.get('mapperConfig.type').patchValue('CUSTOM'); + } + } + + return clientRegistration; + } + + save(): void { + console.log(this.oauth2SettingsForm.value); + this.adminService.saveOAuth2Settings(this.oauth2SettingsForm.value).subscribe( + (oauth2Settings) => { + this.oauth2Settings = oauth2Settings; + this.oauth2SettingsForm.markAsPristine(); + this.oauth2SettingsForm.markAsUntouched(); + } + ); + } + + confirmForm(): FormGroup { + return this.oauth2SettingsForm; + } + + addScope(event: MatChipInputEvent, control: AbstractControl): void { + const input = event.input; + const value = event.value; + const controller = control.get('scope') as FormArray; + if ((value.trim() !== '')) { + controller.push(this.fb.control(value.trim())); + } + + if (input) { + input.value = ''; + } + } + + removeScope(i: number, control: AbstractControl): void { + const controller = control.get('scope') as FormArray; + controller.removeAt(i); + } + + addDomain(): void { + this.clientsDomainsParams.push(this.buildSettingsDomain()); + } + + deleteDomain($event: Event, index: number): void { + if ($event) { + $event.stopPropagation(); + $event.preventDefault(); + } + + const domainName = this.clientsDomainsParams.at(index).get('domainName').value; + this.dialogService.confirm( + this.translate.instant('admin.oauth2.delete-domain-title', {domainName}), + this.translate.instant('admin.oauth2.delete-domain-text'), null, + this.translate.instant('action.delete') + ).subscribe((data) => { + if (data) { + this.clientsDomainsParams.removeAt(index); + } + }) + } + + clientDomainRegistrations(control: AbstractControl): FormArray { + return control.get('clientRegistrations') as FormArray; + } + + addRegistration(control: AbstractControl): void { + this.clientDomainRegistrations(control).push(this.buildSettingsRegistration()); + } + + deleteRegistration($event: Event, controler: AbstractControl, index: number): void { + if ($event) { + $event.stopPropagation(); + $event.preventDefault(); + } + + const registrationId = this.clientDomainRegistrations(controler).at(index).get('registrationId').value; + this.dialogService.confirm( + this.translate.instant('admin.oauth2.delete-registration-title', {name: registrationId}), + this.translate.instant('admin.oauth2.delete-registration-text'), null, + this.translate.instant('action.delete') + ).subscribe((data) => { + if (data) { + this.clientDomainRegistrations(controler).removeAt(index); + } + }) + } +} diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html index 335a7d3c9a..f13809ffa6 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html @@ -54,6 +54,9 @@ tenant.description + + {{ 'tenant.allow-oauth2-configuration' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts index 8a76830093..0d55936039 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts @@ -23,6 +23,7 @@ import { ActionNotificationShow } from '@app/core/notification/notification.acti import { TranslateService } from '@ngx-translate/core'; import { ContactBasedComponent } from '../../components/entity/contact-based.component'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; +import { isDefined } from '@core/utils'; @Component({ selector: 'tb-tenant', @@ -55,7 +56,9 @@ export class TenantComponent extends ContactBasedComponent { isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []], additionalInfo: this.fb.group( { - description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''] + description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], + allowOAuth2Configuration: [isDefined(entity?.additionalInfo?.allowOAuth2Configuration) ? + entity.additionalInfo.allowOAuth2Configuration : true] } ) } @@ -66,7 +69,11 @@ export class TenantComponent extends ContactBasedComponent { this.entityForm.patchValue({title: entity.title}); this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore}); this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine}); - this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); + this.entityForm.patchValue({additionalInfo: { + description: entity.additionalInfo ? entity.additionalInfo.description : '', + allowOAuth2Configuration: isDefined(entity?.additionalInfo?.allowOAuth2Configuration) ? + entity.additionalInfo.allowOAuth2Configuration : true + }}); } updateFormState() { diff --git a/ui-ngx/src/app/shared/models/constants.ts b/ui-ngx/src/app/shared/models/constants.ts index 4c9612489a..1b4570d848 100644 --- a/ui-ngx/src/app/shared/models/constants.ts +++ b/ui-ngx/src/app/shared/models/constants.ts @@ -58,6 +58,7 @@ export const HelpLinks = { linksMap: { outgoingMailSettings: helpBaseUrl + '/docs/user-guide/ui/mail-settings', securitySettings: helpBaseUrl + '/docs/user-guide/ui/security-settings', + oauth2Settings: helpBaseUrl + '/docs/user-guide/oauth-2-support/', ruleEngine: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/overview/', ruleNodeCheckRelation: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#check-relation-filter-node', ruleNodeCheckExistenceFields: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#check-existence-fields-node', @@ -108,7 +109,7 @@ export const HelpLinks = { widgetsConfigLatest: helpBaseUrl + '/docs/user-guide/ui/dashboards#latest', widgetsConfigRpc: helpBaseUrl + '/docs/user-guide/ui/dashboards#rpc', widgetsConfigAlarm: helpBaseUrl + '/docs/user-guide/ui/dashboards#alarm', - widgetsConfigStatic: helpBaseUrl + '/docs/user-guide/ui/dashboards#static' + widgetsConfigStatic: helpBaseUrl + '/docs/user-guide/ui/dashboards#static', } }; diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts index f71ac1bb2e..c52c759719 100644 --- a/ui-ngx/src/app/shared/models/settings.models.ts +++ b/ui-ngx/src/app/shared/models/settings.models.ts @@ -23,6 +23,10 @@ export interface AdminSettings { export declare type SmtpProtocol = 'smtp' | 'smtps'; +export declare type ClientAuthenticationMethod = 'basic' | 'post'; +export declare type MapperConfigType = 'BASIC' | 'CUSTOM'; +export declare type TenantNameStrategy = 'DOMAIN' | 'EMAIL' | 'CUSTOM'; + export interface MailServerSettings { mailFrom: string; smtpProtocol: SmtpProtocol; @@ -60,3 +64,55 @@ export interface UpdateMessage { message: string; updateAvailable: boolean; } + +export interface OAuth2Settings { + clientsDomainsParams: DomainParams[]; +} + +export interface DomainParams { + domainName: string; + redirectUriTemplate: string; + clientRegistrations: ClientRegistration[]; +} + +export interface ClientRegistration { + registrationId: string; + clientName: string; + loginButtonLabel: string; + loginButtonIcon: string; + clientId: string; + clientSecret: string; + accessTokenUri: string; + authorizationUri: string; + scope: string[]; + jwkSetUri: string; + userInfoUri: string; + clientAuthenticationMethod: ClientAuthenticationMethod + userNameAttributeName: string; + mapperConfig: MapperConfig +} + +export interface MapperConfig { + allowUserCreation: boolean; + activateUser: boolean; + type: MapperConfigType; + basic?: MapperConfigBasic; + custom?: MapperConfigCustom; +} + +export interface MapperConfigBasic { + emailAttributeKey: string; + firstNameAttributeKey?: string; + lastNameAttributeKey?: string; + tenantNameStrategy: TenantNameStrategy; + tenantNamePattern?: string; + customerNamePattern?: string; + defaultDashboardName?: string; + alwaysFullScreen?: boolean; +} + +export interface MapperConfigCustom { + url: string; + username?: string; + password?: string; +} diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 0d4925d89f..d1817e89ea 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -119,8 +119,62 @@ "general-policy": "General policy", "max-failed-login-attempts": "Maximum number of failed login attempts, before account is locked", "minimum-max-failed-login-attempts-range": "Maximum number of failed login attempts can't be negative", - "user-lockout-notification-email": "In case user account lockout, send notification to email" - }, + "user-lockout-notification-email": "In case user account lockout, send notification to email", + "domain-name": "Domain name", + "error-verification-url": "A domain name shouldn't contain symbols '/' and ':'. Example: thingsboard.io", + "add-domain": "Add domain", + "new-domain": "New domain", + "add-registration": "Add registration", + "oauth2": { + "settings": "OAuth2 settings", + "registration-id": "Registration ID", + "registration-id-required": "Registration ID is required.", + "client-name": "Client name", + "client-name-required": "Client name is required.", + "client-id": "Client ID", + "client-id-required": "Client ID is required.", + "client-secret": "Client secret", + "client-secret-required": "Client secret is required.", + "access-token-uri": "Access token URI", + "access-token-uri-required": "Access token URI is required.", + "authorization-uri": "Authorization URI", + "authorization-uri-required": "Authorization URI is required.", + "uri-pattern-error": "Invalid URI format.", + "scope": "Scope", + "redirect-uri-template": "Redirect URI template", + "redirect-uri-template-required": "Redirect URI template is required.", + "jwk-set-uri": "JSON Web Key URI", + "jwk-set-uri-required": "JSON Web Key URI is required.", + "user-info-uri": "User info URI", + "user-info-uri-required": "User info URI is required.", + "client-authentication-method": "Client authentication method", + "user-name-attribute-name": "User name attribute key", + "user-name-attribute-name-required": "User name attribute key is required", + "allow-user-creation": "Allow user creation", + "activate-user": "Activate user", + "type": "Mapper type", + "email-attribute-key": "Email attribute key", + "email-attribute-key-required": "Email attribute key is required.", + "first-name-attribute-key": "First name attribute key", + "last-name-attribute-key": "Last name attribute key", + "tenant-name-strategy": "Tenant name strategy", + "tenant-name-pattern": "Tenant name pattern", + "tenant-name-pattern-required": "Tenant name pattern is required.", + "customer-name-pattern": "Customer name pattern", + "default-dashboard-name": "Default dashboard name", + "always-fullscreen": "Always fullscreen", + "url": "URL", + "url-required": "URL is required.", + "url-pattern": "Invalid URL format.", + "login-button-label": "Login button label", + "login-button-label-required": "Login button label is required.", + "login-button-icon": "Login button icon", + "delete-domain-title": "Are you sure you want to delete the domain '{{domainName}}'?", + "delete-domain-text": "Be careful, after the confirmation a domain and all registration data will be unavailable.", + "delete-registration-title": "Are you sure you want to delete the registration '{{name}}'?", + "delete-registration-text": "Be careful, after the confirmation a registration data will be unavailable." + } + }, "alarm": { "alarm": "Alarm", "alarms": "Alarms", @@ -1561,7 +1615,8 @@ "isolated-tb-core": "Processing in isolated ThingsBoard Core container", "isolated-tb-rule-engine": "Processing in isolated ThingsBoard Rule Engine container", "isolated-tb-core-details": "Requires separate microservice(s) per isolated Tenant", - "isolated-tb-rule-engine-details": "Requires separate microservice(s) per isolated Tenant" + "isolated-tb-rule-engine-details": "Requires separate microservice(s) per isolated Tenant", + "allow-oauth2-configuration": "Allow OAuth2 configuration" }, "timeinterval": { "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }", From 483a3b847b8266d71e39477094add11836076219 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Fri, 10 Jul 2020 11:33:47 +0300 Subject: [PATCH 066/117] Add tenant menu settingOAuth2 Add validation unique parameters to settings OAuth2 --- ui-ngx/src/app/core/auth/auth.models.ts | 2 + ui-ngx/src/app/core/auth/auth.reducer.ts | 1 + ui-ngx/src/app/core/auth/auth.service.ts | 12 ++++- ui-ngx/src/app/core/services/menu.service.ts | 24 ++++++++- .../home/pages/admin/admin-routing.module.ts | 6 +-- .../admin/oauth2-settings.component.html | 15 ++++-- .../pages/admin/oauth2-settings.component.ts | 49 +++++++++++++++---- .../home/pages/tenant/tenant.component.ts | 4 +- .../assets/locale/locale.constant-en_US.json | 2 + 9 files changed, 94 insertions(+), 21 deletions(-) diff --git a/ui-ngx/src/app/core/auth/auth.models.ts b/ui-ngx/src/app/core/auth/auth.models.ts index c7210da6ed..573b692a29 100644 --- a/ui-ngx/src/app/core/auth/auth.models.ts +++ b/ui-ngx/src/app/core/auth/auth.models.ts @@ -22,6 +22,7 @@ export interface AuthPayload { userTokenAccessEnabled: boolean; allowedDashboardIds: string[]; forceFullscreen: boolean; + allowOAuth2Configuration: boolean; } export interface AuthState { @@ -33,4 +34,5 @@ export interface AuthState { allowedDashboardIds: string[]; forceFullscreen: boolean; lastPublicDashboardId: string; + allowOAuth2Configuration: boolean; } diff --git a/ui-ngx/src/app/core/auth/auth.reducer.ts b/ui-ngx/src/app/core/auth/auth.reducer.ts index 6e02d03191..0e7ca095af 100644 --- a/ui-ngx/src/app/core/auth/auth.reducer.ts +++ b/ui-ngx/src/app/core/auth/auth.reducer.ts @@ -22,6 +22,7 @@ const emptyUserAuthState: AuthPayload = { userDetails: null, userTokenAccessEnabled: false, forceFullscreen: false, + allowOAuth2Configuration: false, allowedDashboardIds: [] }; diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index fad2a5e145..4089eb8979 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -425,15 +425,25 @@ export class AuthService { } } + private loadIsOAuth2ConfigurationAllow(authUser: AuthUser): Observable { + if (authUser.authority === Authority.TENANT_ADMIN) { + return this.http.get('/api/oauth2/config/isAllowed', defaultHttpOptions()); + } else { + return of(false); + } + } + private loadSystemParams(authPayload: AuthPayload): Observable { const sources: Array> = [this.loadIsUserTokenAccessEnabled(authPayload.authUser), this.fetchAllowedDashboardIds(authPayload), + this.loadIsOAuth2ConfigurationAllow(authPayload.authUser), this.timeService.loadMaxDatapointsLimit()]; return forkJoin(sources) .pipe(map((data) => { const userTokenAccessEnabled: boolean = data[0]; const allowedDashboardIds: string[] = data[1]; - return {userTokenAccessEnabled, allowedDashboardIds}; + const allowOAuth2Configuration: boolean = data[2]; + return {userTokenAccessEnabled, allowedDashboardIds, allowOAuth2Configuration}; })); } diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index ea04968f71..429f65b736 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -18,12 +18,13 @@ import { Injectable } from '@angular/core'; import { AuthService } from '../auth/auth.service'; import { select, Store } from '@ngrx/store'; import { AppState } from '../core.state'; -import { selectAuthUser, selectIsAuthenticated } from '../auth/auth.selectors'; +import { getCurrentAuthState, selectAuthUser, selectIsAuthenticated } from '../auth/auth.selectors'; import { take } from 'rxjs/operators'; import { HomeSection, MenuSection } from '@core/services/menu.models'; import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { Authority } from '@shared/models/authority.enum'; import { AuthUser } from '@shared/models/user.model'; +import { AuthState } from '@core/auth/auth.models'; @Injectable({ providedIn: 'root' @@ -43,6 +44,8 @@ export class MenuService { ); } + authState: AuthState = getCurrentAuthState(this.store); + private buildMenu() { this.store.pipe(select(selectAuthUser), take(1)).subscribe( (authUser: AuthUser) => { @@ -233,6 +236,25 @@ export class MenuService { icon: 'track_changes' } ); + + if (this.authState.allowOAuth2Configuration) { + sections.push({ + name: 'admin.system-settings', + type: 'toggle', + path: '/settings', + height: '40px', + icon: 'settings', + pages: [ + { + name: 'admin.oauth2.settings', + type: 'link', + path: '/settings/oauth2-settings', + icon: 'security' + } + ] + }) + } + return sections; } diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index 32c25c6e7d..329e313110 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -28,7 +28,7 @@ const routes: Routes = [ { path: 'settings', data: { - auth: [Authority.SYS_ADMIN], + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], breadcrumb: { label: 'admin.system-settings', icon: 'settings' @@ -37,7 +37,7 @@ const routes: Routes = [ children: [ { path: '', - redirectTo: 'general', + redirectTo: Authority.TENANT_ADMIN ? 'oauth2-settings' : 'general', pathMatch: 'full' }, { @@ -84,7 +84,7 @@ const routes: Routes = [ component: OAuth2SettingsComponent, canDeactivate: [ConfirmOnExitGuard], data: { - auth: [Authority.SYS_ADMIN], + auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], title: 'admin.oauth2.settings', breadcrumb: { label: 'admin.oauth2.settings', diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index 87f1871ec2..5c8c92658e 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -52,6 +52,9 @@ {{ 'admin.error-verification-url' | translate }} + + {{ 'admin.domain-name-unique' | translate }} + @@ -84,6 +87,9 @@ {{ 'admin.oauth2.registration-id-required' | translate }} + + {{ 'admin.oauth2.registration-id-unique' | translate }} + @@ -140,10 +146,8 @@ admin.oauth2.scope - + {{scope}} cancel @@ -152,6 +156,9 @@ matChipInputAddOnBlur (matChipInputTokenEnd)="addScope($event, registration)"> + + {{ 'admin.oauth2.jwk-set-uri-required' | translate }} +
diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index d128ede633..47333a99b1 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -112,14 +112,44 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private buildOAuth2SettingsForm(): void { this.oauth2SettingsForm = this.fb.group({ - clientsDomainsParams: this.fb.array([]) + clientsDomainsParams: this.fb.array([], Validators.required) }); } private initOAuth2Settings(oauth2Settings: OAuth2Settings): void { - oauth2Settings.clientsDomainsParams.forEach((domaindomain) => { - this.clientsDomainsParams.push(this.buildSettingsDomain(domaindomain)); - }); + if(oauth2Settings.clientsDomainsParams) { + oauth2Settings.clientsDomainsParams.forEach((domaindomain) => { + this.clientsDomainsParams.push(this.buildSettingsDomain(domaindomain)); + }); + } + } + + private uniqueDomainValidator(control: AbstractControl): { [key: string]: boolean } | null { + if (control.value !== null && control?.root) { + const listDomainName = []; + control.root.value.clientsDomainsParams.forEach((domain) => { + listDomainName.push(domain.domainName); + }) + if (listDomainName.indexOf(control.value) > -1) { + return {unique: true}; + } + } + return null; + } + + private uniqueRegistrationIdValidator(control: AbstractControl): { [key: string]: boolean } | null { + if (control.value !== null && control?.root) { + const listRegistration = []; + control.root.value.clientsDomainsParams.forEach((domain) => { + domain.clientRegistrations.forEach((client) => { + listRegistration.push(client.registrationId); + }) + }) + if (listRegistration.indexOf(control.value) > -1) { + return {unique: true}; + } + } + return null; } private buildSettingsDomain(domainParams?: DomainParams): FormGroup { @@ -130,9 +160,9 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } url += '/login/oauth2/code/'; const formDomain = this.fb.group({ - domainName: ['', [Validators.required, Validators.pattern('((?![:/]).)*$')]], + domainName: [null, [Validators.required, Validators.pattern('((?![:/]).)*$'), this.uniqueDomainValidator]], redirectUriTemplate: [url, [Validators.required, Validators.pattern(this.URL_REGEXP)]], - clientRegistrations: this.fb.array([]) + clientRegistrations: this.fb.array([], Validators.required) }); this.subscriptions.push(formDomain.get('domainName').valueChanges.subscribe((domain) => { @@ -156,7 +186,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private buildSettingsRegistration(registrationData?: ClientRegistration): FormGroup { const clientRegistration = this.fb.group({ - registrationId: [null, [Validators.required]], + registrationId: [null, [Validators.required, this.uniqueRegistrationIdValidator]], clientName: [null, [Validators.required]], loginButtonLabel: [null, [Validators.required]], loginButtonIcon: [null], @@ -202,7 +232,6 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } save(): void { - console.log(this.oauth2SettingsForm.value); this.adminService.saveOAuth2Settings(this.oauth2SettingsForm.value).subscribe( (oauth2Settings) => { this.oauth2Settings = oauth2Settings; @@ -246,7 +275,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha const domainName = this.clientsDomainsParams.at(index).get('domainName').value; this.dialogService.confirm( - this.translate.instant('admin.oauth2.delete-domain-title', {domainName}), + this.translate.instant('admin.oauth2.delete-domain-title', {domainName: domainName || ''}), this.translate.instant('admin.oauth2.delete-domain-text'), null, this.translate.instant('action.delete') ).subscribe((data) => { @@ -272,7 +301,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha const registrationId = this.clientDomainRegistrations(controler).at(index).get('registrationId').value; this.dialogService.confirm( - this.translate.instant('admin.oauth2.delete-registration-title', {name: registrationId}), + this.translate.instant('admin.oauth2.delete-registration-title', {name: registrationId || ''}), this.translate.instant('admin.oauth2.delete-registration-text'), null, this.translate.instant('action.delete') ).subscribe((data) => { diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts index 0d55936039..aa40d792b0 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts @@ -58,7 +58,7 @@ export class TenantComponent extends ContactBasedComponent { { description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], allowOAuth2Configuration: [isDefined(entity?.additionalInfo?.allowOAuth2Configuration) ? - entity.additionalInfo.allowOAuth2Configuration : true] + entity.additionalInfo.allowOAuth2Configuration : false] } ) } @@ -72,7 +72,7 @@ export class TenantComponent extends ContactBasedComponent { this.entityForm.patchValue({additionalInfo: { description: entity.additionalInfo ? entity.additionalInfo.description : '', allowOAuth2Configuration: isDefined(entity?.additionalInfo?.allowOAuth2Configuration) ? - entity.additionalInfo.allowOAuth2Configuration : true + entity.additionalInfo.allowOAuth2Configuration : false }}); } diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index d1817e89ea..0866117933 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -121,6 +121,7 @@ "minimum-max-failed-login-attempts-range": "Maximum number of failed login attempts can't be negative", "user-lockout-notification-email": "In case user account lockout, send notification to email", "domain-name": "Domain name", + "domain-name-unique": "Domain name need to unique for the system.", "error-verification-url": "A domain name shouldn't contain symbols '/' and ':'. Example: thingsboard.io", "add-domain": "Add domain", "new-domain": "New domain", @@ -129,6 +130,7 @@ "settings": "OAuth2 settings", "registration-id": "Registration ID", "registration-id-required": "Registration ID is required.", + "registration-id-unique": "Registration ID need to unique for the system.", "client-name": "Client name", "client-name-required": "Client name is required.", "client-id": "Client ID", From d8fecd18d9cd3bb97a90827772393ca055b5dca0 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 10 Jul 2020 13:16:34 +0300 Subject: [PATCH 067/117] Get 'clientName' from 'registrationId' --- .../server/common/data/oauth2/OAuth2ClientRegistration.java | 1 - .../server/dao/oauth2/HybridClientRegistrationRepository.java | 2 +- .../org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java | 3 --- .../thingsboard/server/dao/service/BaseOAuth2ServiceTest.java | 1 - 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java index 2051e4b850..0a4156bcc5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -40,7 +40,6 @@ public class OAuth2ClientRegistration { private String userNameAttributeName; private String jwkSetUri; private String clientAuthenticationMethod; - private String clientName; private String loginButtonLabel; private String loginButtonIcon; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java index ab8a14e078..7309cc7edb 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java @@ -39,13 +39,13 @@ public class HybridClientRegistrationRepository implements ClientRegistrationRep private ClientRegistration toSpringClientRegistration(String redirectUriTemplate, OAuth2ClientRegistration localClientRegistration){ return ClientRegistration.withRegistrationId(localClientRegistration.getRegistrationId()) + .clientName(localClientRegistration.getRegistrationId()) .clientId(localClientRegistration.getClientId()) .authorizationUri(localClientRegistration.getAuthorizationUri()) .clientSecret(localClientRegistration.getClientSecret()) .tokenUri(localClientRegistration.getAccessTokenUri()) .redirectUriTemplate(redirectUriTemplate) .scope(localClientRegistration.getScope()) - .clientName(localClientRegistration.getClientName()) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .userInfoUri(localClientRegistration.getUserInfoUri()) .userNameAttributeName(localClientRegistration.getUserNameAttributeName()) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 25dec8e696..92790b3d98 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -521,9 +521,6 @@ public class OAuth2ServiceImpl implements OAuth2Service { if (StringUtils.isEmpty(clientRegistration.getClientAuthenticationMethod())) { throw new DataValidationException("Client authentication method should be specified!"); } - if (StringUtils.isEmpty(clientRegistration.getClientName())) { - throw new DataValidationException("Client name should be specified!"); - } if (StringUtils.isEmpty(clientRegistration.getLoginButtonLabel())) { throw new DataValidationException("Login button label should be specified!"); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index e502b660ec..ab572e7e53 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -493,7 +493,6 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { .userNameAttributeName("userNameAttributeName") .jwkSetUri("jwkSetUri") .clientAuthenticationMethod("clientAuthenticationMethod") - .clientName("clientName") .loginButtonLabel("loginButtonLabel") .build(); } From 9eb9302ee3070e5af69f9d410d39bc2a312c7609 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 10 Jul 2020 15:54:34 +0300 Subject: [PATCH 068/117] Used DB lock instead of Java lock --- .../server/controller/OAuth2Controller.java | 1 - .../thingsboard/server/dao/lock/LockKey.java | 30 ++++++ .../server/dao/lock/LockService.java | 25 +++++ .../server/dao/lock/LockServiceImpl.java | 43 +++++++++ .../server/dao/oauth2/OAuth2ServiceImpl.java | 91 ++++++++++--------- .../server/dao/sql/lock/LockRepository.java | 20 ++++ .../dao/sql/lock/PsqlLockRepository.java | 42 +++++++++ 7 files changed, 210 insertions(+), 42 deletions(-) create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockKey.java create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockService.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/lock/LockRepository.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index b3b2b1eb83..ba300eec71 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -43,7 +43,6 @@ public class OAuth2Controller extends BaseController { @Autowired private OAuth2Service oauth2Service; - // TODO ask why POST @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) @ResponseBody public List getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockKey.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockKey.java new file mode 100644 index 0000000000..5de01b3e2c --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockKey.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.dao.lock; + +public enum LockKey { + OAUTH2_CONFIG(999); + + private int id; + + LockKey(int id) { + this.id = id; + } + + public int getId() { + return id; + } +} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockService.java new file mode 100644 index 0000000000..4ffd05233d --- /dev/null +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockService.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.lock; + +public interface LockService { + /** + * Lock with SQL Database till the end of transaction + * + * @param key identifier + */ + void transactionLock(LockKey key); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java new file mode 100644 index 0000000000..703a6686a8 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java @@ -0,0 +1,43 @@ +/** + * 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.lock; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.thingsboard.server.dao.sql.lock.LockRepository; + +import javax.annotation.PostConstruct; + +@Service +@Slf4j +public class LockServiceImpl implements LockService { + + @Autowired(required = false) + private LockRepository lockRepository; + + @PostConstruct + public void init(){ + log.warn("Locking with DB is not enabled."); + } + + @Override + public void transactionLock(LockKey key) { + if (lockRepository == null) return; + log.trace("Locking transaction key [{}]", key); + lockRepository.transactionLock(key.getId()); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 92790b3d98..90d19a19f3 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -1,12 +1,12 @@ /** * 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 - *

+ * + * 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. @@ -28,6 +28,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import org.thingsboard.server.common.data.*; import org.thingsboard.server.common.data.id.*; @@ -36,13 +37,14 @@ import org.thingsboard.server.common.data.oauth2.*; import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.exception.IncorrectParameterException; +import org.thingsboard.server.dao.lock.LockKey; +import org.thingsboard.server.dao.lock.LockService; import org.thingsboard.server.dao.settings.AdminSettingsService; import org.thingsboard.server.dao.tenant.TenantService; import java.io.IOException; import java.util.*; import java.util.concurrent.ExecutionException; -import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -51,11 +53,8 @@ import static org.thingsboard.server.dao.oauth2.OAuth2Utils.*; @Slf4j @Service public class OAuth2ServiceImpl implements OAuth2Service { - private static final ObjectMapper mapper = new ObjectMapper(); - private final ReentrantLock clientRegistrationSaveLock = new ReentrantLock(); - @Autowired private Environment environment; @@ -68,6 +67,9 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Autowired private TenantService tenantService; + @Autowired + private LockService lockService; + private boolean isInstall() { return environment.acceptsProfiles("install"); } @@ -123,52 +125,59 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) { validate(oAuth2ClientsParams); - validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); - clientRegistrationSaveLock.lock(); - try { - validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); - AdminSettings oauth2SystemAdminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); - if (oauth2SystemAdminSettings == null) { - oauth2SystemAdminSettings = createSystemAdminSettings(); - } - String json = toJson(oAuth2ClientsParams); - ((ObjectNode) oauth2SystemAdminSettings.getJsonValue()).put(SYSTEM_SETTINGS_OAUTH2_VALUE, json); - adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, oauth2SystemAdminSettings); - } finally { - clientRegistrationSaveLock.unlock(); - } + + transactionalSaveSystemOAuth2ClientsParams(oAuth2ClientsParams); return getSystemOAuth2ClientsParams(); } + @Transactional + private void transactionalSaveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) { + long acquireStart = System.currentTimeMillis(); + lockService.transactionLock(LockKey.OAUTH2_CONFIG); + log.trace("[{}] Waited for lock {} ms.", TenantId.SYS_TENANT_ID, System.currentTimeMillis() - acquireStart); + + validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); + AdminSettings oauth2SystemAdminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + if (oauth2SystemAdminSettings == null) { + oauth2SystemAdminSettings = createSystemAdminSettings(); + } + String json = toJson(oAuth2ClientsParams); + ((ObjectNode) oauth2SystemAdminSettings.getJsonValue()).put(SYSTEM_SETTINGS_OAUTH2_VALUE, json); + adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, oauth2SystemAdminSettings); + } + @Override public OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) { validate(oAuth2ClientsParams); - validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); - clientRegistrationSaveLock.lock(); - try { - validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); - Set domainNames = oAuth2ClientsParams.getClientsDomainsParams().stream() - .map(OAuth2ClientsDomainParams::getDomainName) - .collect(Collectors.toSet()); - processTenantAdminSettings(tenantId, domainNames); + transactionalSaveTenantOAuth2ClientsParams(tenantId, oAuth2ClientsParams); - List attributes = createOAuth2ClientsParamsAttributes(oAuth2ClientsParams); - try { - attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, attributes).get(); - } catch (Exception e) { - log.error("Unable to save OAuth2 Client Registration Params to attributes!", e); - throw new IncorrectParameterException("Unable to save OAuth2 Client Registration Params to attributes!"); - } + return getTenantOAuth2ClientsParams(tenantId); + } - } finally { - clientRegistrationSaveLock.unlock(); - } + @Transactional + private void transactionalSaveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) { + long acquireStart = System.currentTimeMillis(); + lockService.transactionLock(LockKey.OAUTH2_CONFIG); + log.trace("[{}] Waited for lock {} ms.", tenantId, System.currentTimeMillis() - acquireStart); - return getTenantOAuth2ClientsParams(tenantId); + validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); + + Set domainNames = oAuth2ClientsParams.getClientsDomainsParams().stream() + .map(OAuth2ClientsDomainParams::getDomainName) + .collect(Collectors.toSet()); + processTenantAdminSettings(tenantId, domainNames); + + List attributes = createOAuth2ClientsParamsAttributes(oAuth2ClientsParams); + try { + attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, attributes).get(); + } catch (Exception e) { + log.error("Unable to save OAuth2 Client Registration Params to attributes!", e); + throw new IncorrectParameterException("Unable to save OAuth2 Client Registration Params to attributes!"); + } } private List createOAuth2ClientsParamsAttributes(OAuth2ClientsParams oAuth2ClientsParams) { diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/lock/LockRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/lock/LockRepository.java new file mode 100644 index 0000000000..eced945acd --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/lock/LockRepository.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.dao.sql.lock; + +public interface LockRepository { + void transactionLock(Integer key); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java new file mode 100644 index 0000000000..0ae44edffa --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.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.dao.sql.lock; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import org.thingsboard.server.dao.util.PsqlDao; +import org.thingsboard.server.dao.util.SqlDao; + +@Slf4j +@SqlDao +@PsqlDao +@Repository +public class PsqlLockRepository implements LockRepository { + private static final String TRANSACTION_LOCK_QUERY = "SELECT pg_advisory_xact_lock(?)"; + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Override + public void transactionLock(Integer key) { + jdbcTemplate.query(TRANSACTION_LOCK_QUERY, + preparedStatement -> preparedStatement.setInt(1, key), + resultSet -> {} + ); + } +} From 3d533b7c2384f0aac122eb7e13c0bd39aeaa1bd3 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 13 Jul 2020 12:08:32 +0300 Subject: [PATCH 069/117] Add type and clear code --- ui-ngx/src/app/core/guards/redirect.guard.ts | 35 +++++++++++++++++++ .../home/pages/admin/admin-routing.module.ts | 3 +- .../admin/oauth2-settings.component.html | 32 ++++++----------- .../pages/admin/oauth2-settings.component.ts | 26 ++++++++------ .../src/app/shared/models/settings.models.ts | 3 +- .../assets/locale/locale.constant-en_US.json | 2 -- 6 files changed, 65 insertions(+), 36 deletions(-) create mode 100644 ui-ngx/src/app/core/guards/redirect.guard.ts diff --git a/ui-ngx/src/app/core/guards/redirect.guard.ts b/ui-ngx/src/app/core/guards/redirect.guard.ts new file mode 100644 index 0000000000..106cc0840b --- /dev/null +++ b/ui-ngx/src/app/core/guards/redirect.guard.ts @@ -0,0 +1,35 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { AuthState } from '@core/auth/auth.models'; +import { select, Store } from '@ngrx/store'; +import { selectAuth } from '@core/auth/auth.selectors'; +import { take } from 'rxjs/operators'; +import { AppState } from '@core/core.state'; +import { Authority } from '@shared/models/authority.enum'; + +@Injectable({ + providedIn: 'root' +}) +export class RedirectGuard implements CanActivate { + constructor(private store: Store, + private router: Router) { } + + canActivate( + next: ActivatedRouteSnapshot, + state: RouterStateSnapshot) { + let auth: AuthState = null; + this.store.pipe(select(selectAuth), take(1)).subscribe( + (authState: AuthState) => { + auth = authState; + } + ); + + if (auth?.userDetails?.authority === Authority.TENANT_ADMIN) { + this.router.navigateByUrl('/settings/oauth2-settings'); + return false; + } + this.router.navigateByUrl('/settings/general'); + return false; + } + +} diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index 329e313110..a6b18cfd29 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -23,6 +23,7 @@ import { Authority } from '@shared/models/authority.enum'; import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component'; import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; import { OAuth2SettingsComponent } from '@home/pages/admin/oauth2-settings.component'; +import { RedirectGuard } from '@core/guards/redirect.guard'; const routes: Routes = [ { @@ -37,7 +38,7 @@ const routes: Routes = [ children: [ { path: '', - redirectTo: Authority.TENANT_ADMIN ? 'oauth2-settings' : 'general', + canActivate: [RedirectGuard], pathMatch: 'full' }, { diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index 5c8c92658e..e5ea872fbc 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -80,26 +80,16 @@ delete

-
- - admin.oauth2.registration-id - - - {{ 'admin.oauth2.registration-id-required' | translate }} - - - {{ 'admin.oauth2.registration-id-unique' | translate }} - - - - - admin.oauth2.client-name - - - {{ 'admin.oauth2.client-name-required' | translate }} - - -
+ + admin.oauth2.registration-id + + + {{ 'admin.oauth2.registration-id-required' | translate }} + + + {{ 'admin.oauth2.registration-id-unique' | translate }} + +
@@ -310,7 +300,7 @@ common.password - +
diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index 47333a99b1..ab472643a6 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -16,7 +16,14 @@ import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { ClientRegistration, DomainParams, OAuth2Settings } from '@shared/models/settings.models'; +import { + ClientAuthenticationMethod, + ClientRegistration, + DomainParams, + MapperConfigType, + OAuth2Settings, + TenantNameStrategy +} from '@shared/models/settings.models'; import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { AdminService } from '@core/http/admin.service'; @@ -44,9 +51,9 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha oauth2SettingsForm: FormGroup; oauth2Settings: OAuth2Settings; - clientAuthenticationMethods = ['basic', 'post']; - converterTypesExternalUser = ['BASIC', 'CUSTOM']; - tenantNameStrategies = ['DOMAIN', 'EMAIL', 'CUSTOM']; + clientAuthenticationMethods: ClientAuthenticationMethod[] = ['BASIC', 'POST']; + converterTypesExternalUser: MapperConfigType[] = ['BASIC', 'CUSTOM']; + tenantNameStrategies: TenantNameStrategy[] = ['DOMAIN', 'EMAIL', 'CUSTOM']; constructor(protected store: Store, private adminService: AdminService, @@ -117,7 +124,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } private initOAuth2Settings(oauth2Settings: OAuth2Settings): void { - if(oauth2Settings.clientsDomainsParams) { + if (oauth2Settings.clientsDomainsParams) { oauth2Settings.clientsDomainsParams.forEach((domaindomain) => { this.clientsDomainsParams.push(this.buildSettingsDomain(domaindomain)); }); @@ -173,7 +180,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha formDomain.get('redirectUriTemplate').patchValue(uri); })); - if(domainParams){ + if (domainParams) { domainParams.clientRegistrations.forEach((registration) => { this.clientDomainRegistrations(formDomain).push(this.buildSettingsRegistration(registration)); }) @@ -187,7 +194,6 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private buildSettingsRegistration(registrationData?: ClientRegistration): FormGroup { const clientRegistration = this.fb.group({ registrationId: [null, [Validators.required, this.uniqueRegistrationIdValidator]], - clientName: [null, [Validators.required]], loginButtonLabel: [null, [Validators.required]], loginButtonIcon: [null], clientId: ['', [Validators.required]], @@ -197,7 +203,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha scope: this.fb.array([], [Validators.required]), jwkSetUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], userInfoUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], - clientAuthenticationMethod: ['post', [Validators.required]], + clientAuthenticationMethod: ['POST', [Validators.required]], userNameAttributeName: ['email', [Validators.required]], mapperConfig: this.fb.group({ allowUserCreation: [true], @@ -219,11 +225,11 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } })); - if(registrationData){ + if (registrationData) { registrationData.scope.forEach(() => { (clientRegistration.get('scope') as FormArray).push(this.fb.control('')) }) - if(registrationData.mapperConfig.type !== 'BASIC'){ + if (registrationData.mapperConfig.type !== 'BASIC') { clientRegistration.get('mapperConfig.type').patchValue('CUSTOM'); } } diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts index c52c759719..c4a1504f0f 100644 --- a/ui-ngx/src/app/shared/models/settings.models.ts +++ b/ui-ngx/src/app/shared/models/settings.models.ts @@ -23,7 +23,7 @@ export interface AdminSettings { export declare type SmtpProtocol = 'smtp' | 'smtps'; -export declare type ClientAuthenticationMethod = 'basic' | 'post'; +export declare type ClientAuthenticationMethod = 'BASIC' | 'POST'; export declare type MapperConfigType = 'BASIC' | 'CUSTOM'; export declare type TenantNameStrategy = 'DOMAIN' | 'EMAIL' | 'CUSTOM'; @@ -77,7 +77,6 @@ export interface DomainParams { export interface ClientRegistration { registrationId: string; - clientName: string; loginButtonLabel: string; loginButtonIcon: string; clientId: string; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 0866117933..239a580ef7 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -131,8 +131,6 @@ "registration-id": "Registration ID", "registration-id-required": "Registration ID is required.", "registration-id-unique": "Registration ID need to unique for the system.", - "client-name": "Client name", - "client-name-required": "Client name is required.", "client-id": "Client ID", "client-id-required": "Client ID is required.", "client-secret": "Client secret", From 294664b74607e261e9924fc3208effe78512f14a Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 23 Jul 2020 12:54:24 +0300 Subject: [PATCH 070/117] Minnor fix --- ui-ngx/src/app/core/guards/redirect.guard.ts | 16 ++++++++++++++++ .../pages/admin/oauth2-settings.component.html | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/ui-ngx/src/app/core/guards/redirect.guard.ts b/ui-ngx/src/app/core/guards/redirect.guard.ts index 106cc0840b..652f1ff026 100644 --- a/ui-ngx/src/app/core/guards/redirect.guard.ts +++ b/ui-ngx/src/app/core/guards/redirect.guard.ts @@ -1,3 +1,19 @@ +/// +/// 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. +/// + import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; import { AuthState } from '@core/auth/auth.models'; diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index e5ea872fbc..f1c3bfaf62 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -295,7 +295,7 @@
common.username - + From 21275c789fc6ea136eaacbe92843b0b45beec030 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 23 Jul 2020 13:02:07 +0300 Subject: [PATCH 071/117] Improvement Translated --- ui-ngx/src/app/core/services/menu.service.ts | 2 +- ui-ngx/src/assets/locale/locale.constant-en_US.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index 429f65b736..bb39cfa415 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -239,7 +239,7 @@ export class MenuService { if (this.authState.allowOAuth2Configuration) { sections.push({ - name: 'admin.system-settings', + name: 'admin.settings', type: 'toggle', path: '/settings', height: '40px', diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 239a580ef7..d12d3a0e36 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -99,7 +99,7 @@ "proxy-user": "Proxy user", "proxy-password": "Proxy password", "send-test-mail": "Send test mail", - "security-settings": "Security settings", + "security-settings": "Security Settings", "password-policy": "Password policy", "minimum-password-length": "Minimum password length", "minimum-password-length-required": "Minimum password length is required", @@ -126,8 +126,9 @@ "add-domain": "Add domain", "new-domain": "New domain", "add-registration": "Add registration", + "settings": "Settings", "oauth2": { - "settings": "OAuth2 settings", + "settings": "OAuth2 Settings", "registration-id": "Registration ID", "registration-id-required": "Registration ID is required.", "registration-id-unique": "Registration ID need to unique for the system.", From 58464c54dc9f529c43eeb190aecb9ee8148a3d05 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Mon, 3 Aug 2020 17:31:02 +0300 Subject: [PATCH 072/117] Fix merge --- .../data/kv/BaseEntityAttributeKvEntry.java | 15 +++++---------- .../common/data/kv/EntityAttributeKvEntry.java | 5 ++--- .../server/dao/lock/LockServiceImpl.java | 4 +++- .../server/dao/model/sql/AttributeKvEntity.java | 2 +- .../server/dao/oauth2/OAuth2ServiceImpl.java | 6 +++--- .../server/dao/sql/lock/PsqlLockRepository.java | 2 -- 6 files changed, 14 insertions(+), 20 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseEntityAttributeKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseEntityAttributeKvEntry.java index aac91f5613..5c97acd90e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseEntityAttributeKvEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseEntityAttributeKvEntry.java @@ -19,26 +19,21 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import org.thingsboard.server.common.data.EntityType; +import java.util.UUID; + @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) public class BaseEntityAttributeKvEntry extends BaseAttributeKvEntry implements EntityAttributeKvEntry { - private final EntityType entityType; - private final String entityId; + private final UUID entityId; - public BaseEntityAttributeKvEntry(EntityType entityType, String entityId, long lastUpdateTs, KvEntry kv) { + public BaseEntityAttributeKvEntry(UUID entityId, long lastUpdateTs, KvEntry kv) { super(kv, lastUpdateTs); - this.entityType = entityType; this.entityId = entityId; } @Override - public EntityType getEntityType() { - return entityType; - } - - @Override - public String getEntityId() { + public UUID getEntityId() { return entityId; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/EntityAttributeKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/EntityAttributeKvEntry.java index b62576b8e0..ceb6c62a3c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/EntityAttributeKvEntry.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/kv/EntityAttributeKvEntry.java @@ -15,10 +15,9 @@ */ package org.thingsboard.server.common.data.kv; -import org.thingsboard.server.common.data.EntityType; +import java.util.UUID; public interface EntityAttributeKvEntry extends AttributeKvEntry { - EntityType getEntityType(); - String getEntityId(); + UUID getEntityId(); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java index 703a6686a8..ff1e052f97 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java @@ -31,7 +31,9 @@ public class LockServiceImpl implements LockService { @PostConstruct public void init(){ - log.warn("Locking with DB is not enabled."); + if (lockRepository == null) { + log.warn("Locking with DB is not enabled."); + } } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java index 7787f21830..c652a67469 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java @@ -89,6 +89,6 @@ public class AttributeKvEntity implements ToData, Serializable kvEntry = new JsonDataEntry(id.getAttributeKey(), jsonValue); } - return new BaseEntityAttributeKvEntry(id.getEntityType(), id.getEntityId(), lastUpdateTs, kvEntry); + return new BaseEntityAttributeKvEntry(id.getEntityId(), lastUpdateTs, kvEntry); }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 90d19a19f3..df1cd5a049 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -333,14 +333,14 @@ public class OAuth2ServiceImpl implements OAuth2Service { @Override public Map getAllOAuth2ClientsParams() { OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(); - ListenableFuture> jsonFuture = getAllOAuth2ClientsParamsAttribute(); + ListenableFuture> jsonFuture = getAllOAuth2ClientsParamsAttribute(); try { return Futures.transform(jsonFuture, clientsParamsByKvEntryKey -> { Map tenantClientParams = clientsParamsByKvEntryKey != null ? clientsParamsByKvEntryKey.entrySet().stream() .collect(Collectors.toMap( - entry -> new TenantId(UUIDConverter.fromString(entry.getKey())), + entry -> new TenantId(entry.getKey()), entry -> constructOAuth2ClientsParams(entry.getValue()) )) : new HashMap<>(); @@ -405,7 +405,7 @@ public class OAuth2ServiceImpl implements OAuth2Service { } // TODO maybe it's better to load all tenants and get attribute for each one - private ListenableFuture> getAllOAuth2ClientsParamsAttribute() { + private ListenableFuture> getAllOAuth2ClientsParamsAttribute() { ListenableFuture> entityAttributeKvEntriesFuture; try { entityAttributeKvEntriesFuture = attributesService.findAllByAttributeKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java index 0ae44edffa..76c4b6ebc8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java @@ -20,10 +20,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import org.thingsboard.server.dao.util.PsqlDao; -import org.thingsboard.server.dao.util.SqlDao; @Slf4j -@SqlDao @PsqlDao @Repository public class PsqlLockRepository implements LockRepository { From 61a6fdbc03694ac4656a2bafc1e8be4018dfc49d Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 5 Aug 2020 17:35:36 +0300 Subject: [PATCH 073/117] Moved OAuth2 config to separate table in DB --- .../main/data/upgrade/3.1.0/schema_update.sql | 53 ++ .../server/controller/BaseController.java | 35 +- .../server/controller/OAuth2Controller.java | 67 +- .../install/ThingsboardInstallService.java | 3 + .../install/SqlDatabaseUpgradeService.java | 9 + .../Oauth2AuthenticationSuccessHandler.java | 6 +- .../service/security/permission/Resource.java | 2 +- .../permission/TenantAdminPermissions.java | 8 + .../server/dao/oauth2/OAuth2Service.java | 29 +- .../server/common/data/EntityType.java | 2 +- .../common/data/id/EntityIdFactory.java | 2 + ...d.java => OAuth2ClientRegistrationId.java} | 16 +- .../common/data/oauth2/OAuth2ClientInfo.java | 19 +- .../data/oauth2/OAuth2ClientRegistration.java | 41 +- .../data/oauth2/OAuth2ClientsParams.java | 2 +- .../server/dao/model/ModelConstants.java | 6 +- .../sql/OAuth2ClientRegistrationEntity.java | 204 ++++++ .../HybridClientRegistrationRepository.java | 10 +- .../oauth2/OAuth2ClientRegistrationDao.java | 36 + .../server/dao/oauth2/OAuth2ServiceImpl.java | 644 +++++------------- .../server/dao/oauth2/OAuth2Utils.java | 18 - .../JpaOAuth2ClientRegistrationDao.java | 86 +++ .../OAuth2ClientRegistrationRepository.java | 37 + .../server/dao/tenant/TenantServiceImpl.java | 2 +- .../resources/sql/schema-entities-hsql.sql | 37 + .../main/resources/sql/schema-entities.sql | 36 + .../dao/service/BaseOAuth2ServiceTest.java | 471 ++++--------- .../resources/sql/hsql/drop-all-tables.sql | 1 + .../resources/sql/psql/drop-all-tables.sql | 1 + .../sql/timescale/drop-all-tables.sql | 1 + 30 files changed, 939 insertions(+), 945 deletions(-) create mode 100644 application/src/main/data/upgrade/3.1.0/schema_update.sql rename common/data/src/main/java/org/thingsboard/server/common/data/id/{OAuth2IntegrationId.java => OAuth2ClientRegistrationId.java} (63%) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql new file mode 100644 index 0000000000..06daa9541c --- /dev/null +++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql @@ -0,0 +1,53 @@ +-- +-- 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. +-- + +DROP TABLE IF EXISTS oauth2_client_registration; + +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( + id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + tenant_id uuid, + registration_id varchar(255), + domain_name varchar(255), + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + redirect_uri_template 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), + CONSTRAINT oauth2_registration_id_unq_key UNIQUE (registration_id) +); \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 2c202e71b2..3757223fda 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -47,22 +47,10 @@ 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.AlarmId; -import org.thingsboard.server.common.data.id.AssetId; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.DashboardId; -import org.thingsboard.server.common.data.id.DeviceId; -import org.thingsboard.server.common.data.id.EntityId; -import org.thingsboard.server.common.data.id.EntityIdFactory; -import org.thingsboard.server.common.data.id.EntityViewId; -import org.thingsboard.server.common.data.id.RuleChainId; -import org.thingsboard.server.common.data.id.RuleNodeId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.id.UserId; -import org.thingsboard.server.common.data.id.WidgetTypeId; -import org.thingsboard.server.common.data.id.WidgetsBundleId; +import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.DataType; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; @@ -87,6 +75,7 @@ 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.OAuth2Service; import org.thingsboard.server.dao.relation.RelationService; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.tenant.TenantService; @@ -161,6 +150,9 @@ public abstract class BaseController { @Autowired protected DashboardService dashboardService; + @Autowired + protected OAuth2Service oAuth2Service; + @Autowired protected ComponentDiscoveryService componentDescriptorService; @@ -390,6 +382,9 @@ public abstract class BaseController { case WIDGET_TYPE: checkWidgetTypeId(new WidgetTypeId(entityId.getId()), operation); return; + case OAUTH2_CLIENT_REGISTRATION: + checkOAuth2ClientRegistrationId(new OAuth2ClientRegistrationId(entityId.getId()), operation); + return; default: throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); } @@ -542,6 +537,18 @@ public abstract class BaseController { } } + OAuth2ClientRegistration checkOAuth2ClientRegistrationId(OAuth2ClientRegistrationId clientRegistrationId, Operation operation) throws ThingsboardException { + try { + validateId(clientRegistrationId, "Incorrect oAuth2ClientRegistrationId " + clientRegistrationId); + OAuth2ClientRegistration clientRegistration = oAuth2Service.findClientRegistrationById(getCurrentUser().getTenantId(), clientRegistrationId); + checkNotNull(clientRegistration); + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION, operation, clientRegistrationId, clientRegistration); + return clientRegistration; + } catch (Exception e) { + throw handleException(e, false); + } + } + ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { try { log.debug("[{}] Lookup component descriptor", clazz); diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index ba300eec71..9033b17c47 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -20,8 +20,12 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; +import org.thingsboard.server.common.data.Dashboard; +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.EntityId; +import org.thingsboard.server.common.data.id.DashboardId; +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; @@ -40,14 +44,14 @@ import java.util.List; @RequestMapping("/api") @Slf4j public class OAuth2Controller extends BaseController { - @Autowired - private OAuth2Service oauth2Service; + public static final String CLIENT_REGISTRATION_ID = "clientRegistrationId"; + private static final String REGISTRATION_ID = "registrationId"; @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) @ResponseBody public List getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { try { - return oauth2Service.getOAuth2Clients(request.getServerName()); + return oAuth2Service.getOAuth2Clients(request.getServerName()); } catch (Exception e) { throw handleException(e); } @@ -60,13 +64,15 @@ public class OAuth2Controller extends BaseController { try { Authority authority = getCurrentUser().getAuthority(); checkOAuth2ConfigPermissions(Operation.READ); - OAuth2ClientsParams oAuth2ClientsParams = null; + List clientRegistrations = null; if (Authority.SYS_ADMIN.equals(authority)) { - oAuth2ClientsParams = oauth2Service.getSystemOAuth2ClientsParams(); + clientRegistrations = oAuth2Service.findClientRegistrationsByTenantId(TenantId.SYS_TENANT_ID); } else if (Authority.TENANT_ADMIN.equals(authority)) { - oAuth2ClientsParams = oauth2Service.getTenantOAuth2ClientsParams(getCurrentUser().getTenantId()); + clientRegistrations = oAuth2Service.findClientRegistrationsByTenantId(getCurrentUser().getTenantId()); + } else { + throw new IllegalStateException("Authority " + authority + " cannot get client registrations."); } - return oAuth2ClientsParams; + return new OAuth2ClientsParams(clientRegistrations); } catch (Exception e) { throw handleException(e); } @@ -75,18 +81,37 @@ public class OAuth2Controller extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) - public OAuth2ClientsParams saveClientParams(@RequestBody OAuth2ClientsParams oAuth2ClientsParams) throws ThingsboardException { + public OAuth2ClientRegistration saveClientRegistration(@RequestBody OAuth2ClientRegistration clientRegistration) throws ThingsboardException { try { - Authority authority = getCurrentUser().getAuthority(); - checkOAuth2ConfigPermissions(Operation.WRITE); - OAuth2ClientsParams savedOAuth2ClientsParams = null; - if (Authority.SYS_ADMIN.equals(authority)) { - savedOAuth2ClientsParams = oauth2Service.saveSystemOAuth2ClientsParams(oAuth2ClientsParams); - } else if (Authority.TENANT_ADMIN.equals(authority)) { - savedOAuth2ClientsParams = oauth2Service.saveTenantOAuth2ClientsParams(getCurrentUser().getTenantId(), oAuth2ClientsParams); - } - return savedOAuth2ClientsParams; + clientRegistration.setTenantId(getCurrentUser().getTenantId()); + checkEntity(clientRegistration.getId(), clientRegistration, Resource.OAUTH2_CONFIGURATION); + return oAuth2Service.saveClientRegistration(clientRegistration); + } catch (Exception e) { + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/oauth2/config/{clientRegistrationId}", method = RequestMethod.DELETE) + @ResponseStatus(value = HttpStatus.OK) + public void deleteClientRegistration(@PathVariable(CLIENT_REGISTRATION_ID) String strClientRegistrationId) throws ThingsboardException { + checkParameter(CLIENT_REGISTRATION_ID, strClientRegistrationId); + try { + OAuth2ClientRegistrationId clientRegistrationId = new OAuth2ClientRegistrationId(toUUID(strClientRegistrationId)); + OAuth2ClientRegistration clientRegistration = checkOAuth2ClientRegistrationId(clientRegistrationId, Operation.DELETE); + oAuth2Service.deleteClientRegistrationById(getCurrentUser().getTenantId(), clientRegistrationId); + + logEntityAction(clientRegistrationId, clientRegistration, + null, + ActionType.DELETED, null, strClientRegistrationId); + } catch (Exception e) { + + logEntityAction(emptyId(EntityType.OAUTH2_CLIENT_REGISTRATION), + null, + null, + ActionType.DELETED, e, strClientRegistrationId); + throw handleException(e); } } @@ -96,7 +121,7 @@ public class OAuth2Controller extends BaseController { @ResponseBody public Boolean isOAuth2ConfigurationAllowed() throws ThingsboardException { try { - return oauth2Service.isOAuth2ClientRegistrationAllowed(getTenantId()); + return oAuth2Service.isOAuth2ClientRegistrationAllowed(getTenantId()); } catch (Exception e) { throw handleException(e); } @@ -105,4 +130,8 @@ public class OAuth2Controller extends BaseController { private void checkOAuth2ConfigPermissions(Operation operation) throws ThingsboardException { accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION, operation); } + + private void checkOAuth2ConfigPermissions(Operation operation, OAuth2ClientRegistration clientRegistration) throws ThingsboardException { + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION, operation, clientRegistration.getId(), clientRegistration); + } } diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 7d80b115d5..1b1355571d 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -173,6 +173,9 @@ public class ThingsboardInstallService { databaseEntitiesUpgradeService.upgradeDatabase("3.0.1"); log.info("Updating system data..."); systemDataLoaderService.updateSystemWidgets(); + case "3.1.0": + log.info("Upgrading ThingsBoard from version 3.1.0 to 3.2.0 ..."); + databaseEntitiesUpgradeService.upgradeDatabase("3.1.0"); break; default: throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion); diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index e21472ee12..6f60cc345b 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -294,6 +294,15 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService } catch (Exception e) { log.error("Failed updating schema!!!", e); } + case "3.1.0": + try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { + log.info("Updating schema ..."); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.0", "schema_update.sql"); + loadSql(schemaUpdateFile, conn); + log.info("Schema updated."); + } catch (Exception e) { + log.error("Failed updating schema!!!", e); + } break; default: throw new RuntimeException("Unable to upgrade SQL database, unsupported fromVersion: " + fromVersion); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 0cff27ae2e..e3d21b448d 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -65,11 +65,9 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS try { OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; - Pair clientRegistrationPair = oAuth2Service.getClientRegistrationWithTenant(token.getAuthorizedClientRegistrationId()); - TenantId tenantId = clientRegistrationPair.getKey(); - OAuth2ClientRegistration clientRegistration = clientRegistrationPair.getValue(); + OAuth2ClientRegistration clientRegistration = oAuth2Service.findClientRegistrationByRegistrationId(token.getAuthorizedClientRegistrationId()); OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); - SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, tenantId, clientRegistration.getMapperConfig()); + SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, clientRegistration.getTenantId(), clientRegistration.getMapperConfig()); JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index dfdea59e05..84819dda48 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -32,7 +32,7 @@ public enum Resource { USER(EntityType.USER), WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE), WIDGET_TYPE(EntityType.WIDGET_TYPE), - OAUTH2_CONFIGURATION(), + OAUTH2_CONFIGURATION(EntityType.OAUTH2_CLIENT_REGISTRATION), ; private final EntityType entityType; diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index 703c238c64..8476994a4e 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -113,5 +113,13 @@ public class TenantAdminPermissions extends AbstractPermissions { public boolean hasPermission(SecurityUser user, Operation operation) { return oAuth2Service.isOAuth2ClientRegistrationAllowed(user.getTenantId()); } + + @Override + public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { + if (!user.getTenantId().equals(entity.getTenantId())) { + return false; + } + return hasPermission(user, operation); + } }; } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index bfeb8f52a1..6d4be61dc7 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -15,39 +15,30 @@ */ package org.thingsboard.server.dao.oauth2; -import org.apache.commons.lang3.tuple.Pair; -import org.thingsboard.server.common.data.id.CustomerId; -import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistration; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; import java.util.List; -import java.util.Map; +import java.util.UUID; public interface OAuth2Service { - Pair getClientRegistrationWithTenant(String registrationId); - - ExtendedOAuth2ClientRegistration getExtendedClientRegistration(String registrationId); - List getOAuth2Clients(String domainName); - OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams); + OAuth2ClientRegistration saveClientRegistration(OAuth2ClientRegistration clientRegistration); - OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams); + List findClientRegistrationsByTenantId(TenantId tenantId); - OAuth2ClientsParams getSystemOAuth2ClientsParams(); + OAuth2ClientRegistration findClientRegistrationByRegistrationId(String registrationId); - OAuth2ClientsParams getTenantOAuth2ClientsParams(TenantId tenantId); + OAuth2ClientRegistration findClientRegistrationById(TenantId tenantId, OAuth2ClientRegistrationId id); - void deleteTenantOAuth2ClientsParams(TenantId tenantId); + List findAllClientRegistrations(); - void deleteSystemOAuth2ClientsParams(); + void deleteClientRegistrationsByTenantId(TenantId tenantId); - boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId); - - Map getAllOAuth2ClientsParams(); + void deleteClientRegistrationById(TenantId tenantId, OAuth2ClientRegistrationId id); + boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index 7e43c59797..d3ea208aa5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; * @author Andrew Shvayka */ public enum EntityType { - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, 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, OAUTH2_CLIENT_REGISTRATION } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index 11db1da1a0..03f05c54a4 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -62,6 +62,8 @@ public class EntityIdFactory { return new WidgetsBundleId(uuid); case WIDGET_TYPE: return new WidgetTypeId(uuid); + case OAUTH2_CLIENT_REGISTRATION: + return new OAuth2ClientRegistrationId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2IntegrationId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java similarity index 63% rename from common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2IntegrationId.java rename to common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java index 30fd55d204..3e4ad56f19 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2IntegrationId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java @@ -17,19 +17,23 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class OAuth2IntegrationId extends UUIDBased { - - private static final long serialVersionUID = 1L; +public class OAuth2ClientRegistrationId extends UUIDBased implements EntityId { @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)); + } + + @Override + public EntityType getEntityType() { + return EntityType.OAUTH2_CLIENT_REGISTRATION; } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientInfo.java index 0ee5832e63..f15706a55e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientInfo.java +++ b/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 { +@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(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java index 0a4156bcc5..a0a7dcc48b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -15,21 +15,26 @@ */ package org.thingsboard.server.common.data.oauth2; +import com.fasterxml.jackson.annotation.JsonProperty; import lombok.*; import org.thingsboard.server.common.data.BaseData; -import org.thingsboard.server.common.data.id.OAuth2IntegrationId; +import org.thingsboard.server.common.data.HasName; +import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; +import org.thingsboard.server.common.data.id.TenantId; import java.util.List; -@EqualsAndHashCode +@EqualsAndHashCode(callSuper = true) @Data @ToString(exclude = {"clientSecret"}) -@Builder(toBuilder = true) @NoArgsConstructor -@AllArgsConstructor -public class OAuth2ClientRegistration { +public class OAuth2ClientRegistration extends BaseData implements HasTenantId, HasName { + private TenantId tenantId; private String registrationId; + private String domainName; + private String redirectUriTemplate; private OAuth2MapperConfig mapperConfig; private String clientId; private String clientSecret; @@ -42,4 +47,30 @@ public class OAuth2ClientRegistration { private String clientAuthenticationMethod; private String loginButtonLabel; private String loginButtonIcon; + + public OAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration) { + super(clientRegistration); + this.tenantId = clientRegistration.getTenantId(); + this.registrationId = clientRegistration.getRegistrationId(); + this.domainName = clientRegistration.getDomainName(); + this.redirectUriTemplate = clientRegistration.getRedirectUriTemplate(); + 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; + } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java index ef08a892a1..f442669b68 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java @@ -26,5 +26,5 @@ import java.util.List; @NoArgsConstructor @AllArgsConstructor public class OAuth2ClientsParams { - private List clientsDomainsParams; + private List clientRegistrations; } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index b7748d46da..0c31675dc2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -355,22 +355,22 @@ public class ModelConstants { public static final String RULE_NODE_CONFIGURATION_PROPERTY = "configuration"; /** - * Cassandra OAuth2 client registration constants. + * OAuth2 client registration constants. */ + public static final String OAUTH2_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; public static final String OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_client_registration"; public static final String OAUTH2_CLIENT_REGISTRATION_ID_PROPERTY = "registration_id"; + public static final String OAUTH2_DOMAIN_NAME_PROPERTY = "domain_name"; 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_AUTHORIZATION_GRANT_TYPE_PROPERTY = "authorization_grant_type"; 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_CLIENT_NAME_PROPERTY = "client_name"; 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"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java new file mode 100644 index 0000000000..0c62de3bb2 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java @@ -0,0 +1,204 @@ +/** + * 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.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 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 { + + @Column(name = ModelConstants.OAUTH2_TENANT_ID_PROPERTY, columnDefinition = "uuid") + private UUID tenantId; + + @Column(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_ID_PROPERTY) + private String registrationId; + @Column(name = ModelConstants.OAUTH2_DOMAIN_NAME_PROPERTY) + private String domainName; + @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_REDIRECT_URI_TEMPLATE_PROPERTY) + private String redirectUriTemplate; + @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; + + public OAuth2ClientRegistrationEntity() { + super(); + } + + public OAuth2ClientRegistrationEntity(OAuth2ClientRegistration clientRegistration) { + if (clientRegistration.getId() != null) { + this.setUuid(clientRegistration.getId().getId()); + } + if (clientRegistration.getTenantId() != null) { + this.tenantId = clientRegistration.getTenantId().getId(); + } + this.domainName = clientRegistration.getDomainName(); + this.createdTime = clientRegistration.getCreatedTime(); + this.registrationId = clientRegistration.getRegistrationId(); + this.clientId = clientRegistration.getClientId(); + this.clientSecret = clientRegistration.getClientSecret(); + this.authorizationUri = clientRegistration.getAuthorizationUri(); + this.tokenUri = clientRegistration.getAccessTokenUri(); + this.redirectUriTemplate = clientRegistration.getRedirectUriTemplate(); + this.scope = clientRegistration.getScope().stream().reduce((result, element) -> result + "," + element).orElse(""); + this.userInfoUri = clientRegistration.getUserInfoUri(); + this.userNameAttributeName = clientRegistration.getUserNameAttributeName(); + this.jwkSetUri = clientRegistration.getJwkSetUri(); + this.clientAuthenticationMethod = clientRegistration.getClientAuthenticationMethod(); + this.loginButtonLabel = clientRegistration.getLoginButtonLabel(); + this.loginButtonIcon = clientRegistration.getLoginButtonIcon(); + OAuth2MapperConfig mapperConfig = clientRegistration.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(); + } + } + } + + @Override + public OAuth2ClientRegistration toData() { + OAuth2ClientRegistration clientRegistration = new OAuth2ClientRegistration(); + clientRegistration.setId(new OAuth2ClientRegistrationId(id)); + clientRegistration.setTenantId(new TenantId(tenantId)); + clientRegistration.setRegistrationId(registrationId); + clientRegistration.setDomainName(domainName); + clientRegistration.setCreatedTime(createdTime); + clientRegistration.setMapperConfig( + OAuth2MapperConfig.builder() + .allowUserCreation(allowUserCreation) + .activateUser(activateUser) + .type(type) + .basic( + type == MapperType.BASIC ? + 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) + .build() + : null + ) + .build() + ); + clientRegistration.setClientId(clientId); + clientRegistration.setClientSecret(clientSecret); + clientRegistration.setAuthorizationUri(authorizationUri); + clientRegistration.setAccessTokenUri(tokenUri); + clientRegistration.setRedirectUriTemplate(redirectUriTemplate); + clientRegistration.setScope(Arrays.asList(scope.split(","))); + clientRegistration.setUserInfoUri(userInfoUri); + clientRegistration.setUserNameAttributeName(userNameAttributeName); + clientRegistration.setJwkSetUri(jwkSetUri); + clientRegistration.setClientAuthenticationMethod(clientAuthenticationMethod); + clientRegistration.setLoginButtonLabel(loginButtonLabel); + clientRegistration.setLoginButtonIcon(loginButtonIcon); + return clientRegistration; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java index 7309cc7edb..bdd97322a7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.java @@ -32,19 +32,19 @@ public class HybridClientRegistrationRepository implements ClientRegistrationRep @Override public ClientRegistration findByRegistrationId(String registrationId) { - ExtendedOAuth2ClientRegistration localExtendedClientRegistration = oAuth2Service.getExtendedClientRegistration(registrationId); - return localExtendedClientRegistration == null ? - null : toSpringClientRegistration(localExtendedClientRegistration.getRedirectUriTemplate(), localExtendedClientRegistration.getClientRegistration()); + OAuth2ClientRegistration oAuth2ClientRegistration = oAuth2Service.findClientRegistrationByRegistrationId(registrationId); + return oAuth2ClientRegistration == null ? + null : toSpringClientRegistration(oAuth2ClientRegistration); } - private ClientRegistration toSpringClientRegistration(String redirectUriTemplate, OAuth2ClientRegistration localClientRegistration){ + private ClientRegistration toSpringClientRegistration(OAuth2ClientRegistration localClientRegistration){ return ClientRegistration.withRegistrationId(localClientRegistration.getRegistrationId()) .clientName(localClientRegistration.getRegistrationId()) .clientId(localClientRegistration.getClientId()) .authorizationUri(localClientRegistration.getAuthorizationUri()) .clientSecret(localClientRegistration.getClientSecret()) .tokenUri(localClientRegistration.getAccessTokenUri()) - .redirectUriTemplate(redirectUriTemplate) + .redirectUriTemplate(localClientRegistration.getRedirectUriTemplate()) .scope(localClientRegistration.getScope()) .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .userInfoUri(localClientRegistration.getUserInfoUri()) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java new file mode 100644 index 0000000000..d262702efa --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java @@ -0,0 +1,36 @@ +/** + * 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; + +import java.util.List; +import java.util.UUID; + +public interface OAuth2ClientRegistrationDao extends Dao { + OAuth2ClientRegistration findByRegistrationId(String registrationId); + + List findAll(); + + List findByTenantId(UUID tenantId); + + List findByDomainName(String domainName); + + boolean removeByRegistrationId(String registrationId); + + int removeByTenantId(UUID tenantId); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index df1cd5a049..f06d906e7c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -15,366 +15,119 @@ */ package org.thingsboard.server.dao.oauth2; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.node.ObjectNode; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; -import com.google.common.util.concurrent.MoreExecutors; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.commons.lang3.tuple.Pair; +import org.hibernate.exception.ConstraintViolationException; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.env.Environment; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.*; -import org.thingsboard.server.common.data.id.*; -import org.thingsboard.server.common.data.kv.*; +import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; +import org.thingsboard.server.common.data.exception.ThingsboardException; +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; +import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.*; -import org.thingsboard.server.dao.attributes.AttributesService; +import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.exception.IncorrectParameterException; -import org.thingsboard.server.dao.lock.LockKey; -import org.thingsboard.server.dao.lock.LockService; -import org.thingsboard.server.dao.settings.AdminSettingsService; +import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; -import java.io.IOException; -import java.util.*; -import java.util.concurrent.ExecutionException; -import java.util.function.Consumer; +import javax.transaction.Transactional; +import java.util.List; +import java.util.UUID; import java.util.stream.Collectors; -import static org.thingsboard.server.dao.oauth2.OAuth2Utils.*; +import static org.thingsboard.server.dao.oauth2.OAuth2Utils.ALLOW_OAUTH2_CONFIGURATION; +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 { - private static final ObjectMapper mapper = new ObjectMapper(); - - @Autowired - private Environment environment; - - @Autowired - private AdminSettingsService adminSettingsService; - - @Autowired - private AttributesService attributesService; +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_REGISTRATION_ID = "Incorrect registrationId "; + public static final String INCORRECT_DOMAIN_NAME = "Incorrect domainName "; @Autowired private TenantService tenantService; @Autowired - private LockService lockService; - - private boolean isInstall() { - return environment.acceptsProfiles("install"); - } - - @Override - public Pair getClientRegistrationWithTenant(String registrationId) { - return getExtendedOAuth2ClientRegistrationWithTenant(registrationId) - .map(pair -> ImmutablePair.of(pair.getLeft(), pair.getRight().getClientRegistration())) - .orElse(null); - } - - @Override - public ExtendedOAuth2ClientRegistration getExtendedClientRegistration(String registrationId) { - return getExtendedOAuth2ClientRegistrationWithTenant(registrationId) - .map(Pair::getValue) - .orElse(null); - - } - - private Optional> getExtendedOAuth2ClientRegistrationWithTenant(String registrationId) { - return getAllOAuth2ClientsParams().entrySet().stream() - .map(entry -> { - TenantId tenantId = entry.getKey(); - return entry.getValue().getClientsDomainsParams().stream() - .flatMap(domainParams -> - domainParams.getClientRegistrations().stream() - .map(clientRegistration -> new ExtendedOAuth2ClientRegistration(domainParams.getRedirectUriTemplate(), clientRegistration)) - ) - .filter(registration -> registrationId.equals(registration.getClientRegistration().getRegistrationId())) - .findFirst() - .map(extendedClientRegistration -> ImmutablePair.of(tenantId, extendedClientRegistration)) - .orElse(null); - - }) - .filter(Objects::nonNull) - .findFirst() - .map(entry -> ImmutablePair.of(entry.getKey(), entry.getValue())) - ; - } - + private OAuth2ClientRegistrationDao clientRegistrationDao; @Override public List getOAuth2Clients(String domainName) { - OAuth2ClientsDomainParams oAuth2ClientsDomainParams = getMergedOAuth2ClientsParams(domainName); - return oAuth2ClientsDomainParams != null && oAuth2ClientsDomainParams.getClientRegistrations() != null ? - oAuth2ClientsDomainParams.getClientRegistrations().stream() - .map(OAuth2Utils::toClientInfo) - .collect(Collectors.toList()) - : Collections.emptyList() - ; - } - - @Override - public OAuth2ClientsParams saveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) { - validate(oAuth2ClientsParams); - validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); - - transactionalSaveSystemOAuth2ClientsParams(oAuth2ClientsParams); - - return getSystemOAuth2ClientsParams(); - } - - @Transactional - private void transactionalSaveSystemOAuth2ClientsParams(OAuth2ClientsParams oAuth2ClientsParams) { - long acquireStart = System.currentTimeMillis(); - lockService.transactionLock(LockKey.OAUTH2_CONFIG); - log.trace("[{}] Waited for lock {} ms.", TenantId.SYS_TENANT_ID, System.currentTimeMillis() - acquireStart); - - validateRegistrationIdUniqueness(oAuth2ClientsParams, TenantId.SYS_TENANT_ID); - AdminSettings oauth2SystemAdminSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); - if (oauth2SystemAdminSettings == null) { - oauth2SystemAdminSettings = createSystemAdminSettings(); - } - String json = toJson(oAuth2ClientsParams); - ((ObjectNode) oauth2SystemAdminSettings.getJsonValue()).put(SYSTEM_SETTINGS_OAUTH2_VALUE, json); - adminSettingsService.saveAdminSettings(TenantId.SYS_TENANT_ID, oauth2SystemAdminSettings); + log.trace("Executing getOAuth2Clients [{}]", domainName); + validateString(domainName, INCORRECT_DOMAIN_NAME + domainName); + return clientRegistrationDao.findByDomainName(domainName).stream() + .map(OAuth2Utils::toClientInfo) + .collect(Collectors.toList()); } @Override - public OAuth2ClientsParams saveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) { - validate(oAuth2ClientsParams); - validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); - - transactionalSaveTenantOAuth2ClientsParams(tenantId, oAuth2ClientsParams); - - return getTenantOAuth2ClientsParams(tenantId); - } - - @Transactional - private void transactionalSaveTenantOAuth2ClientsParams(TenantId tenantId, OAuth2ClientsParams oAuth2ClientsParams) { - long acquireStart = System.currentTimeMillis(); - lockService.transactionLock(LockKey.OAUTH2_CONFIG); - log.trace("[{}] Waited for lock {} ms.", tenantId, System.currentTimeMillis() - acquireStart); - - validateRegistrationIdUniqueness(oAuth2ClientsParams, tenantId); - - Set domainNames = oAuth2ClientsParams.getClientsDomainsParams().stream() - .map(OAuth2ClientsDomainParams::getDomainName) - .collect(Collectors.toSet()); - processTenantAdminSettings(tenantId, domainNames); - - List attributes = createOAuth2ClientsParamsAttributes(oAuth2ClientsParams); + public OAuth2ClientRegistration saveClientRegistration(OAuth2ClientRegistration clientRegistration) { + log.trace("Executing saveClientRegistration [{}]", clientRegistration); + clientRegistrationValidator.validate(clientRegistration, OAuth2ClientRegistration::getTenantId); + OAuth2ClientRegistration savedClientRegistration; try { - attributesService.save(tenantId, tenantId, DataConstants.SERVER_SCOPE, attributes).get(); - } catch (Exception e) { - log.error("Unable to save OAuth2 Client Registration Params to attributes!", e); - throw new IncorrectParameterException("Unable to save OAuth2 Client Registration Params to attributes!"); - } - } - - private List createOAuth2ClientsParamsAttributes(OAuth2ClientsParams oAuth2ClientsParams) { - String json = toJson(oAuth2ClientsParams); - List attributes = new ArrayList<>(); - long ts = System.currentTimeMillis(); - attributes.add(new BaseAttributeKvEntry(new StringDataEntry(OAUTH2_CLIENT_REGISTRATIONS_PARAMS, json), ts)); - return attributes; - } - - private void processTenantAdminSettings(TenantId tenantId, Set domainNames) { - OAuth2ClientsParams existentClientsParams = getTenantOAuth2ClientsParams(tenantId); - - Set existentDomainNames = existentClientsParams != null && existentClientsParams.getClientsDomainsParams() != null ? - existentClientsParams.getClientsDomainsParams().stream() - .map(OAuth2ClientsDomainParams::getDomainName) - .collect(Collectors.toSet()) - : Collections.emptySet(); - - Set domainNamesToAdd = domainNames.stream() - .filter(domainName -> !existentDomainNames.contains(domainName)) - .collect(Collectors.toSet()); - Set domainNamesToDelete = existentDomainNames.stream() - .filter(domainName -> !domainNames.contains(domainName)) - .collect(Collectors.toSet()); - - domainNamesToAdd.forEach(domainName -> { - String domainSettingsKey = constructAdminSettingsDomainKey(domainName); - if (adminSettingsService.findAdminSettingsByKey(tenantId, domainSettingsKey) != null) { - log.error("Current domain name [{}] already registered in the system!", domainName); - throw new IncorrectParameterException("Current domain name [" + domainName + "] already registered in the system!"); - } - }); - - domainNamesToAdd.forEach(domainName -> { - String domainSettingsKey = constructAdminSettingsDomainKey(domainName); - AdminSettings tenantAdminSettings = createTenantAdminSettings(tenantId, domainSettingsKey); - adminSettingsService.saveAdminSettings(tenantId, tenantAdminSettings); - }); - - domainNamesToDelete.forEach(domainName -> { - String domainSettingsKey = constructAdminSettingsDomainKey(domainName); - adminSettingsService.deleteAdminSettingsByKey(tenantId, domainSettingsKey); - }); - } - - private AdminSettings createTenantAdminSettings(TenantId tenantId, String clientRegistrationsKey) { - AdminSettings clientRegistrationParamsSettings = new AdminSettings(); - clientRegistrationParamsSettings.setKey(clientRegistrationsKey); - ObjectNode node = mapper.createObjectNode(); - node.put("entityType", EntityType.TENANT.name()); - node.put("entityId", tenantId.toString()); - clientRegistrationParamsSettings.setJsonValue(node); - return clientRegistrationParamsSettings; - } - - private AdminSettings createSystemAdminSettings() { - AdminSettings clientRegistrationParamsSettings = new AdminSettings(); - clientRegistrationParamsSettings.setKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); - ObjectNode clientRegistrationsNode = mapper.createObjectNode(); - - clientRegistrationParamsSettings.setJsonValue(clientRegistrationsNode); - - return clientRegistrationParamsSettings; - } - - private void validateRegistrationIdUniqueness(OAuth2ClientsParams inputOAuth2ClientsParams, TenantId tenantId) { - List registrationIds = toClientRegistrationStream(inputOAuth2ClientsParams) - .map(OAuth2ClientRegistration::getRegistrationId) - .collect(Collectors.toList()); - - boolean regIdDuplicates = registrationIds.stream() - .anyMatch(registrationId -> Collections.frequency(registrationIds, registrationId) > 1); - if (regIdDuplicates) { - throw new DataValidationException("All registration IDs should be unique!"); - } - - getAllOAuth2ClientsParams().forEach((paramsTenantId, oAuth2ClientsParams) -> { - if (tenantId.equals(paramsTenantId)) return; - Set duplicatedRegistrationIds = toClientRegistrationStream(oAuth2ClientsParams) - .map(OAuth2ClientRegistration::getRegistrationId) - .filter(registrationIds::contains) - .collect(Collectors.toSet()); - if (!duplicatedRegistrationIds.isEmpty()) { - log.error("RegistrationIds [{}] are already registered in the system!", duplicatedRegistrationIds); - throw new IncorrectParameterException("RegistrationIds [" + duplicatedRegistrationIds + "] are already registered in the system!"); - } - }); - } - - private void validate(OAuth2ClientsParams oAuth2ClientsParams) { - validateRedirectUris(oAuth2ClientsParams); - validateDomainNames(oAuth2ClientsParams); - - toClientRegistrationStream(oAuth2ClientsParams) - .forEach(validator); - } - - private void validateDomainNames(OAuth2ClientsParams oAuth2ClientsParams) { - List domainNames = oAuth2ClientsParams.getClientsDomainsParams().stream() - .map(OAuth2ClientsDomainParams::getDomainName) - .collect(Collectors.toList()); - - domainNames.forEach(domainName -> { - if (StringUtils.isEmpty(domainName)) { - throw new DataValidationException("Domain name should be specified!"); + savedClientRegistration = clientRegistrationDao.save(clientRegistration.getTenantId(), clientRegistration); + } catch (Exception t) { + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("oauth2_registration_id_unq_key")) { + throw new DataValidationException("Client registration with such registrationId already exists!"); + } else { + throw t; } - }); - - boolean duplicateDomainNames = domainNames.stream() - .anyMatch(domainName -> Collections.frequency(domainNames, domainName) > 1); - if (duplicateDomainNames) { - throw new DataValidationException("All domain names should be unique!"); } + return savedClientRegistration; } - private void validateRedirectUris(OAuth2ClientsParams oAuth2ClientsParams) { - oAuth2ClientsParams.getClientsDomainsParams().stream() - .forEach(oAuth2ClientsDomainParams -> { - if (StringUtils.isEmpty(oAuth2ClientsDomainParams.getRedirectUriTemplate())) { - throw new DataValidationException("Redirect uri template should be specified!"); - } - }); + @Override + public List findClientRegistrationsByTenantId(TenantId tenantId) { + log.trace("Executing findClientRegistrationsByTenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + return clientRegistrationDao.findByTenantId(tenantId.getId()); } @Override - public OAuth2ClientsParams getSystemOAuth2ClientsParams() { - AdminSettings oauth2ClientsParamsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAUTH2_CLIENT_REGISTRATIONS_PARAMS); - String json = null; - if (oauth2ClientsParamsSettings != null) { - json = oauth2ClientsParamsSettings.getJsonValue().get(SYSTEM_SETTINGS_OAUTH2_VALUE).asText(); - } - return constructOAuth2ClientsParams(json); + public OAuth2ClientRegistration findClientRegistrationByRegistrationId(String registrationId) { + log.trace("Executing findClientRegistrationByRegistrationId [{}]", registrationId); + validateString(registrationId, INCORRECT_REGISTRATION_ID + registrationId); + return clientRegistrationDao.findByRegistrationId(registrationId); } @Override - public OAuth2ClientsParams getTenantOAuth2ClientsParams(TenantId tenantId) { - ListenableFuture jsonFuture; - if (isOAuth2ClientRegistrationAllowed(tenantId)) { - jsonFuture = getOAuth2ClientsParamsAttribute(tenantId); - } else { - jsonFuture = Futures.immediateFuture(""); - } - try { - return Futures.transform(jsonFuture, this::constructOAuth2ClientsParams, MoreExecutors.directExecutor()).get(); - } catch (InterruptedException | ExecutionException e) { - log.error("Failed to read OAuth2 Clients Params from attributes!", e); - throw new RuntimeException("Failed to read OAuth2 Clients Params from attributes!", e); - } + public OAuth2ClientRegistration findClientRegistrationById(TenantId tenantId, OAuth2ClientRegistrationId id) { + log.trace("Executing findClientRegistrationById [{}]", id); + validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); + return clientRegistrationDao.findById(tenantId, id.getId()); } - // TODO this is just for test, maybe there's a better way to test it without exporting to interface @Override - public Map getAllOAuth2ClientsParams() { - OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(); - ListenableFuture> jsonFuture = getAllOAuth2ClientsParamsAttribute(); - try { - return Futures.transform(jsonFuture, - clientsParamsByKvEntryKey -> { - Map tenantClientParams = clientsParamsByKvEntryKey != null ? - clientsParamsByKvEntryKey.entrySet().stream() - .collect(Collectors.toMap( - entry -> new TenantId(entry.getKey()), - entry -> constructOAuth2ClientsParams(entry.getValue()) - )) - : new HashMap<>(); - if (systemOAuth2ClientsParams.getClientsDomainsParams() != null) { - tenantClientParams.put(TenantId.SYS_TENANT_ID, systemOAuth2ClientsParams); - } - return tenantClientParams; - }, - MoreExecutors.directExecutor() - ).get(); - } catch (InterruptedException | ExecutionException e) { - log.error("Failed to read OAuth2 Clients Params from attributes!", e); - throw new RuntimeException("Failed to read OAuth2 Clients Params from attributes!", e); - } + public List findAllClientRegistrations() { + log.trace("Executing findAllClientRegistrations"); + return clientRegistrationDao.findAll(); } @Override - public void deleteTenantOAuth2ClientsParams(TenantId tenantId) { - OAuth2ClientsParams params = getTenantOAuth2ClientsParams(tenantId); - if (params == null || params.getClientsDomainsParams() == null) return; - params.getClientsDomainsParams().forEach(domainParams -> { - String settingsKey = constructAdminSettingsDomainKey(domainParams.getDomainName()); - adminSettingsService.deleteAdminSettingsByKey(tenantId, settingsKey); - }); - attributesService.removeAll(tenantId, tenantId, DataConstants.SERVER_SCOPE, Collections.singletonList(OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + @Transactional + public void deleteClientRegistrationsByTenantId(TenantId tenantId) { + log.trace("Executing deleteClientRegistrationsByTenantId [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + clientRegistrationDao.removeByTenantId(tenantId.getId()); } @Override - public void deleteSystemOAuth2ClientsParams() { - adminSettingsService.deleteAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS); + public void deleteClientRegistrationById(TenantId tenantId, OAuth2ClientRegistrationId id) { + log.trace("Executing deleteClientRegistrationById [{}]", id); + validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); + clientRegistrationDao.removeById(tenantId, id.getId()); } @Override public boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId) { + log.trace("Executing isOAuth2ClientRegistrationAllowed [{}]", tenantId); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); Tenant tenant = tenantService.findTenantById(tenantId); if (tenant == null) return false; JsonNode allowOAuth2ConfigurationJsonNode = tenant.getAdditionalInfo() != null ? tenant.getAdditionalInfo().get(ALLOW_OAUTH2_CONFIGURATION) : null; @@ -385,185 +138,98 @@ public class OAuth2ServiceImpl implements OAuth2Service { } } - private ListenableFuture getOAuth2ClientsParamsAttribute(TenantId tenantId) { - ListenableFuture> attributeKvEntriesFuture; - try { - attributeKvEntriesFuture = attributesService.find(tenantId, tenantId, DataConstants.SERVER_SCOPE, - Collections.singletonList(OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); - } catch (Exception e) { - log.error("Unable to read OAuth2 Clients Params from attributes!", e); - throw new IncorrectParameterException("Unable to read OAuth2 Clients Params from attributes!"); - } - return Futures.transform(attributeKvEntriesFuture, attributeKvEntries -> { - if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { - AttributeKvEntry kvEntry = attributeKvEntries.get(0); - return kvEntry.getValueAsString(); - } else { - return ""; - } - }, MoreExecutors.directExecutor()); - } - - // TODO maybe it's better to load all tenants and get attribute for each one - private ListenableFuture> getAllOAuth2ClientsParamsAttribute() { - ListenableFuture> entityAttributeKvEntriesFuture; - try { - entityAttributeKvEntriesFuture = attributesService.findAllByAttributeKey(OAUTH2_CLIENT_REGISTRATIONS_PARAMS); - } catch (Exception e) { - log.error("Unable to read OAuth2 Clients Params from attributes!", e); - throw new IncorrectParameterException("Unable to read OAuth2 Clients Params from attributes!"); - } - return Futures.transform(entityAttributeKvEntriesFuture, attributeKvEntries -> { - if (attributeKvEntries != null && !attributeKvEntries.isEmpty()) { - return attributeKvEntries.stream() - .collect(Collectors.toMap(EntityAttributeKvEntry::getEntityId, EntityAttributeKvEntry::getValueAsString)); - } else { - return Collections.emptyMap(); - } - }, MoreExecutors.directExecutor()); - } - - private OAuth2ClientsDomainParams getMergedOAuth2ClientsParams(String domainName) { - OAuth2ClientsDomainParams result = OAuth2ClientsDomainParams.builder() - .domainName(domainName) - .clientRegistrations(new ArrayList<>()) - .build(); - - OAuth2ClientsParams systemOAuth2ClientsParams = getSystemOAuth2ClientsParams(); - OAuth2ClientsDomainParams systemOAuth2ClientsDomainParams = systemOAuth2ClientsParams != null && systemOAuth2ClientsParams.getClientsDomainsParams() != null ? - systemOAuth2ClientsParams.getClientsDomainsParams().stream() - .filter(oAuth2ClientsDomainParams -> domainName.equals(oAuth2ClientsDomainParams.getDomainName())) - .findFirst() - .orElse(null) - : null; - - result = mergeDomainParams(result, systemOAuth2ClientsDomainParams); - - AdminSettings oauth2ClientsSettings = adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, constructAdminSettingsDomainKey(domainName)); - if (oauth2ClientsSettings != null) { - String strEntityType = oauth2ClientsSettings.getJsonValue().get("entityType").asText(); - String strEntityId = oauth2ClientsSettings.getJsonValue().get("entityId").asText(); - EntityId entityId = EntityIdFactory.getByTypeAndId(strEntityType, strEntityId); - if (!entityId.getEntityType().equals(EntityType.TENANT)) { - log.error("Only tenant can configure OAuth2 for certain domain!"); - throw new IllegalStateException("Only tenant can configure OAuth2 for certain domain!"); - } - TenantId tenantId = (TenantId) entityId; - OAuth2ClientsParams tenantOAuth2ClientsParams = getTenantOAuth2ClientsParams(tenantId); - OAuth2ClientsDomainParams tenantDomainsParams = tenantOAuth2ClientsParams != null && tenantOAuth2ClientsParams.getClientsDomainsParams() != null ? - tenantOAuth2ClientsParams.getClientsDomainsParams().stream().findFirst().orElse(null) : null; - result = mergeDomainParams(result, tenantDomainsParams); - } - return result; - } - - private OAuth2ClientsDomainParams mergeDomainParams(OAuth2ClientsDomainParams sourceParams, OAuth2ClientsDomainParams newParams) { - if (newParams == null) return sourceParams; - - OAuth2ClientsDomainParams.OAuth2ClientsDomainParamsBuilder mergedParamsBuilder = sourceParams.toBuilder(); - - if (newParams.getClientRegistrations() != null) { - List mergedClientRegistrations = sourceParams.getClientRegistrations() != null ? - sourceParams.getClientRegistrations() : new ArrayList<>(); - mergedClientRegistrations.addAll(newParams.getClientRegistrations()); - mergedParamsBuilder.clientRegistrations(mergedClientRegistrations); - } - - return mergedParamsBuilder.build(); - } + private DataValidator clientRegistrationValidator = + new DataValidator() { - private OAuth2ClientsParams constructOAuth2ClientsParams(String json) { - OAuth2ClientsParams result = null; - if (!StringUtils.isEmpty(json)) { - try { - result = mapper.readValue(json, OAuth2ClientsParams.class); - } catch (IOException e) { - log.error("Unable to read OAuth2 Clients Params from JSON!", e); - throw new IncorrectParameterException("Unable to read OAuth2 Clients Params from JSON!"); - } - } - if (result == null) { - result = new OAuth2ClientsParams(); - } - return result; - } + @Override + protected void validateCreate(TenantId tenantId, OAuth2ClientRegistration clientRegistration) { + } - private String toJson(OAuth2ClientsParams oAuth2ClientsParams) { - String json; - try { - json = mapper.writeValueAsString(oAuth2ClientsParams); - } catch (JsonProcessingException e) { - log.error("Unable to convert OAuth2 Client Registration Params to JSON!", e); - throw new IncorrectParameterException("Unable to convert OAuth2 Client Registration Params to JSON!"); - } - return json; - } + @Override + protected void validateUpdate(TenantId tenantId, OAuth2ClientRegistration clientRegistration) { + } - private final Consumer validator = clientRegistration -> { - if (StringUtils.isEmpty(clientRegistration.getRegistrationId())) { - throw new DataValidationException("Registration ID should be specified!"); - } - 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.getJwkSetUri())) { - throw new DataValidationException("Jwk set uri 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.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!"); - } - } - }; + @Override + protected void validateDataImpl(TenantId tenantId, OAuth2ClientRegistration clientRegistration) { + if (StringUtils.isEmpty(clientRegistration.getRegistrationId())) { + throw new DataValidationException("Registration ID should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getDomainName())) { + throw new DataValidationException("Domain name should be specified!"); + } + if (StringUtils.isEmpty(clientRegistration.getRedirectUriTemplate())) { + throw new DataValidationException("Redirect URI template should be specified!"); + } + 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.getJwkSetUri())) { + throw new DataValidationException("Jwk set uri 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.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!"); + } + } + if (clientRegistration.getTenantId() == null) { + throw new DataValidationException("Client registration should be assigned to tenant!"); + } else if (!TenantId.SYS_TENANT_ID.equals(clientRegistration.getTenantId())) { + Tenant tenant = tenantService.findTenantById(clientRegistration.getTenantId()); + if (tenant == null) { + throw new DataValidationException("Client registration is referencing to non-existent tenant!"); + } + } + } + }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java index 003f87cc73..7ccad60bd7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -23,22 +23,9 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; import java.util.stream.Stream; public class OAuth2Utils { - public static final String OAUTH2_CLIENT_REGISTRATIONS_PARAMS = "oauth2ClientRegistrationsParams"; - public static final String OAUTH2_CLIENT_REGISTRATIONS_DOMAIN_NAME_PREFIX = "oauth2ClientRegistrationsDomainNamePrefix"; public static final String ALLOW_OAUTH2_CONFIGURATION = "allowOAuth2Configuration"; - public static final String SYSTEM_SETTINGS_OAUTH2_VALUE = "value"; public static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; - public static String constructAdminSettingsDomainKey(String domainName) { - String clientRegistrationsKey; - if (StringUtils.isEmpty(domainName)) { - clientRegistrationsKey = OAUTH2_CLIENT_REGISTRATIONS_PARAMS; - } else { - clientRegistrationsKey = OAUTH2_CLIENT_REGISTRATIONS_DOMAIN_NAME_PREFIX + "_" + domainName; - } - return clientRegistrationsKey; - } - public static OAuth2ClientInfo toClientInfo(OAuth2ClientRegistration clientRegistration) { OAuth2ClientInfo client = new OAuth2ClientInfo(); client.setName(clientRegistration.getLoginButtonLabel()); @@ -46,9 +33,4 @@ public class OAuth2Utils { client.setIcon(clientRegistration.getLoginButtonIcon()); return client; } - - public static Stream toClientRegistrationStream(OAuth2ClientsParams oAuth2ClientsParams) { - return oAuth2ClientsParams.getClientsDomainsParams().stream() - .flatMap(oAuth2ClientsDomainParams -> oAuth2ClientsDomainParams.getClientRegistrations().stream()); - } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java new file mode 100644 index 0000000000..d40fb060dc --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java @@ -0,0 +1,86 @@ +/** + * 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.OAuth2ClientRegistration; +import org.thingsboard.server.dao.DaoUtil; +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.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +public class JpaOAuth2ClientRegistrationDao extends JpaAbstractDao implements OAuth2ClientRegistrationDao { + private final OAuth2ClientRegistrationRepository repository; + + @Override + protected Class getEntityClass() { + return OAuth2ClientRegistrationEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return repository; + } + + @Override + public OAuth2ClientRegistration findByRegistrationId(String registrationId) { + Optional entity = repository.findByRegistrationId(registrationId); + return DaoUtil.getData(entity); + } + + @Override + public List findAll() { + Iterable entities = repository.findAll(); + List result = new ArrayList<>(); + entities.forEach(entity -> { + result.add(DaoUtil.getData(entity)); + }); + return result; + } + + @Override + public List findByTenantId(UUID tenantId) { + List entities = repository.findAllByTenantId(tenantId); + return entities.stream().map(DaoUtil::getData).collect(Collectors.toList()); + } + + @Override + public List findByDomainName(String domainName) { + List entities = repository.findAllByDomainName(domainName); + return entities.stream().map(DaoUtil::getData).collect(Collectors.toList()); + } + + @Override + public boolean removeByRegistrationId(String registrationId) { + repository.deleteByRegistrationId(registrationId); + return !repository.existsByRegistrationId(registrationId); + } + + @Override + public int removeByTenantId(UUID tenantId) { + return repository.deleteByTenantId(tenantId); + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java new file mode 100644 index 0000000000..17984ee057 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.java @@ -0,0 +1,37 @@ +/** + * 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 org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +public interface OAuth2ClientRegistrationRepository extends CrudRepository { + Optional findByRegistrationId(String registrationId); + + List findAllByTenantId(UUID tenantId); + + List findAllByDomainName(String domainName); + + int deleteByRegistrationId(String registrationId); + + int deleteByTenantId(UUID tenantId); + + boolean existsByRegistrationId(String registrationId); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 4708694cee..df0dfb666a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -105,7 +105,7 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe public void deleteTenant(TenantId tenantId) { log.trace("Executing deleteTenant [{}]", tenantId); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - oAuth2Service.deleteTenantOAuth2ClientsParams(tenantId); + oAuth2Service.deleteClientRegistrationsByTenantId(tenantId); customerService.deleteCustomersByTenantId(tenantId); widgetsBundleService.deleteWidgetsBundlesByTenantId(tenantId); dashboardService.deleteDashboardsByTenantId(tenantId); diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 664925db76..bb38f27aef 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -289,3 +289,40 @@ CREATE TABLE IF NOT EXISTS ts_kv_dictionary ( key_id int GENERATED BY DEFAULT AS IDENTITY(start with 0 increment by 1) UNIQUE, CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) ); + + +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( + id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + tenant_id uuid, + registration_id varchar(255), + domain_name varchar(255), + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + redirect_uri_template 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), + CONSTRAINT oauth2_registration_id_unq_key UNIQUE (registration_id) +); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 50924bbbd4..441b676647 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -306,6 +306,42 @@ CREATE TABLE IF NOT EXISTS ts_kv_dictionary CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) ); +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( + id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + tenant_id uuid, + registration_id varchar(255), + domain_name varchar(255), + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + redirect_uri_template 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), + CONSTRAINT oauth2_registration_id_unq_key UNIQUE (registration_id) +); + CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) LANGUAGE plpgsql AS $$ diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index ab572e7e53..a0e430fdcd 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -23,6 +23,7 @@ import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Tenant; +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.*; import org.thingsboard.server.dao.attributes.AttributesService; @@ -30,6 +31,7 @@ import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.oauth2.OAuth2Utils; +import javax.transaction.Transactional; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; @@ -55,24 +57,15 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { Assert.assertNotNull(savedTenant); tenantId = savedTenant.getId(); - Assert.assertNull(oAuth2Service.getSystemOAuth2ClientsParams().getClientsDomainsParams()); - Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams()); - - Assert.assertTrue(attributesService.findAll(tenantId, tenantId, DataConstants.SERVER_SCOPE).get().isEmpty()); - Assert.assertNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + Assert.assertTrue(oAuth2Service.findAllClientRegistrations().isEmpty()); } @After public void after() throws Exception { - clearSysAdmin(); - tenantService.deleteTenant(tenantId); + oAuth2Service.deleteClientRegistrationsByTenantId(TenantId.SYS_TENANT_ID); - Assert.assertNull(oAuth2Service.getSystemOAuth2ClientsParams().getClientsDomainsParams()); - Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams()); - - Assert.assertTrue(attributesService.findAll(tenantId, tenantId, DataConstants.SERVER_SCOPE).get().isEmpty()); - Assert.assertNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + Assert.assertTrue(oAuth2Service.findAllClientRegistrations().isEmpty()); } @Test @@ -93,226 +86,107 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { Assert.assertTrue(oAuth2Service.isOAuth2ClientRegistrationAllowed(tenantId)); } - @Test - public void testSaveSystemOAuth2() throws IOException { - updateTenantAllowOAuth2Setting(true); - Assert.assertTrue(oAuth2Service.isOAuth2ClientRegistrationAllowed(tenantId)); - } - - @Test(expected = DataValidationException.class) - public void testSaveSystemParamsWithDuplicateDomains() { - oAuth2Service.saveSystemOAuth2ClientsParams(clientsParamsWithDuplicateDomains()); - } - - @Test(expected = DataValidationException.class) - public void testSaveSystemParamsWithDuplicateRegistrationIds() { - oAuth2Service.saveSystemOAuth2ClientsParams(clientsParamsWithDuplicateRegistrationIds()); - } - @Test(expected = DataValidationException.class) - public void testSaveTenantParamsWithDuplicateRegistrationIds() { - oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParamsWithDuplicateRegistrationIds()); - } - - @Test - public void testSaveSystemParams() { - OAuth2ClientsParams clientsParams = validClientsParams(); - OAuth2ClientsParams savedClientParams = oAuth2Service.saveSystemOAuth2ClientsParams(clientsParams); - - Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); - Assert.assertEquals(clientsParams, savedClientParams); - } - - @Test - public void testSaveSystemParamsWithMultipleDomains() { - OAuth2ClientsParams clientsParams = validClientsParamsWithThreeDomains(); - OAuth2ClientsParams savedClientParams = oAuth2Service.saveSystemOAuth2ClientsParams(clientsParams); + public void testSaveDuplicateRegistrationId() { + OAuth2ClientRegistration first = validClientRegistration("duplicated_reg_id", TenantId.SYS_TENANT_ID); + OAuth2ClientRegistration second = validClientRegistration("duplicated_reg_id", tenantId); - Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); - Assert.assertEquals(clientsParams, savedClientParams); + oAuth2Service.saveClientRegistration(first); + oAuth2Service.saveClientRegistration(second); } @Test - public void testFindSystemParams() { - OAuth2ClientsParams clientsParams = validClientsParams(); - oAuth2Service.saveSystemOAuth2ClientsParams(clientsParams); - - Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); - - OAuth2ClientsParams foundClientParams = oAuth2Service.getSystemOAuth2ClientsParams(); - Assert.assertNotNull(foundClientParams); - Assert.assertEquals(clientsParams, foundClientParams); - } - - @Test - public void testSaveTenantParams() { - OAuth2ClientsParams clientsParams = validClientsParams(); - OAuth2ClientsDomainParams domainParams = clientsParams.getClientsDomainsParams().get(0); - String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainParams.getDomainName()); - - Assert.assertNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); - - OAuth2ClientsParams savedClientParams = oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParams); - - Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); - Assert.assertNotNull(savedClientParams); - - OAuth2ClientsDomainParams savedDomainParams = savedClientParams.getClientsDomainsParams().get(0); - Assert.assertEquals(domainParams.getDomainName(), savedDomainParams.getDomainName()); - Assert.assertEquals(domainParams.getClientRegistrations(), savedDomainParams.getClientRegistrations()); + public void testCreateNewSystemParams() { + OAuth2ClientRegistration clientRegistration = validClientRegistration(UUID.randomUUID().toString(), TenantId.SYS_TENANT_ID); + OAuth2ClientRegistration savedClientRegistration = oAuth2Service.saveClientRegistration(clientRegistration); + + Assert.assertNotNull(savedClientRegistration); + Assert.assertNotNull(savedClientRegistration.getId()); + clientRegistration.setId(savedClientRegistration.getId()); + clientRegistration.setCreatedTime(savedClientRegistration.getCreatedTime()); + Assert.assertEquals(clientRegistration, savedClientRegistration); } @Test - public void testSaveTenantMultipleParams() { - OAuth2ClientsParams clientsParams = validClientsParamsWithThreeDomains(); - - clientsParams.getClientsDomainsParams().forEach(oAuth2ClientsDomainParams -> { - String domainName = oAuth2ClientsDomainParams.getDomainName(); - String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainName); - Assert.assertNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); - }); - - OAuth2ClientsParams savedClientParams = oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParams); - Assert.assertNotNull(savedClientParams); - - clientsParams.getClientsDomainsParams().forEach(oAuth2ClientsDomainParams -> { - String domainName = oAuth2ClientsDomainParams.getDomainName(); - String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainName); - Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); - }); - - Assert.assertEquals(clientsParams, savedClientParams); + public void testFindSystemParamsByTenant() { + OAuth2ClientRegistration clientRegistration = validClientRegistration(UUID.randomUUID().toString(), TenantId.SYS_TENANT_ID); + oAuth2Service.saveClientRegistration(clientRegistration); + + List clientRegistrationsByTenantId = oAuth2Service.findClientRegistrationsByTenantId(TenantId.SYS_TENANT_ID); + Assert.assertEquals(1, clientRegistrationsByTenantId.size()); + Assert.assertEquals(1, oAuth2Service.findAllClientRegistrations().size()); + OAuth2ClientRegistration foundClientRegistration = clientRegistrationsByTenantId.get(0); + Assert.assertNotNull(foundClientRegistration); + clientRegistration.setId(foundClientRegistration.getId()); + clientRegistration.setCreatedTime(foundClientRegistration.getCreatedTime()); + Assert.assertEquals(clientRegistration, foundClientRegistration); } @Test - public void testRewriteSameDomainTenantParams() { - OAuth2ClientsParams clientsParams = validClientsParamsWithThreeDomains(); - oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParams); - - List clientsDomainsParams = clientsParams.getClientsDomainsParams(); - OAuth2ClientsParams updatedClientsParams = validClientsParamsWithThreeDomains(); - String sameDomainName = clientsDomainsParams.get(0).getDomainName(); - updatedClientsParams.getClientsDomainsParams().get(0).setDomainName(sameDomainName); - OAuth2ClientsParams rewrittenClientParams = oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, updatedClientsParams); - Assert.assertEquals(updatedClientsParams, rewrittenClientParams); - - clientsParams.getClientsDomainsParams().forEach(oAuth2ClientsDomainParams -> { - String domainName = oAuth2ClientsDomainParams.getDomainName(); - String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainName); - if (domainName.equals(sameDomainName)) { - Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); - } else { - Assert.assertNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); - } - }); - updatedClientsParams.getClientsDomainsParams().forEach(oAuth2ClientsDomainParams -> { - String domainName = oAuth2ClientsDomainParams.getDomainName(); - String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainName); - Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); - }); + public void testCreateNewTenantParams() { + OAuth2ClientRegistration clientRegistration = validClientRegistration(UUID.randomUUID().toString(), tenantId); + OAuth2ClientRegistration savedClientRegistration = oAuth2Service.saveClientRegistration(clientRegistration); + + Assert.assertNotNull(savedClientRegistration); + Assert.assertNotNull(savedClientRegistration.getId()); + clientRegistration.setId(savedClientRegistration.getId()); + clientRegistration.setCreatedTime(savedClientRegistration.getCreatedTime()); + Assert.assertEquals(clientRegistration, savedClientRegistration); } @Test - public void testAddDeleteTenantDomainParams() { - OAuth2ClientsParams clientsParams = validClientsParamsWithThreeDomains(); - oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParams); - - List clientsDomainsParams = clientsParams.getClientsDomainsParams(); - OAuth2ClientsParams updatedClientsParams = validClientsParamsWithThreeDomains(); - for (int i = 0; i < updatedClientsParams.getClientsDomainsParams().size(); i++) { - String domainName = clientsDomainsParams.get(i).getDomainName(); - updatedClientsParams.getClientsDomainsParams().get(i).setDomainName(domainName); - } - OAuth2ClientsParams rewrittenClientParams = oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, updatedClientsParams); - Assert.assertEquals(updatedClientsParams, rewrittenClientParams); - - clientsParams.getClientsDomainsParams().forEach(oAuth2ClientsDomainParams -> { - String domainName = oAuth2ClientsDomainParams.getDomainName(); - String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainName); - Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); - }); + public void testFindTenantParams() { + OAuth2ClientRegistration clientRegistration = validClientRegistration(UUID.randomUUID().toString(), tenantId); + oAuth2Service.saveClientRegistration(clientRegistration); + + List clientRegistrationsByTenantId = oAuth2Service.findClientRegistrationsByTenantId(tenantId); + Assert.assertEquals(1, clientRegistrationsByTenantId.size()); + Assert.assertEquals(1, oAuth2Service.findAllClientRegistrations().size()); + OAuth2ClientRegistration foundClientRegistration = clientRegistrationsByTenantId.get(0); + Assert.assertNotNull(foundClientRegistration); + clientRegistration.setId(foundClientRegistration.getId()); + clientRegistration.setCreatedTime(foundClientRegistration.getCreatedTime()); + Assert.assertEquals(clientRegistration, foundClientRegistration); } @Test - public void testFindTenantParams() { - OAuth2ClientsParams clientsParams = validClientsParams(); - OAuth2ClientsDomainParams domainParams = clientsParams.getClientsDomainsParams().get(0); - String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainParams.getDomainName()); + public void testGetClientRegistrationWithTenant() { + OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(UUID.randomUUID().toString(), tenantId); + OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(UUID.randomUUID().toString(), TenantId.SYS_TENANT_ID); - Assert.assertNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + OAuth2ClientRegistration savedTenantClientRegistration = oAuth2Service.saveClientRegistration(tenantClientRegistration); + OAuth2ClientRegistration savedSysAdminClientRegistration = oAuth2Service.saveClientRegistration(sysAdminClientRegistration); - OAuth2ClientsParams savedClientsParams = oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, clientsParams); + Assert.assertEquals(2, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertNotNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); + Assert.assertEquals(savedTenantClientRegistration, oAuth2Service.findClientRegistrationsByTenantId(tenantId).get(0)); + Assert.assertEquals(savedSysAdminClientRegistration, oAuth2Service.findClientRegistrationsByTenantId(TenantId.SYS_TENANT_ID).get(0)); - OAuth2ClientsParams foundClientsParams = oAuth2Service.getTenantOAuth2ClientsParams(tenantId); - Assert.assertEquals(savedClientsParams, foundClientsParams); - } + Assert.assertEquals(savedTenantClientRegistration, + oAuth2Service.findClientRegistrationByRegistrationId(savedTenantClientRegistration.getRegistrationId())); + Assert.assertEquals(savedSysAdminClientRegistration, + oAuth2Service.findClientRegistrationByRegistrationId(savedSysAdminClientRegistration.getRegistrationId())); - @Test - public void testGetClientRegistrationWithTenant() { - OAuth2ClientsParams tenantClientsParams = validClientsParams(); - OAuth2ClientsParams sysAdminClientsParams = validClientsParams(); - - oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, tenantClientsParams); - oAuth2Service.saveSystemOAuth2ClientsParams(sysAdminClientsParams); - - OAuth2Utils.toClientRegistrationStream(tenantClientsParams) - .forEach(clientRegistration -> { - Pair pair = oAuth2Service.getClientRegistrationWithTenant(clientRegistration.getRegistrationId()); - Assert.assertEquals(tenantId, pair.getKey()); - Assert.assertEquals(clientRegistration.getRegistrationId(), pair.getValue().getRegistrationId()); - }); - OAuth2Utils.toClientRegistrationStream(sysAdminClientsParams) - .forEach(clientRegistration -> { - Pair pair = oAuth2Service.getClientRegistrationWithTenant(clientRegistration.getRegistrationId()); - Assert.assertNotNull(pair); - Assert.assertEquals(TenantId.SYS_TENANT_ID, pair.getKey()); - Assert.assertEquals(clientRegistration.getRegistrationId(), pair.getValue().getRegistrationId()); - }); - } - - @Test - public void testGetExtendedClientRegistration() { - OAuth2ClientsParams tenantClientsParams = validClientsParams(); - OAuth2ClientsParams sysAdminClientsParams = validClientsParams(); - - oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, tenantClientsParams); - oAuth2Service.saveSystemOAuth2ClientsParams(sysAdminClientsParams); - - Stream.concat( - OAuth2Utils.toClientRegistrationStream(tenantClientsParams), - OAuth2Utils.toClientRegistrationStream(sysAdminClientsParams) - ) - .forEach(clientRegistration -> { - ExtendedOAuth2ClientRegistration foundExtendedClientRegistration = oAuth2Service.getExtendedClientRegistration(clientRegistration.getRegistrationId()); - Assert.assertNotNull(foundExtendedClientRegistration); - Assert.assertEquals(clientRegistration, foundExtendedClientRegistration.getClientRegistration()); - }); + Assert.assertEquals(savedTenantClientRegistration, + oAuth2Service.findClientRegistrationById(tenantId, savedTenantClientRegistration.getId())); + Assert.assertEquals(savedSysAdminClientRegistration, + oAuth2Service.findClientRegistrationById(TenantId.SYS_TENANT_ID, savedSysAdminClientRegistration.getId())); } @Test public void testGetOAuth2Clients() { - OAuth2ClientsParams tenantClientsParams = validClientsParams(); - OAuth2ClientsParams sysAdminClientsParams = validClientsParams(); - - OAuth2ClientsDomainParams tenantDomainParams = tenantClientsParams.getClientsDomainsParams().get(0); - OAuth2ClientsDomainParams systemDomainParams = sysAdminClientsParams.getClientsDomainsParams().get(0); - systemDomainParams.setDomainName(tenantDomainParams.getDomainName()); + String testDomainName = "test_domain"; + OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(testDomainName, UUID.randomUUID().toString(), tenantId); + OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(testDomainName, UUID.randomUUID().toString(), TenantId.SYS_TENANT_ID); - oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, tenantClientsParams); - oAuth2Service.saveSystemOAuth2ClientsParams(sysAdminClientsParams); + oAuth2Service.saveClientRegistration(tenantClientRegistration); + oAuth2Service.saveClientRegistration(sysAdminClientRegistration); - List oAuth2Clients = oAuth2Service.getOAuth2Clients(tenantDomainParams.getDomainName()); - - Set actualLabels = Stream.concat( - tenantDomainParams.getClientRegistrations().stream() - .map(OAuth2ClientRegistration::getLoginButtonLabel), - systemDomainParams.getClientRegistrations().stream() - .map(OAuth2ClientRegistration::getLoginButtonLabel) - ).collect(Collectors.toSet()); + List oAuth2Clients = oAuth2Service.getOAuth2Clients(testDomainName); + Set actualLabels = new HashSet<>(Arrays.asList(tenantClientRegistration.getLoginButtonLabel(), + sysAdminClientRegistration.getLoginButtonLabel())); Set foundLabels = oAuth2Clients.stream().map(OAuth2ClientInfo::getName).collect(Collectors.toSet()); Assert.assertEquals(actualLabels, foundLabels); @@ -320,67 +194,39 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testGetEmptyOAuth2Clients() { + String testDomainName = "test_domain"; + OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(testDomainName, UUID.randomUUID().toString(), tenantId); + OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(testDomainName, UUID.randomUUID().toString(), TenantId.SYS_TENANT_ID); + oAuth2Service.saveClientRegistration(tenantClientRegistration); + oAuth2Service.saveClientRegistration(sysAdminClientRegistration); List oAuth2Clients = oAuth2Service.getOAuth2Clients("random-domain"); Assert.assertTrue(oAuth2Clients.isEmpty()); } @Test - public void testGetAllOAuth2ClientsParams() { - OAuth2ClientsParams tenantClientsParams = validClientsParams(); - OAuth2ClientsParams sysAdminClientsParams = validClientsParams(); - - Map emptyParams = oAuth2Service.getAllOAuth2ClientsParams(); - Assert.assertTrue(emptyParams.isEmpty()); - - OAuth2ClientsParams savedTenantParams = oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, tenantClientsParams); - OAuth2ClientsParams savedSystemParams = oAuth2Service.saveSystemOAuth2ClientsParams(sysAdminClientsParams); - - Map clientsParams = oAuth2Service.getAllOAuth2ClientsParams(); - - OAuth2ClientsParams foundTenantParams = clientsParams.get(tenantId); - Assert.assertEquals(savedTenantParams, foundTenantParams); - - OAuth2ClientsParams foundSystemParams = clientsParams.get(TenantId.SYS_TENANT_ID); - Assert.assertEquals(savedSystemParams, foundSystemParams); - } - - @Test - public void testDeleteSystemOAuth2ClientsParams() { - OAuth2ClientsParams sysAdminClientsParams = validClientsParams(); - - Assert.assertNull(oAuth2Service.getSystemOAuth2ClientsParams().getClientsDomainsParams()); - - oAuth2Service.saveSystemOAuth2ClientsParams(sysAdminClientsParams); - - Assert.assertNotNull(oAuth2Service.getSystemOAuth2ClientsParams().getClientsDomainsParams()); - - oAuth2Service.deleteSystemOAuth2ClientsParams(); - Assert.assertNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + public void testDeleteOAuth2ClientRegistration() { + OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(UUID.randomUUID().toString(), tenantId); + OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(UUID.randomUUID().toString(), TenantId.SYS_TENANT_ID); + OAuth2ClientRegistration savedTenantRegistration = oAuth2Service.saveClientRegistration(tenantClientRegistration); + OAuth2ClientRegistration savedSysAdminRegistration = oAuth2Service.saveClientRegistration(sysAdminClientRegistration); + + oAuth2Service.deleteClientRegistrationById(tenantId, savedTenantRegistration.getId()); + List foundRegistrations = oAuth2Service.findAllClientRegistrations(); + Assert.assertEquals(1, foundRegistrations.size()); + Assert.assertEquals(savedSysAdminRegistration, foundRegistrations.get(0)); } @Test - public void testDeleteTenantOAuth2ClientsParams() { - OAuth2ClientsParams tenantClientsParams = validClientsParams(); - - Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams()); - - oAuth2Service.saveTenantOAuth2ClientsParams(tenantId, tenantClientsParams); - - Assert.assertNotNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams()); - - oAuth2Service.deleteTenantOAuth2ClientsParams(tenantId); - Assert.assertNull(oAuth2Service.getTenantOAuth2ClientsParams(tenantId).getClientsDomainsParams()); - tenantClientsParams.getClientsDomainsParams().forEach(oAuth2ClientsDomainParams -> { - String domainName = oAuth2ClientsDomainParams.getDomainName(); - String domainKey = OAuth2Utils.constructAdminSettingsDomainKey(domainName); - Assert.assertNull(adminSettingsService.findAdminSettingsByKey(tenantId, domainKey)); - }); - } - - - private void clearSysAdmin() { - oAuth2Service.deleteSystemOAuth2ClientsParams(); - Assert.assertNull(adminSettingsService.findAdminSettingsByKey(TenantId.SYS_TENANT_ID, OAuth2Utils.OAUTH2_CLIENT_REGISTRATIONS_PARAMS)); + public void testDeleteTenantOAuth2ClientRegistrations() { + oAuth2Service.saveClientRegistration(validClientRegistration(UUID.randomUUID().toString(), tenantId)); + oAuth2Service.saveClientRegistration(validClientRegistration(UUID.randomUUID().toString(), tenantId)); + oAuth2Service.saveClientRegistration(validClientRegistration(UUID.randomUUID().toString(), tenantId)); + Assert.assertEquals(3, oAuth2Service.findAllClientRegistrations().size()); + Assert.assertEquals(3, oAuth2Service.findClientRegistrationsByTenantId(tenantId).size()); + + oAuth2Service.deleteClientRegistrationsByTenantId(tenantId); + Assert.assertEquals(0, oAuth2Service.findAllClientRegistrations().size()); + Assert.assertEquals(0, oAuth2Service.findClientRegistrationsByTenantId(tenantId).size()); } private void updateTenantAllowOAuth2Setting(Boolean allowOAuth2) throws IOException { @@ -394,87 +240,17 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { } } - private OAuth2ClientsParams validClientsParams() { - OAuth2ClientRegistration first = validClientRegistration(); - OAuth2ClientRegistration second = validClientRegistration(); - return OAuth2ClientsParams.builder() - .clientsDomainsParams(Collections.singletonList( - OAuth2ClientsDomainParams.builder() - .domainName(UUID.randomUUID().toString()) - .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") - .clientRegistrations(Arrays.asList(first, second)) - .build() - )) - .build(); - } - - private OAuth2ClientsParams validClientsParamsWithThreeDomains() { - OAuth2ClientRegistration first = validClientRegistration(); - OAuth2ClientRegistration second = validClientRegistration(); - OAuth2ClientRegistration third = validClientRegistration(); - return OAuth2ClientsParams.builder() - .clientsDomainsParams(Arrays.asList( - OAuth2ClientsDomainParams.builder() - .domainName(UUID.randomUUID().toString()) - .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") - .clientRegistrations(Arrays.asList(first, second)) - .build(), - OAuth2ClientsDomainParams.builder() - .domainName(UUID.randomUUID().toString()) - .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") - .clientRegistrations(Arrays.asList(third)) - .build() - )) - .build(); - } - - - private OAuth2ClientsParams clientsParamsWithDuplicateDomains() { - OAuth2ClientRegistration first = validClientRegistration(); - OAuth2ClientRegistration second = validClientRegistration(); - OAuth2ClientRegistration third = validClientRegistration(); - return OAuth2ClientsParams.builder() - .clientsDomainsParams(Arrays.asList( - OAuth2ClientsDomainParams.builder() - .domainName("domain") - .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") - .clientRegistrations(Collections.singletonList(first)) - .build(), - OAuth2ClientsDomainParams.builder() - .domainName("domain") - .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") - .clientRegistrations(Collections.singletonList(second)) - .build(), - OAuth2ClientsDomainParams.builder() - .domainName(UUID.randomUUID().toString()) - .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") - .clientRegistrations(Collections.singletonList(third)) - .build() - )) - .build(); - } - - private OAuth2ClientsParams clientsParamsWithDuplicateRegistrationIds() { - OAuth2ClientRegistration first = validClientRegistration(); - first.setRegistrationId("registrationId"); - OAuth2ClientRegistration second = validClientRegistration(); - OAuth2ClientRegistration third = validClientRegistration(); - third.setRegistrationId("registrationId"); - return OAuth2ClientsParams.builder() - .clientsDomainsParams(Arrays.asList( - OAuth2ClientsDomainParams.builder() - .domainName(UUID.randomUUID().toString()) - .redirectUriTemplate("http://localhost:8080/login/oauth2/code/") - .clientRegistrations(Arrays.asList(first, second, third)) - .build() - )) - .build(); + private OAuth2ClientRegistration validClientRegistration(String registrationId, TenantId tenantId) { + return validClientRegistration("domainName", registrationId, tenantId); } - private OAuth2ClientRegistration validClientRegistration() { - return OAuth2ClientRegistration.builder() - .registrationId(UUID.randomUUID().toString()) - .mapperConfig(OAuth2MapperConfig.builder() + private OAuth2ClientRegistration validClientRegistration(String domainName, String registrationId, TenantId tenantId) { + OAuth2ClientRegistration clientRegistration = new OAuth2ClientRegistration(); + clientRegistration.setTenantId(tenantId); + clientRegistration.setRegistrationId(registrationId); + clientRegistration.setDomainName(domainName); + clientRegistration.setMapperConfig( + OAuth2MapperConfig.builder() .allowUserCreation(true) .activateUser(true) .type(MapperType.CUSTOM) @@ -483,17 +259,20 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { .url("localhost:8082") .build() ) - .build()) - .clientId("clientId") - .clientSecret("clientSecret") - .authorizationUri("authorizationUri") - .accessTokenUri("tokenUri") - .scope(Arrays.asList("scope1", "scope2")) - .userInfoUri("userInfoUri") - .userNameAttributeName("userNameAttributeName") - .jwkSetUri("jwkSetUri") - .clientAuthenticationMethod("clientAuthenticationMethod") - .loginButtonLabel("loginButtonLabel") - .build(); + .build() + ); + clientRegistration.setClientId("clientId"); + clientRegistration.setClientSecret("clientSecret"); + clientRegistration.setAuthorizationUri("authorizationUri"); + clientRegistration.setAccessTokenUri("tokenUri"); + clientRegistration.setRedirectUriTemplate("redirectUriTemplate"); + clientRegistration.setScope(Arrays.asList("scope1", "scope2")); + clientRegistration.setUserInfoUri("userInfoUri"); + clientRegistration.setUserNameAttributeName("userNameAttributeName"); + clientRegistration.setJwkSetUri("jwkSetUri"); + clientRegistration.setClientAuthenticationMethod("clientAuthenticationMethod"); + clientRegistration.setLoginButtonLabel("loginButtonLabel"); + clientRegistration.setLoginButtonIcon("loginButtonIcon"); + return clientRegistration; } } diff --git a/dao/src/test/resources/sql/hsql/drop-all-tables.sql b/dao/src/test/resources/sql/hsql/drop-all-tables.sql index d99a6d5e0f..e55c9bfcd5 100644 --- a/dao/src/test/resources/sql/hsql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/hsql/drop-all-tables.sql @@ -20,4 +20,5 @@ DROP TABLE IF EXISTS widgets_bundle; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS entity_view; +DROP TABLE IF EXISTS oauth2_client_registration; DROP FUNCTION IF EXISTS to_uuid; diff --git a/dao/src/test/resources/sql/psql/drop-all-tables.sql b/dao/src/test/resources/sql/psql/drop-all-tables.sql index b2e4a27963..259a3e9fed 100644 --- a/dao/src/test/resources/sql/psql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/psql/drop-all-tables.sql @@ -21,4 +21,5 @@ DROP TABLE IF EXISTS widgets_bundle; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS entity_view; +DROP TABLE IF EXISTS oauth2_client_registration; DROP TABLE IF EXISTS tb_schema_settings; \ No newline at end of file diff --git a/dao/src/test/resources/sql/timescale/drop-all-tables.sql b/dao/src/test/resources/sql/timescale/drop-all-tables.sql index b2e4a27963..259a3e9fed 100644 --- a/dao/src/test/resources/sql/timescale/drop-all-tables.sql +++ b/dao/src/test/resources/sql/timescale/drop-all-tables.sql @@ -21,4 +21,5 @@ DROP TABLE IF EXISTS widgets_bundle; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS entity_view; +DROP TABLE IF EXISTS oauth2_client_registration; DROP TABLE IF EXISTS tb_schema_settings; \ No newline at end of file From 9b38574faea0e76fafe0501f3492253c26b33f05 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 5 Aug 2020 18:14:30 +0300 Subject: [PATCH 074/117] Fix sysadmin permissions --- .../permission/SysAdminPermissions.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java index 869217e4cf..f85b6c4906 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java @@ -39,7 +39,7 @@ public class SysAdminPermissions extends AbstractPermissions { put(Resource.USER, userPermissionChecker); put(Resource.WIDGETS_BUNDLE, systemEntityPermissionChecker); put(Resource.WIDGET_TYPE, systemEntityPermissionChecker); - put(Resource.OAUTH2_CONFIGURATION, PermissionChecker.allowAllPermissionChecker); + put(Resource.OAUTH2_CONFIGURATION, sysAdminOAuth2ConfigPermissionChecker); } private static final PermissionChecker systemEntityPermissionChecker = new PermissionChecker() { @@ -66,4 +66,19 @@ public class SysAdminPermissions extends AbstractPermissions { }; + private final PermissionChecker sysAdminOAuth2ConfigPermissionChecker = new PermissionChecker() { + @Override + public boolean hasPermission(SecurityUser user, Operation operation) { + return true; + } + + @Override + public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { + if (entity.getTenantId() != null && !entity.getTenantId().isNullUid()) { + return false; + } + return true; + } + }; + } From 32c8d7e5fd58b485762d4aa88765c179abffb352 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 12 Aug 2020 13:14:10 +0300 Subject: [PATCH 075/117] Removed 'registrationId' from ClientRegistration --- .../main/data/upgrade/3.1.0/schema_update.sql | 4 +- .../server/controller/BaseController.java | 2 +- .../Oauth2AuthenticationSuccessHandler.java | 3 +- .../server/dao/oauth2/OAuth2Service.java | 4 +- .../data/oauth2/OAuth2ClientRegistration.java | 2 - .../server/dao/model/ModelConstants.java | 1 - .../sql/OAuth2ClientRegistrationEntity.java | 4 -- .../HybridClientRegistrationRepository.java | 9 ++- .../oauth2/OAuth2ClientRegistrationDao.java | 4 -- .../server/dao/oauth2/OAuth2ServiceImpl.java | 32 ++--------- .../server/dao/oauth2/OAuth2Utils.java | 2 +- .../JpaOAuth2ClientRegistrationDao.java | 12 ---- .../OAuth2ClientRegistrationRepository.java | 7 --- .../resources/sql/schema-entities-hsql.sql | 4 +- .../main/resources/sql/schema-entities.sql | 4 +- .../dao/service/BaseOAuth2ServiceTest.java | 55 +++++++------------ 16 files changed, 39 insertions(+), 110 deletions(-) diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql index 06daa9541c..76c3efcdfa 100644 --- a/application/src/main/data/upgrade/3.1.0/schema_update.sql +++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql @@ -21,7 +21,6 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( created_time bigint NOT NULL, additional_info varchar, tenant_id uuid, - registration_id varchar(255), domain_name varchar(255), client_id varchar(255), client_secret varchar(255), @@ -48,6 +47,5 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( basic_always_full_screen boolean, custom_url varchar(255), custom_username varchar(255), - custom_password varchar(255), - CONSTRAINT oauth2_registration_id_unq_key UNIQUE (registration_id) + custom_password varchar(255) ); \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 3757223fda..1a2a54b1d1 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -540,7 +540,7 @@ public abstract class BaseController { OAuth2ClientRegistration checkOAuth2ClientRegistrationId(OAuth2ClientRegistrationId clientRegistrationId, Operation operation) throws ThingsboardException { try { validateId(clientRegistrationId, "Incorrect oAuth2ClientRegistrationId " + clientRegistrationId); - OAuth2ClientRegistration clientRegistration = oAuth2Service.findClientRegistrationById(getCurrentUser().getTenantId(), clientRegistrationId); + OAuth2ClientRegistration clientRegistration = oAuth2Service.findClientRegistration(clientRegistrationId.getId()); checkNotNull(clientRegistration); accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION, operation, clientRegistrationId, clientRegistration); return clientRegistration; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index e3d21b448d..9274e08939 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -36,6 +36,7 @@ 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") public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { @@ -65,7 +66,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS try { OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; - OAuth2ClientRegistration clientRegistration = oAuth2Service.findClientRegistrationByRegistrationId(token.getAuthorizedClientRegistrationId()); + OAuth2ClientRegistration clientRegistration = oAuth2Service.findClientRegistration(UUID.fromString(token.getAuthorizedClientRegistrationId())); OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, clientRegistration.getTenantId(), clientRegistration.getMapperConfig()); diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 6d4be61dc7..c8da37e9e5 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -30,9 +30,7 @@ public interface OAuth2Service { List findClientRegistrationsByTenantId(TenantId tenantId); - OAuth2ClientRegistration findClientRegistrationByRegistrationId(String registrationId); - - OAuth2ClientRegistration findClientRegistrationById(TenantId tenantId, OAuth2ClientRegistrationId id); + OAuth2ClientRegistration findClientRegistration(UUID id); List findAllClientRegistrations(); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java index a0a7dcc48b..1da7942b17 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -32,7 +32,6 @@ import java.util.List; public class OAuth2ClientRegistration extends BaseData implements HasTenantId, HasName { private TenantId tenantId; - private String registrationId; private String domainName; private String redirectUriTemplate; private OAuth2MapperConfig mapperConfig; @@ -51,7 +50,6 @@ public class OAuth2ClientRegistration extends BaseData { - OAuth2ClientRegistration findByRegistrationId(String registrationId); - List findAll(); List findByTenantId(UUID tenantId); List findByDomainName(String domainName); - boolean removeByRegistrationId(String registrationId); - int removeByTenantId(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index f06d906e7c..535378c9a8 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -46,7 +46,6 @@ import static org.thingsboard.server.dao.service.Validator.validateString; 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_REGISTRATION_ID = "Incorrect registrationId "; public static final String INCORRECT_DOMAIN_NAME = "Incorrect domainName "; @Autowired @@ -68,18 +67,7 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se public OAuth2ClientRegistration saveClientRegistration(OAuth2ClientRegistration clientRegistration) { log.trace("Executing saveClientRegistration [{}]", clientRegistration); clientRegistrationValidator.validate(clientRegistration, OAuth2ClientRegistration::getTenantId); - OAuth2ClientRegistration savedClientRegistration; - try { - savedClientRegistration = clientRegistrationDao.save(clientRegistration.getTenantId(), clientRegistration); - } catch (Exception t) { - ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); - if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("oauth2_registration_id_unq_key")) { - throw new DataValidationException("Client registration with such registrationId already exists!"); - } else { - throw t; - } - } - return savedClientRegistration; + return clientRegistrationDao.save(clientRegistration.getTenantId(), clientRegistration); } @Override @@ -90,17 +78,10 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } @Override - public OAuth2ClientRegistration findClientRegistrationByRegistrationId(String registrationId) { - log.trace("Executing findClientRegistrationByRegistrationId [{}]", registrationId); - validateString(registrationId, INCORRECT_REGISTRATION_ID + registrationId); - return clientRegistrationDao.findByRegistrationId(registrationId); - } - - @Override - public OAuth2ClientRegistration findClientRegistrationById(TenantId tenantId, OAuth2ClientRegistrationId id) { - log.trace("Executing findClientRegistrationById [{}]", id); + public OAuth2ClientRegistration findClientRegistration(UUID id) { + log.trace("Executing findClientRegistration [{}]", id); validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); - return clientRegistrationDao.findById(tenantId, id.getId()); + return clientRegistrationDao.findById(null, id); } @Override @@ -138,7 +119,7 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } } - private DataValidator clientRegistrationValidator = + private final DataValidator clientRegistrationValidator = new DataValidator() { @Override @@ -151,9 +132,6 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se @Override protected void validateDataImpl(TenantId tenantId, OAuth2ClientRegistration clientRegistration) { - if (StringUtils.isEmpty(clientRegistration.getRegistrationId())) { - throw new DataValidationException("Registration ID should be specified!"); - } if (StringUtils.isEmpty(clientRegistration.getDomainName())) { throw new DataValidationException("Domain name should be specified!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java index 7ccad60bd7..948c894540 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -29,7 +29,7 @@ public class OAuth2Utils { public static OAuth2ClientInfo toClientInfo(OAuth2ClientRegistration clientRegistration) { OAuth2ClientInfo client = new OAuth2ClientInfo(); client.setName(clientRegistration.getLoginButtonLabel()); - client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getRegistrationId())); + client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistration.getUuidId().toString())); client.setIcon(clientRegistration.getLoginButtonIcon()); return client; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java index d40fb060dc..ef10245e19 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java @@ -45,12 +45,6 @@ public class JpaOAuth2ClientRegistrationDao extends JpaAbstractDao entity = repository.findByRegistrationId(registrationId); - return DaoUtil.getData(entity); - } - @Override public List findAll() { Iterable entities = repository.findAll(); @@ -73,12 +67,6 @@ public class JpaOAuth2ClientRegistrationDao extends JpaAbstractDao { - Optional findByRegistrationId(String registrationId); - List findAllByTenantId(UUID tenantId); List findAllByDomainName(String domainName); - int deleteByRegistrationId(String registrationId); - int deleteByTenantId(UUID tenantId); - - boolean existsByRegistrationId(String registrationId); } diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index bb38f27aef..711ab8037e 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -296,7 +296,6 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( created_time bigint NOT NULL, additional_info varchar, tenant_id uuid, - registration_id varchar(255), domain_name varchar(255), client_id varchar(255), client_secret varchar(255), @@ -323,6 +322,5 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( basic_always_full_screen boolean, custom_url varchar(255), custom_username varchar(255), - custom_password varchar(255), - CONSTRAINT oauth2_registration_id_unq_key UNIQUE (registration_id) + custom_password varchar(255) ); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 441b676647..84bfaea563 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -311,7 +311,6 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( created_time bigint NOT NULL, additional_info varchar, tenant_id uuid, - registration_id varchar(255), domain_name varchar(255), client_id varchar(255), client_secret varchar(255), @@ -338,8 +337,7 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( basic_always_full_screen boolean, custom_url varchar(255), custom_username varchar(255), - custom_password varchar(255), - CONSTRAINT oauth2_registration_id_unq_key UNIQUE (registration_id) + custom_password varchar(255) ); CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index a0e430fdcd..d9165b08b8 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -86,18 +86,10 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { Assert.assertTrue(oAuth2Service.isOAuth2ClientRegistrationAllowed(tenantId)); } - @Test(expected = DataValidationException.class) - public void testSaveDuplicateRegistrationId() { - OAuth2ClientRegistration first = validClientRegistration("duplicated_reg_id", TenantId.SYS_TENANT_ID); - OAuth2ClientRegistration second = validClientRegistration("duplicated_reg_id", tenantId); - - oAuth2Service.saveClientRegistration(first); - oAuth2Service.saveClientRegistration(second); - } @Test public void testCreateNewSystemParams() { - OAuth2ClientRegistration clientRegistration = validClientRegistration(UUID.randomUUID().toString(), TenantId.SYS_TENANT_ID); + OAuth2ClientRegistration clientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); OAuth2ClientRegistration savedClientRegistration = oAuth2Service.saveClientRegistration(clientRegistration); Assert.assertNotNull(savedClientRegistration); @@ -109,7 +101,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testFindSystemParamsByTenant() { - OAuth2ClientRegistration clientRegistration = validClientRegistration(UUID.randomUUID().toString(), TenantId.SYS_TENANT_ID); + OAuth2ClientRegistration clientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); oAuth2Service.saveClientRegistration(clientRegistration); List clientRegistrationsByTenantId = oAuth2Service.findClientRegistrationsByTenantId(TenantId.SYS_TENANT_ID); @@ -124,7 +116,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testCreateNewTenantParams() { - OAuth2ClientRegistration clientRegistration = validClientRegistration(UUID.randomUUID().toString(), tenantId); + OAuth2ClientRegistration clientRegistration = validClientRegistration(tenantId); OAuth2ClientRegistration savedClientRegistration = oAuth2Service.saveClientRegistration(clientRegistration); Assert.assertNotNull(savedClientRegistration); @@ -136,7 +128,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testFindTenantParams() { - OAuth2ClientRegistration clientRegistration = validClientRegistration(UUID.randomUUID().toString(), tenantId); + OAuth2ClientRegistration clientRegistration = validClientRegistration(tenantId); oAuth2Service.saveClientRegistration(clientRegistration); List clientRegistrationsByTenantId = oAuth2Service.findClientRegistrationsByTenantId(tenantId); @@ -151,8 +143,8 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testGetClientRegistrationWithTenant() { - OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(UUID.randomUUID().toString(), tenantId); - OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(UUID.randomUUID().toString(), TenantId.SYS_TENANT_ID); + OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId); + OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); OAuth2ClientRegistration savedTenantClientRegistration = oAuth2Service.saveClientRegistration(tenantClientRegistration); OAuth2ClientRegistration savedSysAdminClientRegistration = oAuth2Service.saveClientRegistration(sysAdminClientRegistration); @@ -163,22 +155,16 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { Assert.assertEquals(savedSysAdminClientRegistration, oAuth2Service.findClientRegistrationsByTenantId(TenantId.SYS_TENANT_ID).get(0)); Assert.assertEquals(savedTenantClientRegistration, - oAuth2Service.findClientRegistrationByRegistrationId(savedTenantClientRegistration.getRegistrationId())); - Assert.assertEquals(savedSysAdminClientRegistration, - oAuth2Service.findClientRegistrationByRegistrationId(savedSysAdminClientRegistration.getRegistrationId())); - - Assert.assertEquals(savedTenantClientRegistration, - oAuth2Service.findClientRegistrationById(tenantId, savedTenantClientRegistration.getId())); + oAuth2Service.findClientRegistration(savedTenantClientRegistration.getUuidId())); Assert.assertEquals(savedSysAdminClientRegistration, - oAuth2Service.findClientRegistrationById(TenantId.SYS_TENANT_ID, savedSysAdminClientRegistration.getId())); - + oAuth2Service.findClientRegistration(savedSysAdminClientRegistration.getUuidId())); } @Test public void testGetOAuth2Clients() { String testDomainName = "test_domain"; - OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(testDomainName, UUID.randomUUID().toString(), tenantId); - OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(testDomainName, UUID.randomUUID().toString(), TenantId.SYS_TENANT_ID); + OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId, testDomainName); + OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID, testDomainName); oAuth2Service.saveClientRegistration(tenantClientRegistration); oAuth2Service.saveClientRegistration(sysAdminClientRegistration); @@ -195,8 +181,8 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testGetEmptyOAuth2Clients() { String testDomainName = "test_domain"; - OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(testDomainName, UUID.randomUUID().toString(), tenantId); - OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(testDomainName, UUID.randomUUID().toString(), TenantId.SYS_TENANT_ID); + OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId, testDomainName); + OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID, testDomainName); oAuth2Service.saveClientRegistration(tenantClientRegistration); oAuth2Service.saveClientRegistration(sysAdminClientRegistration); List oAuth2Clients = oAuth2Service.getOAuth2Clients("random-domain"); @@ -205,8 +191,8 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testDeleteOAuth2ClientRegistration() { - OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(UUID.randomUUID().toString(), tenantId); - OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(UUID.randomUUID().toString(), TenantId.SYS_TENANT_ID); + OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId); + OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); OAuth2ClientRegistration savedTenantRegistration = oAuth2Service.saveClientRegistration(tenantClientRegistration); OAuth2ClientRegistration savedSysAdminRegistration = oAuth2Service.saveClientRegistration(sysAdminClientRegistration); @@ -218,9 +204,9 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testDeleteTenantOAuth2ClientRegistrations() { - oAuth2Service.saveClientRegistration(validClientRegistration(UUID.randomUUID().toString(), tenantId)); - oAuth2Service.saveClientRegistration(validClientRegistration(UUID.randomUUID().toString(), tenantId)); - oAuth2Service.saveClientRegistration(validClientRegistration(UUID.randomUUID().toString(), tenantId)); + oAuth2Service.saveClientRegistration(validClientRegistration(tenantId)); + oAuth2Service.saveClientRegistration(validClientRegistration(tenantId)); + oAuth2Service.saveClientRegistration(validClientRegistration(tenantId)); Assert.assertEquals(3, oAuth2Service.findAllClientRegistrations().size()); Assert.assertEquals(3, oAuth2Service.findClientRegistrationsByTenantId(tenantId).size()); @@ -240,14 +226,13 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { } } - private OAuth2ClientRegistration validClientRegistration(String registrationId, TenantId tenantId) { - return validClientRegistration("domainName", registrationId, tenantId); + private OAuth2ClientRegistration validClientRegistration(TenantId tenantId) { + return validClientRegistration(tenantId, "domainName"); } - private OAuth2ClientRegistration validClientRegistration(String domainName, String registrationId, TenantId tenantId) { + private OAuth2ClientRegistration validClientRegistration(TenantId tenantId, String domainName) { OAuth2ClientRegistration clientRegistration = new OAuth2ClientRegistration(); clientRegistration.setTenantId(tenantId); - clientRegistration.setRegistrationId(registrationId); clientRegistration.setDomainName(domainName); clientRegistration.setMapperConfig( OAuth2MapperConfig.builder() From 2c537b28f4376911f8c744fc511252e12daaac39 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 12 Aug 2020 17:31:34 +0300 Subject: [PATCH 076/117] Added ClientRegistrationTemplate --- .../main/data/upgrade/3.1.0/schema_update.sql | 25 ++++ .../server/controller/BaseController.java | 20 +++ .../server/controller/OAuth2Controller.java | 62 +++++++- .../service/security/permission/Resource.java | 1 + .../permission/SysAdminPermissions.java | 1 + .../permission/TenantAdminPermissions.java | 15 +- .../oauth2/OAuth2ConfigTemplateService.java | 31 ++++ .../server/common/data/EntityType.java | 2 +- .../common/data/id/EntityIdFactory.java | 2 + .../OAuth2ClientRegistrationTemplateId.java | 39 +++++ .../data/oauth2/OAuth2ClientRegistration.java | 6 +- .../OAuth2ClientRegistrationTemplate.java | 65 ++++++++ .../server/dao/model/ModelConstants.java | 2 + .../sql/OAuth2ClientRegistrationEntity.java | 2 +- ...Auth2ClientRegistrationTemplateEntity.java | 139 ++++++++++++++++++ .../OAuth2ClientRegistrationTemplateDao.java | 25 ++++ .../OAuth2ConfigTemplateServiceImpl.java | 107 ++++++++++++++ ...paOAuth2ClientRegistrationTemplateDao.java | 53 +++++++ ...2ClientRegistrationTemplateRepository.java | 24 +++ .../resources/sql/schema-entities-hsql.sql | 23 +++ .../main/resources/sql/schema-entities.sql | 23 +++ .../BaseOAuth2ConfigTemplateServiceTest.java | 129 ++++++++++++++++ .../OAuth2ConfigTemplateServiceSqlTest.java | 23 +++ .../resources/sql/hsql/drop-all-tables.sql | 1 + .../resources/sql/psql/drop-all-tables.sql | 1 + .../sql/timescale/drop-all-tables.sql | 1 + 26 files changed, 812 insertions(+), 10 deletions(-) create mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateService.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationTemplateId.java create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationTemplateEntity.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationTemplateDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationTemplateDao.java create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationTemplateRepository.java create mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java create mode 100644 dao/src/test/java/org/thingsboard/server/dao/service/sql/OAuth2ConfigTemplateServiceSqlTest.java diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql index 76c3efcdfa..a0eacbacbd 100644 --- a/application/src/main/data/upgrade/3.1.0/schema_update.sql +++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql @@ -48,4 +48,29 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( custom_url varchar(255), custom_username varchar(255), custom_password varchar(255) +); + +DROP TABLE IF EXISTS oauth2_client_registration_template; + +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, + tenant_id uuid, + 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), + 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, + CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) ); \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 1a2a54b1d1..371b423105 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -51,6 +51,7 @@ import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.DataType; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; @@ -75,6 +76,7 @@ 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; @@ -153,6 +155,9 @@ public abstract class BaseController { @Autowired protected OAuth2Service oAuth2Service; + @Autowired + protected OAuth2ConfigTemplateService oAuth2ConfigTemplateService; + @Autowired protected ComponentDiscoveryService componentDescriptorService; @@ -385,6 +390,9 @@ public abstract class BaseController { case OAUTH2_CLIENT_REGISTRATION: checkOAuth2ClientRegistrationId(new OAuth2ClientRegistrationId(entityId.getId()), operation); return; + case OAUTH2_CLIENT_REGISTRATION_TEMPLATE: + checkOAuth2ClientRegistrationTemplateId(new OAuth2ClientRegistrationTemplateId(entityId.getId()), operation); + return; default: throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); } @@ -549,6 +557,18 @@ public abstract class BaseController { } } + OAuth2ClientRegistrationTemplate checkOAuth2ClientRegistrationTemplateId(OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId, Operation operation) throws ThingsboardException { + try { + validateId(clientRegistrationTemplateId, "Incorrect oAuth2ClientRegistrationTemplateId " + clientRegistrationTemplateId); + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = oAuth2ConfigTemplateService.findClientRegistrationTemplateById(clientRegistrationTemplateId); + checkNotNull(clientRegistrationTemplate); + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, operation, clientRegistrationTemplateId, clientRegistrationTemplate); + return clientRegistrationTemplate; + } catch (Exception e) { + throw handleException(e, false); + } + } + ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { try { log.debug("[{}] Lookup component descriptor", clazz); diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index 9033b17c47..da510fe84e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -26,9 +26,11 @@ import org.thingsboard.server.common.data.audit.ActionType; import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.oauth2.OAuth2Service; @@ -44,8 +46,8 @@ import java.util.List; @RequestMapping("/api") @Slf4j public class OAuth2Controller extends BaseController { - public static final String CLIENT_REGISTRATION_ID = "clientRegistrationId"; - private static final String REGISTRATION_ID = "registrationId"; + private static final String CLIENT_REGISTRATION_ID = "clientRegistrationId"; + private static final String CLIENT_REGISTRATION_TEMPLATE_ID = "clientRegistrationTemplateId"; @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) @ResponseBody @@ -91,6 +93,19 @@ public class OAuth2Controller extends BaseController { } } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") + @RequestMapping(value = "/oauth2/config/template", method = RequestMethod.POST) + @ResponseStatus(value = HttpStatus.OK) + public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(@RequestBody OAuth2ClientRegistrationTemplate clientRegistrationTemplate) throws ThingsboardException { + try { + clientRegistrationTemplate.setTenantId(getCurrentUser().getTenantId()); + checkEntity(clientRegistrationTemplate.getId(), clientRegistrationTemplate, Resource.OAUTH2_CONFIGURATION_TEMPLATE); + return oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); + } catch (Exception e) { + throw handleException(e); + } + } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/oauth2/config/{clientRegistrationId}", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) @@ -116,6 +131,31 @@ public class OAuth2Controller extends BaseController { } } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") + @RequestMapping(value = "/oauth2/config/template/{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 { + OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId = new OAuth2ClientRegistrationTemplateId(toUUID(strClientRegistrationTemplateId)); + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = checkOAuth2ClientRegistrationTemplateId(clientRegistrationTemplateId, Operation.DELETE); + oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplateId); + + logEntityAction(clientRegistrationTemplateId, clientRegistrationTemplate, + null, + ActionType.DELETED, null, strClientRegistrationTemplateId); + + } catch (Exception e) { + + logEntityAction(emptyId(EntityType.OAUTH2_CLIENT_REGISTRATION_TEMPLATE), + null, + null, + ActionType.DELETED, e, strClientRegistrationTemplateId); + + throw handleException(e); + } + } + @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/oauth2/config/isAllowed", method = RequestMethod.GET) @ResponseBody @@ -127,11 +167,25 @@ public class OAuth2Controller extends BaseController { } } + + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/oauth2/config/template", method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public List getClientRegistrationTemplates() throws ThingsboardException { + try { + checkOAuth2ConfigTemplatePermissions(Operation.READ); + return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates(); + } catch (Exception e) { + throw handleException(e); + } + } + private void checkOAuth2ConfigPermissions(Operation operation) throws ThingsboardException { accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION, operation); } - private void checkOAuth2ConfigPermissions(Operation operation, OAuth2ClientRegistration clientRegistration) throws ThingsboardException { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION, operation, clientRegistration.getId(), clientRegistration); + private void checkOAuth2ConfigTemplatePermissions(Operation operation) throws ThingsboardException { + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, operation); } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index 84819dda48..7bae743c22 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -33,6 +33,7 @@ public enum Resource { WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE), WIDGET_TYPE(EntityType.WIDGET_TYPE), OAUTH2_CONFIGURATION(EntityType.OAUTH2_CLIENT_REGISTRATION), + OAUTH2_CONFIGURATION_TEMPLATE(EntityType.OAUTH2_CLIENT_REGISTRATION_TEMPLATE), ; private final EntityType entityType; diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java index f85b6c4906..ba295a064d 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java @@ -40,6 +40,7 @@ public class SysAdminPermissions extends AbstractPermissions { put(Resource.WIDGETS_BUNDLE, systemEntityPermissionChecker); put(Resource.WIDGET_TYPE, systemEntityPermissionChecker); put(Resource.OAUTH2_CONFIGURATION, sysAdminOAuth2ConfigPermissionChecker); + put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, PermissionChecker.allowAllPermissionChecker); } private static final PermissionChecker systemEntityPermissionChecker = new PermissionChecker() { diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index 8476994a4e..0153affa23 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -48,6 +48,7 @@ public class TenantAdminPermissions extends AbstractPermissions { put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); put(Resource.WIDGET_TYPE, widgetsPermissionChecker); put(Resource.OAUTH2_CONFIGURATION, tenantOAuth2ConfigPermissionChecker); + put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, tenantOAuth2ConfigTemplatePermissionChecker); } public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { @@ -108,7 +109,7 @@ public class TenantAdminPermissions extends AbstractPermissions { }; - private final PermissionChecker tenantOAuth2ConfigPermissionChecker = new PermissionChecker() { + private final PermissionChecker tenantOAuth2ConfigPermissionChecker = new PermissionChecker() { @Override public boolean hasPermission(SecurityUser user, Operation operation) { return oAuth2Service.isOAuth2ClientRegistrationAllowed(user.getTenantId()); @@ -122,4 +123,16 @@ public class TenantAdminPermissions extends AbstractPermissions { return hasPermission(user, operation); } }; + + private static final PermissionChecker tenantOAuth2ConfigTemplatePermissionChecker = new PermissionChecker() { + @Override + public boolean hasPermission(SecurityUser user, Operation operation) { + return operation == Operation.READ; + } + + @Override + public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { + return operation == Operation.READ; + } + }; } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateService.java new file mode 100644 index 0000000000..b3718a8d92 --- /dev/null +++ b/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 findAllClientRegistrationTemplates(); + + void deleteClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId); +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java index d3ea208aa5..7791597141 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/EntityType.java @@ -19,5 +19,5 @@ package org.thingsboard.server.common.data; * @author Andrew Shvayka */ public enum EntityType { - TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, OAUTH2_CLIENT_REGISTRATION + TENANT, CUSTOMER, USER, DASHBOARD, ASSET, DEVICE, ALARM, RULE_CHAIN, RULE_NODE, ENTITY_VIEW, WIDGETS_BUNDLE, WIDGET_TYPE, OAUTH2_CLIENT_REGISTRATION, OAUTH2_CLIENT_REGISTRATION_TEMPLATE } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index 03f05c54a4..41d9a929f2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -64,6 +64,8 @@ public class EntityIdFactory { return new WidgetTypeId(uuid); case OAUTH2_CLIENT_REGISTRATION: return new OAuth2ClientRegistrationId(uuid); + case OAUTH2_CLIENT_REGISTRATION_TEMPLATE: + return new OAuth2ClientRegistrationTemplateId(uuid); } throw new IllegalArgumentException("EntityType " + type + " is not supported!"); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationTemplateId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationTemplateId.java new file mode 100644 index 0000000000..03edd5054b --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationTemplateId.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.id; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.thingsboard.server.common.data.EntityType; + +import java.util.UUID; + +public class OAuth2ClientRegistrationTemplateId extends UUIDBased implements EntityId { + + @JsonCreator + public OAuth2ClientRegistrationTemplateId(@JsonProperty("id") UUID id) { + super(id); + } + + public static OAuth2ClientRegistrationTemplateId fromString(String clientRegistrationTemplateId) { + return new OAuth2ClientRegistrationTemplateId(UUID.fromString(clientRegistrationTemplateId)); + } + + @Override + public EntityType getEntityType() { + return EntityType.OAUTH2_CLIENT_REGISTRATION_TEMPLATE; + } +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java index 1da7942b17..1e7ff2a1e7 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -49,9 +49,9 @@ public class OAuth2ClientRegistration extends BaseData implements HasTenantId, HasName { + + private TenantId tenantId; + private String providerId; + private OAuth2BasicMapperConfig basic; + private String authorizationUri; + private String accessTokenUri; + private List scope; + private String userInfoUri; + private String userNameAttributeName; + private String jwkSetUri; + private String clientAuthenticationMethod; + + public OAuth2ClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { + super(clientRegistrationTemplate); + this.tenantId = clientRegistrationTemplate.tenantId; + this.providerId = clientRegistrationTemplate.providerId; + 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; + } + + @Override + public String getName() { + return providerId; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 84a1973e20..4015473d9f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -359,6 +359,8 @@ public class ModelConstants { */ public static final String OAUTH2_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; public static final String OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_client_registration"; + public static final String OAUTH2_CLIENT_REGISTRATION_TEMPLATE_COLUMN_FAMILY_NAME = "oauth2_client_registration_template"; + public static final String OAUTH2_TEMPLATE_PROVIDER_ID_PROPERTY = "provider_id"; public static final String OAUTH2_DOMAIN_NAME_PROPERTY = "domain_name"; public static final String OAUTH2_CLIENT_ID_PROPERTY = "client_id"; public static final String OAUTH2_CLIENT_SECRET_PROPERTY = "client_secret"; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java index cc494cd707..db05b02a05 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java @@ -151,8 +151,8 @@ public class OAuth2ClientRegistrationEntity extends BaseSqlEntity { + + @Column(name = ModelConstants.OAUTH2_TENANT_ID_PROPERTY, columnDefinition = "uuid") + private UUID tenantId; + + @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; + @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; + + public OAuth2ClientRegistrationTemplateEntity() { + } + + public OAuth2ClientRegistrationTemplateEntity(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { + if (clientRegistrationTemplate.getId() != null) { + this.setUuid(clientRegistrationTemplate.getId().getId()); + } + if (clientRegistrationTemplate.getTenantId() != null) { + this.tenantId = clientRegistrationTemplate.getTenantId().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(); + 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.setTenantId(new TenantId(tenantId)); + clientRegistrationTemplate.setCreatedTime(createdTime); + + 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); + return clientRegistrationTemplate; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationTemplateDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationTemplateDao.java new file mode 100644 index 0000000000..75fcbf1b10 --- /dev/null +++ b/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 { + List findAll(); +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java new file mode 100644 index 0000000000..afe4d845e6 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java @@ -0,0 +1,107 @@ +/** + * 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, OAuth2ClientRegistrationTemplate::getTenantId); + OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate; + try { + savedClientRegistrationTemplate = clientRegistrationTemplateDao.save(clientRegistrationTemplate.getTenantId(), 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 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 DataValidator clientRegistrationTemplateValidator = + new DataValidator() { + + @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!"); + } + if (clientRegistrationTemplate.getTenantId() == null + || !TenantId.SYS_TENANT_ID.equals(clientRegistrationTemplate.getTenantId())) { + throw new DataValidationException("Client registration template should be assigned to system admin!"); + } + } + }; +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationTemplateDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationTemplateDao.java new file mode 100644 index 0000000000..70c3c03479 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationTemplateDao.java @@ -0,0 +1,53 @@ +/** + * 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.OAuth2ClientRegistrationTemplate; +import org.thingsboard.server.dao.DaoUtil; +import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationTemplateEntity; +import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationTemplateDao; +import org.thingsboard.server.dao.sql.JpaAbstractDao; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +@Component +@RequiredArgsConstructor +public class JpaOAuth2ClientRegistrationTemplateDao extends JpaAbstractDao implements OAuth2ClientRegistrationTemplateDao { + private final OAuth2ClientRegistrationTemplateRepository repository; + + @Override + protected Class getEntityClass() { + return OAuth2ClientRegistrationTemplateEntity.class; + } + + @Override + protected CrudRepository getCrudRepository() { + return repository; + } + + @Override + public List findAll() { + Iterable entities = repository.findAll(); + List result = new ArrayList<>(); + entities.forEach(entity -> result.add(DaoUtil.getData(entity))); + return result; + } +} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationTemplateRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationTemplateRepository.java new file mode 100644 index 0000000000..8ffd380496 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationTemplateRepository.java @@ -0,0 +1,24 @@ +/** + * 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 org.springframework.data.repository.CrudRepository; +import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationTemplateEntity; + +import java.util.UUID; + +public interface OAuth2ClientRegistrationTemplateRepository extends CrudRepository { +} diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 711ab8037e..cb05bf800c 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -324,3 +324,26 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( custom_username varchar(255), custom_password varchar(255) ); + +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, + tenant_id uuid, + 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), + 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, + CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) +); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 84bfaea563..65d1138b99 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -340,6 +340,29 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( custom_password varchar(255) ); +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, + tenant_id uuid, + 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), + 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, + CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) +); + CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) LANGUAGE plpgsql AS $$ diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java new file mode 100644 index 0000000000..824e992c21 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java @@ -0,0 +1,129 @@ +/** + * 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.service; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; +import org.thingsboard.server.dao.exception.DataValidationException; +import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; + +import java.util.Arrays; +import java.util.UUID; + +public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { + + @Autowired + protected OAuth2ConfigTemplateService oAuth2ConfigTemplateService; + + private TenantId tenantId; + + @Before + public void beforeRun() throws Exception { + Assert.assertTrue(oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().isEmpty()); + } + + @After + public void after() throws Exception { + oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().forEach(clientRegistrationTemplate -> { + oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplate.getId()); + }); + + Assert.assertTrue(oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().isEmpty()); + } + + + @Test(expected = DataValidationException.class) + public void testSaveDuplicateProviderId() { + OAuth2ClientRegistrationTemplate first = validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, "providerId"); + OAuth2ClientRegistrationTemplate second = validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, "providerId"); + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(first); + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(second); + } + + @Test + public void testCreateNewTemplate() { + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString()); + OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); + + Assert.assertNotNull(savedClientRegistrationTemplate); + Assert.assertNotNull(savedClientRegistrationTemplate.getId()); + clientRegistrationTemplate.setId(savedClientRegistrationTemplate.getId()); + clientRegistrationTemplate.setCreatedTime(savedClientRegistrationTemplate.getCreatedTime()); + Assert.assertEquals(clientRegistrationTemplate, savedClientRegistrationTemplate); + } + + @Test + public void testFindTemplate() { + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString()); + OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); + + OAuth2ClientRegistrationTemplate foundClientRegistrationTemplate = oAuth2ConfigTemplateService.findClientRegistrationTemplateById(savedClientRegistrationTemplate.getId()); + Assert.assertEquals(savedClientRegistrationTemplate, foundClientRegistrationTemplate); + } + + @Test + public void testFindAll() { + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString())); + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString())); + + Assert.assertEquals(2, oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().size()); + } + + @Test + public void testDeleteTemplate() { + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString())); + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString())); + OAuth2ClientRegistrationTemplate saved = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString())); + + Assert.assertEquals(3, oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().size()); + Assert.assertNotNull(oAuth2ConfigTemplateService.findClientRegistrationTemplateById(saved.getId())); + + oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(saved.getId()); + + Assert.assertEquals(2, oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().size()); + Assert.assertNull(oAuth2ConfigTemplateService.findClientRegistrationTemplateById(saved.getId())); + } + + private OAuth2ClientRegistrationTemplate validClientRegistrationTemplate(TenantId tenantId, String providerId) { + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = new OAuth2ClientRegistrationTemplate(); + clientRegistrationTemplate.setProviderId(providerId); + clientRegistrationTemplate.setTenantId(tenantId); + clientRegistrationTemplate.setBasic( + OAuth2BasicMapperConfig.builder() + .firstNameAttributeKey("firstName") + .lastNameAttributeKey("lastName") + .emailAttributeKey("email") + .tenantNamePattern("tenant") + .defaultDashboardName("Test") + .alwaysFullScreen(true) + .build() + ); + clientRegistrationTemplate.setAuthorizationUri("authorizationUri"); + clientRegistrationTemplate.setAccessTokenUri("tokenUri"); + clientRegistrationTemplate.setScope(Arrays.asList("scope1", "scope2")); + clientRegistrationTemplate.setUserInfoUri("userInfoUri"); + clientRegistrationTemplate.setUserNameAttributeName("userNameAttributeName"); + clientRegistrationTemplate.setJwkSetUri("jwkSetUri"); + clientRegistrationTemplate.setClientAuthenticationMethod("clientAuthenticationMethod"); + return clientRegistrationTemplate; + } +} diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/sql/OAuth2ConfigTemplateServiceSqlTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/sql/OAuth2ConfigTemplateServiceSqlTest.java new file mode 100644 index 0000000000..53fd2e37b4 --- /dev/null +++ b/dao/src/test/java/org/thingsboard/server/dao/service/sql/OAuth2ConfigTemplateServiceSqlTest.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.service.sql; + +import org.thingsboard.server.dao.service.BaseOAuth2ConfigTemplateServiceTest; +import org.thingsboard.server.dao.service.DaoSqlTest; + +@DaoSqlTest +public class OAuth2ConfigTemplateServiceSqlTest extends BaseOAuth2ConfigTemplateServiceTest { +} diff --git a/dao/src/test/resources/sql/hsql/drop-all-tables.sql b/dao/src/test/resources/sql/hsql/drop-all-tables.sql index e55c9bfcd5..432814cb04 100644 --- a/dao/src/test/resources/sql/hsql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/hsql/drop-all-tables.sql @@ -21,4 +21,5 @@ DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS entity_view; DROP TABLE IF EXISTS oauth2_client_registration; +DROP TABLE IF EXISTS oauth2_client_registration_template; DROP FUNCTION IF EXISTS to_uuid; diff --git a/dao/src/test/resources/sql/psql/drop-all-tables.sql b/dao/src/test/resources/sql/psql/drop-all-tables.sql index 259a3e9fed..f7ce197023 100644 --- a/dao/src/test/resources/sql/psql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/psql/drop-all-tables.sql @@ -22,4 +22,5 @@ DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS entity_view; DROP TABLE IF EXISTS oauth2_client_registration; +DROP TABLE IF EXISTS oauth2_client_registration_template; DROP TABLE IF EXISTS tb_schema_settings; \ No newline at end of file diff --git a/dao/src/test/resources/sql/timescale/drop-all-tables.sql b/dao/src/test/resources/sql/timescale/drop-all-tables.sql index 259a3e9fed..f7ce197023 100644 --- a/dao/src/test/resources/sql/timescale/drop-all-tables.sql +++ b/dao/src/test/resources/sql/timescale/drop-all-tables.sql @@ -22,4 +22,5 @@ DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS entity_view; DROP TABLE IF EXISTS oauth2_client_registration; +DROP TABLE IF EXISTS oauth2_client_registration_template; DROP TABLE IF EXISTS tb_schema_settings; \ No newline at end of file From da9f29c9333d859bda350fed7a3949ed1e1bfcb6 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 13 Aug 2020 11:48:14 +0300 Subject: [PATCH 077/117] Added 'comment' to ClientRegistrationTemplate --- application/src/main/data/upgrade/3.1.0/schema_update.sql | 1 + .../common/data/oauth2/OAuth2ClientRegistrationTemplate.java | 2 ++ .../java/org/thingsboard/server/dao/model/ModelConstants.java | 1 + .../dao/model/sql/OAuth2ClientRegistrationTemplateEntity.java | 4 ++++ dao/src/main/resources/sql/schema-entities-hsql.sql | 1 + dao/src/main/resources/sql/schema-entities.sql | 1 + .../dao/service/BaseOAuth2ConfigTemplateServiceTest.java | 1 + 7 files changed, 11 insertions(+) diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql index a0eacbacbd..445df2b97e 100644 --- a/application/src/main/data/upgrade/3.1.0/schema_update.sql +++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql @@ -72,5 +72,6 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( basic_customer_name_pattern varchar(255), basic_default_dashboard_name varchar(255), basic_always_full_screen boolean, + comment varchar, CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) ); \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java index 4e0d0c0e65..f3cb077e78 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java @@ -43,6 +43,7 @@ public class OAuth2ClientRegistrationTemplate extends BaseData Date: Thu, 13 Aug 2020 14:14:38 +0300 Subject: [PATCH 078/117] Added logic to send 'access token' to custom mapper --- .../main/data/upgrade/3.1.0/schema_update.sql | 3 ++- .../auth/oauth2/BasicOAuth2ClientMapper.java | 2 +- .../auth/oauth2/CustomOAuth2ClientMapper.java | 11 ++++++++--- .../security/auth/oauth2/OAuth2ClientMapper.java | 2 +- .../Oauth2AuthenticationSuccessHandler.java | 16 +++++++++++----- .../data/oauth2/OAuth2CustomMapperConfig.java | 1 + .../server/dao/model/ModelConstants.java | 1 + .../sql/OAuth2ClientRegistrationEntity.java | 4 ++++ .../server/dao/oauth2/OAuth2ServiceImpl.java | 3 --- .../main/resources/sql/schema-entities-hsql.sql | 3 ++- dao/src/main/resources/sql/schema-entities.sql | 3 ++- 11 files changed, 33 insertions(+), 16 deletions(-) diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql index 445df2b97e..639fb09432 100644 --- a/application/src/main/data/upgrade/3.1.0/schema_update.sql +++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql @@ -47,7 +47,8 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( basic_always_full_screen boolean, custom_url varchar(255), custom_username varchar(255), - custom_password varchar(255) + custom_password varchar(255), + custom_send_token boolean ); DROP TABLE IF EXISTS oauth2_client_registration_template; diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java index 8c7c4d9bb4..5bee8e866e 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java @@ -35,7 +35,7 @@ public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implemen private static final String END_PLACEHOLDER_PREFIX = "}"; @Override - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, TenantId parentTenantId, OAuth2MapperConfig config) { + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, TenantId parentTenantId, OAuth2MapperConfig config) { OAuth2User oauth2User = new OAuth2User(); Map attributes = token.getPrincipal().getAttributes(); String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java index 0dce6f1e98..21c60b943b 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java @@ -32,21 +32,26 @@ 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, TenantId parentTenantId, OAuth2MapperConfig config) { - OAuth2User oauth2User = getOAuth2User(token, config.getCustom()); + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, TenantId parentTenantId, OAuth2MapperConfig config) { + OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom()); return getOrCreateSecurityUserFromOAuth2User(parentTenantId, oauth2User, config.isAllowUserCreation(), config.isActivateUser()); } - private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, OAuth2CustomMapperConfig 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 { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java index 6490322b0b..cdd0313f51 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java @@ -21,5 +21,5 @@ import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.service.security.model.SecurityUser; public interface OAuth2ClientMapper { - SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, TenantId parentTenantId, OAuth2MapperConfig config); + SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, TenantId parentTenantId, OAuth2MapperConfig config); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 9274e08939..7c0097fbfb 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -15,14 +15,13 @@ */ package org.thingsboard.server.service.security.auth.oauth2; -import org.apache.commons.lang3.tuple.Pair; 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.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; @@ -45,16 +44,19 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS private final RefreshTokenRepository refreshTokenRepository; private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; private final OAuth2Service oAuth2Service; + private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; @Autowired public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory, final RefreshTokenRepository refreshTokenRepository, final OAuth2ClientMapperProvider oauth2ClientMapperProvider, - final OAuth2Service oAuth2Service) { + final OAuth2Service oAuth2Service, + final OAuth2AuthorizedClientService oAuth2AuthorizedClientService) { this.tokenFactory = tokenFactory; this.refreshTokenRepository = refreshTokenRepository; this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; this.oAuth2Service = oAuth2Service; + this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; } @Override @@ -67,8 +69,12 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; OAuth2ClientRegistration clientRegistration = oAuth2Service.findClientRegistration(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, clientRegistration.getTenantId(), clientRegistration.getMapperConfig()); + SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(), + clientRegistration.getTenantId(), clientRegistration.getMapperConfig()); JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java index 8ac72e9d8a..cead19c39c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.java @@ -25,4 +25,5 @@ public class OAuth2CustomMapperConfig { private final String url; private final String username; private final String password; + private final boolean sendToken; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 8042d13927..1bd80240bc 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -388,6 +388,7 @@ public class ModelConstants { 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"; /** diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java index db05b02a05..4e99dbbb5f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java @@ -95,6 +95,8 @@ public class OAuth2ClientRegistrationEntity extends BaseSqlEntity Date: Thu, 13 Aug 2020 18:21:26 +0300 Subject: [PATCH 079/117] Added new fields to ClientRegistrationTemplate --- .../main/data/upgrade/3.1.0/schema_update.sql | 3 ++ .../OAuth2ClientRegistrationTemplate.java | 29 ++++++++++++++++--- .../server/dao/model/ModelConstants.java | 3 ++ ...Auth2ClientRegistrationTemplateEntity.java | 16 ++++++++++ .../resources/sql/schema-entities-hsql.sql | 3 ++ .../main/resources/sql/schema-entities.sql | 3 ++ .../BaseOAuth2ConfigTemplateServiceTest.java | 2 ++ 7 files changed, 55 insertions(+), 4 deletions(-) diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql index 639fb09432..2f5f3ca41a 100644 --- a/application/src/main/data/upgrade/3.1.0/schema_update.sql +++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql @@ -56,6 +56,7 @@ DROP TABLE IF EXISTS oauth2_client_registration_template; 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, tenant_id uuid, provider_id varchar(255), authorization_uri varchar(255), @@ -74,5 +75,7 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( basic_default_dashboard_name varchar(255), basic_always_full_screen boolean, comment varchar, + icon varchar(255), + help_link varchar(255), CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) ); \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java index f3cb077e78..7f16940e7f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java @@ -15,13 +15,13 @@ */ package org.thingsboard.server.common.data.oauth2; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.NoArgsConstructor; -import lombok.ToString; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.*; import org.thingsboard.server.common.data.BaseData; import org.thingsboard.server.common.data.HasName; import org.thingsboard.server.common.data.HasTenantId; +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; import org.thingsboard.server.common.data.id.TenantId; @@ -44,6 +44,16 @@ public class OAuth2ClientRegistrationTemplate extends BaseData additionalInfo, () -> additionalInfoBytes); + } + + public void setAdditionalInfo(JsonNode addInfo) { + SearchTextBasedWithAdditionalInfo.setJson(addInfo, json -> this.additionalInfo = json, bytes -> this.additionalInfoBytes = bytes); } @Override diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 1bd80240bc..e1b1907ae1 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -390,6 +390,9 @@ public class ModelConstants { 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_TEMPLATE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; + public static final String OAUTH2_TEMPLATE_ICON_PROPERTY = "icon"; + public static final String OAUTH2_TEMPLATE_HELP_LINK_PROPERTY = "help_link"; /** * Cassandra attributes and timeseries constants. diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationTemplateEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationTemplateEntity.java index 1c32efd625..40d1a02552 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationTemplateEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationTemplateEntity.java @@ -15,8 +15,10 @@ */ 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.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; @@ -76,6 +78,14 @@ public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity Date: Fri, 14 Aug 2020 13:13:28 +0300 Subject: [PATCH 080/117] Load 'github' and 'google' templates on install --- .../github_config.json | 14 +++++++++++ .../google_config.json | 20 +++++++++++++++ .../install/ThingsboardInstallService.java | 3 +++ .../DefaultSystemDataLoaderService.java | 5 ++++ .../service/install/InstallScripts.java | 25 +++++++++++++++++++ .../install/SystemDataLoaderService.java | 2 ++ 6 files changed, 69 insertions(+) create mode 100644 application/src/main/data/json/system/oauth2_config_templates/github_config.json create mode 100644 application/src/main/data/json/system/oauth2_config_templates/google_config.json diff --git a/application/src/main/data/json/system/oauth2_config_templates/github_config.json b/application/src/main/data/json/system/oauth2_config_templates/github_config.json new file mode 100644 index 0000000000..17dc1fda4a --- /dev/null +++ b/application/src/main/data/json/system/oauth2_config_templates/github_config.json @@ -0,0 +1,14 @@ +{ + "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": {}, + "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 Github Documentation", + "icon": "mdi:github", + "helpLink": "https://docs.github.com/en/developers/apps/creating-an-oauth-app" +} \ No newline at end of file diff --git a/application/src/main/data/json/system/oauth2_config_templates/google_config.json b/application/src/main/data/json/system/oauth2_config_templates/google_config.json new file mode 100644 index 0000000000..99053b68e6 --- /dev/null +++ b/application/src/main/data/json/system/oauth2_config_templates/google_config.json @@ -0,0 +1,20 @@ +{ + "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, + "icon": "mdi:google", + "helpLink": "https://developers.google.com/adwords/api/docs/guides/authentication" +} \ No newline at end of file diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 1b1355571d..7059e0fd63 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -176,6 +176,8 @@ public class ThingsboardInstallService { case "3.1.0": log.info("Upgrading ThingsBoard from version 3.1.0 to 3.2.0 ..."); databaseEntitiesUpgradeService.upgradeDatabase("3.1.0"); + log.info("Updating system data..."); + systemDataLoaderService.createOAuth2Templates(); break; default: throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion); @@ -207,6 +209,7 @@ public class ThingsboardInstallService { systemDataLoaderService.createSysAdmin(); systemDataLoaderService.createAdminSettings(); systemDataLoaderService.loadSystemWidgets(); + systemDataLoaderService.createOAuth2Templates(); // systemDataLoaderService.loadSystemPlugins(); // systemDataLoaderService.loadSystemRules(); diff --git a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java index 6aa3e0cd2d..dcb5ffccde 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java @@ -136,6 +136,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(); diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 5181834b80..1b5827b6f7 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/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; @@ -60,6 +62,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"; @@ -79,6 +82,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); } @@ -209,4 +215,23 @@ 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 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); + clientRegistrationTemplate.setTenantId(TenantId.SYS_TENANT_ID); + 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); + } + } + ); + } + } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java index 76e65deaa4..4742d30277 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SystemDataLoaderService.java @@ -21,6 +21,8 @@ public interface SystemDataLoaderService { void createAdminSettings() throws Exception; + void createOAuth2Templates() throws Exception; + void loadSystemWidgets() throws Exception; void updateSystemWidgets() throws Exception; From 3109e416709b737c336b07edca12c58f19e94cf8 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 14 Aug 2020 14:26:04 +0300 Subject: [PATCH 081/117] Added 'facebook' template --- .../facebook_config.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 application/src/main/data/json/system/oauth2_config_templates/facebook_config.json diff --git a/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json b/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json new file mode 100644 index 0000000000..1c81a443f3 --- /dev/null +++ b/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json @@ -0,0 +1,19 @@ +{ + "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, + "icon": "mdi:facebook", + "helpLink": "https://developers.facebook.com/docs/facebook-login/web#logindialog" +} \ No newline at end of file From 2cd5b646e3acfb031ff25de0b58d75a72a3ca857 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Mon, 31 Aug 2020 10:43:15 +0300 Subject: [PATCH 082/117] Fixed merge --- .../main/data/upgrade/3.1.0/schema_update.sql | 66 --------------- .../main/data/upgrade/3.1.1/schema_update.sql | 81 +++++++++++++++++++ .../install/ThingsboardInstallService.java | 6 +- .../install/SqlDatabaseUpgradeService.java | 11 +-- .../dao/settings/AdminSettingsService.java | 2 - .../settings/AdminSettingsServiceImpl.java | 9 --- 6 files changed, 90 insertions(+), 85 deletions(-) create mode 100644 application/src/main/data/upgrade/3.1.1/schema_update.sql diff --git a/application/src/main/data/upgrade/3.1.0/schema_update.sql b/application/src/main/data/upgrade/3.1.0/schema_update.sql index ef313a6000..e3b2cfb9a4 100644 --- a/application/src/main/data/upgrade/3.1.0/schema_update.sql +++ b/application/src/main/data/upgrade/3.1.0/schema_update.sql @@ -14,70 +14,4 @@ -- limitations under the License. -- -DROP TABLE IF EXISTS oauth2_client_registration; - -CREATE TABLE IF NOT EXISTS oauth2_client_registration ( - id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, - created_time bigint NOT NULL, - additional_info varchar, - tenant_id uuid, - domain_name varchar(255), - client_id varchar(255), - client_secret varchar(255), - authorization_uri varchar(255), - token_uri varchar(255), - redirect_uri_template 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 -); - -DROP TABLE IF EXISTS oauth2_client_registration_template; - -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, - tenant_id uuid, - 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), - 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, - icon varchar(255), - help_link varchar(255), - CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) -); - CREATE INDEX IF NOT EXISTS idx_alarm_tenant_alarm_type_created_time ON alarm(tenant_id, type, created_time DESC); diff --git a/application/src/main/data/upgrade/3.1.1/schema_update.sql b/application/src/main/data/upgrade/3.1.1/schema_update.sql new file mode 100644 index 0000000000..58c4ff5b67 --- /dev/null +++ b/application/src/main/data/upgrade/3.1.1/schema_update.sql @@ -0,0 +1,81 @@ +-- +-- 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. +-- + +DROP TABLE IF EXISTS oauth2_client_registration; + +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( + id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, + created_time bigint NOT NULL, + additional_info varchar, + tenant_id uuid, + domain_name varchar(255), + client_id varchar(255), + client_secret varchar(255), + authorization_uri varchar(255), + token_uri varchar(255), + redirect_uri_template 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 +); + +DROP TABLE IF EXISTS oauth2_client_registration_template; + +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, + tenant_id uuid, + 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), + 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, + icon varchar(255), + help_link varchar(255), + CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) +); diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index 4d90360d6d..205602fca3 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -177,9 +177,9 @@ public class ThingsboardInstallService { databaseEntitiesUpgradeService.upgradeDatabase("3.1.0"); log.info("Updating system data..."); systemDataLoaderService.updateSystemWidgets(); - case "3.1.0": - log.info("Upgrading ThingsBoard from version 3.1.0 to 3.2.0 ..."); - databaseEntitiesUpgradeService.upgradeDatabase("3.1.0"); + case "3.1.1": + log.info("Upgrading ThingsBoard from version 3.1.1 to 3.2.0 ..."); + databaseEntitiesUpgradeService.upgradeDatabase("3.1.1"); log.info("Updating system data..."); systemDataLoaderService.createOAuth2Templates(); break; diff --git a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java index 8ffde0f256..12fb354846 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java +++ b/application/src/main/java/org/thingsboard/server/service/install/SqlDatabaseUpgradeService.java @@ -294,22 +294,23 @@ public class SqlDatabaseUpgradeService implements DatabaseEntitiesUpgradeService } catch (Exception e) { log.error("Failed updating schema!!!", e); } + break; case "3.1.0": try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.0", "schema_update.sql"); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.0", SCHEMA_UPDATE_SQL); loadSql(schemaUpdateFile, conn); log.info("Schema updated."); - } catch (Exception e) { - log.error("Failed updating schema!!!", e); } break; - case "3.1.0": + case "3.1.1": try (Connection conn = DriverManager.getConnection(dbUrl, dbUserName, dbPassword)) { log.info("Updating schema ..."); - schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.0", SCHEMA_UPDATE_SQL); + schemaUpdateFile = Paths.get(installScripts.getDataDir(), "upgrade", "3.1.1", "schema_update.sql"); loadSql(schemaUpdateFile, conn); log.info("Schema updated."); + } catch (Exception e) { + log.error("Failed updating schema!!!", e); } break; default: diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java index 58b25f2dee..a33b0c095f 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java @@ -25,8 +25,6 @@ public interface AdminSettingsService { AdminSettings findAdminSettingsByKey(TenantId tenantId, String key); - void deleteAdminSettingsByKey(TenantId tenantId, String key); - AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java index 563130bd12..ec57567a1a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsServiceImpl.java @@ -48,15 +48,6 @@ public class AdminSettingsServiceImpl implements AdminSettingsService { return adminSettingsDao.findByKey(tenantId, key); } - @Override - public void deleteAdminSettingsByKey(TenantId tenantId, String key) { - log.trace("Executing deleteAdminSettingsByKey [{}]", key); - AdminSettings adminSettings = findAdminSettingsByKey(tenantId, key); - if (adminSettings != null) { - adminSettingsDao.removeById(tenantId, adminSettings.getId().getId()); - } - } - @Override public AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings) { log.trace("Executing saveAdminSettings [{}]", adminSettings); From edcaebe9ac409fb8f3f036cff3d74a9bd17e9f51 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 7 Sep 2020 17:19:12 +0300 Subject: [PATCH 083/117] Refactoring layout oauth2 templated --- ui-ngx/src/app/core/http/admin.service.ts | 6 +- .../modules/home/pages/admin/admin.module.ts | 4 +- .../edit-redirect-uri-panel.component.html | 49 ++ .../edit-redirect-uri-panel.component.scss | 18 + .../edit-redirect-uri-panel.component.ts | 76 +++ .../admin/oauth2-settings.component.html | 570 +++++++++--------- .../admin/oauth2-settings.component.scss | 1 + .../pages/admin/oauth2-settings.component.ts | 112 +++- .../src/app/shared/models/settings.models.ts | 5 +- .../assets/locale/locale.constant-en_US.json | 2 +- 10 files changed, 529 insertions(+), 314 deletions(-) create mode 100644 ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.html create mode 100644 ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.scss create mode 100644 ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.ts diff --git a/ui-ngx/src/app/core/http/admin.service.ts b/ui-ngx/src/app/core/http/admin.service.ts index ddc2069d4a..1fb6f717c3 100644 --- a/ui-ngx/src/app/core/http/admin.service.ts +++ b/ui-ngx/src/app/core/http/admin.service.ts @@ -63,8 +63,12 @@ export class AdminService { return this.http.get(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config)); } + public getOAuth2Template(config?: RequestConfig): Observable { + return this.http.get(`/api/oauth2/config/template`, defaultHttpOptionsFromConfig(config)); + } + public saveOAuth2Settings(OAuth2Setting: OAuth2Settings, - config?: RequestConfig): Observable { + config?: RequestConfig): Observable { return this.http.post('/api/oauth2/config', OAuth2Setting, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts index 73fef6a32f..189550d1c2 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin.module.ts @@ -24,6 +24,7 @@ import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-sett import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; import { HomeComponentsModule } from '@modules/home/components/home-components.module'; import { OAuth2SettingsComponent } from '@modules/home/pages/admin/oauth2-settings.component'; +import { EditRedirectUriPanelComponent } from './edit-redirect-uri-panel.component'; @NgModule({ declarations: @@ -31,7 +32,8 @@ import { OAuth2SettingsComponent } from '@modules/home/pages/admin/oauth2-settin GeneralSettingsComponent, MailServerComponent, SecuritySettingsComponent, - OAuth2SettingsComponent + OAuth2SettingsComponent, + EditRedirectUriPanelComponent ], imports: [ CommonModule, diff --git a/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.html b/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.html new file mode 100644 index 0000000000..6f4efc061c --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.html @@ -0,0 +1,49 @@ + +
+
+ + admin.oauth2.redirect-uri-template + + + {{ 'admin.oauth2.redirect-uri-template-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + +
+
+ + + +
+
diff --git a/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.scss b/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.scss new file mode 100644 index 0000000000..43b16cf165 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.scss @@ -0,0 +1,18 @@ +/** + * 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. + */ +:host { + background-color: #f9f9f9; +} diff --git a/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.ts b/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.ts new file mode 100644 index 0000000000..44f6bff0f3 --- /dev/null +++ b/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.ts @@ -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. +/// + +import { Component, Inject, InjectionToken, OnInit, SkipSelf } from '@angular/core'; +import { ErrorStateMatcher } from '@angular/material/core'; +import { Store } from '@ngrx/store'; +import { AppState } from '@core/core.state'; +import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; +import { PageComponent } from '@shared/components/page.component'; +import { OverlayRef } from '@angular/cdk/overlay'; + +export const EDIT_REDIRECT_URI_PANEL_DATA = new InjectionToken('EditRedirectUriPanelData'); + +export interface EditRedirectUriPanelData { + redirectURI: any; +} + +@Component({ + selector: 'tb-edit-redirect-uri-panel', + templateUrl: './edit-redirect-uri-panel.component.html', + providers: [{provide: ErrorStateMatcher, useExisting: EditRedirectUriPanelComponent}], + styleUrls: ['./edit-redirect-uri-panel.component.scss'] +}) +export class EditRedirectUriPanelComponent extends PageComponent implements OnInit, ErrorStateMatcher { + + private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/; + + redirectURIFormGroup: FormGroup; + + result: any = null; + + submitted = false; + + constructor(protected store: Store, + @Inject(EDIT_REDIRECT_URI_PANEL_DATA) public data: EditRedirectUriPanelData, + @SkipSelf() private errorStateMatcher: ErrorStateMatcher, + public overlayRef: OverlayRef, + public fb: FormBuilder) { + super(store); + } + + ngOnInit(): void { + this.redirectURIFormGroup = this.fb.group({ + value: [this.data.redirectURI, [Validators.required, Validators.pattern(this.URL_REGEXP)]] + }); + } + + isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { + const originalErrorState = this.errorStateMatcher.isErrorState(control, form); + const customErrorState = !!(control && control.invalid && this.submitted); + return originalErrorState || customErrorState; + } + + cancel(): void { + this.overlayRef.dispose(); + } + + update(): void { + this.submitted = true; + this.result = this.redirectURIFormGroup.get('value').value; + this.overlayRef.dispose(); + } +} diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index f1c3bfaf62..bb77210ba8 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -40,300 +40,316 @@ {{ domain.get('domainName').value ? domain.get('domainName').value : ("admin.new-domain" | translate) }} - + + - - admin.domain-name - - - {{ 'admin.error-verification-url' | translate }} - - - {{ 'admin.domain-name-unique' | translate }} - - - - - admin.oauth2.redirect-uri-template - - - {{ 'admin.oauth2.redirect-uri-template-required' | translate }} - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - - - -
- -
-
- -
- - admin.oauth2.registration-id - - - {{ 'admin.oauth2.registration-id-required' | translate }} - - - {{ 'admin.oauth2.registration-id-unique' | translate }} - - - -
- - admin.oauth2.client-id - - - {{ 'admin.oauth2.client-id-required' | translate }} - - - - - admin.oauth2.client-secret - - - {{ 'admin.oauth2.client-secret-required' | translate }} - - -
- -
- - admin.oauth2.access-token-uri - - - {{ 'admin.oauth2.access-token-uri-required' | translate }} - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - - - - admin.oauth2.authorization-uri - - - {{ 'admin.oauth2.authorization-uri-required' | translate }} - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - -
- - - admin.oauth2.scope - - - {{scope}} - cancel - - - - - {{ 'admin.oauth2.jwk-set-uri-required' | translate }} - - - -
- - admin.oauth2.jwk-set-uri - - - {{ 'admin.oauth2.jwk-set-uri-required' | translate }} - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - - - - admin.oauth2.user-info-uri - - - {{ 'admin.oauth2.user-info-uri-required' | translate }} - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - -
- - - admin.oauth2.client-authentication-method - - - {{ clientAuthenticationMethod | uppercase }} - - - - - - admin.oauth2.user-name-attribute-name - - - {{ 'admin.oauth2.user-name-attribute-name-required' | translate }} - - - -
-
- - {{ 'admin.oauth2.allow-user-creation' | translate }} - - - {{ 'admin.oauth2.activate-user' | translate }} - -
- - - admin.oauth2.type - - - {{ converterTypeExternalUser }} - - - - -
- - admin.oauth2.email-attribute-key - - - {{ 'admin.oauth2.email-attribute-key-required' | translate }} - + + + admin.domain-name + + + {{ 'admin.error-verification-url' | translate }} + + + {{ 'admin.domain-name-unique' | translate }} + + + + +
+ + + + {{ registration.get('providerName').value }} + + + + + + + +
+ + Login Provider + + + {{ provider | uppercase }} + +
- admin.oauth2.first-name-attribute-key - - - - - admin.oauth2.last-name-attribute-key - - -
- -
- - admin.oauth2.tenant-name-strategy - - - {{ tenantNameStrategy }} - - - - - - admin.oauth2.tenant-name-pattern - - - {{ 'admin.oauth2.tenant-name-pattern-required' | translate }} + admin.oauth2.client-id + + + {{ 'admin.oauth2.client-id-required' | translate }} -
- - admin.oauth2.customer-name-pattern - - - -
- admin.oauth2.default-dashboard-name - + admin.oauth2.client-secret + + + {{ 'admin.oauth2.client-secret-required' | translate }} + - - - {{ 'admin.oauth2.always-fullscreen' | translate}} -
-
- -
- - admin.oauth2.url - - - {{ 'admin.oauth2.url-required' | translate }} - - - {{ 'admin.oauth2.url-pattern' | translate }} - - + + +
+ + admin.oauth2.access-token-uri + + + {{ 'admin.oauth2.access-token-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + + + + admin.oauth2.authorization-uri + + + {{ 'admin.oauth2.authorization-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + +
+ +
+ + admin.oauth2.jwk-set-uri + + + {{ 'admin.oauth2.jwk-set-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + + + + admin.oauth2.user-info-uri + + + {{ 'admin.oauth2.user-info-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + +
+ + + admin.oauth2.client-authentication-method + + + {{ clientAuthenticationMethod | uppercase }} + + + + +
+ + admin.oauth2.login-button-label + + + {{ 'admin.oauth2.login-button-label-required' | translate }} + + + + + admin.oauth2.login-button-icon + + +
+ + + admin.oauth2.scope + + + {{scope}} + cancel + + + + + {{ 'admin.oauth2.jwk-set-uri-required' | translate }} + + + +
+ + + admin.oauth2.user-name-attribute-name + + + {{ 'admin.oauth2.user-name-attribute-name-required' | translate }} + + + +
+
+ + {{ 'admin.oauth2.allow-user-creation' | translate }} + + + {{ 'admin.oauth2.activate-user' | translate }} + +
+ + + admin.oauth2.type + + + {{ converterTypeExternalUser }} + + + + +
+ + admin.oauth2.email-attribute-key + + + {{ 'admin.oauth2.email-attribute-key-required' | translate }} + + + +
+ + admin.oauth2.first-name-attribute-key + + + + + admin.oauth2.last-name-attribute-key + + +
+ +
+ + admin.oauth2.tenant-name-strategy + + + {{ tenantNameStrategy }} + + + + + + admin.oauth2.tenant-name-pattern + + + {{ 'admin.oauth2.tenant-name-pattern-required' | translate }} + + +
+ + + admin.oauth2.customer-name-pattern + + + +
+ + admin.oauth2.default-dashboard-name + + + + + {{ 'admin.oauth2.always-fullscreen' | translate}} + +
+
+ +
+ + admin.oauth2.url + + + {{ 'admin.oauth2.url-required' | translate }} + + + {{ 'admin.oauth2.url-pattern' | translate }} + + + +
+ + common.username + + + + + common.password + + +
+
+
+
+
-
- - common.username - - - - - common.password - - -
-
- -
- - admin.oauth2.login-button-label - - - {{ 'admin.oauth2.login-button-label-required' | translate }} - - - - - admin.oauth2.login-button-icon - - -
-
- + + +
+
+ +
+
- - -
- -
+ diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss index 95f47b7023..d174ba2dcf 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss @@ -20,6 +20,7 @@ .registration-card{ margin-bottom: 8px; + border: 1px solid rgba(0, 0, 0, 0.2); } .container{ diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index ab472643a6..0459213ee9 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -14,7 +14,7 @@ /// limitations under the License. /// -import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, ViewContainerRef } from '@angular/core'; import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ClientAuthenticationMethod, @@ -32,9 +32,16 @@ import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; import { COMMA, ENTER } from '@angular/cdk/keycodes'; import { MatChipInputEvent } from '@angular/material/chips'; import { WINDOW } from '@core/services/window.service'; -import { Subscription } from 'rxjs'; +import { forkJoin, Subscription } from 'rxjs'; import { DialogService } from '@core/services/dialog.service'; import { TranslateService } from '@ngx-translate/core'; +import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; +import { ComponentPortal, PortalInjector } from '@angular/cdk/portal'; +import { + EDIT_REDIRECT_URI_PANEL_DATA, + EditRedirectUriPanelComponent, + EditRedirectUriPanelData +} from './edit-redirect-uri-panel.component'; @Component({ selector: 'tb-oauth2-settings', @@ -45,6 +52,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/; private subscriptions: Subscription[] = []; + private templates = []; readonly separatorKeysCodes: number[] = [ENTER, COMMA]; @@ -55,8 +63,13 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha converterTypesExternalUser: MapperConfigType[] = ['BASIC', 'CUSTOM']; tenantNameStrategies: TenantNameStrategy[] = ['DOMAIN', 'EMAIL', 'CUSTOM']; + templateProvider = ['Custom']; + constructor(protected store: Store, private adminService: AdminService, + private overlay: Overlay, + private viewContainerRef: ViewContainerRef, + private cd: ChangeDetectorRef, private fb: FormBuilder, private dialogService: DialogService, private translate: TranslateService, @@ -66,8 +79,12 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha ngOnInit(): void { this.buildOAuth2SettingsForm(); - this.adminService.getOAuth2Settings().subscribe( - (oauth2Settings) => { + forkJoin([ + this.adminService.getOAuth2Template(), + this.adminService.getOAuth2Settings() + ]).subscribe( + ([templates, oauth2Settings]) => { + this.initTemplates(templates); this.oauth2Settings = oauth2Settings; this.initOAuth2Settings(this.oauth2Settings); this.oauth2SettingsForm.reset(this.oauth2Settings); @@ -79,7 +96,13 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha super.ngOnDestroy(); this.subscriptions.forEach((subscription) => { subscription.unsubscribe(); - }) + }); + } + + private initTemplates(templates: any): void { + this.templates = templates; + templates.map(provider => this.templateProvider.push(provider.name)); + this.templateProvider.sort(); } get clientsDomainsParams(): FormArray { @@ -114,7 +137,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha url: [null, [Validators.required, Validators.pattern(this.URL_REGEXP)]], username: [null], password: [null] - }) + }); } private buildOAuth2SettingsForm(): void { @@ -136,7 +159,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha const listDomainName = []; control.root.value.clientsDomainsParams.forEach((domain) => { listDomainName.push(domain.domainName); - }) + }); if (listDomainName.indexOf(control.value) > -1) { return {unique: true}; } @@ -144,21 +167,6 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha return null; } - private uniqueRegistrationIdValidator(control: AbstractControl): { [key: string]: boolean } | null { - if (control.value !== null && control?.root) { - const listRegistration = []; - control.root.value.clientsDomainsParams.forEach((domain) => { - domain.clientRegistrations.forEach((client) => { - listRegistration.push(client.registrationId); - }) - }) - if (listRegistration.indexOf(control.value) > -1) { - return {unique: true}; - } - } - return null; - } - private buildSettingsDomain(domainParams?: DomainParams): FormGroup { let url = this.window.location.protocol + '//' + this.window.location.hostname; const port = this.window.location.port; @@ -174,7 +182,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.subscriptions.push(formDomain.get('domainName').valueChanges.subscribe((domain) => { if (!domain) { - domain = this.window.location.hostname + domain = this.window.location.hostname; } const uri = this.window.location.protocol + `//${domain}/login/oauth2/code/`; formDomain.get('redirectUriTemplate').patchValue(uri); @@ -183,7 +191,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha if (domainParams) { domainParams.clientRegistrations.forEach((registration) => { this.clientDomainRegistrations(formDomain).push(this.buildSettingsRegistration(registration)); - }) + }); } else { this.clientDomainRegistrations(formDomain).push(this.buildSettingsRegistration()); } @@ -193,7 +201,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private buildSettingsRegistration(registrationData?: ClientRegistration): FormGroup { const clientRegistration = this.fb.group({ - registrationId: [null, [Validators.required, this.uniqueRegistrationIdValidator]], + providerName: ['Custom', [Validators.required]], loginButtonLabel: [null, [Validators.required]], loginButtonIcon: [null], clientId: ['', [Validators.required]], @@ -227,8 +235,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha if (registrationData) { registrationData.scope.forEach(() => { - (clientRegistration.get('scope') as FormArray).push(this.fb.control('')) - }) + (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); + }); if (registrationData.mapperConfig.type !== 'BASIC') { clientRegistration.get('mapperConfig.type').patchValue('CUSTOM'); } @@ -288,7 +296,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha if (data) { this.clientsDomainsParams.removeAt(index); } - }) + }); } clientDomainRegistrations(control: AbstractControl): FormArray { @@ -305,15 +313,57 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha $event.preventDefault(); } - const registrationId = this.clientDomainRegistrations(controler).at(index).get('registrationId').value; + const providerName = this.clientDomainRegistrations(controler).at(index).get('providerName').value; this.dialogService.confirm( - this.translate.instant('admin.oauth2.delete-registration-title', {name: registrationId || ''}), + this.translate.instant('admin.oauth2.delete-registration-title', {name: providerName || ''}), this.translate.instant('admin.oauth2.delete-registration-text'), null, this.translate.instant('action.delete') ).subscribe((data) => { if (data) { this.clientDomainRegistrations(controler).removeAt(index); } - }) + }); + } + + editRedirectURI($event: MouseEvent, index: number) { + if ($event) { + $event.stopPropagation(); + $event.preventDefault(); + } + const target = $event.target || $event.currentTarget; + const config = new OverlayConfig(); + config.backdropClass = 'cdk-overlay-transparent-backdrop'; + config.hasBackdrop = true; + const connectedPosition: ConnectedPosition = { + originX: 'end', + originY: 'bottom', + overlayX: 'end', + overlayY: 'top' + }; + config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) + .withPositions([connectedPosition]); + + const overlayRef = this.overlay.create(config); + overlayRef.backdropClick().subscribe(() => { + overlayRef.dispose(); + }); + + const redirectURI = this.clientsDomainsParams.at(index).get('redirectUriTemplate').value; + + const injectionTokens = new WeakMap([ + [EDIT_REDIRECT_URI_PANEL_DATA, { + redirectURI + } as EditRedirectUriPanelData], + [OverlayRef, overlayRef] + ]); + const injector = new PortalInjector(this.viewContainerRef.injector, injectionTokens); + const componentRef = overlayRef.attach(new ComponentPortal(EditRedirectUriPanelComponent, + this.viewContainerRef, injector)); + componentRef.onDestroy(() => { + if (componentRef.instance.result !== null) { + const attributeValue = componentRef.instance.result; + this.clientsDomainsParams.at(index).get('redirectUriTemplate').patchValue(attributeValue, {emitEvent: true}); + } + }); } } diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts index c4a1504f0f..2016a309c6 100644 --- a/ui-ngx/src/app/shared/models/settings.models.ts +++ b/ui-ngx/src/app/shared/models/settings.models.ts @@ -76,7 +76,6 @@ export interface DomainParams { } export interface ClientRegistration { - registrationId: string; loginButtonLabel: string; loginButtonIcon: string; clientId: string; @@ -86,9 +85,9 @@ export interface ClientRegistration { scope: string[]; jwkSetUri: string; userInfoUri: string; - clientAuthenticationMethod: ClientAuthenticationMethod + clientAuthenticationMethod: ClientAuthenticationMethod; userNameAttributeName: string; - mapperConfig: MapperConfig + mapperConfig: MapperConfig; } export interface MapperConfig { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index c8967f0636..aea8c1cfe4 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -125,7 +125,7 @@ "error-verification-url": "A domain name shouldn't contain symbols '/' and ':'. Example: thingsboard.io", "add-domain": "Add domain", "new-domain": "New domain", - "add-registration": "Add registration", + "add-provider": "Add provider", "settings": "Settings", "oauth2": { "settings": "OAuth2 Settings", From 14939c27e61a17480cb46688233cd695a55aa492 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Tue, 15 Sep 2020 17:31:55 +0300 Subject: [PATCH 084/117] Added 'deleteByDomain' for OAuth2Service --- .../server/controller/OAuth2Controller.java | 24 +++++++++++++++++++ .../server/dao/oauth2/OAuth2Service.java | 2 ++ .../oauth2/OAuth2ClientRegistrationDao.java | 2 ++ .../server/dao/oauth2/OAuth2ServiceImpl.java | 12 +++++++++- .../JpaOAuth2ClientRegistrationDao.java | 5 ++++ .../OAuth2ClientRegistrationRepository.java | 2 ++ .../dao/service/BaseOAuth2ServiceTest.java | 14 +++++++++++ 7 files changed, 60 insertions(+), 1 deletion(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index da510fe84e..bf010ce383 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -47,6 +47,7 @@ import java.util.List; @Slf4j public class OAuth2Controller extends BaseController { private static final String CLIENT_REGISTRATION_ID = "clientRegistrationId"; + private static final String DOMAIN = "domain"; private static final String CLIENT_REGISTRATION_TEMPLATE_ID = "clientRegistrationTemplateId"; @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) @@ -131,6 +132,29 @@ public class OAuth2Controller extends BaseController { } } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(value = "/oauth2/config/domain/{domain}", method = RequestMethod.DELETE) + @ResponseStatus(value = HttpStatus.OK) + public void deleteClientRegistrationForDomain(@PathVariable(DOMAIN) String domain) throws ThingsboardException { + checkParameter(DOMAIN, domain); + try { + oAuth2Service.deleteClientRegistrationsByDomain(getCurrentUser().getTenantId(), domain); + + logEntityAction(emptyId(EntityType.OAUTH2_CLIENT_REGISTRATION), null, + null, + ActionType.DELETED, null, domain); + + } catch (Exception e) { + logEntityAction(emptyId(EntityType.OAUTH2_CLIENT_REGISTRATION), + null, + null, + ActionType.DELETED, e, domain); + + throw handleException(e); + } + } + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/oauth2/config/template/{clientRegistrationTemplateId}", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index c8da37e9e5..5ad706cf77 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -38,5 +38,7 @@ public interface OAuth2Service { void deleteClientRegistrationById(TenantId tenantId, OAuth2ClientRegistrationId id); + void deleteClientRegistrationsByDomain(TenantId tenantId, String domain); + boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java index d5cded18d3..8152e9a8db 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationDao.java @@ -28,5 +28,7 @@ public interface OAuth2ClientRegistrationDao extends Dao findByDomainName(String domainName); + int removeByTenantIdAndDomainName(UUID tenantId, String domainName); + int removeByTenantId(UUID tenantId); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index d6a22b3c6b..7522008c06 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -100,11 +100,21 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se @Override public void deleteClientRegistrationById(TenantId tenantId, OAuth2ClientRegistrationId id) { - log.trace("Executing deleteClientRegistrationById [{}]", id); + log.trace("Executing deleteClientRegistrationById [{}], [{}]", tenantId, id); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); clientRegistrationDao.removeById(tenantId, id.getId()); } + @Override + @Transactional + public void deleteClientRegistrationsByDomain(TenantId tenantId, String domain) { + log.trace("Executing deleteClientRegistrationsByDomain [{}], [{}]", tenantId, domain); + validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + validateString(domain, INCORRECT_DOMAIN_NAME + domain); + clientRegistrationDao.removeByTenantIdAndDomainName(tenantId.getId(), domain); + } + @Override public boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId) { log.trace("Executing isOAuth2ClientRegistrationAllowed [{}]", tenantId); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java index ef10245e19..d5e17ac635 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java @@ -67,6 +67,11 @@ public class JpaOAuth2ClientRegistrationDao extends JpaAbstractDao findAllByDomainName(String domainName); + int deleteByTenantIdAndDomainName(UUID tenantId, String domainName); + int deleteByTenantId(UUID tenantId); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index d9165b08b8..34b10c2af9 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -215,6 +215,20 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { Assert.assertEquals(0, oAuth2Service.findClientRegistrationsByTenantId(tenantId).size()); } + @Test + public void testDeleteTenantDomainOAuth2ClientRegistrations() { + oAuth2Service.saveClientRegistration(validClientRegistration(tenantId, "domain1")); + oAuth2Service.saveClientRegistration(validClientRegistration(tenantId, "domain1")); + oAuth2Service.saveClientRegistration(validClientRegistration(tenantId, "domain2")); + oAuth2Service.saveClientRegistration(validClientRegistration(TenantId.SYS_TENANT_ID, "domain2")); + Assert.assertEquals(4, oAuth2Service.findAllClientRegistrations().size()); + Assert.assertEquals(3, oAuth2Service.findClientRegistrationsByTenantId(tenantId).size()); + + oAuth2Service.deleteClientRegistrationsByDomain(tenantId, "domain1"); + Assert.assertEquals(2, oAuth2Service.findAllClientRegistrations().size()); + Assert.assertEquals(1, oAuth2Service.findClientRegistrationsByTenantId(tenantId).size()); + } + private void updateTenantAllowOAuth2Setting(Boolean allowOAuth2) throws IOException { Tenant tenant = tenantService.findTenantById(tenantId); if (allowOAuth2 == null) { From c3407bfddcc7b6550149e244523e2ccd14d5d8a4 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 16 Sep 2020 16:42:39 +0300 Subject: [PATCH 085/117] Group ClientRegistrations by Domain --- .../server/controller/OAuth2Controller.java | 33 ++-- .../server/dao/oauth2/OAuth2Service.java | 5 +- .../data/oauth2/ClientRegistrationDto.java | 31 +++ .../oauth2/OAuth2ClientsDomainParams.java | 2 +- .../data/oauth2/OAuth2ClientsParams.java | 5 +- .../server/dao/oauth2/OAuth2ServiceImpl.java | 180 +++++++++--------- .../server/dao/oauth2/OAuth2Utils.java | 74 ++++++- .../dao/service/BaseOAuth2ServiceTest.java | 106 +++++++---- 8 files changed, 293 insertions(+), 143 deletions(-) create mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index bf010ce383..1067b1f647 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -28,10 +28,7 @@ import org.thingsboard.server.common.data.id.DashboardId; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; +import org.thingsboard.server.common.data.oauth2.*; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.queue.util.TbCoreComponent; @@ -40,6 +37,7 @@ import org.thingsboard.server.service.security.permission.Resource; import javax.servlet.http.HttpServletRequest; import java.util.List; +import java.util.stream.Collectors; @RestController @TbCoreComponent @@ -67,15 +65,13 @@ public class OAuth2Controller extends BaseController { try { Authority authority = getCurrentUser().getAuthority(); checkOAuth2ConfigPermissions(Operation.READ); - List clientRegistrations = null; if (Authority.SYS_ADMIN.equals(authority)) { - clientRegistrations = oAuth2Service.findClientRegistrationsByTenantId(TenantId.SYS_TENANT_ID); + return oAuth2Service.findClientsParamsByTenantId(TenantId.SYS_TENANT_ID); } else if (Authority.TENANT_ADMIN.equals(authority)) { - clientRegistrations = oAuth2Service.findClientRegistrationsByTenantId(getCurrentUser().getTenantId()); + return oAuth2Service.findClientsParamsByTenantId(getCurrentUser().getTenantId()); } else { throw new IllegalStateException("Authority " + authority + " cannot get client registrations."); } - return new OAuth2ClientsParams(clientRegistrations); } catch (Exception e) { throw handleException(e); } @@ -84,11 +80,24 @@ public class OAuth2Controller extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) - public OAuth2ClientRegistration saveClientRegistration(@RequestBody OAuth2ClientRegistration clientRegistration) throws ThingsboardException { + public OAuth2ClientsParams saveClientParams(@RequestBody OAuth2ClientsParams clientsParams) throws ThingsboardException { try { - clientRegistration.setTenantId(getCurrentUser().getTenantId()); - checkEntity(clientRegistration.getId(), clientRegistration, Resource.OAUTH2_CONFIGURATION); - return oAuth2Service.saveClientRegistration(clientRegistration); + TenantId tenantId; + Authority authority = getCurrentUser().getAuthority(); + if (Authority.SYS_ADMIN.equals(authority)) { + tenantId = TenantId.SYS_TENANT_ID; + } else if (Authority.TENANT_ADMIN.equals(authority)) { + tenantId = getCurrentUser().getTenantId(); + } else { + throw new IllegalStateException("Authority " + authority + " cannot save client registrations."); + } + List clientRegistrationDtos = clientsParams.getOAuth2DomainDtos().stream() + .flatMap(domainParams -> domainParams.getClientRegistrations().stream()) + .collect(Collectors.toList()); + for (ClientRegistrationDto clientRegistrationDto : clientRegistrationDtos) { + checkEntity(clientRegistrationDto.getId(), () -> tenantId, Resource.OAUTH2_CONFIGURATION); + } + return oAuth2Service.saveClientsParams(tenantId, clientsParams); } catch (Exception e) { throw handleException(e); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 5ad706cf77..bc366b130a 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -19,6 +19,7 @@ import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; import java.util.List; import java.util.UUID; @@ -26,9 +27,9 @@ import java.util.UUID; public interface OAuth2Service { List getOAuth2Clients(String domainName); - OAuth2ClientRegistration saveClientRegistration(OAuth2ClientRegistration clientRegistration); + OAuth2ClientsParams saveClientsParams(TenantId tenantId, OAuth2ClientsParams clientsParams); - List findClientRegistrationsByTenantId(TenantId tenantId); + OAuth2ClientsParams findClientsParamsByTenantId(TenantId tenantId); OAuth2ClientRegistration findClientRegistration(UUID id); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java new file mode 100644 index 0000000000..3394d92f0e --- /dev/null +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java @@ -0,0 +1,31 @@ +package org.thingsboard.server.common.data.oauth2; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.*; +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; +import org.thingsboard.server.common.data.id.TenantId; + +import java.util.List; + +@EqualsAndHashCode +@Data +@ToString(exclude = {"clientSecret"}) +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class ClientRegistrationDto { + private OAuth2ClientRegistrationId id; + private long createdTime; + private OAuth2MapperConfig mapperConfig; + private String clientId; + private String clientSecret; + private String authorizationUri; + private String accessTokenUri; + private List scope; + private String userInfoUri; + private String userNameAttributeName; + private String jwkSetUri; + private String clientAuthenticationMethod; + private String loginButtonLabel; + private String loginButtonIcon; +} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java index 699bf90c37..33670c7b2b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.java @@ -28,5 +28,5 @@ import java.util.List; public class OAuth2ClientsDomainParams { private String domainName; private String redirectUriTemplate; - private List clientRegistrations; + private List clientRegistrations; } \ No newline at end of file diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java index f442669b68..b42571b2d6 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java @@ -16,8 +16,11 @@ package org.thingsboard.server.common.data.oauth2; import lombok.*; +import org.thingsboard.server.common.data.id.TenantId; +import java.util.Collection; import java.util.List; +import java.util.Objects; @EqualsAndHashCode @Data @@ -26,5 +29,5 @@ import java.util.List; @NoArgsConstructor @AllArgsConstructor public class OAuth2ClientsParams { - private List clientRegistrations; + private List oAuth2DomainDtos; } \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 7522008c06..a5464cd8aa 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -33,8 +33,9 @@ import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.tenant.TenantService; import javax.transaction.Transactional; -import java.util.List; -import java.util.UUID; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.stream.Collectors; import static org.thingsboard.server.dao.oauth2.OAuth2Utils.ALLOW_OAUTH2_CONFIGURATION; @@ -64,17 +65,22 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } @Override - public OAuth2ClientRegistration saveClientRegistration(OAuth2ClientRegistration clientRegistration) { - log.trace("Executing saveClientRegistration [{}]", clientRegistration); - clientRegistrationValidator.validate(clientRegistration, OAuth2ClientRegistration::getTenantId); - return clientRegistrationDao.save(clientRegistration.getTenantId(), clientRegistration); + @Transactional + public OAuth2ClientsParams saveClientsParams(TenantId tenantId, OAuth2ClientsParams clientsParams) { + log.trace("Executing saveClientsParams [{}] [{}]", tenantId, clientsParams); + clientParamsValidator.accept(tenantId, clientsParams); + List inputClientRegistrations = OAuth2Utils.toClientRegistrations(tenantId, clientsParams); + List savedClientRegistrations = inputClientRegistrations.stream() + .map(clientRegistration -> clientRegistrationDao.save(clientRegistration.getTenantId(), clientRegistration)) + .collect(Collectors.toList()); + return OAuth2Utils.toOAuth2ClientsParams(savedClientRegistrations); } @Override - public List findClientRegistrationsByTenantId(TenantId tenantId) { - log.trace("Executing findClientRegistrationsByTenantId [{}]", tenantId); + public OAuth2ClientsParams findClientsParamsByTenantId(TenantId tenantId) { + log.trace("Executing findClientsParamsByTenantId [{}]", tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - return clientRegistrationDao.findByTenantId(tenantId.getId()); + return OAuth2Utils.toOAuth2ClientsParams(clientRegistrationDao.findByTenantId(tenantId.getId())); } @Override @@ -129,92 +135,90 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } } - private final DataValidator clientRegistrationValidator = - new DataValidator() { - - @Override - protected void validateCreate(TenantId tenantId, OAuth2ClientRegistration clientRegistration) { + private final BiConsumer clientParamsValidator = (tenantId, clientsParams) -> { + if (clientsParams == null || clientsParams.getOAuth2DomainDtos() == null + || clientsParams.getOAuth2DomainDtos().isEmpty()) { + throw new DataValidationException("Domain params should be specified!"); + } + for (OAuth2ClientsDomainParams domainParams : clientsParams.getOAuth2DomainDtos()) { + if (StringUtils.isEmpty(domainParams.getDomainName())) { + throw new DataValidationException("Domain name should be specified!"); + } + if (StringUtils.isEmpty(domainParams.getRedirectUriTemplate())) { + throw new DataValidationException("Redirect URI template should be specified!"); + } + 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!"); } - - @Override - protected void validateUpdate(TenantId tenantId, OAuth2ClientRegistration clientRegistration) { + if (StringUtils.isEmpty(clientRegistration.getClientSecret())) { + throw new DataValidationException("Client secret should be specified!"); } - - @Override - protected void validateDataImpl(TenantId tenantId, OAuth2ClientRegistration clientRegistration) { - if (StringUtils.isEmpty(clientRegistration.getDomainName())) { - throw new DataValidationException("Domain name should be specified!"); - } - if (StringUtils.isEmpty(clientRegistration.getRedirectUriTemplate())) { - throw new DataValidationException("Redirect URI template should be specified!"); - } - 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!"); + 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!"); } - OAuth2MapperConfig mapperConfig = clientRegistration.getMapperConfig(); - if (mapperConfig == null) { - throw new DataValidationException("Mapper config should be specified!"); + if (StringUtils.isEmpty(basicConfig.getEmailAttributeKey())) { + throw new DataValidationException("Email attribute key should be specified!"); } - if (mapperConfig.getType() == null) { - throw new DataValidationException("Mapper config type should be specified!"); + if (basicConfig.getTenantNameStrategy() == null) { + throw new DataValidationException("Tenant name strategy 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 (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!"); - } + } + if (mapperConfig.getType() == MapperType.CUSTOM) { + OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom(); + if (customConfig == null) { + throw new DataValidationException("Custom config should be specified!"); } - if (clientRegistration.getTenantId() == null) { - throw new DataValidationException("Client registration should be assigned to tenant!"); - } else if (!TenantId.SYS_TENANT_ID.equals(clientRegistration.getTenantId())) { - Tenant tenant = tenantService.findTenantById(clientRegistration.getTenantId()); - if (tenant == null) { - throw new DataValidationException("Client registration is referencing to non-existent tenant!"); - } + if (StringUtils.isEmpty(customConfig.getUrl())) { + throw new DataValidationException("Custom mapper URL should be specified!"); } } - }; + } + } + if (tenantId == null) { + throw new DataValidationException("Client registration should be assigned to tenant!"); + } else if (!TenantId.SYS_TENANT_ID.equals(tenantId)) { + Tenant tenant = tenantService.findTenantById(tenantId); + if (tenant == null) { + throw new DataValidationException("Client registration is referencing to non-existent tenant!"); + } + } + }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java index 948c894540..3570b8a1e9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -16,10 +16,14 @@ package org.thingsboard.server.dao.oauth2; import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; +import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; import java.util.stream.Stream; public class OAuth2Utils { @@ -33,4 +37,68 @@ public class OAuth2Utils { client.setIcon(clientRegistration.getLoginButtonIcon()); return client; } + + public static List toClientRegistrations(TenantId tenantId, OAuth2ClientsParams clientsParams) { + return clientsParams.getOAuth2DomainDtos().stream() + .flatMap(domainParams -> domainParams.getClientRegistrations().stream() + .map(clientRegistrationDto -> OAuth2Utils.toClientRegistration(tenantId, domainParams.getDomainName(), + domainParams.getRedirectUriTemplate(), clientRegistrationDto) + )) + .collect(Collectors.toList()); + } + + public static OAuth2ClientsParams toOAuth2ClientsParams(List clientRegistrations) { + Map domainParamsMap = new HashMap<>(); + for (OAuth2ClientRegistration clientRegistration : clientRegistrations) { + String domainName = clientRegistration.getDomainName(); + OAuth2ClientsDomainParams domainParams = domainParamsMap.computeIfAbsent(domainName, + key -> new OAuth2ClientsDomainParams(domainName, clientRegistration.getRedirectUriTemplate(), new ArrayList<>()) + ); + domainParams.getClientRegistrations() + .add(toClientRegistrationDto(clientRegistration)); + } + return new OAuth2ClientsParams(new ArrayList<>(domainParamsMap.values())); + } + + public static ClientRegistrationDto toClientRegistrationDto(OAuth2ClientRegistration oAuth2ClientRegistration) { + return ClientRegistrationDto.builder() + .id(oAuth2ClientRegistration.getId()) + .createdTime(oAuth2ClientRegistration.getCreatedTime()) + .mapperConfig(oAuth2ClientRegistration.getMapperConfig()) + .clientId(oAuth2ClientRegistration.getClientId()) + .clientSecret(oAuth2ClientRegistration.getClientSecret()) + .authorizationUri(oAuth2ClientRegistration.getAuthorizationUri()) + .accessTokenUri(oAuth2ClientRegistration.getAccessTokenUri()) + .scope(oAuth2ClientRegistration.getScope()) + .userInfoUri(oAuth2ClientRegistration.getUserInfoUri()) + .userNameAttributeName(oAuth2ClientRegistration.getUserNameAttributeName()) + .jwkSetUri(oAuth2ClientRegistration.getJwkSetUri()) + .clientAuthenticationMethod(oAuth2ClientRegistration.getClientAuthenticationMethod()) + .loginButtonLabel(oAuth2ClientRegistration.getLoginButtonLabel()) + .loginButtonIcon(oAuth2ClientRegistration.getLoginButtonIcon()) + .build(); + } + + public static OAuth2ClientRegistration toClientRegistration(TenantId tenantId, String domainName, String redirectUriTemplate, + ClientRegistrationDto clientRegistrationDto) { + OAuth2ClientRegistration clientRegistration = new OAuth2ClientRegistration(); + clientRegistration.setId(clientRegistrationDto.getId()); + clientRegistration.setTenantId(tenantId); + clientRegistration.setCreatedTime(clientRegistrationDto.getCreatedTime()); + clientRegistration.setDomainName(domainName); + clientRegistration.setRedirectUriTemplate(redirectUriTemplate); + clientRegistration.setMapperConfig(clientRegistrationDto.getMapperConfig()); + clientRegistration.setClientId(clientRegistrationDto.getClientId()); + clientRegistration.setClientSecret(clientRegistrationDto.getClientSecret()); + clientRegistration.setAuthorizationUri(clientRegistrationDto.getAuthorizationUri()); + clientRegistration.setAccessTokenUri(clientRegistrationDto.getAccessTokenUri()); + clientRegistration.setScope(clientRegistrationDto.getScope()); + clientRegistration.setUserInfoUri(clientRegistrationDto.getUserInfoUri()); + clientRegistration.setUserNameAttributeName(clientRegistrationDto.getUserNameAttributeName()); + clientRegistration.setJwkSetUri(clientRegistrationDto.getJwkSetUri()); + clientRegistration.setClientAuthenticationMethod(clientRegistrationDto.getClientAuthenticationMethod()); + clientRegistration.setLoginButtonLabel(clientRegistrationDto.getLoginButtonLabel()); + clientRegistration.setLoginButtonIcon(clientRegistrationDto.getLoginButtonIcon()); + return clientRegistration; + } } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index 34b10c2af9..d1f6b6f8e8 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -38,6 +38,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; import static org.thingsboard.server.dao.oauth2.OAuth2Utils.ALLOW_OAUTH2_CONFIGURATION; +import static org.thingsboard.server.dao.oauth2.OAuth2Utils.toClientRegistrations; public class BaseOAuth2ServiceTest extends AbstractServiceTest { @@ -90,9 +91,13 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testCreateNewSystemParams() { OAuth2ClientRegistration clientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); - OAuth2ClientRegistration savedClientRegistration = oAuth2Service.saveClientRegistration(clientRegistration); + OAuth2ClientsParams savedClientsParams = oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(clientRegistration))); + Assert.assertNotNull(savedClientsParams); - Assert.assertNotNull(savedClientRegistration); + List savedClientRegistrations = OAuth2Utils.toClientRegistrations(TenantId.SYS_TENANT_ID, savedClientsParams); + Assert.assertEquals(1, savedClientRegistrations.size()); + + OAuth2ClientRegistration savedClientRegistration = savedClientRegistrations.get(0); Assert.assertNotNull(savedClientRegistration.getId()); clientRegistration.setId(savedClientRegistration.getId()); clientRegistration.setCreatedTime(savedClientRegistration.getCreatedTime()); @@ -102,12 +107,14 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testFindSystemParamsByTenant() { OAuth2ClientRegistration clientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); - oAuth2Service.saveClientRegistration(clientRegistration); + oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(clientRegistration))); - List clientRegistrationsByTenantId = oAuth2Service.findClientRegistrationsByTenantId(TenantId.SYS_TENANT_ID); - Assert.assertEquals(1, clientRegistrationsByTenantId.size()); + OAuth2ClientsParams foundClientsParams = oAuth2Service.findClientsParamsByTenantId(TenantId.SYS_TENANT_ID); + Assert.assertEquals(1, foundClientsParams.getOAuth2DomainDtos().size()); Assert.assertEquals(1, oAuth2Service.findAllClientRegistrations().size()); - OAuth2ClientRegistration foundClientRegistration = clientRegistrationsByTenantId.get(0); + + List foundClientRegistrations = OAuth2Utils.toClientRegistrations(TenantId.SYS_TENANT_ID, foundClientsParams); + OAuth2ClientRegistration foundClientRegistration = foundClientRegistrations.get(0); Assert.assertNotNull(foundClientRegistration); clientRegistration.setId(foundClientRegistration.getId()); clientRegistration.setCreatedTime(foundClientRegistration.getCreatedTime()); @@ -117,7 +124,13 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testCreateNewTenantParams() { OAuth2ClientRegistration clientRegistration = validClientRegistration(tenantId); - OAuth2ClientRegistration savedClientRegistration = oAuth2Service.saveClientRegistration(clientRegistration); + OAuth2ClientsParams savedClientsParams = oAuth2Service.saveClientsParams(tenantId, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(clientRegistration))); + Assert.assertNotNull(savedClientsParams); + + List savedClientRegistrations = OAuth2Utils.toClientRegistrations(tenantId, savedClientsParams); + Assert.assertEquals(1, savedClientRegistrations.size()); + + OAuth2ClientRegistration savedClientRegistration = savedClientRegistrations.get(0); Assert.assertNotNull(savedClientRegistration); Assert.assertNotNull(savedClientRegistration.getId()); @@ -129,12 +142,15 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testFindTenantParams() { OAuth2ClientRegistration clientRegistration = validClientRegistration(tenantId); - oAuth2Service.saveClientRegistration(clientRegistration); + oAuth2Service.saveClientsParams(tenantId, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(clientRegistration))); - List clientRegistrationsByTenantId = oAuth2Service.findClientRegistrationsByTenantId(tenantId); - Assert.assertEquals(1, clientRegistrationsByTenantId.size()); + OAuth2ClientsParams foundClientsParams = oAuth2Service.findClientsParamsByTenantId(tenantId); + Assert.assertEquals(1, foundClientsParams.getOAuth2DomainDtos().size()); Assert.assertEquals(1, oAuth2Service.findAllClientRegistrations().size()); - OAuth2ClientRegistration foundClientRegistration = clientRegistrationsByTenantId.get(0); + + List foundClientRegistrations = OAuth2Utils.toClientRegistrations(tenantId, foundClientsParams); + OAuth2ClientRegistration foundClientRegistration = foundClientRegistrations.get(0); + Assert.assertNotNull(foundClientRegistration); clientRegistration.setId(foundClientRegistration.getId()); clientRegistration.setCreatedTime(foundClientRegistration.getCreatedTime()); @@ -146,18 +162,20 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId); OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); - OAuth2ClientRegistration savedTenantClientRegistration = oAuth2Service.saveClientRegistration(tenantClientRegistration); - OAuth2ClientRegistration savedSysAdminClientRegistration = oAuth2Service.saveClientRegistration(sysAdminClientRegistration); + OAuth2ClientsParams savedTenantClientsParams = oAuth2Service.saveClientsParams(tenantId, + OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(tenantClientRegistration))); + OAuth2ClientsParams savedSysAdminClientsParams = oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, + OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(sysAdminClientRegistration))); Assert.assertEquals(2, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertEquals(savedTenantClientRegistration, oAuth2Service.findClientRegistrationsByTenantId(tenantId).get(0)); - Assert.assertEquals(savedSysAdminClientRegistration, oAuth2Service.findClientRegistrationsByTenantId(TenantId.SYS_TENANT_ID).get(0)); + Assert.assertEquals(savedTenantClientsParams, oAuth2Service.findClientsParamsByTenantId(tenantId)); + Assert.assertEquals(savedSysAdminClientsParams, oAuth2Service.findClientsParamsByTenantId(TenantId.SYS_TENANT_ID)); - Assert.assertEquals(savedTenantClientRegistration, - oAuth2Service.findClientRegistration(savedTenantClientRegistration.getUuidId())); - Assert.assertEquals(savedSysAdminClientRegistration, - oAuth2Service.findClientRegistration(savedSysAdminClientRegistration.getUuidId())); + OAuth2ClientRegistration savedTenantClientRegistration = toClientRegistrations(tenantId, savedTenantClientsParams).get(0); + Assert.assertEquals(savedTenantClientRegistration, oAuth2Service.findClientRegistration(savedTenantClientRegistration.getUuidId())); + OAuth2ClientRegistration savedSysAdminClientRegistration = toClientRegistrations(TenantId.SYS_TENANT_ID, savedSysAdminClientsParams).get(0); + Assert.assertEquals(savedSysAdminClientRegistration, oAuth2Service.findClientRegistration(savedSysAdminClientRegistration.getUuidId())); } @Test @@ -166,8 +184,8 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId, testDomainName); OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID, testDomainName); - oAuth2Service.saveClientRegistration(tenantClientRegistration); - oAuth2Service.saveClientRegistration(sysAdminClientRegistration); + oAuth2Service.saveClientsParams(tenantId, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(tenantClientRegistration))); + oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(sysAdminClientRegistration))); List oAuth2Clients = oAuth2Service.getOAuth2Clients(testDomainName); @@ -183,8 +201,8 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { String testDomainName = "test_domain"; OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId, testDomainName); OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID, testDomainName); - oAuth2Service.saveClientRegistration(tenantClientRegistration); - oAuth2Service.saveClientRegistration(sysAdminClientRegistration); + oAuth2Service.saveClientsParams(tenantId, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(tenantClientRegistration))); + oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(sysAdminClientRegistration))); List oAuth2Clients = oAuth2Service.getOAuth2Clients("random-domain"); Assert.assertTrue(oAuth2Clients.isEmpty()); } @@ -193,8 +211,14 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { public void testDeleteOAuth2ClientRegistration() { OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId); OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); - OAuth2ClientRegistration savedTenantRegistration = oAuth2Service.saveClientRegistration(tenantClientRegistration); - OAuth2ClientRegistration savedSysAdminRegistration = oAuth2Service.saveClientRegistration(sysAdminClientRegistration); + + OAuth2ClientsParams savedTenantClientsParams = oAuth2Service.saveClientsParams(tenantId, + OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(tenantClientRegistration))); + OAuth2ClientsParams savedSysAdminClientsParams = oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, + OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(sysAdminClientRegistration))); + + OAuth2ClientRegistration savedTenantRegistration = toClientRegistrations(tenantId, savedTenantClientsParams).get(0); + OAuth2ClientRegistration savedSysAdminRegistration = toClientRegistrations(TenantId.SYS_TENANT_ID, savedSysAdminClientsParams).get(0); oAuth2Service.deleteClientRegistrationById(tenantId, savedTenantRegistration.getId()); List foundRegistrations = oAuth2Service.findAllClientRegistrations(); @@ -204,29 +228,39 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testDeleteTenantOAuth2ClientRegistrations() { - oAuth2Service.saveClientRegistration(validClientRegistration(tenantId)); - oAuth2Service.saveClientRegistration(validClientRegistration(tenantId)); - oAuth2Service.saveClientRegistration(validClientRegistration(tenantId)); + oAuth2Service.saveClientsParams(tenantId, OAuth2Utils.toOAuth2ClientsParams(Arrays.asList( + validClientRegistration(tenantId, "domain"), + validClientRegistration(tenantId, "domain"), + validClientRegistration(tenantId, "domain") + ))); Assert.assertEquals(3, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertEquals(3, oAuth2Service.findClientRegistrationsByTenantId(tenantId).size()); + Assert.assertEquals(1, oAuth2Service.findClientsParamsByTenantId(tenantId).getOAuth2DomainDtos().size()); oAuth2Service.deleteClientRegistrationsByTenantId(tenantId); Assert.assertEquals(0, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertEquals(0, oAuth2Service.findClientRegistrationsByTenantId(tenantId).size()); + Assert.assertEquals(0, oAuth2Service.findClientsParamsByTenantId(tenantId).getOAuth2DomainDtos().size()); } @Test public void testDeleteTenantDomainOAuth2ClientRegistrations() { - oAuth2Service.saveClientRegistration(validClientRegistration(tenantId, "domain1")); - oAuth2Service.saveClientRegistration(validClientRegistration(tenantId, "domain1")); - oAuth2Service.saveClientRegistration(validClientRegistration(tenantId, "domain2")); - oAuth2Service.saveClientRegistration(validClientRegistration(TenantId.SYS_TENANT_ID, "domain2")); + oAuth2Service.saveClientsParams(tenantId, OAuth2Utils.toOAuth2ClientsParams(Arrays.asList( + validClientRegistration(tenantId, "domain1"), + validClientRegistration(tenantId, "domain1"), + validClientRegistration(tenantId, "domain2") + ))); + oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toOAuth2ClientsParams(Arrays.asList( + validClientRegistration(TenantId.SYS_TENANT_ID, "domain2") + ))); Assert.assertEquals(4, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertEquals(3, oAuth2Service.findClientRegistrationsByTenantId(tenantId).size()); + OAuth2ClientsParams tenantClientsParams = oAuth2Service.findClientsParamsByTenantId(tenantId); + List tenantClientRegistrations = toClientRegistrations(tenantId, tenantClientsParams); + Assert.assertEquals(2, tenantClientsParams.getOAuth2DomainDtos().size()); + Assert.assertEquals(3, tenantClientRegistrations.size()); oAuth2Service.deleteClientRegistrationsByDomain(tenantId, "domain1"); Assert.assertEquals(2, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertEquals(1, oAuth2Service.findClientRegistrationsByTenantId(tenantId).size()); + Assert.assertEquals(1, oAuth2Service.findClientsParamsByTenantId(tenantId).getOAuth2DomainDtos().size()); + Assert.assertEquals(1, toClientRegistrations(tenantId, oAuth2Service.findClientsParamsByTenantId(tenantId)).size()); } private void updateTenantAllowOAuth2Setting(Boolean allowOAuth2) throws IOException { From ae92f065e68d996432823928f2b2a64e532c5f81 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Tue, 8 Sep 2020 17:05:13 +0300 Subject: [PATCH 086/117] Add select login provider templated --- ui-ngx/src/app/core/http/admin.service.ts | 5 +- ui-ngx/src/app/core/services/menu.service.ts | 10 +- .../home/pages/admin/admin-routing.module.ts | 6 +- .../admin/oauth2-settings.component.html | 415 ++++++++++-------- .../admin/oauth2-settings.component.scss | 23 + .../pages/admin/oauth2-settings.component.ts | 55 ++- .../src/app/shared/models/settings.models.ts | 14 + .../assets/locale/locale.constant-en_US.json | 9 +- 8 files changed, 325 insertions(+), 212 deletions(-) diff --git a/ui-ngx/src/app/core/http/admin.service.ts b/ui-ngx/src/app/core/http/admin.service.ts index 1fb6f717c3..38a7c6e5e9 100644 --- a/ui-ngx/src/app/core/http/admin.service.ts +++ b/ui-ngx/src/app/core/http/admin.service.ts @@ -20,6 +20,7 @@ import { Observable } from 'rxjs'; import { HttpClient } from '@angular/common/http'; import { AdminSettings, + ClientProviderTemplated, MailServerSettings, OAuth2Settings, SecuritySettings, @@ -63,8 +64,8 @@ export class AdminService { return this.http.get(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config)); } - public getOAuth2Template(config?: RequestConfig): Observable { - return this.http.get(`/api/oauth2/config/template`, defaultHttpOptionsFromConfig(config)); + public getOAuth2Template(config?: RequestConfig): Observable> { + return this.http.get>(`/api/oauth2/config/template`, defaultHttpOptionsFromConfig(config)); } public saveOAuth2Settings(OAuth2Setting: OAuth2Settings, diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index bb39cfa415..7d3503a613 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -120,9 +120,9 @@ export class MenuService { icon: 'security' }, { - name: 'admin.oauth2.settings', + name: 'admin.oauth2.oauth2', type: 'link', - path: '/settings/oauth2-settings', + path: '/settings/oauth2', icon: 'security' } ] @@ -246,13 +246,13 @@ export class MenuService { icon: 'settings', pages: [ { - name: 'admin.oauth2.settings', + name: 'admin.oauth2.oauth2', type: 'link', - path: '/settings/oauth2-settings', + path: '/settings/oauth2', icon: 'security' } ] - }) + }); } return sections; diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index a6b18cfd29..16635ef53f 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -81,14 +81,14 @@ const routes: Routes = [ } }, { - path: 'oauth2-settings', + path: 'oauth2', component: OAuth2SettingsComponent, canDeactivate: [ConfirmOnExitGuard], data: { auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], - title: 'admin.oauth2.settings', + title: 'admin.oauth2.oauth2', breadcrumb: { - label: 'admin.oauth2.settings', + label: 'admin.oauth2.oauth2', icon: 'security' } } diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index bb77210ba8..fce4f0339f 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -19,7 +19,7 @@
- admin.oauth2.settings + admin.oauth2.oauth2
@@ -91,7 +91,7 @@
- Login Provider + admin.oauth2.login-provider {{ provider | uppercase }} @@ -117,223 +117,254 @@
- - -
- - admin.oauth2.access-token-uri - - - {{ 'admin.oauth2.access-token-uri-required' | translate }} - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - - - - admin.oauth2.authorization-uri - - - {{ 'admin.oauth2.authorization-uri-required' | translate }} - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - -
- -
- - admin.oauth2.jwk-set-uri - - - {{ 'admin.oauth2.jwk-set-uri-required' | translate }} - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - - - - admin.oauth2.user-info-uri - - - {{ 'admin.oauth2.user-info-uri-required' | translate }} - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - -
- - - admin.oauth2.client-authentication-method - - - {{ clientAuthenticationMethod | uppercase }} - - - - -
- - admin.oauth2.login-button-label - - - {{ 'admin.oauth2.login-button-label-required' | translate }} - - - - - admin.oauth2.login-button-icon - - -
- - - admin.oauth2.scope - - - {{scope}} - cancel - - - - - {{ 'admin.oauth2.jwk-set-uri-required' | translate }} - - - -
- - - admin.oauth2.user-name-attribute-name - - - {{ 'admin.oauth2.user-name-attribute-name-required' | translate }} - - - -
-
- - {{ 'admin.oauth2.allow-user-creation' | translate }} - - - {{ 'admin.oauth2.activate-user' | translate }} - -
- - - admin.oauth2.type - - - {{ converterTypeExternalUser }} - - - - -
- - admin.oauth2.email-attribute-key - - - {{ 'admin.oauth2.email-attribute-key-required' | translate }} - - - -
+ + + + {{ 'admin.oauth2.custom-setting' | translate }} + + + + + +
- admin.oauth2.first-name-attribute-key - + admin.oauth2.access-token-uri + + + + {{ 'admin.oauth2.access-token-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + - admin.oauth2.last-name-attribute-key - + admin.oauth2.authorization-uri + + + + {{ 'admin.oauth2.authorization-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} +
- - admin.oauth2.tenant-name-strategy - - - {{ tenantNameStrategy }} - - + + admin.oauth2.jwk-set-uri + + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + - - admin.oauth2.tenant-name-pattern - - - {{ 'admin.oauth2.tenant-name-pattern-required' | translate }} + + admin.oauth2.user-info-uri + + + + {{ 'admin.oauth2.user-info-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }}
- admin.oauth2.customer-name-pattern - + admin.oauth2.client-authentication-method + + + {{ clientAuthenticationMethod | uppercase }} + + -
+
- admin.oauth2.default-dashboard-name - + admin.oauth2.login-button-label + + + {{ 'admin.oauth2.login-button-label-required' | translate }} + - - {{ 'admin.oauth2.always-fullscreen' | translate}} - + + admin.oauth2.login-button-icon + +
-
- -
- - admin.oauth2.url - - - {{ 'admin.oauth2.url-required' | translate }} - - - {{ 'admin.oauth2.url-pattern' | translate }} + +
+
+ + {{ 'admin.oauth2.allow-user-creation' | translate }} + + + {{ 'admin.oauth2.activate-user' | translate }} + +
+
+ + + admin.oauth2.scope + + + {{scope}} + cancel + + + + + + + + + admin.oauth2.user-name-attribute-name + + + {{ 'admin.oauth2.user-name-attribute-name-required' | translate }} -
+
- common.username - + admin.oauth2.type + + + {{ converterTypeExternalUser }} + + - - common.password - - -
-
-
-
-
+
+ + admin.oauth2.email-attribute-key + + + {{ 'admin.oauth2.email-attribute-key-required' | translate }} + + + +
+ + admin.oauth2.first-name-attribute-key + + + + + admin.oauth2.last-name-attribute-key + + +
+ +
+ + admin.oauth2.tenant-name-strategy + + + {{ tenantNameStrategy }} + + + + + + admin.oauth2.tenant-name-pattern + + + {{ 'admin.oauth2.tenant-name-pattern-required' | translate }} + + +
+ + + admin.oauth2.customer-name-pattern + + + +
+ + admin.oauth2.default-dashboard-name + + + + + {{ 'admin.oauth2.always-fullscreen' | translate}} + +
+
+ +
+ + admin.oauth2.url + + + {{ 'admin.oauth2.url-required' | translate }} + + + {{ 'admin.oauth2.url-pattern' | translate }} + + + +
+ + common.username + + + + + common.password + + +
+
+ + + + + diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss index d174ba2dcf..ae7e94a5ac 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss @@ -21,9 +21,32 @@ .registration-card{ margin-bottom: 8px; border: 1px solid rgba(0, 0, 0, 0.2); + + .custom-settings{ + font-size: 16px; + .mat-expansion-panel-header{ + padding: 0 2px; + } + } } .container{ margin-bottom: 16px; } } + +:host ::ng-deep{ + .registration-card{ + .custom-settings{ + .mat-expansion-panel-body{ + padding: 0 2px 16px; + } + .mat-tab-label{ + text-transform: none; + } + .mat-form-field-suffix .mat-icon-button .mat-icon{ + font-size: 18px; + } + } + } +} diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index 0459213ee9..074236c006 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -18,6 +18,7 @@ import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, ViewContainerR import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ClientAuthenticationMethod, + ClientProviderTemplated, ClientRegistration, DomainParams, MapperConfigType, @@ -50,9 +51,24 @@ import { }) export class OAuth2SettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy { - private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/; + private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.,?+=&%@\-/]*)?$/; private subscriptions: Subscription[] = []; - private templates = []; + private templates = new Map(); + private defaultProvider = { + providerName: 'Custom', + clientAuthenticationMethod: 'Post', + userNameAttributeName: 'email', + mapperConfig: { + allowUserCreation: true, + activateUser: false, + type: 'BASIC', + basic: { + emailAttributeKey: 'email', + tenantNameStrategy: 'DOMAIN', + alwaysFullScreen: false + } + } + }; readonly separatorKeysCodes: number[] = [ENTER, COMMA]; @@ -99,9 +115,9 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha }); } - private initTemplates(templates: any): void { - this.templates = templates; - templates.map(provider => this.templateProvider.push(provider.name)); + private initTemplates(templates: ClientProviderTemplated[]): void { + templates.map(provider => this.templates.set(provider.name, provider)); + this.templateProvider.push(...Array.from(this.templates.keys())); this.templateProvider.sort(); } @@ -118,7 +134,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha tenantNamePattern: [null], customerNamePattern: [null], defaultDashboardName: [null], - alwaysFullScreen: [false], + alwaysFullScreen: [false] }); this.subscriptions.push(basicGroup.get('tenantNameStrategy').valueChanges.subscribe((domain) => { @@ -209,7 +225,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha accessTokenUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], authorizationUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], scope: this.fb.array([], [Validators.required]), - jwkSetUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], + jwkSetUri: ['', [Validators.pattern(this.URL_REGEXP)]], userInfoUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], clientAuthenticationMethod: ['POST', [Validators.required]], userNameAttributeName: ['email', [Validators.required]], @@ -233,6 +249,27 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } })); + this.subscriptions.push(clientRegistration.get('providerName').valueChanges.subscribe((provider) => { + (clientRegistration.get('scope') as FormArray).clear(); + if (provider === 'Custom') { + clientRegistration.reset(this.defaultProvider, {emitEvent: false}); + clientRegistration.get('accessTokenUri').enable(); + clientRegistration.get('authorizationUri').enable(); + clientRegistration.get('jwkSetUri').enable(); + clientRegistration.get('userInfoUri').enable(); + } else { + const template = this.templates.get(provider); + template.scope.forEach(() => { + (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); + }); + clientRegistration.get('accessTokenUri').disable(); + clientRegistration.get('authorizationUri').disable(); + clientRegistration.get('jwkSetUri').disable(); + clientRegistration.get('userInfoUri').disable(); + clientRegistration.patchValue(this.templates.get(provider), {emitEvent: false}); + } + })); + if (registrationData) { registrationData.scope.forEach(() => { (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); @@ -366,4 +403,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } }); } + + toggleEditMode(control: AbstractControl, path: string) { + control.get(path).disabled ? control.get(path).enable() : control.get(path).disable(); + } } diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts index 2016a309c6..8fe83aee09 100644 --- a/ui-ngx/src/app/shared/models/settings.models.ts +++ b/ui-ngx/src/app/shared/models/settings.models.ts @@ -14,6 +14,9 @@ /// limitations under the License. /// +import { EntityId } from '@shared/models/id/entity-id'; +import { TenantId } from '@shared/models/id/tenant-id'; + export const smtpPortPattern: RegExp = /^([0-9]{1,4}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$/; export interface AdminSettings { @@ -75,6 +78,17 @@ export interface DomainParams { clientRegistrations: ClientRegistration[]; } +export interface ClientProviderTemplated extends ClientRegistration{ + additionalInfo: string; + comment: string; + createdTime: number; + helpLink: string; + id: EntityId; + name: string; + providerId: string; + tenantId: TenantId; +} + export interface ClientRegistration { loginButtonLabel: string; loginButtonIcon: string; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index aea8c1cfe4..802575c0ff 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -128,7 +128,7 @@ "add-provider": "Add provider", "settings": "Settings", "oauth2": { - "settings": "OAuth2 Settings", + "oauth2": "OAuth2", "registration-id": "Registration ID", "registration-id-required": "Registration ID is required.", "registration-id-unique": "Registration ID need to unique for the system.", @@ -145,7 +145,6 @@ "redirect-uri-template": "Redirect URI template", "redirect-uri-template-required": "Redirect URI template is required.", "jwk-set-uri": "JSON Web Key URI", - "jwk-set-uri-required": "JSON Web Key URI is required.", "user-info-uri": "User info URI", "user-info-uri-required": "User info URI is required.", "client-authentication-method": "Client authentication method", @@ -173,7 +172,11 @@ "delete-domain-title": "Are you sure you want to delete the domain '{{domainName}}'?", "delete-domain-text": "Be careful, after the confirmation a domain and all registration data will be unavailable.", "delete-registration-title": "Are you sure you want to delete the registration '{{name}}'?", - "delete-registration-text": "Be careful, after the confirmation a registration data will be unavailable." + "delete-registration-text": "Be careful, after the confirmation a registration data will be unavailable.", + "login-provider": "Login provider", + "custom-setting": "Custom settings", + "general": "General", + "mapper": "Mapper" } }, "alarm": { From d5cfccc1cb3526329a417dca3d4810263a3b6908 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 17 Sep 2020 10:36:50 +0300 Subject: [PATCH 087/117] Updated license header --- .../common/data/oauth2/ClientRegistrationDto.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java index 3394d92f0e..6f762c0e7a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java @@ -1,3 +1,18 @@ +/** + * 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; From bfc3e75fc9707aacee3970252ae0b3ae8fd928d4 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 17 Sep 2020 11:14:27 +0300 Subject: [PATCH 088/117] Unpacked Domain objects from ClientParams --- .../server/controller/OAuth2Controller.java | 16 ++-- .../server/dao/oauth2/OAuth2Service.java | 6 +- .../data/oauth2/OAuth2ClientsParams.java | 33 ------- .../server/dao/oauth2/OAuth2ServiceImpl.java | 23 +++-- .../server/dao/oauth2/OAuth2Utils.java | 8 +- .../dao/service/BaseOAuth2ServiceTest.java | 90 +++++++++---------- 6 files changed, 66 insertions(+), 110 deletions(-) delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index 1067b1f647..1f6f5e0ccf 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -16,21 +16,17 @@ package org.thingsboard.server.controller; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; -import org.thingsboard.server.common.data.Dashboard; 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.DashboardId; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; 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.common.data.security.Authority; -import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.queue.util.TbCoreComponent; import org.thingsboard.server.service.security.permission.Operation; import org.thingsboard.server.service.security.permission.Resource; @@ -61,14 +57,14 @@ public class OAuth2Controller extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") @ResponseBody - public OAuth2ClientsParams getCurrentClientsParams() throws ThingsboardException { + public List getCurrentClientsParams() throws ThingsboardException { try { Authority authority = getCurrentUser().getAuthority(); checkOAuth2ConfigPermissions(Operation.READ); if (Authority.SYS_ADMIN.equals(authority)) { - return oAuth2Service.findClientsParamsByTenantId(TenantId.SYS_TENANT_ID); + return oAuth2Service.findDomainsParamsByTenantId(TenantId.SYS_TENANT_ID); } else if (Authority.TENANT_ADMIN.equals(authority)) { - return oAuth2Service.findClientsParamsByTenantId(getCurrentUser().getTenantId()); + return oAuth2Service.findDomainsParamsByTenantId(getCurrentUser().getTenantId()); } else { throw new IllegalStateException("Authority " + authority + " cannot get client registrations."); } @@ -80,7 +76,7 @@ public class OAuth2Controller extends BaseController { @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) - public OAuth2ClientsParams saveClientParams(@RequestBody OAuth2ClientsParams clientsParams) throws ThingsboardException { + public List saveClientParams(@RequestBody List domainsParams) throws ThingsboardException { try { TenantId tenantId; Authority authority = getCurrentUser().getAuthority(); @@ -91,13 +87,13 @@ public class OAuth2Controller extends BaseController { } else { throw new IllegalStateException("Authority " + authority + " cannot save client registrations."); } - List clientRegistrationDtos = clientsParams.getOAuth2DomainDtos().stream() + List clientRegistrationDtos = domainsParams.stream() .flatMap(domainParams -> domainParams.getClientRegistrations().stream()) .collect(Collectors.toList()); for (ClientRegistrationDto clientRegistrationDto : clientRegistrationDtos) { checkEntity(clientRegistrationDto.getId(), () -> tenantId, Resource.OAUTH2_CONFIGURATION); } - return oAuth2Service.saveClientsParams(tenantId, clientsParams); + return oAuth2Service.saveDomainsParams(tenantId, domainsParams); } catch (Exception e) { throw handleException(e); } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index bc366b130a..2f7156756a 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -19,7 +19,7 @@ import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientsDomainParams; import java.util.List; import java.util.UUID; @@ -27,9 +27,9 @@ import java.util.UUID; public interface OAuth2Service { List getOAuth2Clients(String domainName); - OAuth2ClientsParams saveClientsParams(TenantId tenantId, OAuth2ClientsParams clientsParams); + List saveDomainsParams(TenantId tenantId, List domainsParams); - OAuth2ClientsParams findClientsParamsByTenantId(TenantId tenantId); + List findDomainsParamsByTenantId(TenantId tenantId); OAuth2ClientRegistration findClientRegistration(UUID id); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java deleted file mode 100644 index b42571b2d6..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.java +++ /dev/null @@ -1,33 +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.common.data.oauth2; - -import lombok.*; -import org.thingsboard.server.common.data.id.TenantId; - -import java.util.Collection; -import java.util.List; -import java.util.Objects; - -@EqualsAndHashCode -@Data -@ToString -@Builder(toBuilder = true) -@NoArgsConstructor -@AllArgsConstructor -public class OAuth2ClientsParams { - private List oAuth2DomainDtos; -} \ No newline at end of file diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index a5464cd8aa..70b0f1edfd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -66,21 +66,21 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se @Override @Transactional - public OAuth2ClientsParams saveClientsParams(TenantId tenantId, OAuth2ClientsParams clientsParams) { - log.trace("Executing saveClientsParams [{}] [{}]", tenantId, clientsParams); - clientParamsValidator.accept(tenantId, clientsParams); - List inputClientRegistrations = OAuth2Utils.toClientRegistrations(tenantId, clientsParams); + public List saveDomainsParams(TenantId tenantId, List domainsParams) { + log.trace("Executing saveDomainsParams [{}] [{}]", tenantId, domainsParams); + clientParamsValidator.accept(tenantId, domainsParams); + List inputClientRegistrations = OAuth2Utils.toClientRegistrations(tenantId, domainsParams); List savedClientRegistrations = inputClientRegistrations.stream() .map(clientRegistration -> clientRegistrationDao.save(clientRegistration.getTenantId(), clientRegistration)) .collect(Collectors.toList()); - return OAuth2Utils.toOAuth2ClientsParams(savedClientRegistrations); + return OAuth2Utils.toDomainsParams(savedClientRegistrations); } @Override - public OAuth2ClientsParams findClientsParamsByTenantId(TenantId tenantId) { - log.trace("Executing findClientsParamsByTenantId [{}]", tenantId); + public List findDomainsParamsByTenantId(TenantId tenantId) { + log.trace("Executing findDomainsParamsByTenantId [{}]", tenantId); validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - return OAuth2Utils.toOAuth2ClientsParams(clientRegistrationDao.findByTenantId(tenantId.getId())); + return OAuth2Utils.toDomainsParams(clientRegistrationDao.findByTenantId(tenantId.getId())); } @Override @@ -135,12 +135,11 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } } - private final BiConsumer clientParamsValidator = (tenantId, clientsParams) -> { - if (clientsParams == null || clientsParams.getOAuth2DomainDtos() == null - || clientsParams.getOAuth2DomainDtos().isEmpty()) { + private final BiConsumer> clientParamsValidator = (tenantId, domainsParams) -> { + if (domainsParams == null || domainsParams.isEmpty()) { throw new DataValidationException("Domain params should be specified!"); } - for (OAuth2ClientsDomainParams domainParams : clientsParams.getOAuth2DomainDtos()) { + for (OAuth2ClientsDomainParams domainParams : domainsParams) { if (StringUtils.isEmpty(domainParams.getDomainName())) { throw new DataValidationException("Domain name should be specified!"); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java index 3570b8a1e9..0434f1d621 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -38,8 +38,8 @@ public class OAuth2Utils { return client; } - public static List toClientRegistrations(TenantId tenantId, OAuth2ClientsParams clientsParams) { - return clientsParams.getOAuth2DomainDtos().stream() + public static List toClientRegistrations(TenantId tenantId, List domainsParams) { + return domainsParams.stream() .flatMap(domainParams -> domainParams.getClientRegistrations().stream() .map(clientRegistrationDto -> OAuth2Utils.toClientRegistration(tenantId, domainParams.getDomainName(), domainParams.getRedirectUriTemplate(), clientRegistrationDto) @@ -47,7 +47,7 @@ public class OAuth2Utils { .collect(Collectors.toList()); } - public static OAuth2ClientsParams toOAuth2ClientsParams(List clientRegistrations) { + public static List toDomainsParams(List clientRegistrations) { Map domainParamsMap = new HashMap<>(); for (OAuth2ClientRegistration clientRegistration : clientRegistrations) { String domainName = clientRegistration.getDomainName(); @@ -57,7 +57,7 @@ public class OAuth2Utils { domainParams.getClientRegistrations() .add(toClientRegistrationDto(clientRegistration)); } - return new OAuth2ClientsParams(new ArrayList<>(domainParamsMap.values())); + return new ArrayList<>(domainParamsMap.values()); } public static ClientRegistrationDto toClientRegistrationDto(OAuth2ClientRegistration oAuth2ClientRegistration) { diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index d1f6b6f8e8..65db6aa212 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -15,27 +15,21 @@ */ package org.thingsboard.server.dao.service; -import org.apache.commons.lang3.tuple.Pair; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.server.common.data.DataConstants; import org.thingsboard.server.common.data.Tenant; -import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.*; import org.thingsboard.server.dao.attributes.AttributesService; -import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.oauth2.OAuth2Utils; -import javax.transaction.Transactional; import java.io.IOException; import java.util.*; import java.util.stream.Collectors; -import java.util.stream.Stream; import static org.thingsboard.server.dao.oauth2.OAuth2Utils.ALLOW_OAUTH2_CONFIGURATION; import static org.thingsboard.server.dao.oauth2.OAuth2Utils.toClientRegistrations; @@ -91,10 +85,10 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testCreateNewSystemParams() { OAuth2ClientRegistration clientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); - OAuth2ClientsParams savedClientsParams = oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(clientRegistration))); - Assert.assertNotNull(savedClientsParams); + List savedDomainsParams = oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toDomainsParams(Collections.singletonList(clientRegistration))); + Assert.assertNotNull(savedDomainsParams); - List savedClientRegistrations = OAuth2Utils.toClientRegistrations(TenantId.SYS_TENANT_ID, savedClientsParams); + List savedClientRegistrations = OAuth2Utils.toClientRegistrations(TenantId.SYS_TENANT_ID, savedDomainsParams); Assert.assertEquals(1, savedClientRegistrations.size()); OAuth2ClientRegistration savedClientRegistration = savedClientRegistrations.get(0); @@ -107,13 +101,13 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testFindSystemParamsByTenant() { OAuth2ClientRegistration clientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); - oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(clientRegistration))); + oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toDomainsParams(Collections.singletonList(clientRegistration))); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findClientsParamsByTenantId(TenantId.SYS_TENANT_ID); - Assert.assertEquals(1, foundClientsParams.getOAuth2DomainDtos().size()); + List foundDomainsParams = oAuth2Service.findDomainsParamsByTenantId(TenantId.SYS_TENANT_ID); + Assert.assertEquals(1, foundDomainsParams.size()); Assert.assertEquals(1, oAuth2Service.findAllClientRegistrations().size()); - List foundClientRegistrations = OAuth2Utils.toClientRegistrations(TenantId.SYS_TENANT_ID, foundClientsParams); + List foundClientRegistrations = OAuth2Utils.toClientRegistrations(TenantId.SYS_TENANT_ID, foundDomainsParams); OAuth2ClientRegistration foundClientRegistration = foundClientRegistrations.get(0); Assert.assertNotNull(foundClientRegistration); clientRegistration.setId(foundClientRegistration.getId()); @@ -124,10 +118,10 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testCreateNewTenantParams() { OAuth2ClientRegistration clientRegistration = validClientRegistration(tenantId); - OAuth2ClientsParams savedClientsParams = oAuth2Service.saveClientsParams(tenantId, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(clientRegistration))); - Assert.assertNotNull(savedClientsParams); + List savedDomainsParams = oAuth2Service.saveDomainsParams(tenantId, OAuth2Utils.toDomainsParams(Collections.singletonList(clientRegistration))); + Assert.assertNotNull(savedDomainsParams); - List savedClientRegistrations = OAuth2Utils.toClientRegistrations(tenantId, savedClientsParams); + List savedClientRegistrations = OAuth2Utils.toClientRegistrations(tenantId, savedDomainsParams); Assert.assertEquals(1, savedClientRegistrations.size()); OAuth2ClientRegistration savedClientRegistration = savedClientRegistrations.get(0); @@ -142,13 +136,13 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testFindTenantParams() { OAuth2ClientRegistration clientRegistration = validClientRegistration(tenantId); - oAuth2Service.saveClientsParams(tenantId, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(clientRegistration))); + oAuth2Service.saveDomainsParams(tenantId, OAuth2Utils.toDomainsParams(Collections.singletonList(clientRegistration))); - OAuth2ClientsParams foundClientsParams = oAuth2Service.findClientsParamsByTenantId(tenantId); - Assert.assertEquals(1, foundClientsParams.getOAuth2DomainDtos().size()); + List foundDomainsParams = oAuth2Service.findDomainsParamsByTenantId(tenantId); + Assert.assertEquals(1, foundDomainsParams.size()); Assert.assertEquals(1, oAuth2Service.findAllClientRegistrations().size()); - List foundClientRegistrations = OAuth2Utils.toClientRegistrations(tenantId, foundClientsParams); + List foundClientRegistrations = OAuth2Utils.toClientRegistrations(tenantId, foundDomainsParams); OAuth2ClientRegistration foundClientRegistration = foundClientRegistrations.get(0); Assert.assertNotNull(foundClientRegistration); @@ -162,19 +156,19 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId); OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); - OAuth2ClientsParams savedTenantClientsParams = oAuth2Service.saveClientsParams(tenantId, - OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(tenantClientRegistration))); - OAuth2ClientsParams savedSysAdminClientsParams = oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, - OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(sysAdminClientRegistration))); + List savedTenantDomainsParams = oAuth2Service.saveDomainsParams(tenantId, + OAuth2Utils.toDomainsParams(Collections.singletonList(tenantClientRegistration))); + List savedSysAdminDomainsParams = oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, + OAuth2Utils.toDomainsParams(Collections.singletonList(sysAdminClientRegistration))); Assert.assertEquals(2, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertEquals(savedTenantClientsParams, oAuth2Service.findClientsParamsByTenantId(tenantId)); - Assert.assertEquals(savedSysAdminClientsParams, oAuth2Service.findClientsParamsByTenantId(TenantId.SYS_TENANT_ID)); + Assert.assertEquals(savedTenantDomainsParams, oAuth2Service.findDomainsParamsByTenantId(tenantId)); + Assert.assertEquals(savedSysAdminDomainsParams, oAuth2Service.findDomainsParamsByTenantId(TenantId.SYS_TENANT_ID)); - OAuth2ClientRegistration savedTenantClientRegistration = toClientRegistrations(tenantId, savedTenantClientsParams).get(0); + OAuth2ClientRegistration savedTenantClientRegistration = toClientRegistrations(tenantId, savedTenantDomainsParams).get(0); Assert.assertEquals(savedTenantClientRegistration, oAuth2Service.findClientRegistration(savedTenantClientRegistration.getUuidId())); - OAuth2ClientRegistration savedSysAdminClientRegistration = toClientRegistrations(TenantId.SYS_TENANT_ID, savedSysAdminClientsParams).get(0); + OAuth2ClientRegistration savedSysAdminClientRegistration = toClientRegistrations(TenantId.SYS_TENANT_ID, savedSysAdminDomainsParams).get(0); Assert.assertEquals(savedSysAdminClientRegistration, oAuth2Service.findClientRegistration(savedSysAdminClientRegistration.getUuidId())); } @@ -184,8 +178,8 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId, testDomainName); OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID, testDomainName); - oAuth2Service.saveClientsParams(tenantId, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(tenantClientRegistration))); - oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(sysAdminClientRegistration))); + oAuth2Service.saveDomainsParams(tenantId, OAuth2Utils.toDomainsParams(Collections.singletonList(tenantClientRegistration))); + oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toDomainsParams(Collections.singletonList(sysAdminClientRegistration))); List oAuth2Clients = oAuth2Service.getOAuth2Clients(testDomainName); @@ -201,8 +195,8 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { String testDomainName = "test_domain"; OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId, testDomainName); OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID, testDomainName); - oAuth2Service.saveClientsParams(tenantId, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(tenantClientRegistration))); - oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(sysAdminClientRegistration))); + oAuth2Service.saveDomainsParams(tenantId, OAuth2Utils.toDomainsParams(Collections.singletonList(tenantClientRegistration))); + oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toDomainsParams(Collections.singletonList(sysAdminClientRegistration))); List oAuth2Clients = oAuth2Service.getOAuth2Clients("random-domain"); Assert.assertTrue(oAuth2Clients.isEmpty()); } @@ -212,13 +206,13 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId); OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); - OAuth2ClientsParams savedTenantClientsParams = oAuth2Service.saveClientsParams(tenantId, - OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(tenantClientRegistration))); - OAuth2ClientsParams savedSysAdminClientsParams = oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, - OAuth2Utils.toOAuth2ClientsParams(Collections.singletonList(sysAdminClientRegistration))); + List savedTenantDomainsParams = oAuth2Service.saveDomainsParams(tenantId, + OAuth2Utils.toDomainsParams(Collections.singletonList(tenantClientRegistration))); + List savedSysAdminDomainsParams = oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, + OAuth2Utils.toDomainsParams(Collections.singletonList(sysAdminClientRegistration))); - OAuth2ClientRegistration savedTenantRegistration = toClientRegistrations(tenantId, savedTenantClientsParams).get(0); - OAuth2ClientRegistration savedSysAdminRegistration = toClientRegistrations(TenantId.SYS_TENANT_ID, savedSysAdminClientsParams).get(0); + OAuth2ClientRegistration savedTenantRegistration = toClientRegistrations(tenantId, savedTenantDomainsParams).get(0); + OAuth2ClientRegistration savedSysAdminRegistration = toClientRegistrations(TenantId.SYS_TENANT_ID, savedSysAdminDomainsParams).get(0); oAuth2Service.deleteClientRegistrationById(tenantId, savedTenantRegistration.getId()); List foundRegistrations = oAuth2Service.findAllClientRegistrations(); @@ -228,39 +222,39 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testDeleteTenantOAuth2ClientRegistrations() { - oAuth2Service.saveClientsParams(tenantId, OAuth2Utils.toOAuth2ClientsParams(Arrays.asList( + oAuth2Service.saveDomainsParams(tenantId, OAuth2Utils.toDomainsParams(Arrays.asList( validClientRegistration(tenantId, "domain"), validClientRegistration(tenantId, "domain"), validClientRegistration(tenantId, "domain") ))); Assert.assertEquals(3, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertEquals(1, oAuth2Service.findClientsParamsByTenantId(tenantId).getOAuth2DomainDtos().size()); + Assert.assertEquals(1, oAuth2Service.findDomainsParamsByTenantId(tenantId).size()); oAuth2Service.deleteClientRegistrationsByTenantId(tenantId); Assert.assertEquals(0, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertEquals(0, oAuth2Service.findClientsParamsByTenantId(tenantId).getOAuth2DomainDtos().size()); + Assert.assertEquals(0, oAuth2Service.findDomainsParamsByTenantId(tenantId).size()); } @Test public void testDeleteTenantDomainOAuth2ClientRegistrations() { - oAuth2Service.saveClientsParams(tenantId, OAuth2Utils.toOAuth2ClientsParams(Arrays.asList( + oAuth2Service.saveDomainsParams(tenantId, OAuth2Utils.toDomainsParams(Arrays.asList( validClientRegistration(tenantId, "domain1"), validClientRegistration(tenantId, "domain1"), validClientRegistration(tenantId, "domain2") ))); - oAuth2Service.saveClientsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toOAuth2ClientsParams(Arrays.asList( + oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toDomainsParams(Arrays.asList( validClientRegistration(TenantId.SYS_TENANT_ID, "domain2") ))); Assert.assertEquals(4, oAuth2Service.findAllClientRegistrations().size()); - OAuth2ClientsParams tenantClientsParams = oAuth2Service.findClientsParamsByTenantId(tenantId); - List tenantClientRegistrations = toClientRegistrations(tenantId, tenantClientsParams); - Assert.assertEquals(2, tenantClientsParams.getOAuth2DomainDtos().size()); + List tenantDomainsParams = oAuth2Service.findDomainsParamsByTenantId(tenantId); + List tenantClientRegistrations = toClientRegistrations(tenantId, tenantDomainsParams); + Assert.assertEquals(2, tenantDomainsParams.size()); Assert.assertEquals(3, tenantClientRegistrations.size()); oAuth2Service.deleteClientRegistrationsByDomain(tenantId, "domain1"); Assert.assertEquals(2, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertEquals(1, oAuth2Service.findClientsParamsByTenantId(tenantId).getOAuth2DomainDtos().size()); - Assert.assertEquals(1, toClientRegistrations(tenantId, oAuth2Service.findClientsParamsByTenantId(tenantId)).size()); + Assert.assertEquals(1, oAuth2Service.findDomainsParamsByTenantId(tenantId).size()); + Assert.assertEquals(1, toClientRegistrations(tenantId, oAuth2Service.findDomainsParamsByTenantId(tenantId)).size()); } private void updateTenantAllowOAuth2Setting(Boolean allowOAuth2) throws IOException { From 42a80efdbb641337f0b94911888168a312d59378 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 17 Sep 2020 11:18:57 +0300 Subject: [PATCH 089/117] Moved OAuth2Template endpoints to separate controller --- .../OAuth2ConfigTemplateController.java | 93 +++++++++++++++++++ .../server/controller/OAuth2Controller.java | 58 ------------ 2 files changed, 93 insertions(+), 58 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java new file mode 100644 index 0000000000..ed2c479e1f --- /dev/null +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java @@ -0,0 +1,93 @@ +/** + * 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 { + clientRegistrationTemplate.setTenantId(getCurrentUser().getTenantId()); + checkEntity(clientRegistrationTemplate.getId(), clientRegistrationTemplate, Resource.OAUTH2_CONFIGURATION_TEMPLATE); + 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 { + OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId = new OAuth2ClientRegistrationTemplateId(toUUID(strClientRegistrationTemplateId)); + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = checkOAuth2ClientRegistrationTemplateId(clientRegistrationTemplateId, Operation.DELETE); + oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplateId); + + logEntityAction(clientRegistrationTemplateId, clientRegistrationTemplate, + null, + ActionType.DELETED, null, strClientRegistrationTemplateId); + + } catch (Exception e) { + + logEntityAction(emptyId(EntityType.OAUTH2_CLIENT_REGISTRATION_TEMPLATE), + null, + null, + ActionType.DELETED, e, strClientRegistrationTemplateId); + + throw handleException(e); + } + } + + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @RequestMapping(method = RequestMethod.GET, produces = "application/json") + @ResponseBody + public List getClientRegistrationTemplates() throws ThingsboardException { + try { + checkOAuth2ConfigTemplatePermissions(Operation.READ); + return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates(); + } catch (Exception e) { + throw handleException(e); + } + } + + private void checkOAuth2ConfigTemplatePermissions(Operation operation) throws ThingsboardException { + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, operation); + } +} diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index 1f6f5e0ccf..b4a8ead9db 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -23,7 +23,6 @@ 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.OAuth2ClientRegistrationId; -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.common.data.security.Authority; @@ -42,7 +41,6 @@ import java.util.stream.Collectors; public class OAuth2Controller extends BaseController { private static final String CLIENT_REGISTRATION_ID = "clientRegistrationId"; private static final String DOMAIN = "domain"; - private static final String CLIENT_REGISTRATION_TEMPLATE_ID = "clientRegistrationTemplateId"; @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) @ResponseBody @@ -99,19 +97,6 @@ public class OAuth2Controller extends BaseController { } } - @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") - @RequestMapping(value = "/oauth2/config/template", method = RequestMethod.POST) - @ResponseStatus(value = HttpStatus.OK) - public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(@RequestBody OAuth2ClientRegistrationTemplate clientRegistrationTemplate) throws ThingsboardException { - try { - clientRegistrationTemplate.setTenantId(getCurrentUser().getTenantId()); - checkEntity(clientRegistrationTemplate.getId(), clientRegistrationTemplate, Resource.OAUTH2_CONFIGURATION_TEMPLATE); - return oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); - } catch (Exception e) { - throw handleException(e); - } - } - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") @RequestMapping(value = "/oauth2/config/{clientRegistrationId}", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) @@ -160,31 +145,6 @@ public class OAuth2Controller extends BaseController { } } - @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") - @RequestMapping(value = "/oauth2/config/template/{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 { - OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId = new OAuth2ClientRegistrationTemplateId(toUUID(strClientRegistrationTemplateId)); - OAuth2ClientRegistrationTemplate clientRegistrationTemplate = checkOAuth2ClientRegistrationTemplateId(clientRegistrationTemplateId, Operation.DELETE); - oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplateId); - - logEntityAction(clientRegistrationTemplateId, clientRegistrationTemplate, - null, - ActionType.DELETED, null, strClientRegistrationTemplateId); - - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.OAUTH2_CLIENT_REGISTRATION_TEMPLATE), - null, - null, - ActionType.DELETED, e, strClientRegistrationTemplateId); - - throw handleException(e); - } - } - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") @RequestMapping(value = "/oauth2/config/isAllowed", method = RequestMethod.GET) @ResponseBody @@ -196,25 +156,7 @@ public class OAuth2Controller extends BaseController { } } - - - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") - @RequestMapping(value = "/oauth2/config/template", method = RequestMethod.GET, produces = "application/json") - @ResponseBody - public List getClientRegistrationTemplates() throws ThingsboardException { - try { - checkOAuth2ConfigTemplatePermissions(Operation.READ); - return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates(); - } catch (Exception e) { - throw handleException(e); - } - } - private void checkOAuth2ConfigPermissions(Operation operation) throws ThingsboardException { accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION, operation); } - - private void checkOAuth2ConfigTemplatePermissions(Operation operation) throws ThingsboardException { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, operation); - } } From 0cab0325854989a6d4fc277e24fd0e2524dcec58 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 17 Sep 2020 11:35:42 +0300 Subject: [PATCH 090/117] Added loginButton label and icon to OAuth2Template --- .../src/main/data/upgrade/3.1.1/schema_update.sql | 3 ++- .../oauth2/OAuth2ClientRegistrationTemplate.java | 6 ++++-- .../server/dao/model/ModelConstants.java | 3 ++- .../sql/OAuth2ClientRegistrationTemplateEntity.java | 13 ++++++++----- .../dao/oauth2/OAuth2ConfigTemplateServiceImpl.java | 2 +- dao/src/main/resources/sql/schema-entities-hsql.sql | 3 ++- dao/src/main/resources/sql/schema-entities.sql | 3 ++- .../BaseOAuth2ConfigTemplateServiceTest.java | 3 ++- 8 files changed, 23 insertions(+), 13 deletions(-) diff --git a/application/src/main/data/upgrade/3.1.1/schema_update.sql b/application/src/main/data/upgrade/3.1.1/schema_update.sql index 58c4ff5b67..6064ec8833 100644 --- a/application/src/main/data/upgrade/3.1.1/schema_update.sql +++ b/application/src/main/data/upgrade/3.1.1/schema_update.sql @@ -75,7 +75,8 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( basic_default_dashboard_name varchar(255), basic_always_full_screen boolean, comment varchar, - icon varchar(255), + login_button_icon varchar(255), + login_button_label varchar(255), help_link varchar(255), CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) ); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java index 7f16940e7f..e066fa05d2 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java @@ -44,7 +44,8 @@ public class OAuth2ClientRegistrationTemplate extends BaseData clientRegistrationTemplateValidator = + private final DataValidator clientRegistrationTemplateValidator = new DataValidator() { @Override diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index c9ba64d646..0b7f1c848b 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -348,7 +348,8 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( basic_default_dashboard_name varchar(255), basic_always_full_screen boolean, comment varchar, - icon varchar(255), + login_button_icon varchar(255), + login_button_label varchar(255), help_link varchar(255), CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) ); diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 9d50e687e8..a8f014ab22 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -373,7 +373,8 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( basic_default_dashboard_name varchar(255), basic_always_full_screen boolean, comment varchar, - icon varchar(255), + login_button_icon varchar(255), + login_button_label varchar(255), help_link varchar(255), CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) ); diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java index 8836474420..44aadaafb1 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java @@ -125,7 +125,8 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { clientRegistrationTemplate.setJwkSetUri("jwkSetUri"); clientRegistrationTemplate.setClientAuthenticationMethod("clientAuthenticationMethod"); clientRegistrationTemplate.setComment("comment"); - clientRegistrationTemplate.setIcon("icon"); + clientRegistrationTemplate.setLoginButtonIcon("icon"); + clientRegistrationTemplate.setLoginButtonLabel("label"); clientRegistrationTemplate.setHelpLink("helpLink"); return clientRegistrationTemplate; } From a3f93554ee48e249ac48981ef537469eaeccd82c Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 17 Sep 2020 11:37:22 +0300 Subject: [PATCH 091/117] Updated OAuth2Templates --- .../json/system/oauth2_config_templates/facebook_config.json | 3 ++- .../json/system/oauth2_config_templates/github_config.json | 3 ++- .../json/system/oauth2_config_templates/google_config.json | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json b/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json index 1c81a443f3..81097ccc49 100644 --- a/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json +++ b/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json @@ -14,6 +14,7 @@ "tenantNameStrategy": "DOMAIN" }, "comment": null, - "icon": "mdi:facebook", + "loginButtonIcon": "mdi:facebook", + "loginButtonLabel": "Login with Facebook", "helpLink": "https://developers.facebook.com/docs/facebook-login/web#logindialog" } \ No newline at end of file diff --git a/application/src/main/data/json/system/oauth2_config_templates/github_config.json b/application/src/main/data/json/system/oauth2_config_templates/github_config.json index 17dc1fda4a..819ea4b6b6 100644 --- a/application/src/main/data/json/system/oauth2_config_templates/github_config.json +++ b/application/src/main/data/json/system/oauth2_config_templates/github_config.json @@ -9,6 +9,7 @@ "userNameAttributeName": "login", "basic": {}, "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 Github Documentation", - "icon": "mdi:github", + "loginButtonIcon": "mdi:github", + "loginButtonLabel": "Login with Github", "helpLink": "https://docs.github.com/en/developers/apps/creating-an-oauth-app" } \ No newline at end of file diff --git a/application/src/main/data/json/system/oauth2_config_templates/google_config.json b/application/src/main/data/json/system/oauth2_config_templates/google_config.json index 99053b68e6..2573b5ecd0 100644 --- a/application/src/main/data/json/system/oauth2_config_templates/google_config.json +++ b/application/src/main/data/json/system/oauth2_config_templates/google_config.json @@ -15,6 +15,7 @@ "tenantNameStrategy": "DOMAIN" }, "comment": null, - "icon": "mdi:google", + "loginButtonIcon": "mdi:google", + "loginButtonLabel": "Login with Google", "helpLink": "https://developers.google.com/adwords/api/docs/guides/authentication" } \ No newline at end of file From 26bb51f0e4f6f746abd4ff80cd34944e3c5e1fc9 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Thu, 17 Sep 2020 19:31:01 +0300 Subject: [PATCH 092/117] Updated OAuth2 templated --- .../system/oauth2_config_templates/facebook_config.json | 6 +++--- .../json/system/oauth2_config_templates/github_config.json | 6 +++--- .../json/system/oauth2_config_templates/google_config.json | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json b/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json index 81097ccc49..9b059e81c1 100644 --- a/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json +++ b/application/src/main/data/json/system/oauth2_config_templates/facebook_config.json @@ -5,7 +5,7 @@ "scope": ["email","public_profile"], "jwkSetUri": null, "userInfoUri": "https://graph.facebook.com/me?fields=id,name,first_name,last_name,email", - "clientAuthenticationMethod": "basic", + "clientAuthenticationMethod": "BASIC", "userNameAttributeName": "email", "basic": { "emailAttributeKey": "email", @@ -15,6 +15,6 @@ }, "comment": null, "loginButtonIcon": "mdi:facebook", - "loginButtonLabel": "Login with Facebook", + "loginButtonLabel": "Facebook", "helpLink": "https://developers.facebook.com/docs/facebook-login/web#logindialog" -} \ No newline at end of file +} diff --git a/application/src/main/data/json/system/oauth2_config_templates/github_config.json b/application/src/main/data/json/system/oauth2_config_templates/github_config.json index 819ea4b6b6..58812fdc1e 100644 --- a/application/src/main/data/json/system/oauth2_config_templates/github_config.json +++ b/application/src/main/data/json/system/oauth2_config_templates/github_config.json @@ -5,11 +5,11 @@ "scope": ["read:user","user:email"], "jwkSetUri": null, "userInfoUri": "https://api.github.com/user", - "clientAuthenticationMethod": "basic", + "clientAuthenticationMethod": "BASIC", "userNameAttributeName": "login", "basic": {}, "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 Github Documentation", "loginButtonIcon": "mdi:github", - "loginButtonLabel": "Login with Github", + "loginButtonLabel": "Github", "helpLink": "https://docs.github.com/en/developers/apps/creating-an-oauth-app" -} \ No newline at end of file +} diff --git a/application/src/main/data/json/system/oauth2_config_templates/google_config.json b/application/src/main/data/json/system/oauth2_config_templates/google_config.json index 2573b5ecd0..2756bcc957 100644 --- a/application/src/main/data/json/system/oauth2_config_templates/google_config.json +++ b/application/src/main/data/json/system/oauth2_config_templates/google_config.json @@ -6,7 +6,7 @@ "scope": ["email","openid","profile"], "jwkSetUri": "https://www.googleapis.com/oauth2/v3/certs", "userInfoUri": "https://openidconnect.googleapis.com/v1/userinfo", - "clientAuthenticationMethod": "basic", + "clientAuthenticationMethod": "BASIC", "userNameAttributeName": "email", "basic": { "emailAttributeKey": "email", @@ -16,6 +16,6 @@ }, "comment": null, "loginButtonIcon": "mdi:google", - "loginButtonLabel": "Login with Google", + "loginButtonLabel": "Google", "helpLink": "https://developers.google.com/adwords/api/docs/guides/authentication" -} \ No newline at end of file +} From 5275a3302ab8d9fd7927c90f549723edc604d076 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 18 Sep 2020 11:19:41 +0300 Subject: [PATCH 093/117] Added 'additionalInfo' to ClientRegistration --- .../SearchTextBasedWithAdditionalInfo.java | 1 + .../data/oauth2/ClientRegistrationDto.java | 2 ++ .../data/oauth2/OAuth2ClientRegistration.java | 14 ++++++-- .../OAuth2ClientRegistrationTemplate.java | 32 ++++++------------- .../server/dao/model/ModelConstants.java | 1 + .../sql/OAuth2ClientRegistrationEntity.java | 8 +++++ .../server/dao/oauth2/OAuth2Utils.java | 2 ++ .../BaseOAuth2ConfigTemplateServiceTest.java | 1 + .../dao/service/BaseOAuth2ServiceTest.java | 1 + 9 files changed, 37 insertions(+), 25 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java index 8dc9bf6abc..34dc711e0c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java @@ -59,6 +59,7 @@ public abstract class SearchTextBasedWithAdditionalInfo ext } public void setAdditionalInfo(JsonNode addInfo) { + // TODO why set additionalInfoBytes to [110,117,108,108] if JsonNode is null setJson(addInfo, json -> this.additionalInfo = json, bytes -> this.additionalInfoBytes = bytes); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java index 6f762c0e7a..eef79bda6c 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java @@ -16,6 +16,7 @@ package org.thingsboard.server.common.data.oauth2; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.JsonNode; import lombok.*; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.TenantId; @@ -43,4 +44,5 @@ public class ClientRegistrationDto { private String clientAuthenticationMethod; private String loginButtonLabel; private String loginButtonIcon; + private JsonNode additionalInfo; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java index 1e7ff2a1e7..64cfb59511 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -16,10 +16,13 @@ package org.thingsboard.server.common.data.oauth2; import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.*; -import org.thingsboard.server.common.data.BaseData; +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.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.TenantId; @@ -29,7 +32,7 @@ import java.util.List; @Data @ToString(exclude = {"clientSecret"}) @NoArgsConstructor -public class OAuth2ClientRegistration extends BaseData implements HasTenantId, HasName { +public class OAuth2ClientRegistration extends SearchTextBasedWithAdditionalInfo implements HasTenantId, HasName { private TenantId tenantId; private String domainName; @@ -71,4 +74,9 @@ public class OAuth2ClientRegistration extends BaseData implements HasTenantId, HasName { +public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo implements HasTenantId, HasName { private TenantId tenantId; private String providerId; @@ -48,14 +48,6 @@ public class OAuth2ClientRegistrationTemplate extends BaseData additionalInfo, () -> additionalInfoBytes); - } - - public void setAdditionalInfo(JsonNode addInfo) { - SearchTextBasedWithAdditionalInfo.setJson(addInfo, json -> this.additionalInfo = json, bytes -> this.additionalInfoBytes = bytes); } @Override public String getName() { return providerId; } + + @Override + public String getSearchText() { + return getName(); + } } diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java index 2705be5d47..b9d75ab6d0 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/ModelConstants.java @@ -390,6 +390,7 @@ public class ModelConstants { 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; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java index 4e99dbbb5f..4f2513f2ce 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java @@ -15,8 +15,10 @@ */ 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.OAuth2ClientRegistrationId; import org.thingsboard.server.common.data.id.TenantId; @@ -98,6 +100,10 @@ public class OAuth2ClientRegistrationEntity extends BaseSqlEntity Date: Mon, 21 Sep 2020 10:53:42 +0300 Subject: [PATCH 094/117] UI updated OAuth2 for new endpoint --- ui-ngx/src/app/core/http/admin.service.ts | 20 +- .../admin/oauth2-settings.component.html | 43 ++-- .../pages/admin/oauth2-settings.component.ts | 220 ++++++++++++------ .../app/shared/models/entity-type.models.ts | 3 +- .../src/app/shared/models/settings.models.ts | 10 +- .../assets/locale/locale.constant-en_US.json | 4 +- 6 files changed, 191 insertions(+), 109 deletions(-) diff --git a/ui-ngx/src/app/core/http/admin.service.ts b/ui-ngx/src/app/core/http/admin.service.ts index 38a7c6e5e9..d11a8f373e 100644 --- a/ui-ngx/src/app/core/http/admin.service.ts +++ b/ui-ngx/src/app/core/http/admin.service.ts @@ -60,17 +60,27 @@ export class AdminService { defaultHttpOptionsFromConfig(config)); } - public getOAuth2Settings(config?: RequestConfig): Observable { - return this.http.get(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config)); + public getOAuth2Settings(config?: RequestConfig): Observable> { + return this.http.get>(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config)); } public getOAuth2Template(config?: RequestConfig): Observable> { return this.http.get>(`/api/oauth2/config/template`, defaultHttpOptionsFromConfig(config)); } - public saveOAuth2Settings(OAuth2Setting: OAuth2Settings, - config?: RequestConfig): Observable { - return this.http.post('/api/oauth2/config', OAuth2Setting, + public saveOAuth2Settings(OAuth2Setting: OAuth2Settings[], + config?: RequestConfig): Observable> { + return this.http.post>('/api/oauth2/config', OAuth2Setting, + defaultHttpOptionsFromConfig(config)); + } + + public deleteOAuth2Domain(OAuth2Domain: string, config?: RequestConfig) { + return this.http.delete(`/api/oauth2/config/domain/${OAuth2Domain}`, + defaultHttpOptionsFromConfig(config)); + } + + public deleteOAuthCclientRegistrationId(clientRegistrationId: string, config?: RequestConfig) { + return this.http.delete(`/api/oauth2/config/${clientRegistrationId}`, defaultHttpOptionsFromConfig(config)); } diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index fce4f0339f..4e45e02945 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -30,10 +30,10 @@
- +
- + @@ -75,7 +75,7 @@ class="registration-card mat-elevation-z0"> - {{ registration.get('providerName').value }} + {{ getProviderName(registration) }} @@ -152,7 +155,7 @@ @@ -171,7 +174,7 @@ @@ -185,7 +188,7 @@ @@ -207,7 +210,7 @@ -
+
admin.oauth2.login-button-label @@ -304,7 +307,7 @@ - + admin.oauth2.tenant-name-pattern { if (domain === 'CUSTOM') { - basicGroup.get('tenantNamePattern').setValidators(Validators.required); + basicGroup.get('tenantNamePattern').enable(); } else { - basicGroup.get('tenantNamePattern').clearValidators(); + basicGroup.get('tenantNamePattern').disable(); } })); return basicGroup; } - get formCustomGroup(): FormGroup { + private formCustomGroup(mapperConfigCustom?: MapperConfigCustom): FormGroup { return this.fb.group({ - url: [null, [Validators.required, Validators.pattern(this.URL_REGEXP)]], - username: [null], - password: [null] + url: [mapperConfigCustom?.url ? mapperConfigCustom.url : null, [Validators.required, Validators.pattern(this.URL_REGEXP)]], + username: [mapperConfigCustom?.username ? mapperConfigCustom.username : null], + password: [mapperConfigCustom?.password ? mapperConfigCustom.password : null] }); } private buildOAuth2SettingsForm(): void { this.oauth2SettingsForm = this.fb.group({ - clientsDomainsParams: this.fb.array([], Validators.required) + clientDomains: this.fb.array([]) }); } - private initOAuth2Settings(oauth2Settings: OAuth2Settings): void { - if (oauth2Settings.clientsDomainsParams) { - oauth2Settings.clientsDomainsParams.forEach((domaindomain) => { - this.clientsDomainsParams.push(this.buildSettingsDomain(domaindomain)); + private initOAuth2Settings(oauth2Settings: OAuth2Settings[]): void { + if (oauth2Settings) { + oauth2Settings.forEach((domain) => { + this.clientDomains.push(this.buildSettingsDomain(domain)); }); } } private uniqueDomainValidator(control: AbstractControl): { [key: string]: boolean } | null { - if (control.value !== null && control?.root) { + if (control.root.value.clientDomains?.length > 1) { const listDomainName = []; - control.root.value.clientsDomainsParams.forEach((domain) => { + control.root.value.clientDomains.forEach((domain) => { listDomainName.push(domain.domainName); }); if (listDomainName.indexOf(control.value) > -1) { @@ -183,7 +191,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha return null; } - private buildSettingsDomain(domainParams?: DomainParams): FormGroup { + private buildSettingsDomain(domainParams?: OAuth2Settings): FormGroup { let url = this.window.location.protocol + '//' + this.window.location.hostname; const port = this.window.location.port; if (port !== '80' && port !== '443') { @@ -191,8 +199,13 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } url += '/login/oauth2/code/'; const formDomain = this.fb.group({ - domainName: [null, [Validators.required, Validators.pattern('((?![:/]).)*$'), this.uniqueDomainValidator]], - redirectUriTemplate: [url, [Validators.required, Validators.pattern(this.URL_REGEXP)]], + domainName: [domainParams?.domainName ? domainParams.domainName : this.window.location.hostname, [ + Validators.required, + Validators.pattern('((?![:/]).)*$'), + this.uniqueDomainValidator]], + redirectUriTemplate: [domainParams?.redirectUriTemplate ? domainParams.redirectUriTemplate : url, [ + Validators.required, + Validators.pattern(this.URL_REGEXP)]], clientRegistrations: this.fb.array([], Validators.required) }); @@ -201,7 +214,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha domain = this.window.location.hostname; } const uri = this.window.location.protocol + `//${domain}/login/oauth2/code/`; - formDomain.get('redirectUriTemplate').patchValue(uri); + formDomain.get('redirectUriTemplate').patchValue(uri, {emitEvent: false}); })); if (domainParams) { @@ -216,40 +229,59 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } private buildSettingsRegistration(registrationData?: ClientRegistration): FormGroup { + let additionalInfo = null; + if (registrationData?.additionalInfo) { + additionalInfo = JSON.parse(registrationData.additionalInfo); + if (this.templateProvider.indexOf(additionalInfo.providerName) === -1) { + additionalInfo.providerName = 'Custom'; + } + } const clientRegistration = this.fb.group({ - providerName: ['Custom', [Validators.required]], - loginButtonLabel: [null, [Validators.required]], - loginButtonIcon: [null], - clientId: ['', [Validators.required]], - clientSecret: ['', [Validators.required]], - accessTokenUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], - authorizationUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], - scope: this.fb.array([], [Validators.required]), - jwkSetUri: ['', [Validators.pattern(this.URL_REGEXP)]], - userInfoUri: ['', [Validators.required, Validators.pattern(this.URL_REGEXP)]], - clientAuthenticationMethod: ['POST', [Validators.required]], - userNameAttributeName: ['email', [Validators.required]], + id: this.fb.group({ + id: [registrationData?.id?.id ? registrationData.id.id : null], + entityType: [registrationData?.id?.entityType ? registrationData.id.entityType : null] + }), + additionalInfo: this.fb.group({ + providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : 'Custom', Validators.required] + }), + loginButtonLabel: [registrationData?.loginButtonLabel ? registrationData.loginButtonLabel : null, Validators.required], + loginButtonIcon: [registrationData?.loginButtonIcon ? registrationData.loginButtonIcon : null], + clientId: [registrationData?.clientId ? registrationData.clientId : '', Validators.required], + clientSecret: [registrationData?.clientSecret ? registrationData.clientSecret : '', Validators.required], + accessTokenUri: [registrationData?.accessTokenUri ? registrationData.accessTokenUri : '', [ + Validators.required, + Validators.pattern(this.URL_REGEXP)]], + authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '', [ + Validators.required, + Validators.pattern(this.URL_REGEXP)]], + scope: this.fb.array(registrationData?.scope ? registrationData.scope : []), + jwkSetUri: [registrationData?.jwkSetUri ? registrationData.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], + userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '', [ + Validators.required, + Validators.pattern(this.URL_REGEXP)]], + clientAuthenticationMethod: [ + registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : 'POST', Validators.required], + userNameAttributeName: [ + registrationData?.userNameAttributeName ? registrationData.userNameAttributeName : 'email', Validators.required], mapperConfig: this.fb.group({ - allowUserCreation: [true], - activateUser: [false], - type: ['BASIC', [Validators.required]], - basic: this.formBasicGroup + allowUserCreation: [registrationData?.mapperConfig?.allowUserCreation ? registrationData.mapperConfig.allowUserCreation : true], + activateUser: [registrationData?.mapperConfig?.activateUser ? registrationData.mapperConfig.activateUser : false], + type: [registrationData?.mapperConfig?.type ? registrationData.mapperConfig.type : 'BASIC', Validators.required] } ) }); + if (registrationData) { + this.changeMapperConfigType(clientRegistration, registrationData.mapperConfig.type, registrationData.mapperConfig); + } else { + this.changeMapperConfigType(clientRegistration, 'BASIC'); + } + this.subscriptions.push(clientRegistration.get('mapperConfig.type').valueChanges.subscribe((value) => { - const mapperConfig = clientRegistration.get('mapperConfig') as FormGroup; - if (value === 'BASIC') { - mapperConfig.removeControl('custom'); - mapperConfig.addControl('basic', this.formBasicGroup); - } else { - mapperConfig.removeControl('basic'); - mapperConfig.addControl('custom', this.formCustomGroup); - } + this.changeMapperConfigType(clientRegistration, value); })); - this.subscriptions.push(clientRegistration.get('providerName').valueChanges.subscribe((provider) => { + this.subscriptions.push(clientRegistration.get('additionalInfo.providerName').valueChanges.subscribe((provider) => { (clientRegistration.get('scope') as FormArray).clear(); if (provider === 'Custom') { clientRegistration.reset(this.defaultProvider, {emitEvent: false}); @@ -259,6 +291,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha clientRegistration.get('userInfoUri').enable(); } else { const template = this.templates.get(provider); + delete template.id; + delete template.additionalInfo; template.scope.forEach(() => { (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); }); @@ -270,20 +304,23 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } })); - if (registrationData) { - registrationData.scope.forEach(() => { - (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); - }); - if (registrationData.mapperConfig.type !== 'BASIC') { - clientRegistration.get('mapperConfig.type').patchValue('CUSTOM'); - } - } - return clientRegistration; } + private changeMapperConfigType(control: AbstractControl, type: MapperConfigType, predefinedValue?: MapperConfig) { + const mapperConfig = control.get('mapperConfig') as FormGroup; + if (type === 'BASIC') { + mapperConfig.removeControl('custom'); + mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic)); + } else { + mapperConfig.removeControl('basic'); + mapperConfig.addControl('custom', this.formCustomGroup(predefinedValue?.custom)); + } + } + save(): void { - this.adminService.saveOAuth2Settings(this.oauth2SettingsForm.value).subscribe( + const setting = this.prepareFormValue(this.oauth2SettingsForm.getRawValue().clientDomains); + this.adminService.saveOAuth2Settings(setting).subscribe( (oauth2Settings) => { this.oauth2Settings = oauth2Settings; this.oauth2SettingsForm.markAsPristine(); @@ -292,6 +329,18 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha ); } + private prepareFormValue(formValue: OAuth2Settings[]): OAuth2Settings[]{ + formValue.forEach((setting) => { + setting.clientRegistrations.forEach((registration) => { + registration.additionalInfo = JSON.stringify(registration.additionalInfo); + if (registration.id.id === null) { + delete registration.id; + } + }); + }); + return formValue; + } + confirmForm(): FormGroup { return this.oauth2SettingsForm; } @@ -315,7 +364,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } addDomain(): void { - this.clientsDomainsParams.push(this.buildSettingsDomain()); + this.clientDomains.push(this.buildSettingsDomain()); } deleteDomain($event: Event, index: number): void { @@ -324,14 +373,21 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha $event.preventDefault(); } - const domainName = this.clientsDomainsParams.at(index).get('domainName').value; + const domainName = this.clientDomains.at(index).get('domainName').value; this.dialogService.confirm( this.translate.instant('admin.oauth2.delete-domain-title', {domainName: domainName || ''}), this.translate.instant('admin.oauth2.delete-domain-text'), null, this.translate.instant('action.delete') ).subscribe((data) => { if (data) { - this.clientsDomainsParams.removeAt(index); + if (index < this.oauth2Settings.length) { + this.adminService.deleteOAuth2Domain(this.oauth2Settings[index].domainName).subscribe(() => { + this.oauth2Settings.splice(index, 1); + this.clientDomains.removeAt(index); + }); + } else { + this.clientDomains.removeAt(index); + } } }); } @@ -344,20 +400,27 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.clientDomainRegistrations(control).push(this.buildSettingsRegistration()); } - deleteRegistration($event: Event, controler: AbstractControl, index: number): void { + deleteRegistration($event: Event, control: AbstractControl, index: number): void { if ($event) { $event.stopPropagation(); $event.preventDefault(); } - const providerName = this.clientDomainRegistrations(controler).at(index).get('providerName').value; + const providerName = this.clientDomainRegistrations(control).at(index).get('additionalInfo.providerName').value; this.dialogService.confirm( this.translate.instant('admin.oauth2.delete-registration-title', {name: providerName || ''}), this.translate.instant('admin.oauth2.delete-registration-text'), null, this.translate.instant('action.delete') ).subscribe((data) => { if (data) { - this.clientDomainRegistrations(controler).removeAt(index); + const registrationId = this.clientDomainRegistrations(control).at(index).get('id.id').value; + if (registrationId) { + this.adminService.deleteOAuthCclientRegistrationId(registrationId).subscribe(() => { + this.clientDomainRegistrations(control).removeAt(index); + }); + } else { + this.clientDomainRegistrations(control).removeAt(index); + } } }); } @@ -385,7 +448,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha overlayRef.dispose(); }); - const redirectURI = this.clientsDomainsParams.at(index).get('redirectUriTemplate').value; + const redirectURI = this.clientDomains.at(index).get('redirectUriTemplate').value; const injectionTokens = new WeakMap([ [EDIT_REDIRECT_URI_PANEL_DATA, { @@ -399,7 +462,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha componentRef.onDestroy(() => { if (componentRef.instance.result !== null) { const attributeValue = componentRef.instance.result; - this.clientsDomainsParams.at(index).get('redirectUriTemplate').patchValue(attributeValue, {emitEvent: true}); + this.clientDomains.at(index).get('redirectUriTemplate').patchValue(attributeValue, {emitEvent: true}); } }); } @@ -407,4 +470,13 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha toggleEditMode(control: AbstractControl, path: string) { control.get(path).disabled ? control.get(path).enable() : control.get(path).disable(); } + + getProviderName(controller: AbstractControl): string { + return controller.get('additionalInfo.providerName').value; + } + + getHelpLink(controller: AbstractControl): string { + const provider = controller.get('additionalInfo.providerName').value; + return this.templates.get(provider).helpLink; + } } diff --git a/ui-ngx/src/app/shared/models/entity-type.models.ts b/ui-ngx/src/app/shared/models/entity-type.models.ts index 6841a911b7..f3888fe4be 100644 --- a/ui-ngx/src/app/shared/models/entity-type.models.ts +++ b/ui-ngx/src/app/shared/models/entity-type.models.ts @@ -45,7 +45,8 @@ export enum EntityType { RULE_NODE = 'RULE_NODE', ENTITY_VIEW = 'ENTITY_VIEW', WIDGETS_BUNDLE = 'WIDGETS_BUNDLE', - WIDGET_TYPE = 'WIDGET_TYPE' + WIDGET_TYPE = 'WIDGET_TYPE', + OAUTH2_CLIENT_REGISTRATION = 'OAUTH2_CLIENT_REGISTRATION' } export enum AliasEntityType { diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts index 8fe83aee09..f27d80a260 100644 --- a/ui-ngx/src/app/shared/models/settings.models.ts +++ b/ui-ngx/src/app/shared/models/settings.models.ts @@ -69,21 +69,15 @@ export interface UpdateMessage { } export interface OAuth2Settings { - clientsDomainsParams: DomainParams[]; -} - -export interface DomainParams { domainName: string; redirectUriTemplate: string; clientRegistrations: ClientRegistration[]; } export interface ClientProviderTemplated extends ClientRegistration{ - additionalInfo: string; comment: string; createdTime: number; helpLink: string; - id: EntityId; name: string; providerId: string; tenantId: TenantId; @@ -97,11 +91,13 @@ export interface ClientRegistration { accessTokenUri: string; authorizationUri: string; scope: string[]; - jwkSetUri: string; + jwkSetUri?: string; userInfoUri: string; clientAuthenticationMethod: ClientAuthenticationMethod; userNameAttributeName: string; mapperConfig: MapperConfig; + id?: EntityId; + additionalInfo: string; } export interface MapperConfig { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 802575c0ff..50b74e393a 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -166,8 +166,8 @@ "url": "URL", "url-required": "URL is required.", "url-pattern": "Invalid URL format.", - "login-button-label": "Login button label", - "login-button-label-required": "Login button label is required.", + "login-button-label": "Label (Login with ${label})", + "login-button-label-required": "Label is required.", "login-button-icon": "Login button icon", "delete-domain-title": "Are you sure you want to delete the domain '{{domainName}}'?", "delete-domain-text": "Be careful, after the confirmation a domain and all registration data will be unavailable.", From 86aacc07ae70206c55083c3d2293f350d60de792 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Mon, 21 Sep 2020 13:40:24 +0300 Subject: [PATCH 095/117] Set default value to 'false' for 'isOAuth2ConfigAllowed' --- .../org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java | 2 +- .../thingsboard/server/dao/service/BaseOAuth2ServiceTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 70b0f1edfd..788c1293da 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -129,7 +129,7 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se if (tenant == null) return false; JsonNode allowOAuth2ConfigurationJsonNode = tenant.getAdditionalInfo() != null ? tenant.getAdditionalInfo().get(ALLOW_OAUTH2_CONFIGURATION) : null; if (allowOAuth2ConfigurationJsonNode == null) { - return true; + return false; } else { return allowOAuth2ConfigurationJsonNode.asBoolean(); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index 975b6f318b..2231bc6dfe 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -66,7 +66,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testIsOAuth2Allowed_null() throws IOException { updateTenantAllowOAuth2Setting(null); - Assert.assertTrue(oAuth2Service.isOAuth2ClientRegistrationAllowed(tenantId)); + Assert.assertFalse(oAuth2Service.isOAuth2ClientRegistrationAllowed(tenantId)); } @Test From 4c07e721175ed51f86b2aa578dc97ad5db5c201d Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 21 Sep 2020 14:00:13 +0300 Subject: [PATCH 096/117] Refactoring --- ...ard.ts => redirect-guard-settings.service.ts} | 4 ++-- .../home/pages/admin/admin-routing.module.ts | 4 ++-- .../pages/admin/oauth2-settings.component.ts | 16 +++++++++++++--- 3 files changed, 17 insertions(+), 7 deletions(-) rename ui-ngx/src/app/core/guards/{redirect.guard.ts => redirect-guard-settings.service.ts} (93%) diff --git a/ui-ngx/src/app/core/guards/redirect.guard.ts b/ui-ngx/src/app/core/guards/redirect-guard-settings.service.ts similarity index 93% rename from ui-ngx/src/app/core/guards/redirect.guard.ts rename to ui-ngx/src/app/core/guards/redirect-guard-settings.service.ts index 652f1ff026..4c69f21aae 100644 --- a/ui-ngx/src/app/core/guards/redirect.guard.ts +++ b/ui-ngx/src/app/core/guards/redirect-guard-settings.service.ts @@ -26,7 +26,7 @@ import { Authority } from '@shared/models/authority.enum'; @Injectable({ providedIn: 'root' }) -export class RedirectGuard implements CanActivate { +export class RedirectGuardSettings implements CanActivate { constructor(private store: Store, private router: Router) { } @@ -41,7 +41,7 @@ export class RedirectGuard implements CanActivate { ); if (auth?.userDetails?.authority === Authority.TENANT_ADMIN) { - this.router.navigateByUrl('/settings/oauth2-settings'); + this.router.navigateByUrl('/settings/oauth2'); return false; } this.router.navigateByUrl('/settings/general'); diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index 16635ef53f..c4a38fdf30 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -23,7 +23,7 @@ import { Authority } from '@shared/models/authority.enum'; import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component'; import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; import { OAuth2SettingsComponent } from '@home/pages/admin/oauth2-settings.component'; -import { RedirectGuard } from '@core/guards/redirect.guard'; +import { RedirectGuardSettings } from '../../../../core/guards/redirect-guard-settings.service'; const routes: Routes = [ { @@ -38,7 +38,7 @@ const routes: Routes = [ children: [ { path: '', - canActivate: [RedirectGuard], + canActivate: [RedirectGuardSettings], pathMatch: 'full' }, { diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index 5d4c921700..1f627fd2de 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -57,7 +57,9 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private subscriptions: Subscription[] = []; private templates = new Map(); private defaultProvider = { - providerName: 'Custom', + additionalInfo: { + providerName: 'Custom' + }, clientAuthenticationMethod: 'Post', userNameAttributeName: 'email', mapperConfig: { @@ -105,7 +107,6 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.initTemplates(templates); this.oauth2Settings = oauth2Settings; this.initOAuth2Settings(this.oauth2Settings); - // this.oauth2SettingsForm.get('clientDomains').updateValueAndValidity(); } ); } @@ -300,7 +301,13 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha clientRegistration.get('authorizationUri').disable(); clientRegistration.get('jwkSetUri').disable(); clientRegistration.get('userInfoUri').disable(); - clientRegistration.patchValue(this.templates.get(provider), {emitEvent: false}); + clientRegistration.patchValue({ + ...template, ...{ + clientId: '', + clientSecret: '', + id: {id: null, entityType: null} + } + }, {emitEvent: false}); } })); @@ -477,6 +484,9 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha getHelpLink(controller: AbstractControl): string { const provider = controller.get('additionalInfo.providerName').value; + if (provider === null || provider === 'Custom') { + return ''; + } return this.templates.get(provider).helpLink; } } From 556cb5aa5df8ddd7eafe39e5a9336d63d5d88226 Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 21 Sep 2020 16:10:00 +0300 Subject: [PATCH 097/117] Refactoring OAuth2 --- ui-ngx/src/app/core/auth/auth.models.ts | 2 - ui-ngx/src/app/core/auth/auth.reducer.ts | 1 - ui-ngx/src/app/core/auth/auth.service.ts | 14 +---- .../guards/redirect-guard-settings.service.ts | 51 ------------------- ui-ngx/src/app/core/services/menu.service.ts | 24 +-------- .../home/pages/admin/admin-routing.module.ts | 5 +- .../pages/admin/oauth2-settings.component.ts | 36 +++++++------ .../home/pages/tenant/tenant.component.html | 3 -- .../home/pages/tenant/tenant.component.ts | 14 ++--- .../assets/locale/locale.constant-en_US.json | 4 +- 10 files changed, 26 insertions(+), 128 deletions(-) delete mode 100644 ui-ngx/src/app/core/guards/redirect-guard-settings.service.ts diff --git a/ui-ngx/src/app/core/auth/auth.models.ts b/ui-ngx/src/app/core/auth/auth.models.ts index 573b692a29..c7210da6ed 100644 --- a/ui-ngx/src/app/core/auth/auth.models.ts +++ b/ui-ngx/src/app/core/auth/auth.models.ts @@ -22,7 +22,6 @@ export interface AuthPayload { userTokenAccessEnabled: boolean; allowedDashboardIds: string[]; forceFullscreen: boolean; - allowOAuth2Configuration: boolean; } export interface AuthState { @@ -34,5 +33,4 @@ export interface AuthState { allowedDashboardIds: string[]; forceFullscreen: boolean; lastPublicDashboardId: string; - allowOAuth2Configuration: boolean; } diff --git a/ui-ngx/src/app/core/auth/auth.reducer.ts b/ui-ngx/src/app/core/auth/auth.reducer.ts index 0e7ca095af..6e02d03191 100644 --- a/ui-ngx/src/app/core/auth/auth.reducer.ts +++ b/ui-ngx/src/app/core/auth/auth.reducer.ts @@ -22,7 +22,6 @@ const emptyUserAuthState: AuthPayload = { userDetails: null, userTokenAccessEnabled: false, forceFullscreen: false, - allowOAuth2Configuration: false, allowedDashboardIds: [] }; diff --git a/ui-ngx/src/app/core/auth/auth.service.ts b/ui-ngx/src/app/core/auth/auth.service.ts index f0b9c09a1e..32733ac139 100644 --- a/ui-ngx/src/app/core/auth/auth.service.ts +++ b/ui-ngx/src/app/core/auth/auth.service.ts @@ -18,7 +18,7 @@ import { Injectable, NgZone } from '@angular/core'; import { JwtHelperService } from '@auth0/angular-jwt'; import { HttpClient } from '@angular/common/http'; -import { forkJoin, Observable, of, throwError, ReplaySubject } from 'rxjs'; +import { forkJoin, Observable, of, ReplaySubject, throwError } from 'rxjs'; import { catchError, map, mergeMap, tap } from 'rxjs/operators'; import { LoginRequest, LoginResponse, OAuth2Client, PublicLoginRequest } from '@shared/models/login.models'; @@ -425,25 +425,15 @@ export class AuthService { } } - private loadIsOAuth2ConfigurationAllow(authUser: AuthUser): Observable { - if (authUser.authority === Authority.TENANT_ADMIN) { - return this.http.get('/api/oauth2/config/isAllowed', defaultHttpOptions()); - } else { - return of(false); - } - } - private loadSystemParams(authPayload: AuthPayload): Observable { const sources: Array> = [this.loadIsUserTokenAccessEnabled(authPayload.authUser), this.fetchAllowedDashboardIds(authPayload), - this.loadIsOAuth2ConfigurationAllow(authPayload.authUser), this.timeService.loadMaxDatapointsLimit()]; return forkJoin(sources) .pipe(map((data) => { const userTokenAccessEnabled: boolean = data[0]; const allowedDashboardIds: string[] = data[1]; - const allowOAuth2Configuration: boolean = data[2]; - return {userTokenAccessEnabled, allowedDashboardIds, allowOAuth2Configuration}; + return {userTokenAccessEnabled, allowedDashboardIds}; })); } diff --git a/ui-ngx/src/app/core/guards/redirect-guard-settings.service.ts b/ui-ngx/src/app/core/guards/redirect-guard-settings.service.ts deleted file mode 100644 index 4c69f21aae..0000000000 --- a/ui-ngx/src/app/core/guards/redirect-guard-settings.service.ts +++ /dev/null @@ -1,51 +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. -/// - -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; -import { AuthState } from '@core/auth/auth.models'; -import { select, Store } from '@ngrx/store'; -import { selectAuth } from '@core/auth/auth.selectors'; -import { take } from 'rxjs/operators'; -import { AppState } from '@core/core.state'; -import { Authority } from '@shared/models/authority.enum'; - -@Injectable({ - providedIn: 'root' -}) -export class RedirectGuardSettings implements CanActivate { - constructor(private store: Store, - private router: Router) { } - - canActivate( - next: ActivatedRouteSnapshot, - state: RouterStateSnapshot) { - let auth: AuthState = null; - this.store.pipe(select(selectAuth), take(1)).subscribe( - (authState: AuthState) => { - auth = authState; - } - ); - - if (auth?.userDetails?.authority === Authority.TENANT_ADMIN) { - this.router.navigateByUrl('/settings/oauth2'); - return false; - } - this.router.navigateByUrl('/settings/general'); - return false; - } - -} diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index 7d3503a613..14f3a632db 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -18,13 +18,12 @@ import { Injectable } from '@angular/core'; import { AuthService } from '../auth/auth.service'; import { select, Store } from '@ngrx/store'; import { AppState } from '../core.state'; -import { getCurrentAuthState, selectAuthUser, selectIsAuthenticated } from '../auth/auth.selectors'; +import { selectAuthUser, selectIsAuthenticated } from '../auth/auth.selectors'; import { take } from 'rxjs/operators'; import { HomeSection, MenuSection } from '@core/services/menu.models'; import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { Authority } from '@shared/models/authority.enum'; import { AuthUser } from '@shared/models/user.model'; -import { AuthState } from '@core/auth/auth.models'; @Injectable({ providedIn: 'root' @@ -44,8 +43,6 @@ export class MenuService { ); } - authState: AuthState = getCurrentAuthState(this.store); - private buildMenu() { this.store.pipe(select(selectAuthUser), take(1)).subscribe( (authUser: AuthUser) => { @@ -236,25 +233,6 @@ export class MenuService { icon: 'track_changes' } ); - - if (this.authState.allowOAuth2Configuration) { - sections.push({ - name: 'admin.settings', - type: 'toggle', - path: '/settings', - height: '40px', - icon: 'settings', - pages: [ - { - name: 'admin.oauth2.oauth2', - type: 'link', - path: '/settings/oauth2', - icon: 'security' - } - ] - }); - } - return sections; } diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index c4a38fdf30..40a8d3addd 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -23,7 +23,6 @@ import { Authority } from '@shared/models/authority.enum'; import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component'; import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; import { OAuth2SettingsComponent } from '@home/pages/admin/oauth2-settings.component'; -import { RedirectGuardSettings } from '../../../../core/guards/redirect-guard-settings.service'; const routes: Routes = [ { @@ -38,7 +37,7 @@ const routes: Routes = [ children: [ { path: '', - canActivate: [RedirectGuardSettings], + redirectTo: 'general', pathMatch: 'full' }, { @@ -85,7 +84,7 @@ const routes: Routes = [ component: OAuth2SettingsComponent, canDeactivate: [ConfirmOnExitGuard], data: { - auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + auth: [Authority.SYS_ADMIN], title: 'admin.oauth2.oauth2', breadcrumb: { label: 'admin.oauth2.oauth2', diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index 1f627fd2de..7a0e130758 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -60,7 +60,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha additionalInfo: { providerName: 'Custom' }, - clientAuthenticationMethod: 'Post', + clientAuthenticationMethod: 'POST', userNameAttributeName: 'email', mapperConfig: { allowUserCreation: true, @@ -249,17 +249,17 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha loginButtonIcon: [registrationData?.loginButtonIcon ? registrationData.loginButtonIcon : null], clientId: [registrationData?.clientId ? registrationData.clientId : '', Validators.required], clientSecret: [registrationData?.clientSecret ? registrationData.clientSecret : '', Validators.required], - accessTokenUri: [registrationData?.accessTokenUri ? registrationData.accessTokenUri : '', [ - Validators.required, - Validators.pattern(this.URL_REGEXP)]], - authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '', [ - Validators.required, - Validators.pattern(this.URL_REGEXP)]], + accessTokenUri: [registrationData?.accessTokenUri ? registrationData.accessTokenUri : '', + [Validators.required, + Validators.pattern(this.URL_REGEXP)]], + authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '', + [Validators.required, + Validators.pattern(this.URL_REGEXP)]], scope: this.fb.array(registrationData?.scope ? registrationData.scope : []), jwkSetUri: [registrationData?.jwkSetUri ? registrationData.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], - userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '', [ - Validators.required, - Validators.pattern(this.URL_REGEXP)]], + userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '', + [Validators.required, + Validators.pattern(this.URL_REGEXP)]], clientAuthenticationMethod: [ registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : 'POST', Validators.required], userNameAttributeName: [ @@ -285,7 +285,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.subscriptions.push(clientRegistration.get('additionalInfo.providerName').valueChanges.subscribe((provider) => { (clientRegistration.get('scope') as FormArray).clear(); if (provider === 'Custom') { - clientRegistration.reset(this.defaultProvider, {emitEvent: false}); + const defaultSettings = {...this.defaultProvider, ...{id: clientRegistration.get('id').value}}; + clientRegistration.reset(defaultSettings, {emitEvent: false}); clientRegistration.get('accessTokenUri').enable(); clientRegistration.get('authorizationUri').enable(); clientRegistration.get('jwkSetUri').enable(); @@ -294,6 +295,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha const template = this.templates.get(provider); delete template.id; delete template.additionalInfo; + template.clientId = ''; + template.clientSecret = ''; template.scope.forEach(() => { (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); }); @@ -301,13 +304,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha clientRegistration.get('authorizationUri').disable(); clientRegistration.get('jwkSetUri').disable(); clientRegistration.get('userInfoUri').disable(); - clientRegistration.patchValue({ - ...template, ...{ - clientId: '', - clientSecret: '', - id: {id: null, entityType: null} - } - }, {emitEvent: false}); + clientRegistration.patchValue(template, {emitEvent: false}); } })); @@ -469,7 +466,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha componentRef.onDestroy(() => { if (componentRef.instance.result !== null) { const attributeValue = componentRef.instance.result; - this.clientDomains.at(index).get('redirectUriTemplate').patchValue(attributeValue, {emitEvent: true}); + this.clientDomains.at(index).get('redirectUriTemplate').patchValue(attributeValue); + this.clientDomains.at(index).get('redirectUriTemplate').markAsDirty(); } }); } diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html index f13809ffa6..335a7d3c9a 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.html @@ -54,9 +54,6 @@ tenant.description - - {{ 'tenant.allow-oauth2-configuration' | translate }} -
diff --git a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts index aa40d792b0..cbf55857da 100644 --- a/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts +++ b/ui-ngx/src/app/modules/home/pages/tenant/tenant.component.ts @@ -23,7 +23,6 @@ import { ActionNotificationShow } from '@app/core/notification/notification.acti import { TranslateService } from '@ngx-translate/core'; import { ContactBasedComponent } from '../../components/entity/contact-based.component'; import { EntityTableConfig } from '@home/models/entity/entities-table-config.models'; -import { isDefined } from '@core/utils'; @Component({ selector: 'tb-tenant', @@ -54,11 +53,8 @@ export class TenantComponent extends ContactBasedComponent { title: [entity ? entity.title : '', [Validators.required]], isolatedTbCore: [entity ? entity.isolatedTbCore : false, []], isolatedTbRuleEngine: [entity ? entity.isolatedTbRuleEngine : false, []], - additionalInfo: this.fb.group( - { - description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''], - allowOAuth2Configuration: [isDefined(entity?.additionalInfo?.allowOAuth2Configuration) ? - entity.additionalInfo.allowOAuth2Configuration : false] + additionalInfo: this.fb.group({ + description: [entity && entity.additionalInfo ? entity.additionalInfo.description : ''] } ) } @@ -69,11 +65,7 @@ export class TenantComponent extends ContactBasedComponent { this.entityForm.patchValue({title: entity.title}); this.entityForm.patchValue({isolatedTbCore: entity.isolatedTbCore}); this.entityForm.patchValue({isolatedTbRuleEngine: entity.isolatedTbRuleEngine}); - this.entityForm.patchValue({additionalInfo: { - description: entity.additionalInfo ? entity.additionalInfo.description : '', - allowOAuth2Configuration: isDefined(entity?.additionalInfo?.allowOAuth2Configuration) ? - entity.additionalInfo.allowOAuth2Configuration : false - }}); + this.entityForm.patchValue({additionalInfo: {description: entity.additionalInfo ? entity.additionalInfo.description : ''}}); } updateFormState() { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 50b74e393a..78086a7972 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -126,7 +126,6 @@ "add-domain": "Add domain", "new-domain": "New domain", "add-provider": "Add provider", - "settings": "Settings", "oauth2": { "oauth2": "OAuth2", "registration-id": "Registration ID", @@ -1715,8 +1714,7 @@ "isolated-tb-core": "Processing in isolated ThingsBoard Core container", "isolated-tb-rule-engine": "Processing in isolated ThingsBoard Rule Engine container", "isolated-tb-core-details": "Requires separate microservice(s) per isolated Tenant", - "isolated-tb-rule-engine-details": "Requires separate microservice(s) per isolated Tenant", - "allow-oauth2-configuration": "Allow OAuth2 configuration" + "isolated-tb-rule-engine-details": "Requires separate microservice(s) per isolated Tenant" }, "timeinterval": { "seconds-interval": "{ seconds, plural, 1 {1 second} other {# seconds} }", From e778b3155afc2abb60acbd4a4e3d820a5f89f8b3 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Mon, 21 Sep 2020 17:33:27 +0300 Subject: [PATCH 098/117] Remove OAuth2Config functionality from Tenant --- .../main/data/upgrade/3.1.1/schema_update.sql | 2 - .../server/controller/BaseController.java | 43 +--- .../OAuth2ConfigTemplateController.java | 13 +- .../server/controller/OAuth2Controller.java | 66 +----- .../service/install/InstallScripts.java | 1 - .../oauth2/AbstractOAuth2ClientMapper.java | 14 +- .../auth/oauth2/BasicOAuth2ClientMapper.java | 5 +- .../auth/oauth2/CustomOAuth2ClientMapper.java | 5 +- .../auth/oauth2/OAuth2ClientMapper.java | 3 +- .../Oauth2AuthenticationSuccessHandler.java | 2 +- .../permission/SysAdminPermissions.java | 21 +- .../permission/TenantAdminPermissions.java | 37 --- .../server/dao/oauth2/OAuth2Service.java | 12 +- .../data/oauth2/OAuth2ClientRegistration.java | 4 +- .../OAuth2ClientRegistrationTemplate.java | 4 +- .../sql/OAuth2ClientRegistrationEntity.java | 7 - ...Auth2ClientRegistrationTemplateEntity.java | 7 - .../oauth2/OAuth2ClientRegistrationDao.java | 6 +- .../OAuth2ConfigTemplateServiceImpl.java | 8 +- .../server/dao/oauth2/OAuth2ServiceImpl.java | 74 ++---- .../server/dao/oauth2/OAuth2Utils.java | 12 +- .../JpaOAuth2ClientRegistrationDao.java | 16 +- .../OAuth2ClientRegistrationRepository.java | 6 +- .../server/dao/tenant/TenantServiceImpl.java | 1 - .../resources/sql/schema-entities-hsql.sql | 2 - .../main/resources/sql/schema-entities.sql | 2 - .../BaseOAuth2ConfigTemplateServiceTest.java | 23 +- .../dao/service/BaseOAuth2ServiceTest.java | 220 +++++------------- 28 files changed, 118 insertions(+), 498 deletions(-) diff --git a/application/src/main/data/upgrade/3.1.1/schema_update.sql b/application/src/main/data/upgrade/3.1.1/schema_update.sql index 6064ec8833..f0dfbe1ae3 100644 --- a/application/src/main/data/upgrade/3.1.1/schema_update.sql +++ b/application/src/main/data/upgrade/3.1.1/schema_update.sql @@ -20,7 +20,6 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, created_time bigint NOT NULL, additional_info varchar, - tenant_id uuid, domain_name varchar(255), client_id varchar(255), client_secret varchar(255), @@ -57,7 +56,6 @@ 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, - tenant_id uuid, provider_id varchar(255), authorization_uri varchar(255), token_uri varchar(255), diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index 371b423105..577e6c9e83 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -27,19 +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.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.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,8 +38,6 @@ import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.*; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.data.kv.DataType; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; -import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.page.SortOrder; import org.thingsboard.server.common.data.page.TimePageLink; @@ -388,10 +374,7 @@ public abstract class BaseController { checkWidgetTypeId(new WidgetTypeId(entityId.getId()), operation); return; case OAUTH2_CLIENT_REGISTRATION: - checkOAuth2ClientRegistrationId(new OAuth2ClientRegistrationId(entityId.getId()), operation); - return; case OAUTH2_CLIENT_REGISTRATION_TEMPLATE: - checkOAuth2ClientRegistrationTemplateId(new OAuth2ClientRegistrationTemplateId(entityId.getId()), operation); return; default: throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); @@ -545,30 +528,6 @@ public abstract class BaseController { } } - OAuth2ClientRegistration checkOAuth2ClientRegistrationId(OAuth2ClientRegistrationId clientRegistrationId, Operation operation) throws ThingsboardException { - try { - validateId(clientRegistrationId, "Incorrect oAuth2ClientRegistrationId " + clientRegistrationId); - OAuth2ClientRegistration clientRegistration = oAuth2Service.findClientRegistration(clientRegistrationId.getId()); - checkNotNull(clientRegistration); - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION, operation, clientRegistrationId, clientRegistration); - return clientRegistration; - } catch (Exception e) { - throw handleException(e, false); - } - } - - OAuth2ClientRegistrationTemplate checkOAuth2ClientRegistrationTemplateId(OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId, Operation operation) throws ThingsboardException { - try { - validateId(clientRegistrationTemplateId, "Incorrect oAuth2ClientRegistrationTemplateId " + clientRegistrationTemplateId); - OAuth2ClientRegistrationTemplate clientRegistrationTemplate = oAuth2ConfigTemplateService.findClientRegistrationTemplateById(clientRegistrationTemplateId); - checkNotNull(clientRegistrationTemplate); - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, operation, clientRegistrationTemplateId, clientRegistrationTemplate); - return clientRegistrationTemplate; - } catch (Exception e) { - throw handleException(e, false); - } - } - ComponentDescriptor checkComponentDescriptorByClazz(String clazz) throws ThingsboardException { try { log.debug("[{}] Lookup component descriptor", clazz); diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java index ed2c479e1f..1cb969e9e6 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java @@ -25,8 +25,6 @@ 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; @@ -42,8 +40,6 @@ public class OAuth2ConfigTemplateController extends BaseController { @ResponseStatus(value = HttpStatus.OK) public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(@RequestBody OAuth2ClientRegistrationTemplate clientRegistrationTemplate) throws ThingsboardException { try { - clientRegistrationTemplate.setTenantId(getCurrentUser().getTenantId()); - checkEntity(clientRegistrationTemplate.getId(), clientRegistrationTemplate, Resource.OAUTH2_CONFIGURATION_TEMPLATE); return oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); } catch (Exception e) { throw handleException(e); @@ -57,10 +53,10 @@ public class OAuth2ConfigTemplateController extends BaseController { checkParameter(CLIENT_REGISTRATION_TEMPLATE_ID, strClientRegistrationTemplateId); try { OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId = new OAuth2ClientRegistrationTemplateId(toUUID(strClientRegistrationTemplateId)); - OAuth2ClientRegistrationTemplate clientRegistrationTemplate = checkOAuth2ClientRegistrationTemplateId(clientRegistrationTemplateId, Operation.DELETE); oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplateId); - logEntityAction(clientRegistrationTemplateId, clientRegistrationTemplate, + logEntityAction(clientRegistrationTemplateId, + null, null, ActionType.DELETED, null, strClientRegistrationTemplateId); @@ -80,14 +76,9 @@ public class OAuth2ConfigTemplateController extends BaseController { @ResponseBody public List getClientRegistrationTemplates() throws ThingsboardException { try { - checkOAuth2ConfigTemplatePermissions(Operation.READ); return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates(); } catch (Exception e) { throw handleException(e); } } - - private void checkOAuth2ConfigTemplatePermissions(Operation operation) throws ThingsboardException { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, operation); - } } diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index b4a8ead9db..3396c5537d 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -23,16 +23,12 @@ 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.OAuth2ClientRegistrationId; -import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.oauth2.*; -import org.thingsboard.server.common.data.security.Authority; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; +import org.thingsboard.server.common.data.oauth2.OAuth2ClientsDomainParams; import org.thingsboard.server.queue.util.TbCoreComponent; -import org.thingsboard.server.service.security.permission.Operation; -import org.thingsboard.server.service.security.permission.Resource; import javax.servlet.http.HttpServletRequest; import java.util.List; -import java.util.stream.Collectors; @RestController @TbCoreComponent @@ -52,62 +48,39 @@ public class OAuth2Controller extends BaseController { } } - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") @ResponseBody public List getCurrentClientsParams() throws ThingsboardException { try { - Authority authority = getCurrentUser().getAuthority(); - checkOAuth2ConfigPermissions(Operation.READ); - if (Authority.SYS_ADMIN.equals(authority)) { - return oAuth2Service.findDomainsParamsByTenantId(TenantId.SYS_TENANT_ID); - } else if (Authority.TENANT_ADMIN.equals(authority)) { - return oAuth2Service.findDomainsParamsByTenantId(getCurrentUser().getTenantId()); - } else { - throw new IllegalStateException("Authority " + authority + " cannot get client registrations."); - } + return oAuth2Service.findDomainsParams(); } catch (Exception e) { throw handleException(e); } } - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) @ResponseStatus(value = HttpStatus.OK) public List saveClientParams(@RequestBody List domainsParams) throws ThingsboardException { try { - TenantId tenantId; - Authority authority = getCurrentUser().getAuthority(); - if (Authority.SYS_ADMIN.equals(authority)) { - tenantId = TenantId.SYS_TENANT_ID; - } else if (Authority.TENANT_ADMIN.equals(authority)) { - tenantId = getCurrentUser().getTenantId(); - } else { - throw new IllegalStateException("Authority " + authority + " cannot save client registrations."); - } - List clientRegistrationDtos = domainsParams.stream() - .flatMap(domainParams -> domainParams.getClientRegistrations().stream()) - .collect(Collectors.toList()); - for (ClientRegistrationDto clientRegistrationDto : clientRegistrationDtos) { - checkEntity(clientRegistrationDto.getId(), () -> tenantId, Resource.OAUTH2_CONFIGURATION); - } - return oAuth2Service.saveDomainsParams(tenantId, domainsParams); + return oAuth2Service.saveDomainsParams(domainsParams); } catch (Exception e) { throw handleException(e); } } - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/oauth2/config/{clientRegistrationId}", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) public void deleteClientRegistration(@PathVariable(CLIENT_REGISTRATION_ID) String strClientRegistrationId) throws ThingsboardException { checkParameter(CLIENT_REGISTRATION_ID, strClientRegistrationId); try { OAuth2ClientRegistrationId clientRegistrationId = new OAuth2ClientRegistrationId(toUUID(strClientRegistrationId)); - OAuth2ClientRegistration clientRegistration = checkOAuth2ClientRegistrationId(clientRegistrationId, Operation.DELETE); - oAuth2Service.deleteClientRegistrationById(getCurrentUser().getTenantId(), clientRegistrationId); + oAuth2Service.deleteClientRegistrationById(clientRegistrationId); - logEntityAction(clientRegistrationId, clientRegistration, + logEntityAction(clientRegistrationId, + null, null, ActionType.DELETED, null, strClientRegistrationId); @@ -123,13 +96,13 @@ public class OAuth2Controller extends BaseController { } - @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") @RequestMapping(value = "/oauth2/config/domain/{domain}", method = RequestMethod.DELETE) @ResponseStatus(value = HttpStatus.OK) public void deleteClientRegistrationForDomain(@PathVariable(DOMAIN) String domain) throws ThingsboardException { checkParameter(DOMAIN, domain); try { - oAuth2Service.deleteClientRegistrationsByDomain(getCurrentUser().getTenantId(), domain); + oAuth2Service.deleteClientRegistrationsByDomain(domain); logEntityAction(emptyId(EntityType.OAUTH2_CLIENT_REGISTRATION), null, null, @@ -144,19 +117,4 @@ public class OAuth2Controller extends BaseController { throw handleException(e); } } - - @PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") - @RequestMapping(value = "/oauth2/config/isAllowed", method = RequestMethod.GET) - @ResponseBody - public Boolean isOAuth2ConfigurationAllowed() throws ThingsboardException { - try { - return oAuth2Service.isOAuth2ClientRegistrationAllowed(getTenantId()); - } catch (Exception e) { - throw handleException(e); - } - } - - private void checkOAuth2ConfigPermissions(Operation operation) throws ThingsboardException { - accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION, operation); - } } diff --git a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java index 1b5827b6f7..8a7870a1c2 100644 --- a/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java +++ b/application/src/main/java/org/thingsboard/server/service/install/InstallScripts.java @@ -224,7 +224,6 @@ public class InstallScripts { try { JsonNode oauth2ConfigTemplateJson = objectMapper.readTree(path.toFile()); OAuth2ClientRegistrationTemplate clientRegistrationTemplate = objectMapper.treeToValue(oauth2ConfigTemplateJson, OAuth2ClientRegistrationTemplate.class); - clientRegistrationTemplate.setTenantId(TenantId.SYS_TENANT_ID); oAuth2TemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); } catch (Exception e) { log.error("Unable to load oauth2 config templates from json: [{}]", path.toString()); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java index 61d288cdff..651e234f0a 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java +++ b/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; @@ -79,7 +76,7 @@ public abstract class AbstractOAuth2ClientMapper { private final Lock userCreationLock = new ReentrantLock(); - protected SecurityUser getOrCreateSecurityUserFromOAuth2User(TenantId parentTenantId, OAuth2User oauth2User, boolean allowUserCreation, boolean activateUser) { + protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, boolean allowUserCreation, boolean activateUser) { UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail()); User user = userService.findUserByEmail(TenantId.SYS_TENANT_ID, oauth2User.getEmail()); @@ -99,13 +96,8 @@ public abstract class AbstractOAuth2ClientMapper { } else { user.setAuthority(Authority.CUSTOMER_USER); } - TenantId tenantId; - if (TenantId.SYS_TENANT_ID.equals(parentTenantId)) { - tenantId = oauth2User.getTenantId() != null ? - oauth2User.getTenantId() : getTenantId(oauth2User.getTenantName()); - } else { - tenantId = parentTenantId; - } + TenantId tenantId = oauth2User.getTenantId() != null ? + oauth2User.getTenantId() : getTenantId(oauth2User.getTenantName()); user.setTenantId(tenantId); CustomerId customerId = oauth2User.getCustomerId() != null ? oauth2User.getCustomerId() : getCustomerId(user.getTenantId(), oauth2User.getCustomerName()); diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java index 5bee8e866e..7412f2199c 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java @@ -20,7 +20,6 @@ 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.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.service.security.model.SecurityUser; @@ -35,7 +34,7 @@ public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implemen private static final String END_PLACEHOLDER_PREFIX = "}"; @Override - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, TenantId parentTenantId, OAuth2MapperConfig config) { + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) { OAuth2User oauth2User = new OAuth2User(); Map attributes = token.getPrincipal().getAttributes(); String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); @@ -59,7 +58,7 @@ public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implemen oauth2User.setDefaultDashboardName(config.getBasic().getDefaultDashboardName()); } - return getOrCreateSecurityUserFromOAuth2User(parentTenantId, oauth2User, config.isAllowUserCreation(), config.isActivateUser()); + return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser()); } private String getTenantName(Map attributes, OAuth2MapperConfig config) { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java index 21c60b943b..a85da830b0 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/CustomOAuth2ClientMapper.java @@ -23,7 +23,6 @@ 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.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.dao.oauth2.OAuth2User; @@ -39,9 +38,9 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); @Override - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, TenantId parentTenantId, OAuth2MapperConfig config) { + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) { OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom()); - return getOrCreateSecurityUserFromOAuth2User(parentTenantId, oauth2User, config.isAllowUserCreation(), config.isActivateUser()); + return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser()); } private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2CustomMapperConfig custom) { diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java index cdd0313f51..27b24043a5 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapper.java @@ -16,10 +16,9 @@ package org.thingsboard.server.service.security.auth.oauth2; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.service.security.model.SecurityUser; public interface OAuth2ClientMapper { - SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, TenantId parentTenantId, OAuth2MapperConfig config); + SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config); } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java index 7c0097fbfb..ddd5de087d 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/Oauth2AuthenticationSuccessHandler.java @@ -74,7 +74,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS token.getPrincipal().getName()); OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(), - clientRegistration.getTenantId(), clientRegistration.getMapperConfig()); + clientRegistration.getMapperConfig()); JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java index ba295a064d..871de30e6e 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/SysAdminPermissions.java @@ -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,7 +35,7 @@ public class SysAdminPermissions extends AbstractPermissions { put(Resource.USER, userPermissionChecker); put(Resource.WIDGETS_BUNDLE, systemEntityPermissionChecker); put(Resource.WIDGET_TYPE, systemEntityPermissionChecker); - put(Resource.OAUTH2_CONFIGURATION, sysAdminOAuth2ConfigPermissionChecker); + put(Resource.OAUTH2_CONFIGURATION, PermissionChecker.allowAllPermissionChecker); put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, PermissionChecker.allowAllPermissionChecker); } @@ -67,19 +63,4 @@ public class SysAdminPermissions extends AbstractPermissions { }; - private final PermissionChecker sysAdminOAuth2ConfigPermissionChecker = new PermissionChecker() { - @Override - public boolean hasPermission(SecurityUser user, Operation operation) { - return true; - } - - @Override - public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { - if (entity.getTenantId() != null && !entity.getTenantId().isNullUid()) { - return false; - } - return true; - } - }; - } diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java index 0153affa23..c886bccdb8 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/TenantAdminPermissions.java @@ -15,25 +15,17 @@ */ package org.thingsboard.server.service.security.permission; -import org.springframework.beans.factory.annotation.Autowired; 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.dao.oauth2.OAuth2Service; import org.thingsboard.server.service.security.model.SecurityUser; -import java.util.HashMap; - @Component(value="tenantAdminPermissions") public class TenantAdminPermissions extends AbstractPermissions { - @Autowired - private OAuth2Service oAuth2Service; - public TenantAdminPermissions() { super(); put(Resource.ALARM, tenantEntityPermissionChecker); @@ -47,8 +39,6 @@ public class TenantAdminPermissions extends AbstractPermissions { put(Resource.USER, userPermissionChecker); put(Resource.WIDGETS_BUNDLE, widgetsPermissionChecker); put(Resource.WIDGET_TYPE, widgetsPermissionChecker); - put(Resource.OAUTH2_CONFIGURATION, tenantOAuth2ConfigPermissionChecker); - put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, tenantOAuth2ConfigTemplatePermissionChecker); } public static final PermissionChecker tenantEntityPermissionChecker = new PermissionChecker() { @@ -108,31 +98,4 @@ public class TenantAdminPermissions extends AbstractPermissions { } }; - - private final PermissionChecker tenantOAuth2ConfigPermissionChecker = new PermissionChecker() { - @Override - public boolean hasPermission(SecurityUser user, Operation operation) { - return oAuth2Service.isOAuth2ClientRegistrationAllowed(user.getTenantId()); - } - - @Override - public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { - if (!user.getTenantId().equals(entity.getTenantId())) { - return false; - } - return hasPermission(user, operation); - } - }; - - private static final PermissionChecker tenantOAuth2ConfigTemplatePermissionChecker = new PermissionChecker() { - @Override - public boolean hasPermission(SecurityUser user, Operation operation) { - return operation == Operation.READ; - } - - @Override - public boolean hasPermission(SecurityUser user, Operation operation, EntityId entityId, HasTenantId entity) { - return operation == Operation.READ; - } - }; } diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java index 2f7156756a..0f4cd1d0be 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Service.java @@ -27,19 +27,15 @@ import java.util.UUID; public interface OAuth2Service { List getOAuth2Clients(String domainName); - List saveDomainsParams(TenantId tenantId, List domainsParams); + List saveDomainsParams(List domainsParams); - List findDomainsParamsByTenantId(TenantId tenantId); + List findDomainsParams(); OAuth2ClientRegistration findClientRegistration(UUID id); List findAllClientRegistrations(); - void deleteClientRegistrationsByTenantId(TenantId tenantId); + void deleteClientRegistrationById(OAuth2ClientRegistrationId id); - void deleteClientRegistrationById(TenantId tenantId, OAuth2ClientRegistrationId id); - - void deleteClientRegistrationsByDomain(TenantId tenantId, String domain); - - boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId); + void deleteClientRegistrationsByDomain(String domain); } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java index 64cfb59511..9b1fc12dd5 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.java @@ -32,9 +32,8 @@ import java.util.List; @Data @ToString(exclude = {"clientSecret"}) @NoArgsConstructor -public class OAuth2ClientRegistration extends SearchTextBasedWithAdditionalInfo implements HasTenantId, HasName { +public class OAuth2ClientRegistration extends SearchTextBasedWithAdditionalInfo implements HasName { - private TenantId tenantId; private String domainName; private String redirectUriTemplate; private OAuth2MapperConfig mapperConfig; @@ -52,7 +51,6 @@ public class OAuth2ClientRegistration extends SearchTextBasedWithAdditionalInfo< public OAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration) { super(clientRegistration); - this.tenantId = clientRegistration.tenantId; this.domainName = clientRegistration.domainName; this.redirectUriTemplate = clientRegistration.redirectUriTemplate; this.mapperConfig = clientRegistration.mapperConfig; diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java index e08b83fc37..5c2f70874a 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java @@ -31,9 +31,8 @@ import java.util.List; @Data @ToString @NoArgsConstructor -public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo implements HasTenantId, HasName { +public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo implements HasName { - private TenantId tenantId; private String providerId; private OAuth2BasicMapperConfig basic; private String authorizationUri; @@ -50,7 +49,6 @@ public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditio public OAuth2ClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { super(clientRegistrationTemplate); - this.tenantId = clientRegistrationTemplate.tenantId; this.providerId = clientRegistrationTemplate.providerId; this.basic = clientRegistrationTemplate.basic; this.authorizationUri = clientRegistrationTemplate.authorizationUri; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java index 4f2513f2ce..72e8abe4e6 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.java @@ -38,9 +38,6 @@ import java.util.UUID; @Table(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME) public class OAuth2ClientRegistrationEntity extends BaseSqlEntity { - @Column(name = ModelConstants.OAUTH2_TENANT_ID_PROPERTY, columnDefinition = "uuid") - private UUID tenantId; - @Column(name = ModelConstants.OAUTH2_DOMAIN_NAME_PROPERTY) private String domainName; @Column(name = ModelConstants.OAUTH2_CLIENT_ID_PROPERTY) @@ -112,9 +109,6 @@ public class OAuth2ClientRegistrationEntity extends BaseSqlEntity { - @Column(name = ModelConstants.OAUTH2_TENANT_ID_PROPERTY, columnDefinition = "uuid") - private UUID tenantId; - @Column(name = ModelConstants.OAUTH2_TEMPLATE_PROVIDER_ID_PROPERTY) private String providerId; @Column(name = ModelConstants.OAUTH2_AUTHORIZATION_URI_PROPERTY) @@ -95,9 +92,6 @@ public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity { List findAll(); - List findByTenantId(UUID tenantId); - List findByDomainName(String domainName); - int removeByTenantIdAndDomainName(UUID tenantId, String domainName); - - int removeByTenantId(UUID tenantId); + int removeByDomainName(String domainName); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java index 4436be34b6..4eaeb914c9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateServiceImpl.java @@ -44,10 +44,10 @@ public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService imple @Override public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { log.trace("Executing saveClientRegistrationTemplate [{}]", clientRegistrationTemplate); - clientRegistrationTemplateValidator.validate(clientRegistrationTemplate, OAuth2ClientRegistrationTemplate::getTenantId); + clientRegistrationTemplateValidator.validate(clientRegistrationTemplate, o -> TenantId.SYS_TENANT_ID); OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate; try { - savedClientRegistrationTemplate = clientRegistrationTemplateDao.save(clientRegistrationTemplate.getTenantId(), clientRegistrationTemplate); + 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")) { @@ -98,10 +98,6 @@ public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService imple if (clientRegistrationTemplate.getBasic() == null) { throw new DataValidationException("Basic mapper config should be specified!"); } - if (clientRegistrationTemplate.getTenantId() == null - || !TenantId.SYS_TENANT_ID.equals(clientRegistrationTemplate.getTenantId())) { - throw new DataValidationException("Client registration template should be assigned to system admin!"); - } } }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 788c1293da..26bad5566b 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -15,30 +15,22 @@ */ package org.thingsboard.server.dao.oauth2; -import com.fasterxml.jackson.databind.JsonNode; 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.exception.ThingsboardErrorCode; -import org.thingsboard.server.common.data.exception.ThingsboardException; import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; 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 org.thingsboard.server.dao.tenant.TenantService; import javax.transaction.Transactional; import java.util.*; -import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.stream.Collectors; -import static org.thingsboard.server.dao.oauth2.OAuth2Utils.ALLOW_OAUTH2_CONFIGURATION; import static org.thingsboard.server.dao.service.Validator.validateId; import static org.thingsboard.server.dao.service.Validator.validateString; @@ -49,9 +41,6 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se public static final String INCORRECT_CLIENT_REGISTRATION_ID = "Incorrect clientRegistrationId "; public static final String INCORRECT_DOMAIN_NAME = "Incorrect domainName "; - @Autowired - private TenantService tenantService; - @Autowired private OAuth2ClientRegistrationDao clientRegistrationDao; @@ -66,21 +55,20 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se @Override @Transactional - public List saveDomainsParams(TenantId tenantId, List domainsParams) { - log.trace("Executing saveDomainsParams [{}] [{}]", tenantId, domainsParams); - clientParamsValidator.accept(tenantId, domainsParams); - List inputClientRegistrations = OAuth2Utils.toClientRegistrations(tenantId, domainsParams); + public List saveDomainsParams(List domainsParams) { + log.trace("Executing saveDomainsParams [{}]", domainsParams); + clientParamsValidator.accept(domainsParams); + List inputClientRegistrations = OAuth2Utils.toClientRegistrations(domainsParams); List savedClientRegistrations = inputClientRegistrations.stream() - .map(clientRegistration -> clientRegistrationDao.save(clientRegistration.getTenantId(), clientRegistration)) + .map(clientRegistration -> clientRegistrationDao.save(TenantId.SYS_TENANT_ID, clientRegistration)) .collect(Collectors.toList()); return OAuth2Utils.toDomainsParams(savedClientRegistrations); } @Override - public List findDomainsParamsByTenantId(TenantId tenantId) { - log.trace("Executing findDomainsParamsByTenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - return OAuth2Utils.toDomainsParams(clientRegistrationDao.findByTenantId(tenantId.getId())); + public List findDomainsParams() { + log.trace("Executing findDomainsParams"); + return OAuth2Utils.toDomainsParams(clientRegistrationDao.findAll()); } @Override @@ -97,45 +85,21 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } @Override - @Transactional - public void deleteClientRegistrationsByTenantId(TenantId tenantId) { - log.trace("Executing deleteClientRegistrationsByTenantId [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - clientRegistrationDao.removeByTenantId(tenantId.getId()); - } - - @Override - public void deleteClientRegistrationById(TenantId tenantId, OAuth2ClientRegistrationId id) { - log.trace("Executing deleteClientRegistrationById [{}], [{}]", tenantId, id); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + public void deleteClientRegistrationById(OAuth2ClientRegistrationId id) { + log.trace("Executing deleteClientRegistrationById [{}]", id); validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); - clientRegistrationDao.removeById(tenantId, id.getId()); + clientRegistrationDao.removeById(TenantId.SYS_TENANT_ID, id.getId()); } @Override @Transactional - public void deleteClientRegistrationsByDomain(TenantId tenantId, String domain) { - log.trace("Executing deleteClientRegistrationsByDomain [{}], [{}]", tenantId, domain); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); + public void deleteClientRegistrationsByDomain(String domain) { + log.trace("Executing deleteClientRegistrationsByDomain [{}]", domain); validateString(domain, INCORRECT_DOMAIN_NAME + domain); - clientRegistrationDao.removeByTenantIdAndDomainName(tenantId.getId(), domain); + clientRegistrationDao.removeByDomainName(domain); } - @Override - public boolean isOAuth2ClientRegistrationAllowed(TenantId tenantId) { - log.trace("Executing isOAuth2ClientRegistrationAllowed [{}]", tenantId); - validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - Tenant tenant = tenantService.findTenantById(tenantId); - if (tenant == null) return false; - JsonNode allowOAuth2ConfigurationJsonNode = tenant.getAdditionalInfo() != null ? tenant.getAdditionalInfo().get(ALLOW_OAUTH2_CONFIGURATION) : null; - if (allowOAuth2ConfigurationJsonNode == null) { - return false; - } else { - return allowOAuth2ConfigurationJsonNode.asBoolean(); - } - } - - private final BiConsumer> clientParamsValidator = (tenantId, domainsParams) -> { + private final Consumer> clientParamsValidator = domainsParams -> { if (domainsParams == null || domainsParams.isEmpty()) { throw new DataValidationException("Domain params should be specified!"); } @@ -211,13 +175,5 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se } } } - if (tenantId == null) { - throw new DataValidationException("Client registration should be assigned to tenant!"); - } else if (!TenantId.SYS_TENANT_ID.equals(tenantId)) { - Tenant tenant = tenantService.findTenantById(tenantId); - if (tenant == null) { - throw new DataValidationException("Client registration is referencing to non-existent tenant!"); - } - } }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java index ec2bc59ff7..7af0b80ba5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Utils.java @@ -15,8 +15,6 @@ */ package org.thingsboard.server.dao.oauth2; -import org.springframework.util.StringUtils; -import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.*; import java.util.ArrayList; @@ -24,7 +22,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; -import java.util.stream.Stream; public class OAuth2Utils { public static final String ALLOW_OAUTH2_CONFIGURATION = "allowOAuth2Configuration"; @@ -38,10 +35,10 @@ public class OAuth2Utils { return client; } - public static List toClientRegistrations(TenantId tenantId, List domainsParams) { + public static List toClientRegistrations(List domainsParams) { return domainsParams.stream() .flatMap(domainParams -> domainParams.getClientRegistrations().stream() - .map(clientRegistrationDto -> OAuth2Utils.toClientRegistration(tenantId, domainParams.getDomainName(), + .map(clientRegistrationDto -> OAuth2Utils.toClientRegistration(domainParams.getDomainName(), domainParams.getRedirectUriTemplate(), clientRegistrationDto) )) .collect(Collectors.toList()); @@ -80,11 +77,10 @@ public class OAuth2Utils { .build(); } - public static OAuth2ClientRegistration toClientRegistration(TenantId tenantId, String domainName, String redirectUriTemplate, - ClientRegistrationDto clientRegistrationDto) { + public static OAuth2ClientRegistration toClientRegistration(String domainName, String redirectUriTemplate, + ClientRegistrationDto clientRegistrationDto) { OAuth2ClientRegistration clientRegistration = new OAuth2ClientRegistration(); clientRegistration.setId(clientRegistrationDto.getId()); - clientRegistration.setTenantId(tenantId); clientRegistration.setCreatedTime(clientRegistrationDto.getCreatedTime()); clientRegistration.setDomainName(domainName); clientRegistration.setRedirectUriTemplate(redirectUriTemplate); diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java index d5e17ac635..c9ba51b813 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.java @@ -26,7 +26,6 @@ import org.thingsboard.server.dao.sql.JpaAbstractDao; import java.util.ArrayList; import java.util.List; -import java.util.Optional; import java.util.UUID; import java.util.stream.Collectors; @@ -55,12 +54,6 @@ public class JpaOAuth2ClientRegistrationDao extends JpaAbstractDao findByTenantId(UUID tenantId) { - List entities = repository.findAllByTenantId(tenantId); - return entities.stream().map(DaoUtil::getData).collect(Collectors.toList()); - } - @Override public List findByDomainName(String domainName) { List entities = repository.findAllByDomainName(domainName); @@ -68,12 +61,7 @@ public class JpaOAuth2ClientRegistrationDao extends JpaAbstractDao { - List findAllByTenantId(UUID tenantId); - List findAllByDomainName(String domainName); - int deleteByTenantIdAndDomainName(UUID tenantId, String domainName); - - int deleteByTenantId(UUID tenantId); + int deleteByDomainName(String domainName); } diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index df0dfb666a..234781e90c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -105,7 +105,6 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe public void deleteTenant(TenantId tenantId) { log.trace("Executing deleteTenant [{}]", tenantId); Validator.validateId(tenantId, INCORRECT_TENANT_ID + tenantId); - oAuth2Service.deleteClientRegistrationsByTenantId(tenantId); customerService.deleteCustomersByTenantId(tenantId); widgetsBundleService.deleteWidgetsBundlesByTenantId(tenantId); dashboardService.deleteDashboardsByTenantId(tenantId); diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 0b7f1c848b..ca92bfcf4f 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -295,7 +295,6 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, created_time bigint NOT NULL, additional_info varchar, - tenant_id uuid, domain_name varchar(255), client_id varchar(255), client_secret varchar(255), @@ -330,7 +329,6 @@ 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, - tenant_id uuid, provider_id varchar(255), authorization_uri varchar(255), token_uri varchar(255), diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index a8f014ab22..d441e0cfd0 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -320,7 +320,6 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration ( id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, created_time bigint NOT NULL, additional_info varchar, - tenant_id uuid, domain_name varchar(255), client_id varchar(255), client_secret varchar(255), @@ -355,7 +354,6 @@ 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, - tenant_id uuid, provider_id varchar(255), authorization_uri varchar(255), token_uri varchar(255), diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java index f2bf502fab..3d828d1ac5 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java @@ -34,8 +34,6 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { @Autowired protected OAuth2ConfigTemplateService oAuth2ConfigTemplateService; - private TenantId tenantId; - @Before public void beforeRun() throws Exception { Assert.assertTrue(oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().isEmpty()); @@ -53,15 +51,15 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { @Test(expected = DataValidationException.class) public void testSaveDuplicateProviderId() { - OAuth2ClientRegistrationTemplate first = validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, "providerId"); - OAuth2ClientRegistrationTemplate second = validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, "providerId"); + OAuth2ClientRegistrationTemplate first = validClientRegistrationTemplate("providerId"); + OAuth2ClientRegistrationTemplate second = validClientRegistrationTemplate("providerId"); oAuth2ConfigTemplateService.saveClientRegistrationTemplate(first); oAuth2ConfigTemplateService.saveClientRegistrationTemplate(second); } @Test public void testCreateNewTemplate() { - OAuth2ClientRegistrationTemplate clientRegistrationTemplate = validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString()); + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = validClientRegistrationTemplate(UUID.randomUUID().toString()); OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); Assert.assertNotNull(savedClientRegistrationTemplate); @@ -73,7 +71,7 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { @Test public void testFindTemplate() { - OAuth2ClientRegistrationTemplate clientRegistrationTemplate = validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString()); + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = validClientRegistrationTemplate(UUID.randomUUID().toString()); OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); OAuth2ClientRegistrationTemplate foundClientRegistrationTemplate = oAuth2ConfigTemplateService.findClientRegistrationTemplateById(savedClientRegistrationTemplate.getId()); @@ -82,17 +80,17 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { @Test public void testFindAll() { - oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString())); - oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString())); + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); Assert.assertEquals(2, oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().size()); } @Test public void testDeleteTemplate() { - oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString())); - oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString())); - OAuth2ClientRegistrationTemplate saved = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(TenantId.SYS_TENANT_ID, UUID.randomUUID().toString())); + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); + OAuth2ClientRegistrationTemplate saved = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); Assert.assertEquals(3, oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().size()); Assert.assertNotNull(oAuth2ConfigTemplateService.findClientRegistrationTemplateById(saved.getId())); @@ -103,10 +101,9 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { Assert.assertNull(oAuth2ConfigTemplateService.findClientRegistrationTemplateById(saved.getId())); } - private OAuth2ClientRegistrationTemplate validClientRegistrationTemplate(TenantId tenantId, String providerId) { + private OAuth2ClientRegistrationTemplate validClientRegistrationTemplate(String providerId) { OAuth2ClientRegistrationTemplate clientRegistrationTemplate = new OAuth2ClientRegistrationTemplate(); clientRegistrationTemplate.setProviderId(providerId); - clientRegistrationTemplate.setTenantId(tenantId); clientRegistrationTemplate.setAdditionalInfo(mapper.createObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString())); clientRegistrationTemplate.setBasic( OAuth2BasicMapperConfig.builder() diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index 2231bc6dfe..4ff0716266 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -20,18 +20,14 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.thingsboard.server.common.data.Tenant; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.oauth2.*; -import org.thingsboard.server.dao.attributes.AttributesService; import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.oauth2.OAuth2Utils; -import java.io.IOException; import java.util.*; import java.util.stream.Collectors; -import static org.thingsboard.server.dao.oauth2.OAuth2Utils.ALLOW_OAUTH2_CONFIGURATION; import static org.thingsboard.server.dao.oauth2.OAuth2Utils.toClientRegistrations; public class BaseOAuth2ServiceTest extends AbstractServiceTest { @@ -39,56 +35,26 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Autowired protected OAuth2Service oAuth2Service; - @Autowired - protected AttributesService attributesService; - - private TenantId tenantId; - @Before - public void beforeRun() throws Exception { - Tenant tenant = new Tenant(); - tenant.setTitle("My tenant"); - Tenant savedTenant = tenantService.saveTenant(tenant); - Assert.assertNotNull(savedTenant); - tenantId = savedTenant.getId(); - + public void beforeRun() { Assert.assertTrue(oAuth2Service.findAllClientRegistrations().isEmpty()); } @After - public void after() throws Exception { - tenantService.deleteTenant(tenantId); - oAuth2Service.deleteClientRegistrationsByTenantId(TenantId.SYS_TENANT_ID); - + public void after() { + oAuth2Service.findAllClientRegistrations().forEach(clientRegistration -> { + oAuth2Service.deleteClientRegistrationById(clientRegistration.getId()); + }); Assert.assertTrue(oAuth2Service.findAllClientRegistrations().isEmpty()); } @Test - public void testIsOAuth2Allowed_null() throws IOException { - updateTenantAllowOAuth2Setting(null); - Assert.assertFalse(oAuth2Service.isOAuth2ClientRegistrationAllowed(tenantId)); - } - - @Test - public void testIsOAuth2Allowed_false() throws IOException { - updateTenantAllowOAuth2Setting(false); - Assert.assertFalse(oAuth2Service.isOAuth2ClientRegistrationAllowed(tenantId)); - } - - @Test - public void testIsOAuth2Allowed_true() throws IOException { - updateTenantAllowOAuth2Setting(true); - Assert.assertTrue(oAuth2Service.isOAuth2ClientRegistrationAllowed(tenantId)); - } - - - @Test - public void testCreateNewSystemParams() { - OAuth2ClientRegistration clientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); - List savedDomainsParams = oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toDomainsParams(Collections.singletonList(clientRegistration))); + public void testCreateNewParams() { + OAuth2ClientRegistration clientRegistration = validClientRegistration("domain-name"); + List savedDomainsParams = oAuth2Service.saveDomainsParams(OAuth2Utils.toDomainsParams(Collections.singletonList(clientRegistration))); Assert.assertNotNull(savedDomainsParams); - List savedClientRegistrations = OAuth2Utils.toClientRegistrations(TenantId.SYS_TENANT_ID, savedDomainsParams); + List savedClientRegistrations = OAuth2Utils.toClientRegistrations(savedDomainsParams); Assert.assertEquals(1, savedClientRegistrations.size()); OAuth2ClientRegistration savedClientRegistration = savedClientRegistrations.get(0); @@ -96,95 +62,40 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { clientRegistration.setId(savedClientRegistration.getId()); clientRegistration.setCreatedTime(savedClientRegistration.getCreatedTime()); Assert.assertEquals(clientRegistration, savedClientRegistration); - } - - @Test - public void testFindSystemParamsByTenant() { - OAuth2ClientRegistration clientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); - oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toDomainsParams(Collections.singletonList(clientRegistration))); - - List foundDomainsParams = oAuth2Service.findDomainsParamsByTenantId(TenantId.SYS_TENANT_ID); - Assert.assertEquals(1, foundDomainsParams.size()); - Assert.assertEquals(1, oAuth2Service.findAllClientRegistrations().size()); - List foundClientRegistrations = OAuth2Utils.toClientRegistrations(TenantId.SYS_TENANT_ID, foundDomainsParams); - OAuth2ClientRegistration foundClientRegistration = foundClientRegistrations.get(0); - Assert.assertNotNull(foundClientRegistration); - clientRegistration.setId(foundClientRegistration.getId()); - clientRegistration.setCreatedTime(foundClientRegistration.getCreatedTime()); - Assert.assertEquals(clientRegistration, foundClientRegistration); + oAuth2Service.deleteClientRegistrationsByDomain("domain-name"); } @Test - public void testCreateNewTenantParams() { - OAuth2ClientRegistration clientRegistration = validClientRegistration(tenantId); - List savedDomainsParams = oAuth2Service.saveDomainsParams(tenantId, OAuth2Utils.toDomainsParams(Collections.singletonList(clientRegistration))); - Assert.assertNotNull(savedDomainsParams); + public void testFindDomainParams() { + OAuth2ClientRegistration clientRegistration = validClientRegistration(); + oAuth2Service.saveDomainsParams(OAuth2Utils.toDomainsParams(Collections.singletonList(clientRegistration))); - List savedClientRegistrations = OAuth2Utils.toClientRegistrations(tenantId, savedDomainsParams); - Assert.assertEquals(1, savedClientRegistrations.size()); - - OAuth2ClientRegistration savedClientRegistration = savedClientRegistrations.get(0); - - Assert.assertNotNull(savedClientRegistration); - Assert.assertNotNull(savedClientRegistration.getId()); - clientRegistration.setId(savedClientRegistration.getId()); - clientRegistration.setCreatedTime(savedClientRegistration.getCreatedTime()); - Assert.assertEquals(clientRegistration, savedClientRegistration); - } - - @Test - public void testFindTenantParams() { - OAuth2ClientRegistration clientRegistration = validClientRegistration(tenantId); - oAuth2Service.saveDomainsParams(tenantId, OAuth2Utils.toDomainsParams(Collections.singletonList(clientRegistration))); - - List foundDomainsParams = oAuth2Service.findDomainsParamsByTenantId(tenantId); + List foundDomainsParams = oAuth2Service.findDomainsParams(); Assert.assertEquals(1, foundDomainsParams.size()); Assert.assertEquals(1, oAuth2Service.findAllClientRegistrations().size()); - List foundClientRegistrations = OAuth2Utils.toClientRegistrations(tenantId, foundDomainsParams); + List foundClientRegistrations = OAuth2Utils.toClientRegistrations(foundDomainsParams); OAuth2ClientRegistration foundClientRegistration = foundClientRegistrations.get(0); - Assert.assertNotNull(foundClientRegistration); clientRegistration.setId(foundClientRegistration.getId()); clientRegistration.setCreatedTime(foundClientRegistration.getCreatedTime()); Assert.assertEquals(clientRegistration, foundClientRegistration); } - @Test - public void testGetClientRegistrationWithTenant() { - OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId); - OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); - - List savedTenantDomainsParams = oAuth2Service.saveDomainsParams(tenantId, - OAuth2Utils.toDomainsParams(Collections.singletonList(tenantClientRegistration))); - List savedSysAdminDomainsParams = oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, - OAuth2Utils.toDomainsParams(Collections.singletonList(sysAdminClientRegistration))); - - Assert.assertEquals(2, oAuth2Service.findAllClientRegistrations().size()); - - Assert.assertEquals(savedTenantDomainsParams, oAuth2Service.findDomainsParamsByTenantId(tenantId)); - Assert.assertEquals(savedSysAdminDomainsParams, oAuth2Service.findDomainsParamsByTenantId(TenantId.SYS_TENANT_ID)); - - OAuth2ClientRegistration savedTenantClientRegistration = toClientRegistrations(tenantId, savedTenantDomainsParams).get(0); - Assert.assertEquals(savedTenantClientRegistration, oAuth2Service.findClientRegistration(savedTenantClientRegistration.getUuidId())); - OAuth2ClientRegistration savedSysAdminClientRegistration = toClientRegistrations(TenantId.SYS_TENANT_ID, savedSysAdminDomainsParams).get(0); - Assert.assertEquals(savedSysAdminClientRegistration, oAuth2Service.findClientRegistration(savedSysAdminClientRegistration.getUuidId())); - } - @Test public void testGetOAuth2Clients() { String testDomainName = "test_domain"; - OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId, testDomainName); - OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID, testDomainName); + OAuth2ClientRegistration first = validClientRegistration(testDomainName); + OAuth2ClientRegistration second = validClientRegistration(testDomainName); - oAuth2Service.saveDomainsParams(tenantId, OAuth2Utils.toDomainsParams(Collections.singletonList(tenantClientRegistration))); - oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toDomainsParams(Collections.singletonList(sysAdminClientRegistration))); + oAuth2Service.saveDomainsParams(OAuth2Utils.toDomainsParams(Collections.singletonList(first))); + oAuth2Service.saveDomainsParams(OAuth2Utils.toDomainsParams(Collections.singletonList(second))); List oAuth2Clients = oAuth2Service.getOAuth2Clients(testDomainName); - Set actualLabels = new HashSet<>(Arrays.asList(tenantClientRegistration.getLoginButtonLabel(), - sysAdminClientRegistration.getLoginButtonLabel())); + Set actualLabels = new HashSet<>(Arrays.asList(first.getLoginButtonLabel(), + second.getLoginButtonLabel())); Set foundLabels = oAuth2Clients.stream().map(OAuth2ClientInfo::getName).collect(Collectors.toSet()); Assert.assertEquals(actualLabels, foundLabels); @@ -193,88 +104,61 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { @Test public void testGetEmptyOAuth2Clients() { String testDomainName = "test_domain"; - OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId, testDomainName); - OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID, testDomainName); - oAuth2Service.saveDomainsParams(tenantId, OAuth2Utils.toDomainsParams(Collections.singletonList(tenantClientRegistration))); - oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toDomainsParams(Collections.singletonList(sysAdminClientRegistration))); + OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(testDomainName); + OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(testDomainName); + oAuth2Service.saveDomainsParams(OAuth2Utils.toDomainsParams(Collections.singletonList(tenantClientRegistration))); + oAuth2Service.saveDomainsParams(OAuth2Utils.toDomainsParams(Collections.singletonList(sysAdminClientRegistration))); List oAuth2Clients = oAuth2Service.getOAuth2Clients("random-domain"); Assert.assertTrue(oAuth2Clients.isEmpty()); } @Test public void testDeleteOAuth2ClientRegistration() { - OAuth2ClientRegistration tenantClientRegistration = validClientRegistration(tenantId); - OAuth2ClientRegistration sysAdminClientRegistration = validClientRegistration(TenantId.SYS_TENANT_ID); + OAuth2ClientRegistration first = validClientRegistration(); + OAuth2ClientRegistration second = validClientRegistration(); - List savedTenantDomainsParams = oAuth2Service.saveDomainsParams(tenantId, - OAuth2Utils.toDomainsParams(Collections.singletonList(tenantClientRegistration))); - List savedSysAdminDomainsParams = oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, - OAuth2Utils.toDomainsParams(Collections.singletonList(sysAdminClientRegistration))); + List savedFirstDomainsParams = oAuth2Service.saveDomainsParams( + OAuth2Utils.toDomainsParams(Collections.singletonList(first))); + List savedSecondDomainsParams = oAuth2Service.saveDomainsParams( + OAuth2Utils.toDomainsParams(Collections.singletonList(second))); - OAuth2ClientRegistration savedTenantRegistration = toClientRegistrations(tenantId, savedTenantDomainsParams).get(0); - OAuth2ClientRegistration savedSysAdminRegistration = toClientRegistrations(TenantId.SYS_TENANT_ID, savedSysAdminDomainsParams).get(0); + OAuth2ClientRegistration savedFirstRegistration = toClientRegistrations(savedFirstDomainsParams).get(0); + OAuth2ClientRegistration savedSecondRegistration = toClientRegistrations(savedSecondDomainsParams).get(0); - oAuth2Service.deleteClientRegistrationById(tenantId, savedTenantRegistration.getId()); + oAuth2Service.deleteClientRegistrationById(savedFirstRegistration.getId()); List foundRegistrations = oAuth2Service.findAllClientRegistrations(); Assert.assertEquals(1, foundRegistrations.size()); - Assert.assertEquals(savedSysAdminRegistration, foundRegistrations.get(0)); + Assert.assertEquals(savedSecondRegistration, foundRegistrations.get(0)); } @Test - public void testDeleteTenantOAuth2ClientRegistrations() { - oAuth2Service.saveDomainsParams(tenantId, OAuth2Utils.toDomainsParams(Arrays.asList( - validClientRegistration(tenantId, "domain"), - validClientRegistration(tenantId, "domain"), - validClientRegistration(tenantId, "domain") + public void testDeleteDomainOAuth2ClientRegistrations() { + oAuth2Service.saveDomainsParams(OAuth2Utils.toDomainsParams(Arrays.asList( + validClientRegistration("domain1"), + validClientRegistration("domain1"), + validClientRegistration("domain2") ))); - Assert.assertEquals(3, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertEquals(1, oAuth2Service.findDomainsParamsByTenantId(tenantId).size()); - - oAuth2Service.deleteClientRegistrationsByTenantId(tenantId); - Assert.assertEquals(0, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertEquals(0, oAuth2Service.findDomainsParamsByTenantId(tenantId).size()); - } - - @Test - public void testDeleteTenantDomainOAuth2ClientRegistrations() { - oAuth2Service.saveDomainsParams(tenantId, OAuth2Utils.toDomainsParams(Arrays.asList( - validClientRegistration(tenantId, "domain1"), - validClientRegistration(tenantId, "domain1"), - validClientRegistration(tenantId, "domain2") - ))); - oAuth2Service.saveDomainsParams(TenantId.SYS_TENANT_ID, OAuth2Utils.toDomainsParams(Arrays.asList( - validClientRegistration(TenantId.SYS_TENANT_ID, "domain2") + oAuth2Service.saveDomainsParams(OAuth2Utils.toDomainsParams(Arrays.asList( + validClientRegistration("domain2") ))); Assert.assertEquals(4, oAuth2Service.findAllClientRegistrations().size()); - List tenantDomainsParams = oAuth2Service.findDomainsParamsByTenantId(tenantId); - List tenantClientRegistrations = toClientRegistrations(tenantId, tenantDomainsParams); - Assert.assertEquals(2, tenantDomainsParams.size()); - Assert.assertEquals(3, tenantClientRegistrations.size()); + List domainsParams = oAuth2Service.findDomainsParams(); + List clientRegistrations = toClientRegistrations(domainsParams); + Assert.assertEquals(2, domainsParams.size()); + Assert.assertEquals(4, clientRegistrations.size()); - oAuth2Service.deleteClientRegistrationsByDomain(tenantId, "domain1"); + oAuth2Service.deleteClientRegistrationsByDomain("domain1"); Assert.assertEquals(2, oAuth2Service.findAllClientRegistrations().size()); - Assert.assertEquals(1, oAuth2Service.findDomainsParamsByTenantId(tenantId).size()); - Assert.assertEquals(1, toClientRegistrations(tenantId, oAuth2Service.findDomainsParamsByTenantId(tenantId)).size()); - } - - private void updateTenantAllowOAuth2Setting(Boolean allowOAuth2) throws IOException { - Tenant tenant = tenantService.findTenantById(tenantId); - if (allowOAuth2 == null) { - tenant.setAdditionalInfo(mapper.readTree("{}")); - } else { - String additionalInfo = "{\"" + ALLOW_OAUTH2_CONFIGURATION + "\":" + allowOAuth2 + "}"; - tenant.setAdditionalInfo(mapper.readTree(additionalInfo)); - tenantService.saveTenant(tenant); - } + Assert.assertEquals(1, oAuth2Service.findDomainsParams().size()); + Assert.assertEquals(2, toClientRegistrations(oAuth2Service.findDomainsParams()).size()); } - private OAuth2ClientRegistration validClientRegistration(TenantId tenantId) { - return validClientRegistration(tenantId, "domainName"); + private OAuth2ClientRegistration validClientRegistration() { + return validClientRegistration("domainName"); } - private OAuth2ClientRegistration validClientRegistration(TenantId tenantId, String domainName) { + private OAuth2ClientRegistration validClientRegistration(String domainName) { OAuth2ClientRegistration clientRegistration = new OAuth2ClientRegistration(); - clientRegistration.setTenantId(tenantId); clientRegistration.setDomainName(domainName); clientRegistration.setMapperConfig( OAuth2MapperConfig.builder() From 3816172b2ad59736e120ad75861b5ef464af24df Mon Sep 17 00:00:00 2001 From: Vladyslav_Prykhodko Date: Mon, 21 Sep 2020 18:52:54 +0300 Subject: [PATCH 099/117] Refactoring OAuth2 --- .../admin/oauth2-settings.component.html | 10 +-- .../pages/admin/oauth2-settings.component.ts | 20 +++-- .../assets/locale/locale.constant-en_US.json | 79 ++++++++++--------- 3 files changed, 57 insertions(+), 52 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index 4e45e02945..9a0d9a1858 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -37,7 +37,7 @@ - {{ domain.get('domainName').value ? domain.get('domainName').value : ("admin.new-domain" | translate) }} + {{ domain.get('domainName').value ? domain.get('domainName').value : ("admin.oauth2.new-domain" | translate) }}
@@ -396,7 +396,7 @@ [disabled]="(isLoading$ | async)" (click)="addDomain()" type="button"> - {{'admin.add-domain' | translate}} + {{'admin.oauth2.add-domain' | translate}} - -
- diff --git a/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.scss b/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.scss deleted file mode 100644 index 43b16cf165..0000000000 --- a/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.scss +++ /dev/null @@ -1,18 +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. - */ -:host { - background-color: #f9f9f9; -} diff --git a/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.ts b/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.ts deleted file mode 100644 index 44f6bff0f3..0000000000 --- a/ui-ngx/src/app/modules/home/pages/admin/edit-redirect-uri-panel.component.ts +++ /dev/null @@ -1,76 +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. -/// - -import { Component, Inject, InjectionToken, OnInit, SkipSelf } from '@angular/core'; -import { ErrorStateMatcher } from '@angular/material/core'; -import { Store } from '@ngrx/store'; -import { AppState } from '@core/core.state'; -import { FormBuilder, FormControl, FormGroup, FormGroupDirective, NgForm, Validators } from '@angular/forms'; -import { PageComponent } from '@shared/components/page.component'; -import { OverlayRef } from '@angular/cdk/overlay'; - -export const EDIT_REDIRECT_URI_PANEL_DATA = new InjectionToken('EditRedirectUriPanelData'); - -export interface EditRedirectUriPanelData { - redirectURI: any; -} - -@Component({ - selector: 'tb-edit-redirect-uri-panel', - templateUrl: './edit-redirect-uri-panel.component.html', - providers: [{provide: ErrorStateMatcher, useExisting: EditRedirectUriPanelComponent}], - styleUrls: ['./edit-redirect-uri-panel.component.scss'] -}) -export class EditRedirectUriPanelComponent extends PageComponent implements OnInit, ErrorStateMatcher { - - private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.?+=&%@\-/]*)?$/; - - redirectURIFormGroup: FormGroup; - - result: any = null; - - submitted = false; - - constructor(protected store: Store, - @Inject(EDIT_REDIRECT_URI_PANEL_DATA) public data: EditRedirectUriPanelData, - @SkipSelf() private errorStateMatcher: ErrorStateMatcher, - public overlayRef: OverlayRef, - public fb: FormBuilder) { - super(store); - } - - ngOnInit(): void { - this.redirectURIFormGroup = this.fb.group({ - value: [this.data.redirectURI, [Validators.required, Validators.pattern(this.URL_REGEXP)]] - }); - } - - isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean { - const originalErrorState = this.errorStateMatcher.isErrorState(control, form); - const customErrorState = !!(control && control.invalid && this.submitted); - return originalErrorState || customErrorState; - } - - cancel(): void { - this.overlayRef.dispose(); - } - - update(): void { - this.submitted = true; - this.result = this.redirectURIFormGroup.get('value').value; - this.overlayRef.dispose(); - } -} diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index 9a0d9a1858..6e0048612c 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -30,23 +30,20 @@
- -
- - - - - - {{ domain.get('domainName').value ? domain.get('domainName').value : ("admin.oauth2.new-domain" | translate) }} - - - + + {{ 'admin.oauth2.enable' | translate }} + +
+ +
+ + + + + + {{ domainListTittle(domain) }} + + - - - - - - admin.domain-name - - - {{ 'admin.error-verification-url' | translate }} - - - {{ 'admin.domain-name-unique' | translate }} - - - - -
- - - - {{ getProviderName(registration) }} - - - - - - - -
-
- - admin.oauth2.login-provider - - - {{ provider | uppercase }} + + + + + +
+
+
+
+ + admin.oauth2.protocol + + + {{ domainSchemaTranslations.get(protocol) | translate | uppercase }} -
-
-
- admin.oauth2.client-id - - - {{ 'admin.oauth2.client-id-required' | translate }} + admin.domain-name + + + {{ 'admin.error-verification-url' | translate }} + + + {{ 'admin.domain-name-unique' | translate }} +
+
- admin.oauth2.client-secret - - - {{ 'admin.oauth2.client-secret-required' | translate }} - + admin.oauth2.redirect-uri-template + + + + + + + +
- - - - {{ 'admin.oauth2.custom-setting' | translate }} - - - - - -
- - admin.oauth2.access-token-uri - - - - {{ 'admin.oauth2.access-token-uri-required' | translate }} - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - +
- - admin.oauth2.authorization-uri - - - - {{ 'admin.oauth2.authorization-uri-required' | translate }} - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - -
- -
- - admin.oauth2.jwk-set-uri - - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - +
+ +
+
+
+
+ +
+
+ + +
+ + + + {{ getProviderName(registration) }} + + + + + + + +
+
+ + admin.oauth2.login-provider + + + {{ provider | uppercase }} + + + +
+
+ +
+ + admin.oauth2.client-id + + + {{ 'admin.oauth2.client-id-required' | translate }} + + + + + admin.oauth2.client-secret + + + {{ 'admin.oauth2.client-secret-required' | translate }} + + +
+ + + + + {{ 'admin.oauth2.custom-setting' | translate }} + + + + + +
+ + admin.oauth2.access-token-uri + + + + {{ 'admin.oauth2.access-token-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + - - admin.oauth2.user-info-uri - - - - {{ 'admin.oauth2.user-info-uri-required' | translate }} - - - {{ 'admin.oauth2.uri-pattern-error' | translate }} - - -
- - - admin.oauth2.client-authentication-method - - - {{ clientAuthenticationMethod | uppercase }} - - - - -
- - admin.oauth2.login-button-label - - - {{ 'admin.oauth2.login-button-label-required' | translate }} - - + + admin.oauth2.authorization-uri + + + + {{ 'admin.oauth2.authorization-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + +
- - admin.oauth2.login-button-icon - - -
- -
-
- - {{ 'admin.oauth2.allow-user-creation' | translate }} - - - {{ 'admin.oauth2.activate-user' | translate }} - +
+ + admin.oauth2.jwk-set-uri + + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + + + + + admin.oauth2.user-info-uri + + + + {{ 'admin.oauth2.user-info-uri-required' | translate }} + + + {{ 'admin.oauth2.uri-pattern-error' | translate }} + +
-
- - - admin.oauth2.scope - - - {{scope}} - cancel - - - - - - - - - admin.oauth2.user-name-attribute-name - - - {{ 'admin.oauth2.user-name-attribute-name-required' | translate }} - - - -
+ - admin.oauth2.type - - - {{ converterTypeExternalUser }} + admin.oauth2.client-authentication-method + + + {{ clientAuthenticationMethod | uppercase }} -
- - admin.oauth2.email-attribute-key - - - {{ 'admin.oauth2.email-attribute-key-required' | translate }} +
+ + admin.oauth2.login-button-label + + + {{ 'admin.oauth2.login-button-label-required' | translate }} -
- - admin.oauth2.first-name-attribute-key - - + + admin.oauth2.login-button-icon + + +
- - admin.oauth2.last-name-attribute-key - - +
+
+ + {{ 'admin.oauth2.allow-user-creation' | translate }} + + + {{ 'admin.oauth2.activate-user' | translate }} +
+
-
- - admin.oauth2.tenant-name-strategy - - - {{ tenantNameStrategy }} - - - + + admin.oauth2.scope + + + {{scope}} + cancel + + + + + {{ 'admin.oauth2.scope-required' | translate }} + + - - admin.oauth2.tenant-name-pattern - - - {{ 'admin.oauth2.tenant-name-pattern-required' | translate }} - - -
+ + + + admin.oauth2.user-name-attribute-name + + + {{ 'admin.oauth2.user-name-attribute-name-required' | translate }} + + +
- admin.oauth2.customer-name-pattern - + admin.oauth2.type + + + {{ converterTypeExternalUser }} + + -
- - admin.oauth2.default-dashboard-name - +
+ + admin.oauth2.email-attribute-key + + + {{ 'admin.oauth2.email-attribute-key-required' | translate }} + - - {{ 'admin.oauth2.always-fullscreen' | translate}} - -
-
+
+ + admin.oauth2.first-name-attribute-key + + + + + admin.oauth2.last-name-attribute-key + + +
+ +
+ + admin.oauth2.tenant-name-strategy + + + {{ tenantNameStrategy }} + + + + + + admin.oauth2.tenant-name-pattern + + + {{ 'admin.oauth2.tenant-name-pattern-required' | translate }} + + +
-
- - admin.oauth2.url - - - {{ 'admin.oauth2.url-required' | translate }} - - - {{ 'admin.oauth2.url-pattern' | translate }} - - - -
- common.username - + admin.oauth2.customer-name-pattern + - - common.password - +
+ + admin.oauth2.default-dashboard-name + + + + + {{ 'admin.oauth2.always-fullscreen' | translate}} + +
+
+ +
+ + admin.oauth2.url + + + {{ 'admin.oauth2.url-required' | translate }} + + + {{ 'admin.oauth2.url-pattern' | translate }} + -
+ +
+ + common.username + + + + + common.password + + +
+
- -
- - - - - - - + + + + + + + + +
+
+ +
+
- - -
- -
- - - - - -
-
- -
- - -
+ + + +
+ +
+ + +
+ + +
diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss index ae7e94a5ac..0ad644f61b 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.scss @@ -15,11 +15,11 @@ */ :host{ .checkbox-row { - margin-top: 16px; + margin-top: 1em; } .registration-card{ - margin-bottom: 8px; + margin-bottom: 0.5em; border: 1px solid rgba(0, 0, 0, 0.2); .custom-settings{ @@ -31,7 +31,15 @@ } .container{ - margin-bottom: 16px; + margin-bottom: 1em; + + .tb-highlight{ + margin: 0; + } + + .tb-hint{ + padding-bottom: 0; + } } } @@ -39,7 +47,7 @@ .registration-card{ .custom-settings{ .mat-expansion-panel-body{ - padding: 0 2px 16px; + padding: 0 2px 1em; } .mat-tab-label{ text-transform: none; @@ -49,4 +57,10 @@ } } } + .domains-list{ + margin-bottom: 1.5em; + .mat-form-field-suffix .mat-icon-button .mat-icon{ + font-size: 24px; + } + } } diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index fc0729c3e1..b2ffc2bb6e 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -14,12 +14,16 @@ /// limitations under the License. /// -import { ChangeDetectorRef, Component, Inject, OnDestroy, OnInit, ViewContainerRef } from '@angular/core'; +import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; import { ClientAuthenticationMethod, ClientProviderTemplated, ClientRegistration, + DomainInfo, + DomainSchema, + domainSchemaTranslations, + DomainsParam, MapperConfig, MapperConfigBasic, MapperConfigCustom, @@ -38,13 +42,7 @@ import { WINDOW } from '@core/services/window.service'; import { forkJoin, Subscription } from 'rxjs'; import { DialogService } from '@core/services/dialog.service'; import { TranslateService } from '@ngx-translate/core'; -import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; -import { ComponentPortal, PortalInjector } from '@angular/cdk/portal'; -import { - EDIT_REDIRECT_URI_PANEL_DATA, - EditRedirectUriPanelComponent, - EditRedirectUriPanelData -} from './edit-redirect-uri-panel.component'; +import { isDefined } from '@core/utils'; @Component({ selector: 'tb-oauth2-settings', @@ -60,15 +58,15 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha additionalInfo: { providerName: 'Custom' }, - clientAuthenticationMethod: 'POST', + clientAuthenticationMethod: ClientAuthenticationMethod.POST, userNameAttributeName: 'email', mapperConfig: { allowUserCreation: true, activateUser: false, - type: 'BASIC', + type: MapperConfigType.BASIC, basic: { emailAttributeKey: 'email', - tenantNameStrategy: 'DOMAIN', + tenantNameStrategy: TenantNameStrategy.DOMAIN, alwaysFullScreen: false } } @@ -77,19 +75,18 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha readonly separatorKeysCodes: number[] = [ENTER, COMMA]; oauth2SettingsForm: FormGroup; - oauth2Settings: OAuth2Settings[]; + oauth2Settings: OAuth2Settings; - clientAuthenticationMethods: ClientAuthenticationMethod[] = ['BASIC', 'POST']; - converterTypesExternalUser: MapperConfigType[] = ['BASIC', 'CUSTOM']; - tenantNameStrategies: TenantNameStrategy[] = ['DOMAIN', 'EMAIL', 'CUSTOM']; + clientAuthenticationMethods = Object.keys(ClientAuthenticationMethod); + converterTypesExternalUser = Object.keys(MapperConfigType); + tenantNameStrategies = Object.keys(TenantNameStrategy); + protocols = Object.keys(DomainSchema); + domainSchemaTranslations = domainSchemaTranslations; templateProvider = ['Custom']; constructor(protected store: Store, private adminService: AdminService, - private overlay: Overlay, - private viewContainerRef: ViewContainerRef, - private cd: ChangeDetectorRef, private fb: FormBuilder, private dialogService: DialogService, private translate: TranslateService, @@ -124,8 +121,8 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.templateProvider.sort(); } - get clientDomains(): FormArray { - return this.oauth2SettingsForm.get('clientDomains') as FormArray; + get domainsParams(): FormArray { + return this.oauth2SettingsForm.get('domainsParams') as FormArray; } private formBasicGroup(mapperConfigBasic?: MapperConfigBasic): FormGroup { @@ -139,7 +136,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha emailAttributeKey: [mapperConfigBasic?.emailAttributeKey ? mapperConfigBasic.emailAttributeKey : 'email', Validators.required], firstNameAttributeKey: [mapperConfigBasic?.firstNameAttributeKey ? mapperConfigBasic.firstNameAttributeKey : ''], lastNameAttributeKey: [mapperConfigBasic?.lastNameAttributeKey ? mapperConfigBasic.lastNameAttributeKey : ''], - tenantNameStrategy: [mapperConfigBasic?.tenantNameStrategy ? mapperConfigBasic.tenantNameStrategy : 'DOMAIN'], + tenantNameStrategy: [mapperConfigBasic?.tenantNameStrategy ? mapperConfigBasic.tenantNameStrategy : TenantNameStrategy.DOMAIN], tenantNamePattern: [tenantNamePattern, Validators.required], customerNamePattern: [mapperConfigBasic?.customerNamePattern ? mapperConfigBasic.customerNamePattern : null], defaultDashboardName: [mapperConfigBasic?.defaultDashboardName ? mapperConfigBasic.defaultDashboardName : null], @@ -167,69 +164,78 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private buildOAuth2SettingsForm(): void { this.oauth2SettingsForm = this.fb.group({ - clientDomains: this.fb.array([]) + domainsParams: this.fb.array([]), + enabled: [false] }); } - private initOAuth2Settings(oauth2Settings: OAuth2Settings[]): void { + private initOAuth2Settings(oauth2Settings: OAuth2Settings): void { if (oauth2Settings) { - oauth2Settings.forEach((domain) => { - this.clientDomains.push(this.buildSettingsDomain(domain)); + this.oauth2SettingsForm.patchValue({enabled: oauth2Settings.enabled}, {emitEvent: false}); + oauth2Settings.domainsParams.forEach((domain) => { + this.domainsParams.push(this.buildDomainsForm(domain)); }); } } private uniqueDomainValidator(control: AbstractControl): { [key: string]: boolean } | null { - if (control.root.value.clientDomains?.length > 1) { - const listDomainName = []; - control.root.value.clientDomains.forEach((domain) => { - listDomainName.push(domain.domainName); - }); - if (listDomainName.indexOf(control.value) > -1) { + if (control.parent?.parent?.value) { + const domain = control.value; + const listProtocols = control.parent.parent.value + .filter((domainInfo) => domainInfo.name === domain) + .map((domainInfo) => domainInfo.scheme); + if (listProtocols.length > 1 && listProtocols.indexOf(DomainSchema.MIXED) > -1) { return {unique: true}; } } return null; } - private buildSettingsDomain(domainParams?: OAuth2Settings): FormGroup { - let url = this.window.location.protocol + '//' + this.window.location.hostname; - const port = this.window.location.port; - if (port !== '80' && port !== '443') { - url += ':' + port; + public domainListTittle(control: AbstractControl): string { + const domainInfos = control.get('domainInfos').value as DomainInfo[]; + if (domainInfos.length) { + const domainList = new Set(); + domainInfos.forEach((domain) => { + domainList.add(domain.name); + }); + return Array.from(domainList).join(', '); } - url += '/login/oauth2/code/'; + return this.translate.instant('admin.oauth2.new-domain'); + } + + private buildDomainsForm(domainParams?: DomainsParam): FormGroup { const formDomain = this.fb.group({ - domainName: [domainParams?.domainName ? domainParams.domainName : this.window.location.hostname, [ - Validators.required, - Validators.pattern('((?![:/]).)*$'), - this.uniqueDomainValidator]], - redirectUriTemplate: [domainParams?.redirectUriTemplate ? domainParams.redirectUriTemplate : url, [ - Validators.required, - Validators.pattern(this.URL_REGEXP)]], + domainInfos: this.fb.array([], Validators.required), clientRegistrations: this.fb.array([], Validators.required) }); - this.subscriptions.push(formDomain.get('domainName').valueChanges.subscribe((domain) => { - if (!domain) { - domain = this.window.location.hostname; - } - const uri = this.window.location.protocol + `//${domain}/login/oauth2/code/`; - formDomain.get('redirectUriTemplate').patchValue(uri, {emitEvent: false}); - })); - if (domainParams) { + domainParams.domainInfos.forEach((domain) => { + this.clientDomainInfos(formDomain).push(this.buildDomainForm(domain)); + }); domainParams.clientRegistrations.forEach((registration) => { - this.clientDomainRegistrations(formDomain).push(this.buildSettingsRegistration(registration)); + this.clientDomainProviders(formDomain).push(this.buildProviderForm(registration)); }); } else { - this.clientDomainRegistrations(formDomain).push(this.buildSettingsRegistration()); + this.clientDomainProviders(formDomain).push(this.buildProviderForm()); + this.clientDomainInfos(formDomain).push(this.buildDomainForm()); } return formDomain; } - private buildSettingsRegistration(registrationData?: ClientRegistration): FormGroup { + private buildDomainForm(domainInfo?: DomainInfo): FormGroup { + const domain = this.fb.group({ + name: [domainInfo ? domainInfo.name : this.window.location.hostname, [ + Validators.required, + Validators.pattern('((?![:/]).)*$'), + this.uniqueDomainValidator]], + scheme: [domainInfo?.scheme ? domainInfo.scheme : DomainSchema.HTTPS, Validators.required] + }); + return domain; + } + + private buildProviderForm(registrationData?: ClientRegistration): FormGroup { let additionalInfo = null; if (registrationData?.additionalInfo) { additionalInfo = JSON.parse(registrationData.additionalInfo); @@ -237,13 +243,18 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha additionalInfo.providerName = 'Custom'; } } + let defaultProviderName = 'Custom'; + if (this.templateProvider.indexOf('Google')) { + defaultProviderName = 'Google'; + } + const clientRegistration = this.fb.group({ id: this.fb.group({ id: [registrationData?.id?.id ? registrationData.id.id : null], entityType: [registrationData?.id?.entityType ? registrationData.id.entityType : null] }), additionalInfo: this.fb.group({ - providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : 'Custom', Validators.required] + providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : defaultProviderName, Validators.required] }), loginButtonLabel: [registrationData?.loginButtonLabel ? registrationData.loginButtonLabel : null, Validators.required], loginButtonIcon: [registrationData?.loginButtonIcon ? registrationData.loginButtonIcon : null], @@ -255,19 +266,20 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '', [Validators.required, Validators.pattern(this.URL_REGEXP)]], - scope: this.fb.array(registrationData?.scope ? registrationData.scope : []), + scope: this.fb.array(registrationData?.scope ? registrationData.scope : [], Validators.required), jwkSetUri: [registrationData?.jwkSetUri ? registrationData.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '', [Validators.required, Validators.pattern(this.URL_REGEXP)]], clientAuthenticationMethod: [ - registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : 'POST', Validators.required], + registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : ClientAuthenticationMethod.POST, + Validators.required], userNameAttributeName: [ registrationData?.userNameAttributeName ? registrationData.userNameAttributeName : 'email', Validators.required], mapperConfig: this.fb.group({ allowUserCreation: [registrationData?.mapperConfig?.allowUserCreation ? registrationData.mapperConfig.allowUserCreation : true], activateUser: [registrationData?.mapperConfig?.activateUser ? registrationData.mapperConfig.activateUser : false], - type: [registrationData?.mapperConfig?.type ? registrationData.mapperConfig.type : 'BASIC', Validators.required] + type: [registrationData?.mapperConfig?.type ? registrationData.mapperConfig.type : MapperConfigType.BASIC, Validators.required] } ) }); @@ -275,7 +287,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha if (registrationData) { this.changeMapperConfigType(clientRegistration, registrationData.mapperConfig.type, registrationData.mapperConfig); } else { - this.changeMapperConfigType(clientRegistration, 'BASIC'); + this.changeMapperConfigType(clientRegistration, MapperConfigType.BASIC); } this.subscriptions.push(clientRegistration.get('mapperConfig.type').valueChanges.subscribe((value) => { @@ -313,7 +325,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha private changeMapperConfigType(control: AbstractControl, type: MapperConfigType, predefinedValue?: MapperConfig) { const mapperConfig = control.get('mapperConfig') as FormGroup; - if (type === 'BASIC') { + if (type === MapperConfigType.BASIC) { mapperConfig.removeControl('custom'); mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic)); } else { @@ -323,7 +335,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } save(): void { - const setting = this.prepareFormValue(this.oauth2SettingsForm.getRawValue().clientDomains); + const setting = this.prepareFormValue(this.oauth2SettingsForm.getRawValue()); this.adminService.saveOAuth2Settings(setting).subscribe( (oauth2Settings) => { this.oauth2Settings = oauth2Settings; @@ -333,20 +345,16 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha ); } - private prepareFormValue(formValue: OAuth2Settings[]): OAuth2Settings[]{ - const prepereValue = []; - formValue.forEach((setting, index) => { - if (this.clientDomains.at(index).dirty) { - setting.clientRegistrations.forEach((registration) => { - registration.additionalInfo = JSON.stringify(registration.additionalInfo); - if (registration.id.id === null) { - delete registration.id; - } - }); - prepereValue.push(setting); - } + private prepareFormValue(formValue: OAuth2Settings): OAuth2Settings{ + formValue.domainsParams.forEach((setting, index) => { + setting.clientRegistrations.forEach((registration) => { + registration.additionalInfo = JSON.stringify(registration.additionalInfo); + if (registration.id.id === null) { + delete registration.id; + } + }); }); - return prepereValue; + return formValue; } confirmForm(): FormGroup { @@ -359,6 +367,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha const controller = control.get('scope') as FormArray; if ((value.trim() !== '')) { controller.push(this.fb.control(value.trim())); + controller.markAsDirty(); } if (input) { @@ -369,10 +378,12 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha removeScope(i: number, control: AbstractControl): void { const controller = control.get('scope') as FormArray; controller.removeAt(i); + controller.markAsTouched(); + controller.markAsDirty(); } addDomain(): void { - this.clientDomains.push(this.buildSettingsDomain()); + this.domainsParams.push(this.buildDomainsForm()); } deleteDomain($event: Event, index: number): void { @@ -381,97 +392,48 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha $event.preventDefault(); } - const domainName = this.clientDomains.at(index).get('domainName').value; + const domainName = this.domainListTittle(this.domainsParams.at(index)); this.dialogService.confirm( this.translate.instant('admin.oauth2.delete-domain-title', {domainName: domainName || ''}), this.translate.instant('admin.oauth2.delete-domain-text'), null, this.translate.instant('action.delete') ).subscribe((data) => { if (data) { - if (index < this.oauth2Settings.length) { - this.adminService.deleteOAuth2Domain(this.oauth2Settings[index].domainName).subscribe(() => { - this.oauth2Settings.splice(index, 1); - this.clientDomains.removeAt(index); - }); - } else { - this.clientDomains.removeAt(index); - } + this.domainsParams.removeAt(index); + this.domainsParams.markAsTouched(); + this.domainsParams.markAsDirty(); } }); } - clientDomainRegistrations(control: AbstractControl): FormArray { + clientDomainProviders(control: AbstractControl): FormArray { return control.get('clientRegistrations') as FormArray; } - addRegistration(control: AbstractControl): void { - this.clientDomainRegistrations(control).push(this.buildSettingsRegistration()); + clientDomainInfos(control: AbstractControl): FormArray { + return control.get('domainInfos') as FormArray; } - deleteRegistration($event: Event, control: AbstractControl, index: number): void { + addProvider(control: AbstractControl): void { + this.clientDomainProviders(control).push(this.buildProviderForm()); + } + + deleteProvider($event: Event, control: AbstractControl, index: number): void { if ($event) { $event.stopPropagation(); $event.preventDefault(); } - const providerName = this.clientDomainRegistrations(control).at(index).get('additionalInfo.providerName').value; + const providerName = this.clientDomainProviders(control).at(index).get('additionalInfo.providerName').value; this.dialogService.confirm( this.translate.instant('admin.oauth2.delete-registration-title', {name: providerName || ''}), this.translate.instant('admin.oauth2.delete-registration-text'), null, this.translate.instant('action.delete') ).subscribe((data) => { if (data) { - const registrationId = this.clientDomainRegistrations(control).at(index).get('id.id').value; - if (registrationId) { - this.adminService.deleteOAuthCclientRegistrationId(registrationId).subscribe(() => { - this.clientDomainRegistrations(control).removeAt(index); - }); - } else { - this.clientDomainRegistrations(control).removeAt(index); - } - } - }); - } - - editRedirectURI($event: MouseEvent, index: number) { - if ($event) { - $event.stopPropagation(); - $event.preventDefault(); - } - const target = $event.target || $event.currentTarget; - const config = new OverlayConfig(); - config.backdropClass = 'cdk-overlay-transparent-backdrop'; - config.hasBackdrop = true; - const connectedPosition: ConnectedPosition = { - originX: 'end', - originY: 'bottom', - overlayX: 'end', - overlayY: 'top' - }; - config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) - .withPositions([connectedPosition]); - - const overlayRef = this.overlay.create(config); - overlayRef.backdropClick().subscribe(() => { - overlayRef.dispose(); - }); - - const redirectURI = this.clientDomains.at(index).get('redirectUriTemplate').value; - - const injectionTokens = new WeakMap([ - [EDIT_REDIRECT_URI_PANEL_DATA, { - redirectURI - } as EditRedirectUriPanelData], - [OverlayRef, overlayRef] - ]); - const injector = new PortalInjector(this.viewContainerRef.injector, injectionTokens); - const componentRef = overlayRef.attach(new ComponentPortal(EditRedirectUriPanelComponent, - this.viewContainerRef, injector)); - componentRef.onDestroy(() => { - if (componentRef.instance.result !== null) { - const attributeValue = componentRef.instance.result; - this.clientDomains.at(index).get('redirectUriTemplate').patchValue(attributeValue); - this.clientDomains.at(index).get('redirectUriTemplate').markAsDirty(); + this.clientDomainProviders(control).removeAt(index); + this.clientDomainProviders(control).markAsTouched(); + this.clientDomainProviders(control).markAsDirty(); } }); } @@ -491,4 +453,43 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } return this.templates.get(provider).helpLink; } + + addDomainInfo(control: AbstractControl): void { + this.clientDomainInfos(control).push(this.buildDomainForm({ + name: '', + scheme: DomainSchema.HTTPS + })); + } + + removeDomain($event: Event, control: AbstractControl, index: number): void { + if ($event) { + $event.stopPropagation(); + $event.preventDefault(); + } + this.clientDomainInfos(control).removeAt(index); + this.clientDomainInfos(control).markAsTouched(); + this.clientDomainInfos(control).markAsDirty(); + } + + redirectURI(control: AbstractControl, schema?: DomainSchema): string { + const domainInfo = control.value as DomainInfo; + if (domainInfo.name !== '') { + let protocol; + if (isDefined(schema)) { + protocol = schema.toLowerCase(); + } else { + protocol = domainInfo.scheme === DomainSchema.MIXED ? DomainSchema.HTTPS.toLowerCase() : domainInfo.scheme.toLowerCase(); + } + return `${protocol}://${domainInfo.name}/login/oauth2/code/`; + } + return ''; + } + + redirectURIMixed(control: AbstractControl): string { + return this.redirectURI(control, DomainSchema.HTTP); + } + + trackByParams(index: number): number { + return index; + } } diff --git a/ui-ngx/src/app/shared/models/settings.models.ts b/ui-ngx/src/app/shared/models/settings.models.ts index f27d80a260..f31dc5ebe8 100644 --- a/ui-ngx/src/app/shared/models/settings.models.ts +++ b/ui-ngx/src/app/shared/models/settings.models.ts @@ -26,10 +26,6 @@ export interface AdminSettings { export declare type SmtpProtocol = 'smtp' | 'smtps'; -export declare type ClientAuthenticationMethod = 'BASIC' | 'POST'; -export declare type MapperConfigType = 'BASIC' | 'CUSTOM'; -export declare type TenantNameStrategy = 'DOMAIN' | 'EMAIL' | 'CUSTOM'; - export interface MailServerSettings { mailFrom: string; smtpProtocol: SmtpProtocol; @@ -69,9 +65,43 @@ export interface UpdateMessage { } export interface OAuth2Settings { - domainName: string; - redirectUriTemplate: string; + enabled: boolean; + domainsParams: DomainsParam[]; +} + +export interface DomainsParam { clientRegistrations: ClientRegistration[]; + domainInfos: DomainInfo[]; +} + +export interface DomainInfo { + name: string; + scheme: DomainSchema; +} + +export enum DomainSchema{ + HTTP = 'HTTP', + HTTPS = 'HTTPS', + MIXED = 'MIXED' +} + +export const domainSchemaTranslations = new Map( + [ + [DomainSchema.HTTP, 'admin.oauth2.domain-schema-http'], + [DomainSchema.HTTPS, 'admin.oauth2.domain-schema-https'], + [DomainSchema.MIXED, 'admin.oauth2.domain-schema-mixed'] + ] +); + +export enum MapperConfigType{ + BASIC = 'BASIC', + CUSTOM = 'CUSTOM' +} + +export enum TenantNameStrategy{ + DOMAIN = 'DOMAIN', + EMAIL = 'EMAIL', + CUSTOM = 'CUSTOM' } export interface ClientProviderTemplated extends ClientRegistration{ @@ -100,6 +130,11 @@ export interface ClientRegistration { additionalInfo: string; } +export enum ClientAuthenticationMethod { + BASIC = 'BASIC', + POST = 'POST' +} + export interface MapperConfig { allowUserCreation: boolean; activateUser: boolean; diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 8cb452e075..6a4fc5e084 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -128,7 +128,9 @@ "access-token-uri-required": "Access token URI is required.", "activate-user": "Activate user", "add-domain": "Add domain", + "delete-domain": "Delete domain", "add-provider": "Add provider", + "delete-provider": "Delete provider", "allow-user-creation": "Allow user creation", "always-fullscreen": "Always fullscreen", "authorization-uri": "Authorization URI", @@ -141,10 +143,10 @@ "custom-setting": "Custom settings", "customer-name-pattern": "Customer name pattern", "default-dashboard-name": "Default dashboard name", - "delete-domain-text": "Be careful, after the confirmation a domain and all registration data will be unavailable.", - "delete-domain-title": "Are you sure you want to delete the domain '{{domainName}}'?", - "delete-registration-text": "Be careful, after the confirmation a registration data will be unavailable.", - "delete-registration-title": "Are you sure you want to delete the registration '{{name}}'?", + "delete-domain-text": "Be careful, after the confirmation a domain and all provider data will be unavailable.", + "delete-domain-title": "Are you sure you want to delete settings the domain '{{domainName}}'?", + "delete-registration-text": "Be careful, after the confirmation a provider data will be unavailable.", + "delete-registration-title": "Are you sure you want to delete the provider '{{name}}'?", "email-attribute-key": "Email attribute key", "email-attribute-key-required": "Email attribute key is required.", "first-name-attribute-key": "First name attribute key", @@ -160,11 +162,12 @@ "new-domain": "New domain", "oauth2": "OAuth2", "redirect-uri-template": "Redirect URI template", - "redirect-uri-template-required": "Redirect URI template is required.", + "copy-redirect-uri": "Copy redirect URI", "registration-id": "Registration ID", "registration-id-required": "Registration ID is required.", "registration-id-unique": "Registration ID need to unique for the system.", "scope": "Scope", + "scope-required": "Scope is required.", "tenant-name-pattern": "Tenant name pattern", "tenant-name-pattern-required": "Tenant name pattern is required.", "tenant-name-strategy": "Tenant name strategy", @@ -176,7 +179,12 @@ "user-info-uri": "User info URI", "user-info-uri-required": "User info URI is required.", "user-name-attribute-name": "User name attribute key", - "user-name-attribute-name-required": "User name attribute key is required" + "user-name-attribute-name-required": "User name attribute key is required", + "protocol": "Protocol", + "domain-schema-http": "HTTP", + "domain-schema-https": "HTTPS", + "domain-schema-mixed": "HTTP+HTTPS", + "enable": "Enable OAuth2 settings" } }, "alarm": { From 9c5b353a17a21d3d392afc2e3b43d2849100cf8f Mon Sep 17 00:00:00 2001 From: Vladyslav Prykhodko Date: Wed, 7 Oct 2020 00:44:36 +0300 Subject: [PATCH 107/117] UI: Added validation unique domain --- .../admin/oauth2-settings.component.html | 49 +++++++------- .../pages/admin/oauth2-settings.component.ts | 65 ++++++++++--------- .../assets/locale/locale.constant-en_US.json | 2 +- 3 files changed, 60 insertions(+), 56 deletions(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index 6e0048612c..fc8e294774 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -59,30 +59,31 @@
-
-
- - admin.oauth2.protocol - - - {{ domainSchemaTranslations.get(protocol) | translate | uppercase }} - - - - - - admin.domain-name - - - {{ 'admin.error-verification-url' | translate }} - - - {{ 'admin.domain-name-unique' | translate }} - - +
+
+
+ + admin.oauth2.protocol + + + {{ domainSchemaTranslations.get(protocol) | translate | uppercase }} + + + + + admin.domain-name + + + {{ 'admin.error-verification-url' | translate }} + + +
+ + {{ 'admin.domain-name-unique' | translate }} +
-
+
admin.oauth2.redirect-uri-template @@ -94,8 +95,7 @@ - +
-
diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts index b2ffc2bb6e..11b39468e9 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.ts @@ -178,13 +178,14 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha } } - private uniqueDomainValidator(control: AbstractControl): { [key: string]: boolean } | null { - if (control.parent?.parent?.value) { - const domain = control.value; - const listProtocols = control.parent.parent.value + private uniqueDomainValidator(control: FormGroup): { [key: string]: boolean } | null { + if (control.parent?.value) { + const domain = control.value.name; + const listProtocols = control.parent.getRawValue() .filter((domainInfo) => domainInfo.name === domain) .map((domainInfo) => domainInfo.scheme); - if (listProtocols.length > 1 && listProtocols.indexOf(DomainSchema.MIXED) > -1) { + if (listProtocols.length > 1 && listProtocols.indexOf(DomainSchema.MIXED) > -1 || + new Set(listProtocols).size !== listProtocols.length) { return {unique: true}; } } @@ -228,10 +229,9 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha const domain = this.fb.group({ name: [domainInfo ? domainInfo.name : this.window.location.hostname, [ Validators.required, - Validators.pattern('((?![:/]).)*$'), - this.uniqueDomainValidator]], + Validators.pattern('((?![:/]).)*$')]], scheme: [domainInfo?.scheme ? domainInfo.scheme : DomainSchema.HTTPS, Validators.required] - }); + }, {validators: this.uniqueDomainValidator}); return domain; } @@ -288,6 +288,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.changeMapperConfigType(clientRegistration, registrationData.mapperConfig.type, registrationData.mapperConfig); } else { this.changeMapperConfigType(clientRegistration, MapperConfigType.BASIC); + this.setProviderDefaultValue(defaultProviderName, clientRegistration); } this.subscriptions.push(clientRegistration.get('mapperConfig.type').valueChanges.subscribe((value) => { @@ -296,33 +297,37 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha this.subscriptions.push(clientRegistration.get('additionalInfo.providerName').valueChanges.subscribe((provider) => { (clientRegistration.get('scope') as FormArray).clear(); - if (provider === 'Custom') { - const defaultSettings = {...this.defaultProvider, ...{id: clientRegistration.get('id').value}}; - clientRegistration.reset(defaultSettings, {emitEvent: false}); - clientRegistration.get('accessTokenUri').enable(); - clientRegistration.get('authorizationUri').enable(); - clientRegistration.get('jwkSetUri').enable(); - clientRegistration.get('userInfoUri').enable(); - } else { - const template = this.templates.get(provider); - delete template.id; - delete template.additionalInfo; - template.clientId = ''; - template.clientSecret = ''; - template.scope.forEach(() => { - (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); - }); - clientRegistration.get('accessTokenUri').disable(); - clientRegistration.get('authorizationUri').disable(); - clientRegistration.get('jwkSetUri').disable(); - clientRegistration.get('userInfoUri').disable(); - clientRegistration.patchValue(template, {emitEvent: false}); - } + this.setProviderDefaultValue(provider, clientRegistration); })); return clientRegistration; } + private setProviderDefaultValue(provider: string, clientRegistration: FormGroup) { + if (provider === 'Custom') { + const defaultSettings = {...this.defaultProvider, ...{id: clientRegistration.get('id').value}}; + clientRegistration.reset(defaultSettings, {emitEvent: false}); + clientRegistration.get('accessTokenUri').enable(); + clientRegistration.get('authorizationUri').enable(); + clientRegistration.get('jwkSetUri').enable(); + clientRegistration.get('userInfoUri').enable(); + } else { + const template = this.templates.get(provider); + delete template.id; + delete template.additionalInfo; + template.clientId = ''; + template.clientSecret = ''; + template.scope.forEach(() => { + (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); + }); + clientRegistration.get('accessTokenUri').disable(); + clientRegistration.get('authorizationUri').disable(); + clientRegistration.get('jwkSetUri').disable(); + clientRegistration.get('userInfoUri').disable(); + clientRegistration.patchValue(template, {emitEvent: false}); + } + } + private changeMapperConfigType(control: AbstractControl, type: MapperConfigType, predefinedValue?: MapperConfig) { const mapperConfig = control.get('mapperConfig') as FormGroup; if (type === MapperConfigType.BASIC) { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index 6a4fc5e084..df5b962535 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -121,7 +121,7 @@ "minimum-max-failed-login-attempts-range": "Maximum number of failed login attempts can't be negative", "user-lockout-notification-email": "In case user account lockout, send notification to email", "domain-name": "Domain name", - "domain-name-unique": "Domain name need to unique for the system.", + "domain-name-unique": "Domain name and protocol need to unique.", "error-verification-url": "A domain name shouldn't contain symbols '/' and ':'. Example: thingsboard.io", "oauth2": { "access-token-uri": "Access token URI", From 91f05282ea51b355cb2c04948919600cffb73f87 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Wed, 7 Oct 2020 13:06:22 +0300 Subject: [PATCH 108/117] Validate domains for SchemeTypes combinations --- .../server/dao/oauth2/OAuth2ServiceImpl.java | 9 +- .../dao/service/BaseOAuth2ServiceTest.java | 99 ++++++++++++++++++- 2 files changed, 102 insertions(+), 6 deletions(-) diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index dd287f0755..2ad0ad4f20 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -119,10 +119,17 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se if (StringUtils.isEmpty(domainInfo.getName())) { throw new DataValidationException("Domain name should be specified!"); } - if (StringUtils.isEmpty(domainInfo.getScheme())) { + 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!"); } diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java index 2daa0cb337..b54505a3d7 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ServiceTest.java @@ -22,6 +22,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.oauth2.*; +import org.thingsboard.server.dao.exception.DataValidationException; import org.thingsboard.server.dao.oauth2.OAuth2Service; import java.util.*; @@ -45,6 +46,44 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { Assert.assertTrue(oAuth2Service.findOAuth2Params().getDomainsParams().isEmpty()); } + @Test(expected = DataValidationException.class) + public void testSaveHttpAndMixedDomainsTogether() { + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( + OAuth2ClientsDomainParams.builder() + .domainInfos(Sets.newHashSet( + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), + DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + )) + .clientRegistrations(Sets.newHashSet( + validClientRegistrationDto(), + validClientRegistrationDto(), + validClientRegistrationDto() + )) + .build() + )); + oAuth2Service.saveOAuth2Params(clientsParams); + } + + @Test(expected = DataValidationException.class) + public void testSaveHttpsAndMixedDomainsTogether() { + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( + OAuth2ClientsDomainParams.builder() + .domainInfos(Sets.newHashSet( + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(), + DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), + DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() + )) + .clientRegistrations(Sets.newHashSet( + validClientRegistrationDto(), + validClientRegistrationDto(), + validClientRegistrationDto() + )) + .build() + )); + oAuth2Service.saveOAuth2Params(clientsParams); + } + @Test public void testCreateAndFindParams() { OAuth2ClientsParams clientsParams = createDefaultClientsParams(); @@ -178,7 +217,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { Assert.assertTrue(nonExistentDomainClients.isEmpty()); List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain"); - Assert.assertEquals(firstDomainHttpClients.size(), firstDomainHttpClients.size()); + Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( firstDomainHttpClients.stream().anyMatch(clientInfo -> @@ -191,7 +230,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { Assert.assertTrue(firstDomainHttpsClients.isEmpty()); List fourthDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "fourth-domain"); - Assert.assertEquals(fourthDomainHttpClients.size(), secondGroupClientInfos.size()); + Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpClients.size()); secondGroupClientInfos.forEach(secondGroupClientInfo -> { Assert.assertTrue( fourthDomainHttpClients.stream().anyMatch(clientInfo -> @@ -200,7 +239,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { ); }); List fourthDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "fourth-domain"); - Assert.assertEquals(fourthDomainHttpsClients.size(), secondGroupClientInfos.size()); + Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpsClients.size()); secondGroupClientInfos.forEach(secondGroupClientInfo -> { Assert.assertTrue( fourthDomainHttpsClients.stream().anyMatch(clientInfo -> @@ -210,7 +249,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { }); List secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); - Assert.assertEquals(secondDomainHttpClients.size(), firstGroupClientInfos.size() + secondGroupClientInfos.size()); + Assert.assertEquals(firstGroupClientInfos.size() + secondGroupClientInfos.size(), secondDomainHttpClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( secondDomainHttpClients.stream().anyMatch(clientInfo -> @@ -227,7 +266,7 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { }); List secondDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "second-domain"); - Assert.assertEquals(secondDomainHttpsClients.size(), firstGroupClientInfos.size() + thirdGroupClientInfos.size()); + Assert.assertEquals(firstGroupClientInfos.size() + thirdGroupClientInfos.size(), secondDomainHttpsClients.size()); firstGroupClientInfos.forEach(firstGroupClientInfo -> { Assert.assertTrue( secondDomainHttpsClients.stream().anyMatch(clientInfo -> @@ -244,6 +283,56 @@ public class BaseOAuth2ServiceTest extends AbstractServiceTest { }); } + @Test + public void testGetOAuth2ClientsForHttpAndHttps() { + Set firstGroup = Sets.newHashSet( + validClientRegistrationDto(), + validClientRegistrationDto(), + validClientRegistrationDto(), + validClientRegistrationDto() + ); + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( + OAuth2ClientsDomainParams.builder() + .domainInfos(Sets.newHashSet( + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), + DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build() + )) + .clientRegistrations(firstGroup) + .build() + )); + + oAuth2Service.saveOAuth2Params(clientsParams); + OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); + Assert.assertNotNull(foundClientsParams); + Assert.assertEquals(clientsParams, foundClientsParams); + + List firstGroupClientInfos = firstGroup.stream() + .map(clientRegistrationDto -> new OAuth2ClientInfo( + clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) + .collect(Collectors.toList()); + + List firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain"); + Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); + firstGroupClientInfos.forEach(firstGroupClientInfo -> { + Assert.assertTrue( + firstDomainHttpClients.stream().anyMatch(clientInfo -> + clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) + && clientInfo.getName().equals(firstGroupClientInfo.getName())) + ); + }); + + List firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain"); + Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpsClients.size()); + firstGroupClientInfos.forEach(firstGroupClientInfo -> { + Assert.assertTrue( + firstDomainHttpsClients.stream().anyMatch(clientInfo -> + clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) + && clientInfo.getName().equals(firstGroupClientInfo.getName())) + ); + }); + } + @Test public void testGetDisabledOAuth2Clients() { OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( From 90234bf7a36e40c502e6650fd65d827b325bf38d Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 8 Oct 2020 11:37:04 +0300 Subject: [PATCH 109/117] Fixed merge --- .../main/data/upgrade/3.1.1/schema_update.sql | 89 ------------------- .../upgrade/3.1.2/schema_update_before.sql | 68 ++++++++++++++ .../install/ThingsboardInstallService.java | 4 - .../service/security/permission/Resource.java | 1 - ui-ngx/src/app/core/services/menu.service.ts | 1 + 5 files changed, 69 insertions(+), 94 deletions(-) delete mode 100644 application/src/main/data/upgrade/3.1.1/schema_update.sql diff --git a/application/src/main/data/upgrade/3.1.1/schema_update.sql b/application/src/main/data/upgrade/3.1.1/schema_update.sql deleted file mode 100644 index 0b99e797b0..0000000000 --- a/application/src/main/data/upgrade/3.1.1/schema_update.sql +++ /dev/null @@ -1,89 +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. --- - -DROP TABLE IF EXISTS oauth2_client_registration_info; - -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 -); - -DROP TABLE IF EXISTS oauth2_client_registration; - -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 -); - -DROP TABLE IF EXISTS oauth2_client_registration_template; - -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), - 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) -); diff --git a/application/src/main/data/upgrade/3.1.2/schema_update_before.sql b/application/src/main/data/upgrade/3.1.2/schema_update_before.sql index c1591e7831..fe8ee4531b 100644 --- a/application/src/main/data/upgrade/3.1.2/schema_update_before.sql +++ b/application/src/main/data/upgrade/3.1.2/schema_update_before.sql @@ -14,6 +14,74 @@ -- 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), + 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, diff --git a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java index c51bf905f8..6f6bd7a139 100644 --- a/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java +++ b/application/src/main/java/org/thingsboard/server/install/ThingsboardInstallService.java @@ -185,10 +185,6 @@ public class ThingsboardInstallService { databaseEntitiesUpgradeService.upgradeDatabase("3.1.2"); log.info("Updating system data..."); systemDataLoaderService.updateSystemWidgets(); - case "3.1.1": - log.info("Upgrading ThingsBoard from version 3.1.1 to 3.2.0 ..."); - databaseEntitiesUpgradeService.upgradeDatabase("3.1.1"); - log.info("Updating system data..."); systemDataLoaderService.createOAuth2Templates(); break; default: diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index f96c526bfe..671e0539cc 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -34,7 +34,6 @@ public enum Resource { WIDGET_TYPE(EntityType.WIDGET_TYPE), OAUTH2_CONFIGURATION_INFO(EntityType.OAUTH2_CLIENT_REGISTRATION_INFO), OAUTH2_CONFIGURATION_TEMPLATE(EntityType.OAUTH2_CLIENT_REGISTRATION_TEMPLATE), - WIDGET_TYPE(EntityType.WIDGET_TYPE), TENANT_PROFILE(EntityType.TENANT_PROFILE), DEVICE_PROFILE(EntityType.DEVICE_PROFILE); diff --git a/ui-ngx/src/app/core/services/menu.service.ts b/ui-ngx/src/app/core/services/menu.service.ts index c921fde616..e44c3c6ec2 100644 --- a/ui-ngx/src/app/core/services/menu.service.ts +++ b/ui-ngx/src/app/core/services/menu.service.ts @@ -133,6 +133,7 @@ export class MenuService { icon: 'security' }, { + id: guid(), name: 'admin.oauth2.oauth2', type: 'link', path: '/settings/oauth2', From d2f45db0a66298669788a83024a194fdc595be43 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 8 Oct 2020 11:47:11 +0300 Subject: [PATCH 110/117] Removed unused changes to AttributeDao --- .../dao/attributes/AttributesService.java | 2 -- .../server/dao/attributes/AttributesDao.java | 2 -- .../dao/attributes/BaseAttributesService.java | 6 ---- .../dao/model/sql/AttributeKvEntity.java | 33 +++++++------------ .../sql/attributes/AttributeKvRepository.java | 4 --- .../dao/sql/attributes/JpaAttributeDao.java | 12 ------- 6 files changed, 12 insertions(+), 47 deletions(-) diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java index 75122aba57..d34cc157d0 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java @@ -36,8 +36,6 @@ public interface AttributesService { ListenableFuture> findAll(TenantId tenantId, EntityId entityId, String scope); - ListenableFuture> findAllByAttributeKey(String attributeKey); - ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes); ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, String scope, List attributeKeys); diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java index 85f6b2de44..83e705062c 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java @@ -36,8 +36,6 @@ public interface AttributesDao { ListenableFuture> findAll(TenantId tenantId, EntityId entityId, String attributeType); - ListenableFuture> findAllByAttributeKey(String attributeKey); - ListenableFuture save(TenantId tenantId, EntityId entityId, String attributeType, AttributeKvEntry attribute); ListenableFuture> removeAll(TenantId tenantId, EntityId entityId, String attributeType, List keys); diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java index 9956da347d..53cee47511 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java @@ -60,12 +60,6 @@ public class BaseAttributesService implements AttributesService { return attributesDao.findAll(tenantId, entityId, scope); } - @Override - public ListenableFuture> findAllByAttributeKey(String attributeKey) { - Validator.validateString(attributeKey, "Incorrect attribute key " + attributeKey); - return attributesDao.findAllByAttributeKey(attributeKey); - } - @Override public ListenableFuture> save(TenantId tenantId, EntityId entityId, String scope, List attributes) { validate(entityId, scope); diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java index c652a67469..f0de269cea 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AttributeKvEntity.java @@ -16,10 +16,20 @@ package org.thingsboard.server.dao.model.sql; import lombok.Data; -import org.thingsboard.server.common.data.kv.*; +import org.thingsboard.server.common.data.kv.AttributeKvEntry; +import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry; +import org.thingsboard.server.common.data.kv.BooleanDataEntry; +import org.thingsboard.server.common.data.kv.DoubleDataEntry; +import org.thingsboard.server.common.data.kv.JsonDataEntry; +import org.thingsboard.server.common.data.kv.KvEntry; +import org.thingsboard.server.common.data.kv.LongDataEntry; +import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.dao.model.ToData; -import javax.persistence.*; +import javax.persistence.Column; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.Table; import java.io.Serializable; import static org.thingsboard.server.dao.model.ModelConstants.BOOLEAN_VALUE_COLUMN; @@ -32,7 +42,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.STRING_VALUE_COLUM @Data @Entity @Table(name = "attribute_kv") -// TODO maybe move ToData to local field as well (or implement ToData differently) public class AttributeKvEntity implements ToData, Serializable { @EmbeddedId @@ -73,22 +82,4 @@ public class AttributeKvEntity implements ToData, Serializable return new BaseAttributeKvEntry(kvEntry, lastUpdateTs); } - - @Transient - public final ToData toEntityAttributeKvEntry = () -> { - KvEntry kvEntry = null; - if (strValue != null) { - kvEntry = new StringDataEntry(id.getAttributeKey(), strValue); - } else if (booleanValue != null) { - kvEntry = new BooleanDataEntry(id.getAttributeKey(), booleanValue); - } else if (doubleValue != null) { - kvEntry = new DoubleDataEntry(id.getAttributeKey(), doubleValue); - } else if (longValue != null) { - kvEntry = new LongDataEntry(id.getAttributeKey(), longValue); - } else if (jsonValue != null) { - kvEntry = new JsonDataEntry(id.getAttributeKey(), jsonValue); - } - - return new BaseEntityAttributeKvEntry(id.getEntityId(), lastUpdateTs, kvEntry); - }; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java index 185113d8e4..f3ef44e6c7 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/AttributeKvRepository.java @@ -36,10 +36,6 @@ public interface AttributeKvRepository extends CrudRepository findAllByAttributeKey(@Param("attributeKey") String attributeKey); - @Transactional @Modifying @Query("DELETE FROM AttributeKvEntity a WHERE a.id.entityType = :entityType " + diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java index 449a7eef8a..5c89725494 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java @@ -137,18 +137,6 @@ public class JpaAttributeDao extends JpaAbstractDaoListeningExecutorService impl attributeType)))); } - @Override - public ListenableFuture> findAllByAttributeKey(String attributeKey) { - return Futures.immediateFuture( - DaoUtil.convertDataList( - attributeKvRepository.findAllByAttributeKey(attributeKey).stream() - .map(attributeKvEntity -> attributeKvEntity.toEntityAttributeKvEntry) - .collect(Collectors.toList()) - ) - - ); - } - @Override public ListenableFuture save(TenantId tenantId, EntityId entityId, String attributeType, AttributeKvEntry attribute) { AttributeKvEntity entity = new AttributeKvEntity(); From 6ae75e295cf74e538bf499c969201825a290e4f1 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 8 Oct 2020 12:21:53 +0300 Subject: [PATCH 111/117] Removed LockService --- .../thingsboard/server/dao/lock/LockKey.java | 30 ------------- .../server/dao/lock/LockService.java | 25 ----------- .../server/dao/lock/LockServiceImpl.java | 45 ------------------- .../server/dao/sql/lock/LockRepository.java | 20 --------- .../dao/sql/lock/PsqlLockRepository.java | 40 ----------------- 5 files changed, 160 deletions(-) delete mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockKey.java delete mode 100644 common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockService.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/lock/LockRepository.java delete mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockKey.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockKey.java deleted file mode 100644 index 5de01b3e2c..0000000000 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockKey.java +++ /dev/null @@ -1,30 +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.lock; - -public enum LockKey { - OAUTH2_CONFIG(999); - - private int id; - - LockKey(int id) { - this.id = id; - } - - public int getId() { - return id; - } -} diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockService.java deleted file mode 100644 index 4ffd05233d..0000000000 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/lock/LockService.java +++ /dev/null @@ -1,25 +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.lock; - -public interface LockService { - /** - * Lock with SQL Database till the end of transaction - * - * @param key identifier - */ - void transactionLock(LockKey key); -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java deleted file mode 100644 index ff1e052f97..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/lock/LockServiceImpl.java +++ /dev/null @@ -1,45 +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.lock; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.thingsboard.server.dao.sql.lock.LockRepository; - -import javax.annotation.PostConstruct; - -@Service -@Slf4j -public class LockServiceImpl implements LockService { - - @Autowired(required = false) - private LockRepository lockRepository; - - @PostConstruct - public void init(){ - if (lockRepository == null) { - log.warn("Locking with DB is not enabled."); - } - } - - @Override - public void transactionLock(LockKey key) { - if (lockRepository == null) return; - log.trace("Locking transaction key [{}]", key); - lockRepository.transactionLock(key.getId()); - } -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/lock/LockRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/lock/LockRepository.java deleted file mode 100644 index eced945acd..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/lock/LockRepository.java +++ /dev/null @@ -1,20 +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.sql.lock; - -public interface LockRepository { - void transactionLock(Integer key); -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java deleted file mode 100644 index 76c4b6ebc8..0000000000 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/lock/PsqlLockRepository.java +++ /dev/null @@ -1,40 +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.sql.lock; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.stereotype.Repository; -import org.thingsboard.server.dao.util.PsqlDao; - -@Slf4j -@PsqlDao -@Repository -public class PsqlLockRepository implements LockRepository { - private static final String TRANSACTION_LOCK_QUERY = "SELECT pg_advisory_xact_lock(?)"; - - @Autowired - private JdbcTemplate jdbcTemplate; - - @Override - public void transactionLock(Integer key) { - jdbcTemplate.query(TRANSACTION_LOCK_QUERY, - preparedStatement -> preparedStatement.setInt(1, key), - resultSet -> {} - ); - } -} From c42c23c380005902d7605aad970d01e5381dc450 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 8 Oct 2020 12:22:26 +0300 Subject: [PATCH 112/117] Refactored and fixed code --- .../dao/attributes/AttributesService.java | 1 - .../dao/settings/AdminSettingsService.java | 2 +- .../data/kv/BaseEntityAttributeKvEntry.java | 39 ------------------- .../data/kv/EntityAttributeKvEntry.java | 23 ----------- .../server/dao/attributes/AttributesDao.java | 1 - .../dao/attributes/BaseAttributesService.java | 1 - .../dao/sql/attributes/JpaAttributeDao.java | 2 - .../server/dao/tenant/TenantServiceImpl.java | 4 -- .../resources/sql/hsql/drop-all-tables.sql | 2 +- .../resources/sql/psql/drop-all-tables.sql | 4 +- .../sql/timescale/drop-all-tables.sql | 4 +- 11 files changed, 7 insertions(+), 76 deletions(-) delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseEntityAttributeKvEntry.java delete mode 100644 common/data/src/main/java/org/thingsboard/server/common/data/kv/EntityAttributeKvEntry.java diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java index d34cc157d0..3af4737f06 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/attributes/AttributesService.java @@ -19,7 +19,6 @@ import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.EntityAttributeKvEntry; import java.util.Collection; import java.util.List; diff --git a/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java b/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java index a33b0c095f..a0c8d46d72 100644 --- a/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java +++ b/common/dao-api/src/main/java/org/thingsboard/server/dao/settings/AdminSettingsService.java @@ -26,5 +26,5 @@ public interface AdminSettingsService { AdminSettings findAdminSettingsByKey(TenantId tenantId, String key); AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings); - + } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseEntityAttributeKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseEntityAttributeKvEntry.java deleted file mode 100644 index 5c97acd90e..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/BaseEntityAttributeKvEntry.java +++ /dev/null @@ -1,39 +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.common.data.kv; - -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.thingsboard.server.common.data.EntityType; - -import java.util.UUID; - -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class BaseEntityAttributeKvEntry extends BaseAttributeKvEntry implements EntityAttributeKvEntry { - - private final UUID entityId; - - public BaseEntityAttributeKvEntry(UUID entityId, long lastUpdateTs, KvEntry kv) { - super(kv, lastUpdateTs); - this.entityId = entityId; - } - - @Override - public UUID getEntityId() { - return entityId; - } -} diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/kv/EntityAttributeKvEntry.java b/common/data/src/main/java/org/thingsboard/server/common/data/kv/EntityAttributeKvEntry.java deleted file mode 100644 index ceb6c62a3c..0000000000 --- a/common/data/src/main/java/org/thingsboard/server/common/data/kv/EntityAttributeKvEntry.java +++ /dev/null @@ -1,23 +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.common.data.kv; - -import java.util.UUID; - -public interface EntityAttributeKvEntry extends AttributeKvEntry { - - UUID getEntityId(); -} diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java index 83e705062c..8ec43f49a5 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/AttributesDao.java @@ -19,7 +19,6 @@ import com.google.common.util.concurrent.ListenableFuture; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.EntityAttributeKvEntry; import java.util.Collection; import java.util.List; diff --git a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java index 53cee47511..a6924960ca 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java +++ b/dao/src/main/java/org/thingsboard/server/dao/attributes/BaseAttributesService.java @@ -23,7 +23,6 @@ import org.springframework.stereotype.Service; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; import org.thingsboard.server.common.data.kv.AttributeKvEntry; -import org.thingsboard.server.common.data.kv.EntityAttributeKvEntry; import org.thingsboard.server.dao.exception.IncorrectParameterException; import org.thingsboard.server.dao.service.Validator; diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java index 5c89725494..69616155d2 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/attributes/JpaAttributeDao.java @@ -24,12 +24,10 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.id.TenantId; -import org.thingsboard.server.common.data.kv.*; import org.thingsboard.server.common.data.kv.AttributeKvEntry; import org.thingsboard.server.common.stats.StatsFactory; import org.thingsboard.server.dao.DaoUtil; import org.thingsboard.server.dao.attributes.AttributesDao; -import org.thingsboard.server.dao.model.ToData; import org.thingsboard.server.dao.model.sql.AttributeKvCompositeKey; import org.thingsboard.server.dao.model.sql.AttributeKvEntity; import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService; diff --git a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java index 3f37339467..52761184fd 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/tenant/TenantServiceImpl.java @@ -35,7 +35,6 @@ import org.thingsboard.server.dao.device.DeviceService; import org.thingsboard.server.dao.entity.AbstractEntityService; import org.thingsboard.server.dao.entityview.EntityViewService; import org.thingsboard.server.dao.exception.DataValidationException; -import org.thingsboard.server.dao.oauth2.OAuth2Service; import org.thingsboard.server.dao.rule.RuleChainService; import org.thingsboard.server.dao.service.DataValidator; import org.thingsboard.server.dao.service.PaginatedRemover; @@ -85,9 +84,6 @@ public class TenantServiceImpl extends AbstractEntityService implements TenantSe @Autowired private RuleChainService ruleChainService; - @Autowired - private OAuth2Service oAuth2Service; - @Override public Tenant findTenantById(TenantId tenantId) { log.trace("Executing findTenantById [{}]", tenantId); diff --git a/dao/src/test/resources/sql/hsql/drop-all-tables.sql b/dao/src/test/resources/sql/hsql/drop-all-tables.sql index ac10fe8d26..160049a144 100644 --- a/dao/src/test/resources/sql/hsql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/hsql/drop-all-tables.sql @@ -18,12 +18,12 @@ DROP TABLE IF EXISTS ts_kv_latest; DROP TABLE IF EXISTS user_credentials; DROP TABLE IF EXISTS widget_type; DROP TABLE IF EXISTS widgets_bundle; +DROP TABLE IF EXISTS entity_view; DROP TABLE IF EXISTS device_profile; DROP TABLE IF EXISTS tenant_profile; DROP TABLE IF EXISTS rule_node_state; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; -DROP TABLE IF EXISTS entity_view; DROP TABLE IF EXISTS oauth2_client_registration; DROP TABLE IF EXISTS oauth2_client_registration_info; DROP TABLE IF EXISTS oauth2_client_registration_template; diff --git a/dao/src/test/resources/sql/psql/drop-all-tables.sql b/dao/src/test/resources/sql/psql/drop-all-tables.sql index 62eb884457..e277c39403 100644 --- a/dao/src/test/resources/sql/psql/drop-all-tables.sql +++ b/dao/src/test/resources/sql/psql/drop-all-tables.sql @@ -18,13 +18,13 @@ DROP TABLE IF EXISTS ts_kv_dictionary; DROP TABLE IF EXISTS user_credentials; DROP TABLE IF EXISTS widget_type; DROP TABLE IF EXISTS widgets_bundle; +DROP TABLE IF EXISTS entity_view; DROP TABLE IF EXISTS device_profile; DROP TABLE IF EXISTS tenant_profile; DROP TABLE IF EXISTS rule_node_state; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; -DROP TABLE IF EXISTS entity_view; +DROP TABLE IF EXISTS tb_schema_settings; DROP TABLE IF EXISTS oauth2_client_registration; DROP TABLE IF EXISTS oauth2_client_registration_info; DROP TABLE IF EXISTS oauth2_client_registration_template; -DROP TABLE IF EXISTS tb_schema_settings; \ No newline at end of file diff --git a/dao/src/test/resources/sql/timescale/drop-all-tables.sql b/dao/src/test/resources/sql/timescale/drop-all-tables.sql index 48b944d7a3..da7eaff876 100644 --- a/dao/src/test/resources/sql/timescale/drop-all-tables.sql +++ b/dao/src/test/resources/sql/timescale/drop-all-tables.sql @@ -22,7 +22,9 @@ DROP TABLE IF EXISTS rule_node_state; DROP TABLE IF EXISTS rule_node; DROP TABLE IF EXISTS rule_chain; DROP TABLE IF EXISTS entity_view; +DROP TABLE IF EXISTS device_profile; +DROP TABLE IF EXISTS tenant_profile; +DROP TABLE IF EXISTS tb_schema_settings; DROP TABLE IF EXISTS oauth2_client_registration; DROP TABLE IF EXISTS oauth2_client_registration_info; DROP TABLE IF EXISTS oauth2_client_registration_template; -DROP TABLE IF EXISTS tb_schema_settings; \ No newline at end of file From ea9d18e32dbad351d723b0cfcc507c9029b773b8 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 8 Oct 2020 15:59:51 +0300 Subject: [PATCH 113/117] Removed upper-case from providerId --- .../app/modules/home/pages/admin/oauth2-settings.component.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html index fc8e294774..30564e9306 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html +++ b/ui-ngx/src/app/modules/home/pages/admin/oauth2-settings.component.html @@ -156,7 +156,7 @@ admin.oauth2.login-provider - {{ provider | uppercase }} + {{ provider }} From e2791a289b8f4af35cbeb97c43e1095072e4cdda Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 8 Oct 2020 16:04:03 +0300 Subject: [PATCH 114/117] Removed EntityId for OAuth2Config --- .../server/controller/BaseController.java | 3 --- .../OAuth2ConfigTemplateController.java | 17 +++++------------ .../server/controller/OAuth2Controller.java | 4 ++++ .../service/security/permission/Resource.java | 4 ++-- .../server/common/data/id/EntityIdFactory.java | 4 ---- .../data/id/OAuth2ClientRegistrationId.java | 8 +------- .../data/id/OAuth2ClientRegistrationInfoId.java | 8 +------- .../id/OAuth2ClientRegistrationTemplateId.java | 8 +------- 8 files changed, 14 insertions(+), 42 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/controller/BaseController.java b/application/src/main/java/org/thingsboard/server/controller/BaseController.java index f17185dcd0..87ddfbb088 100644 --- a/application/src/main/java/org/thingsboard/server/controller/BaseController.java +++ b/application/src/main/java/org/thingsboard/server/controller/BaseController.java @@ -431,9 +431,6 @@ public abstract class BaseController { case WIDGET_TYPE: checkWidgetTypeId(new WidgetTypeId(entityId.getId()), operation); return; - case OAUTH2_CLIENT_REGISTRATION_INFO: - case OAUTH2_CLIENT_REGISTRATION_TEMPLATE: - return; default: throw new IllegalArgumentException("Unsupported entity type: " + entityId.getEntityType()); } diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java index 1cb969e9e6..35ed01f03e 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.java @@ -25,6 +25,8 @@ 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; @@ -40,6 +42,7 @@ public class OAuth2ConfigTemplateController extends BaseController { @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); @@ -52,21 +55,10 @@ public class OAuth2ConfigTemplateController extends BaseController { 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); - - logEntityAction(clientRegistrationTemplateId, - null, - null, - ActionType.DELETED, null, strClientRegistrationTemplateId); - } catch (Exception e) { - - logEntityAction(emptyId(EntityType.OAUTH2_CLIENT_REGISTRATION_TEMPLATE), - null, - null, - ActionType.DELETED, e, strClientRegistrationTemplateId); - throw handleException(e); } } @@ -76,6 +68,7 @@ public class OAuth2ConfigTemplateController extends BaseController { @ResponseBody public List getClientRegistrationTemplates() throws ThingsboardException { try { + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.READ); return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates(); } catch (Exception e) { throw handleException(e); diff --git a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java index bb3a87ef92..ecd74bef39 100644 --- a/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java +++ b/application/src/main/java/org/thingsboard/server/controller/OAuth2Controller.java @@ -24,6 +24,8 @@ 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; @@ -49,6 +51,7 @@ public class OAuth2Controller extends BaseController { @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); @@ -60,6 +63,7 @@ public class OAuth2Controller extends BaseController { @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) { diff --git a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java index 671e0539cc..08534dc2d4 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java +++ b/application/src/main/java/org/thingsboard/server/service/security/permission/Resource.java @@ -32,8 +32,8 @@ public enum Resource { USER(EntityType.USER), WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE), WIDGET_TYPE(EntityType.WIDGET_TYPE), - OAUTH2_CONFIGURATION_INFO(EntityType.OAUTH2_CLIENT_REGISTRATION_INFO), - OAUTH2_CONFIGURATION_TEMPLATE(EntityType.OAUTH2_CLIENT_REGISTRATION_TEMPLATE), + OAUTH2_CONFIGURATION_INFO(), + OAUTH2_CONFIGURATION_TEMPLATE(), TENANT_PROFILE(EntityType.TENANT_PROFILE), DEVICE_PROFILE(EntityType.DEVICE_PROFILE); diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java index 8113c41945..dc1589814f 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/EntityIdFactory.java @@ -62,10 +62,6 @@ public class EntityIdFactory { return new WidgetsBundleId(uuid); case WIDGET_TYPE: return new WidgetTypeId(uuid); - case OAUTH2_CLIENT_REGISTRATION_INFO: - return new OAuth2ClientRegistrationInfoId(uuid); - case OAUTH2_CLIENT_REGISTRATION_TEMPLATE: - return new OAuth2ClientRegistrationTemplateId(uuid); case DEVICE_PROFILE: return new DeviceProfileId(uuid); case TENANT_PROFILE: diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java index 3e4ad56f19..e87d9c7b2e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java @@ -17,11 +17,10 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class OAuth2ClientRegistrationId extends UUIDBased implements EntityId { +public class OAuth2ClientRegistrationId extends UUIDBased { @JsonCreator public OAuth2ClientRegistrationId(@JsonProperty("id") UUID id) { @@ -31,9 +30,4 @@ public class OAuth2ClientRegistrationId extends UUIDBased implements EntityId { public static OAuth2ClientRegistrationId fromString(String clientRegistrationId) { return new OAuth2ClientRegistrationId(UUID.fromString(clientRegistrationId)); } - - @Override - public EntityType getEntityType() { - return EntityType.OAUTH2_CLIENT_REGISTRATION; - } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java index 999f0d5816..87a2109909 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.java @@ -17,11 +17,10 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class OAuth2ClientRegistrationInfoId extends UUIDBased implements EntityId { +public class OAuth2ClientRegistrationInfoId extends UUIDBased { @JsonCreator public OAuth2ClientRegistrationInfoId(@JsonProperty("id") UUID id) { @@ -31,9 +30,4 @@ public class OAuth2ClientRegistrationInfoId extends UUIDBased implements EntityI public static OAuth2ClientRegistrationInfoId fromString(String clientRegistrationInfoId) { return new OAuth2ClientRegistrationInfoId(UUID.fromString(clientRegistrationInfoId)); } - - @Override - public EntityType getEntityType() { - return EntityType.OAUTH2_CLIENT_REGISTRATION_INFO; - } } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationTemplateId.java b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationTemplateId.java index 03edd5054b..cc6ee3a795 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationTemplateId.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationTemplateId.java @@ -17,11 +17,10 @@ package org.thingsboard.server.common.data.id; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonProperty; -import org.thingsboard.server.common.data.EntityType; import java.util.UUID; -public class OAuth2ClientRegistrationTemplateId extends UUIDBased implements EntityId { +public class OAuth2ClientRegistrationTemplateId extends UUIDBased { @JsonCreator public OAuth2ClientRegistrationTemplateId(@JsonProperty("id") UUID id) { @@ -31,9 +30,4 @@ public class OAuth2ClientRegistrationTemplateId extends UUIDBased implements Ent public static OAuth2ClientRegistrationTemplateId fromString(String clientRegistrationTemplateId) { return new OAuth2ClientRegistrationTemplateId(UUID.fromString(clientRegistrationTemplateId)); } - - @Override - public EntityType getEntityType() { - return EntityType.OAUTH2_CLIENT_REGISTRATION_TEMPLATE; - } } From ac055397b01341838a14754efeec46f6d63a71a0 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Thu, 8 Oct 2020 18:22:16 +0300 Subject: [PATCH 115/117] Added GITHUB mapper type --- .../github_config.json | 5 +- .../upgrade/3.1.1/schema_update_before.sql | 1 + .../auth/oauth2/BasicMapperUtils.java | 78 ++++++++++++++++ .../auth/oauth2/BasicOAuth2ClientMapper.java | 53 +---------- .../auth/oauth2/GithubOAuth2ClientMapper.java | 91 +++++++++++++++++++ .../oauth2/OAuth2ClientMapperProvider.java | 6 ++ .../src/main/resources/thingsboard.yml | 2 + .../server/common/data/oauth2/MapperType.java | 2 +- .../OAuth2ClientRegistrationTemplate.java | 2 + ...actOAuth2ClientRegistrationInfoEntity.java | 2 +- ...Auth2ClientRegistrationTemplateEntity.java | 5 + .../dao/oauth2/OAuth2Configuration.java | 4 +- .../server/dao/oauth2/OAuth2ServiceImpl.java | 16 ++++ .../resources/sql/schema-entities-hsql.sql | 1 + .../main/resources/sql/schema-entities.sql | 1 + .../BaseOAuth2ConfigTemplateServiceTest.java | 2 + 16 files changed, 216 insertions(+), 55 deletions(-) create mode 100644 application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicMapperUtils.java create mode 100644 application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java diff --git a/application/src/main/data/json/system/oauth2_config_templates/github_config.json b/application/src/main/data/json/system/oauth2_config_templates/github_config.json index 58812fdc1e..fff9fcf3a1 100644 --- a/application/src/main/data/json/system/oauth2_config_templates/github_config.json +++ b/application/src/main/data/json/system/oauth2_config_templates/github_config.json @@ -7,7 +7,10 @@ "userInfoUri": "https://api.github.com/user", "clientAuthenticationMethod": "BASIC", "userNameAttributeName": "login", - "basic": {}, + "basic": { + "lastNameAttributeKey": "name", + "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 Github Documentation", "loginButtonIcon": "mdi:github", "loginButtonLabel": "Github", diff --git a/application/src/main/data/upgrade/3.1.1/schema_update_before.sql b/application/src/main/data/upgrade/3.1.1/schema_update_before.sql index fe8ee4531b..871f55d694 100644 --- a/application/src/main/data/upgrade/3.1.1/schema_update_before.sql +++ b/application/src/main/data/upgrade/3.1.1/schema_update_before.sql @@ -67,6 +67,7 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( 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), diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicMapperUtils.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicMapperUtils.java new file mode 100644 index 0000000000..246e74245d --- /dev/null +++ b/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 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 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 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; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java index 7412f2199c..73da9e539f 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicOAuth2ClientMapper.java @@ -16,10 +16,8 @@ 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.common.data.oauth2.OAuth2MapperConfig; import org.thingsboard.server.dao.oauth2.OAuth2User; import org.thingsboard.server.service.security.model.SecurityUser; @@ -30,59 +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 = "}"; - @Override public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) { - OAuth2User oauth2User = new OAuth2User(); Map 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 attributes, OAuth2MapperConfig config) { - switch (config.getBasic().getTenantNameStrategy()) { - case EMAIL: - return getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); - case DOMAIN: - String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); - 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!"); - } - } - - private String getStringAttributeByKey(Map 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; - } } diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/GithubOAuth2ClientMapper.java new file mode 100644 index 0000000000..dcca2b71a1 --- /dev/null +++ b/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 githubMapperConfig = oAuth2Configuration.getGithubMapper(); + String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken); + Map 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 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 {} + + @Data + @ToString + private static class GithubEmailResponse { + private String email; + private boolean verified; + private boolean primary; + private String visibility; + } +} diff --git a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java index 64bdf40970..2e764fe223 100644 --- a/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java +++ b/application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/OAuth2ClientMapperProvider.java @@ -33,12 +33,18 @@ public class OAuth2ClientMapperProvider { @Qualifier("customOAuth2ClientMapper") private OAuth2ClientMapper customOAuth2ClientMapper; + @Autowired + @Qualifier("githubOAuth2ClientMapper") + private OAuth2ClientMapper githubOAuth2ClientMapper; + public OAuth2ClientMapper getOAuth2ClientMapperByType(MapperType oauth2MapperType) { switch (oauth2MapperType) { case CUSTOM: return customOAuth2ClientMapper; case BASIC: return basicOAuth2ClientMapper; + case GITHUB: + return githubOAuth2ClientMapper; default: throw new RuntimeException("OAuth2ClientRegistrationMapper with type " + oauth2MapperType + " is not supported!"); } diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml index c81782a0a5..d0ddeb611a 100644 --- a/application/src/main/resources/thingsboard.yml +++ b/application/src/main/resources/thingsboard.yml @@ -115,6 +115,8 @@ security: oauth2: # Redirect URL where access code from external user management system will be processed loginProcessingUrl: "${SECURITY_OAUTH2_LOGIN_PROCESSING_URL:/login/oauth2/code/}" + githubMapper: + emailUrl: "${SECURITY_OAUTH2_GITHUB_MAPPER_EMAIL_URL_KEY:https://api.github.com/user/emails}" # Dashboard parameters dashboard: diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java index 48f0a77a41..25faad187e 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/MapperType.java @@ -16,5 +16,5 @@ package org.thingsboard.server.common.data.oauth2; public enum MapperType { - BASIC, CUSTOM; + BASIC, CUSTOM, GITHUB; } diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java index 5c2f70874a..897472f154 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistrationTemplate.java @@ -34,6 +34,7 @@ import java.util.List; public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo implements HasName { private String providerId; + private MapperType mapperType; private OAuth2BasicMapperConfig basic; private String authorizationUri; private String accessTokenUri; @@ -50,6 +51,7 @@ public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditio 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; diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java index efa9a563ec..6e9ed4822a 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/sql/AbstractOAuth2ClientRegistrationInfoEntity.java @@ -190,7 +190,7 @@ public abstract class AbstractOAuth2ClientRegistrationInfoEntity githubMapper; } diff --git a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java index 2ad0ad4f20..9fda0e3c2f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java +++ b/dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ServiceImpl.java @@ -184,6 +184,22 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se 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) { diff --git a/dao/src/main/resources/sql/schema-entities-hsql.sql b/dao/src/main/resources/sql/schema-entities-hsql.sql index 8c78d3392a..74f9679b11 100644 --- a/dao/src/main/resources/sql/schema-entities-hsql.sql +++ b/dao/src/main/resources/sql/schema-entities-hsql.sql @@ -386,6 +386,7 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( 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), diff --git a/dao/src/main/resources/sql/schema-entities.sql b/dao/src/main/resources/sql/schema-entities.sql index 5e1a7dbddc..6056ba8895 100644 --- a/dao/src/main/resources/sql/schema-entities.sql +++ b/dao/src/main/resources/sql/schema-entities.sql @@ -412,6 +412,7 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( 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), diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java index 3d828d1ac5..3c32af73a3 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java +++ b/dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.java @@ -21,6 +21,7 @@ import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.thingsboard.server.common.data.id.TenantId; +import org.thingsboard.server.common.data.oauth2.MapperType; import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; import org.thingsboard.server.dao.exception.DataValidationException; @@ -105,6 +106,7 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { OAuth2ClientRegistrationTemplate clientRegistrationTemplate = new OAuth2ClientRegistrationTemplate(); clientRegistrationTemplate.setProviderId(providerId); clientRegistrationTemplate.setAdditionalInfo(mapper.createObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString())); + clientRegistrationTemplate.setMapperType(MapperType.BASIC); clientRegistrationTemplate.setBasic( OAuth2BasicMapperConfig.builder() .firstNameAttributeKey("firstName") From 2db74b25a428629632595b790aacafe3eeecb4a9 Mon Sep 17 00:00:00 2001 From: vzikratyi Date: Fri, 9 Oct 2020 10:37:28 +0300 Subject: [PATCH 116/117] Removed 'name' from Github OAuth2 template --- .../data/json/system/oauth2_config_templates/github_config.json | 1 - 1 file changed, 1 deletion(-) diff --git a/application/src/main/data/json/system/oauth2_config_templates/github_config.json b/application/src/main/data/json/system/oauth2_config_templates/github_config.json index fff9fcf3a1..6c3a51723f 100644 --- a/application/src/main/data/json/system/oauth2_config_templates/github_config.json +++ b/application/src/main/data/json/system/oauth2_config_templates/github_config.json @@ -8,7 +8,6 @@ "clientAuthenticationMethod": "BASIC", "userNameAttributeName": "login", "basic": { - "lastNameAttributeKey": "name", "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 Github Documentation", From 8cfbcee05e4b2aa8721a161df520db0dac245682 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Mon, 12 Oct 2020 10:58:28 +0300 Subject: [PATCH 117/117] Minor fixes --- .../server/common/data/SearchTextBasedWithAdditionalInfo.java | 1 - .../src/app/modules/home/pages/admin/admin-routing.module.ts | 2 +- ui-ngx/src/app/shared/models/entity-type.models.ts | 3 +-- ui-ngx/src/assets/locale/locale.constant-en_US.json | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java b/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java index c87f1ca109..4efd673d7b 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/SearchTextBasedWithAdditionalInfo.java @@ -59,7 +59,6 @@ public abstract class SearchTextBasedWithAdditionalInfo ext } public void setAdditionalInfo(JsonNode addInfo) { - // TODO why set additionalInfoBytes to [110,117,108,108] if JsonNode is null setJson(addInfo, json -> this.additionalInfo = json, bytes -> this.additionalInfoBytes = bytes); } diff --git a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts index 40a8d3addd..af7dc98e19 100644 --- a/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts +++ b/ui-ngx/src/app/modules/home/pages/admin/admin-routing.module.ts @@ -28,7 +28,7 @@ const routes: Routes = [ { path: 'settings', data: { - auth: [Authority.SYS_ADMIN, Authority.TENANT_ADMIN], + auth: [Authority.SYS_ADMIN], breadcrumb: { label: 'admin.system-settings', icon: 'settings' diff --git a/ui-ngx/src/app/shared/models/entity-type.models.ts b/ui-ngx/src/app/shared/models/entity-type.models.ts index 647e05bc9d..c6a94ad8b5 100644 --- a/ui-ngx/src/app/shared/models/entity-type.models.ts +++ b/ui-ngx/src/app/shared/models/entity-type.models.ts @@ -47,8 +47,7 @@ export enum EntityType { RULE_NODE = 'RULE_NODE', ENTITY_VIEW = 'ENTITY_VIEW', WIDGETS_BUNDLE = 'WIDGETS_BUNDLE', - WIDGET_TYPE = 'WIDGET_TYPE', - OAUTH2_CLIENT_REGISTRATION = 'OAUTH2_CLIENT_REGISTRATION' + WIDGET_TYPE = 'WIDGET_TYPE' } export enum AliasEntityType { diff --git a/ui-ngx/src/assets/locale/locale.constant-en_US.json b/ui-ngx/src/assets/locale/locale.constant-en_US.json index e26f307c7a..8f89fa8f39 100644 --- a/ui-ngx/src/assets/locale/locale.constant-en_US.json +++ b/ui-ngx/src/assets/locale/locale.constant-en_US.json @@ -100,7 +100,7 @@ "proxy-user": "Proxy user", "proxy-password": "Proxy password", "send-test-mail": "Send test mail", - "security-settings": "Security Settings", + "security-settings": "Security settings", "password-policy": "Password policy", "minimum-password-length": "Minimum password length", "minimum-password-length-required": "Minimum password length is required",