Showing
16 changed files
with
216 additions
and
55 deletions
@@ -7,7 +7,10 @@ | @@ -7,7 +7,10 @@ | ||
7 | "userInfoUri": "https://api.github.com/user", | 7 | "userInfoUri": "https://api.github.com/user", |
8 | "clientAuthenticationMethod": "BASIC", | 8 | "clientAuthenticationMethod": "BASIC", |
9 | "userNameAttributeName": "login", | 9 | "userNameAttributeName": "login", |
10 | - "basic": {}, | 10 | + "basic": { |
11 | + "lastNameAttributeKey": "name", | ||
12 | + "tenantNameStrategy": "DOMAIN" | ||
13 | + }, | ||
11 | "comment": "In order to log into ThingsBoard you need to have user's email. You may configure and use Custom OAuth2 Mapper to get email information. Please refer to <a href=\"https://docs.github.com/en/rest/reference/users#list-email-addresses-for-the-authenticated-user\">Github Documentation</a>", | 14 | "comment": "In order to log into ThingsBoard you need to have user's email. You may configure and use Custom OAuth2 Mapper to get email information. Please refer to <a href=\"https://docs.github.com/en/rest/reference/users#list-email-addresses-for-the-authenticated-user\">Github Documentation</a>", |
12 | "loginButtonIcon": "mdi:github", | 15 | "loginButtonIcon": "mdi:github", |
13 | "loginButtonLabel": "Github", | 16 | "loginButtonLabel": "Github", |
@@ -67,6 +67,7 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( | @@ -67,6 +67,7 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( | ||
67 | user_name_attribute_name varchar(255), | 67 | user_name_attribute_name varchar(255), |
68 | jwk_set_uri varchar(255), | 68 | jwk_set_uri varchar(255), |
69 | client_authentication_method varchar(255), | 69 | client_authentication_method varchar(255), |
70 | + type varchar(31), | ||
70 | basic_email_attribute_key varchar(31), | 71 | basic_email_attribute_key varchar(31), |
71 | basic_first_name_attribute_key varchar(31), | 72 | basic_first_name_attribute_key varchar(31), |
72 | basic_last_name_attribute_key varchar(31), | 73 | basic_last_name_attribute_key varchar(31), |
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BasicMapperUtils.java
0 → 100644
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.oauth2; | ||
17 | + | ||
18 | +import lombok.extern.slf4j.Slf4j; | ||
19 | +import org.apache.commons.lang3.text.StrSubstitutor; | ||
20 | +import org.springframework.util.StringUtils; | ||
21 | +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | ||
22 | +import org.thingsboard.server.dao.oauth2.OAuth2User; | ||
23 | + | ||
24 | +import java.util.Map; | ||
25 | + | ||
26 | +@Slf4j | ||
27 | +public class BasicMapperUtils { | ||
28 | + private static final String START_PLACEHOLDER_PREFIX = "%{"; | ||
29 | + private static final String END_PLACEHOLDER_PREFIX = "}"; | ||
30 | + | ||
31 | + public static OAuth2User getOAuth2User(String email, Map<String, Object> attributes, OAuth2MapperConfig config) { | ||
32 | + OAuth2User oauth2User = new OAuth2User(); | ||
33 | + oauth2User.setEmail(email); | ||
34 | + oauth2User.setTenantName(getTenantName(email, attributes, config)); | ||
35 | + if (!StringUtils.isEmpty(config.getBasic().getLastNameAttributeKey())) { | ||
36 | + String lastName = getStringAttributeByKey(attributes, config.getBasic().getLastNameAttributeKey()); | ||
37 | + oauth2User.setLastName(lastName); | ||
38 | + } | ||
39 | + if (!StringUtils.isEmpty(config.getBasic().getFirstNameAttributeKey())) { | ||
40 | + String firstName = getStringAttributeByKey(attributes, config.getBasic().getFirstNameAttributeKey()); | ||
41 | + oauth2User.setFirstName(firstName); | ||
42 | + } | ||
43 | + if (!StringUtils.isEmpty(config.getBasic().getCustomerNamePattern())) { | ||
44 | + StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); | ||
45 | + String customerName = sub.replace(config.getBasic().getCustomerNamePattern()); | ||
46 | + oauth2User.setCustomerName(customerName); | ||
47 | + } | ||
48 | + oauth2User.setAlwaysFullScreen(config.getBasic().isAlwaysFullScreen()); | ||
49 | + if (!StringUtils.isEmpty(config.getBasic().getDefaultDashboardName())) { | ||
50 | + oauth2User.setDefaultDashboardName(config.getBasic().getDefaultDashboardName()); | ||
51 | + } | ||
52 | + return oauth2User; | ||
53 | + } | ||
54 | + | ||
55 | + public static String getTenantName(String email, Map<String, Object> attributes, OAuth2MapperConfig config) { | ||
56 | + switch (config.getBasic().getTenantNameStrategy()) { | ||
57 | + case EMAIL: | ||
58 | + return email; | ||
59 | + case DOMAIN: | ||
60 | + return email.substring(email .indexOf("@") + 1); | ||
61 | + case CUSTOM: | ||
62 | + StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); | ||
63 | + return sub.replace(config.getBasic().getTenantNamePattern()); | ||
64 | + default: | ||
65 | + throw new RuntimeException("Tenant Name Strategy with type " + config.getBasic().getTenantNameStrategy() + " is not supported!"); | ||
66 | + } | ||
67 | + } | ||
68 | + | ||
69 | + public static String getStringAttributeByKey(Map<String, Object> attributes, String key) { | ||
70 | + String result = null; | ||
71 | + try { | ||
72 | + result = (String) attributes.get(key); | ||
73 | + } catch (Exception e) { | ||
74 | + log.warn("Can't convert attribute to String by key " + key); | ||
75 | + } | ||
76 | + return result; | ||
77 | + } | ||
78 | +} |
@@ -16,10 +16,8 @@ | @@ -16,10 +16,8 @@ | ||
16 | package org.thingsboard.server.service.security.auth.oauth2; | 16 | package org.thingsboard.server.service.security.auth.oauth2; |
17 | 17 | ||
18 | import lombok.extern.slf4j.Slf4j; | 18 | import lombok.extern.slf4j.Slf4j; |
19 | -import org.apache.commons.lang3.text.StrSubstitutor; | ||
20 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; | 19 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
21 | import org.springframework.stereotype.Service; | 20 | import org.springframework.stereotype.Service; |
22 | -import org.springframework.util.StringUtils; | ||
23 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | 21 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; |
24 | import org.thingsboard.server.dao.oauth2.OAuth2User; | 22 | import org.thingsboard.server.dao.oauth2.OAuth2User; |
25 | import org.thingsboard.server.service.security.model.SecurityUser; | 23 | import org.thingsboard.server.service.security.model.SecurityUser; |
@@ -30,59 +28,12 @@ import java.util.Map; | @@ -30,59 +28,12 @@ import java.util.Map; | ||
30 | @Slf4j | 28 | @Slf4j |
31 | public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { | 29 | public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { |
32 | 30 | ||
33 | - private static final String START_PLACEHOLDER_PREFIX = "%{"; | ||
34 | - private static final String END_PLACEHOLDER_PREFIX = "}"; | ||
35 | - | ||
36 | @Override | 31 | @Override |
37 | public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) { | 32 | public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) { |
38 | - OAuth2User oauth2User = new OAuth2User(); | ||
39 | Map<String, Object> attributes = token.getPrincipal().getAttributes(); | 33 | Map<String, Object> attributes = token.getPrincipal().getAttributes(); |
40 | - String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); | ||
41 | - oauth2User.setEmail(email); | ||
42 | - oauth2User.setTenantName(getTenantName(attributes, config)); | ||
43 | - if (!StringUtils.isEmpty(config.getBasic().getLastNameAttributeKey())) { | ||
44 | - String lastName = getStringAttributeByKey(attributes, config.getBasic().getLastNameAttributeKey()); | ||
45 | - oauth2User.setLastName(lastName); | ||
46 | - } | ||
47 | - if (!StringUtils.isEmpty(config.getBasic().getFirstNameAttributeKey())) { | ||
48 | - String firstName = getStringAttributeByKey(attributes, config.getBasic().getFirstNameAttributeKey()); | ||
49 | - oauth2User.setFirstName(firstName); | ||
50 | - } | ||
51 | - if (!StringUtils.isEmpty(config.getBasic().getCustomerNamePattern())) { | ||
52 | - StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); | ||
53 | - String customerName = sub.replace(config.getBasic().getCustomerNamePattern()); | ||
54 | - oauth2User.setCustomerName(customerName); | ||
55 | - } | ||
56 | - oauth2User.setAlwaysFullScreen(config.getBasic().isAlwaysFullScreen()); | ||
57 | - if (!StringUtils.isEmpty(config.getBasic().getDefaultDashboardName())) { | ||
58 | - oauth2User.setDefaultDashboardName(config.getBasic().getDefaultDashboardName()); | ||
59 | - } | 34 | + String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); |
35 | + OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); | ||
60 | 36 | ||
61 | return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser()); | 37 | return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser()); |
62 | } | 38 | } |
63 | - | ||
64 | - private String getTenantName(Map<String, Object> attributes, OAuth2MapperConfig config) { | ||
65 | - switch (config.getBasic().getTenantNameStrategy()) { | ||
66 | - case EMAIL: | ||
67 | - return getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); | ||
68 | - case DOMAIN: | ||
69 | - String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); | ||
70 | - return email.substring(email .indexOf("@") + 1); | ||
71 | - case CUSTOM: | ||
72 | - StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); | ||
73 | - return sub.replace(config.getBasic().getTenantNamePattern()); | ||
74 | - default: | ||
75 | - throw new RuntimeException("Tenant Name Strategy with type " + config.getBasic().getTenantNameStrategy() + " is not supported!"); | ||
76 | - } | ||
77 | - } | ||
78 | - | ||
79 | - private String getStringAttributeByKey(Map<String, Object> attributes, String key) { | ||
80 | - String result = null; | ||
81 | - try { | ||
82 | - result = (String) attributes.get(key); | ||
83 | - } catch (Exception e) { | ||
84 | - log.warn("Can't convert attribute to String by key " + key); | ||
85 | - } | ||
86 | - return result; | ||
87 | - } | ||
88 | } | 39 | } |
1 | +/** | ||
2 | + * Copyright © 2016-2020 The Thingsboard Authors | ||
3 | + * | ||
4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
5 | + * you may not use this file except in compliance with the License. | ||
6 | + * You may obtain a copy of the License at | ||
7 | + * | ||
8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
9 | + * | ||
10 | + * Unless required by applicable law or agreed to in writing, software | ||
11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
13 | + * See the License for the specific language governing permissions and | ||
14 | + * limitations under the License. | ||
15 | + */ | ||
16 | +package org.thingsboard.server.service.security.auth.oauth2; | ||
17 | + | ||
18 | +import lombok.Data; | ||
19 | +import lombok.ToString; | ||
20 | +import lombok.extern.slf4j.Slf4j; | ||
21 | +import org.springframework.beans.factory.annotation.Autowired; | ||
22 | +import org.springframework.boot.web.client.RestTemplateBuilder; | ||
23 | +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; | ||
24 | +import org.springframework.stereotype.Service; | ||
25 | +import org.springframework.web.client.RestTemplate; | ||
26 | +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | ||
27 | +import org.thingsboard.server.dao.oauth2.OAuth2Configuration; | ||
28 | +import org.thingsboard.server.dao.oauth2.OAuth2User; | ||
29 | +import org.thingsboard.server.service.security.model.SecurityUser; | ||
30 | + | ||
31 | +import java.util.ArrayList; | ||
32 | +import java.util.Map; | ||
33 | +import java.util.Optional; | ||
34 | + | ||
35 | +@Service(value = "githubOAuth2ClientMapper") | ||
36 | +@Slf4j | ||
37 | +public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { | ||
38 | + private static final String EMAIL_URL_KEY = "emailUrl"; | ||
39 | + | ||
40 | + private static final String AUTHORIZATION = "Authorization"; | ||
41 | + | ||
42 | + private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); | ||
43 | + | ||
44 | + @Autowired | ||
45 | + private OAuth2Configuration oAuth2Configuration; | ||
46 | + | ||
47 | + @Override | ||
48 | + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) { | ||
49 | + Map<String, String> githubMapperConfig = oAuth2Configuration.getGithubMapper(); | ||
50 | + String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken); | ||
51 | + Map<String, Object> attributes = token.getPrincipal().getAttributes(); | ||
52 | + OAuth2User oAuth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); | ||
53 | + return getOrCreateSecurityUserFromOAuth2User(oAuth2User, config.isAllowUserCreation(), config.isActivateUser()); | ||
54 | + } | ||
55 | + | ||
56 | + private synchronized String getEmail(String emailUrl, String oauth2Token) { | ||
57 | + restTemplateBuilder = restTemplateBuilder.defaultHeader(AUTHORIZATION, "token " + oauth2Token); | ||
58 | + | ||
59 | + RestTemplate restTemplate = restTemplateBuilder.build(); | ||
60 | + GithubEmailsResponse githubEmailsResponse; | ||
61 | + try { | ||
62 | + githubEmailsResponse = restTemplate.getForEntity(emailUrl, GithubEmailsResponse.class).getBody(); | ||
63 | + if (githubEmailsResponse == null){ | ||
64 | + throw new RuntimeException("Empty Github response!"); | ||
65 | + } | ||
66 | + } catch (Exception e) { | ||
67 | + log.error("There was an error during connection to Github API", e); | ||
68 | + throw new RuntimeException("Unable to login. Please contact your Administrator!"); | ||
69 | + } | ||
70 | + Optional<String> emailOpt = githubEmailsResponse.stream() | ||
71 | + .filter(GithubEmailResponse::isPrimary) | ||
72 | + .map(GithubEmailResponse::getEmail) | ||
73 | + .findAny(); | ||
74 | + if (emailOpt.isPresent()){ | ||
75 | + return emailOpt.get(); | ||
76 | + } else { | ||
77 | + log.error("Could not find primary email from {}.", githubEmailsResponse); | ||
78 | + throw new RuntimeException("Unable to login. Please contact your Administrator!"); | ||
79 | + } | ||
80 | + } | ||
81 | + private static class GithubEmailsResponse extends ArrayList<GithubEmailResponse> {} | ||
82 | + | ||
83 | + @Data | ||
84 | + @ToString | ||
85 | + private static class GithubEmailResponse { | ||
86 | + private String email; | ||
87 | + private boolean verified; | ||
88 | + private boolean primary; | ||
89 | + private String visibility; | ||
90 | + } | ||
91 | +} |
@@ -33,12 +33,18 @@ public class OAuth2ClientMapperProvider { | @@ -33,12 +33,18 @@ public class OAuth2ClientMapperProvider { | ||
33 | @Qualifier("customOAuth2ClientMapper") | 33 | @Qualifier("customOAuth2ClientMapper") |
34 | private OAuth2ClientMapper customOAuth2ClientMapper; | 34 | private OAuth2ClientMapper customOAuth2ClientMapper; |
35 | 35 | ||
36 | + @Autowired | ||
37 | + @Qualifier("githubOAuth2ClientMapper") | ||
38 | + private OAuth2ClientMapper githubOAuth2ClientMapper; | ||
39 | + | ||
36 | public OAuth2ClientMapper getOAuth2ClientMapperByType(MapperType oauth2MapperType) { | 40 | public OAuth2ClientMapper getOAuth2ClientMapperByType(MapperType oauth2MapperType) { |
37 | switch (oauth2MapperType) { | 41 | switch (oauth2MapperType) { |
38 | case CUSTOM: | 42 | case CUSTOM: |
39 | return customOAuth2ClientMapper; | 43 | return customOAuth2ClientMapper; |
40 | case BASIC: | 44 | case BASIC: |
41 | return basicOAuth2ClientMapper; | 45 | return basicOAuth2ClientMapper; |
46 | + case GITHUB: | ||
47 | + return githubOAuth2ClientMapper; | ||
42 | default: | 48 | default: |
43 | throw new RuntimeException("OAuth2ClientRegistrationMapper with type " + oauth2MapperType + " is not supported!"); | 49 | throw new RuntimeException("OAuth2ClientRegistrationMapper with type " + oauth2MapperType + " is not supported!"); |
44 | } | 50 | } |
@@ -115,6 +115,8 @@ security: | @@ -115,6 +115,8 @@ security: | ||
115 | oauth2: | 115 | oauth2: |
116 | # Redirect URL where access code from external user management system will be processed | 116 | # Redirect URL where access code from external user management system will be processed |
117 | loginProcessingUrl: "${SECURITY_OAUTH2_LOGIN_PROCESSING_URL:/login/oauth2/code/}" | 117 | loginProcessingUrl: "${SECURITY_OAUTH2_LOGIN_PROCESSING_URL:/login/oauth2/code/}" |
118 | + githubMapper: | ||
119 | + emailUrl: "${SECURITY_OAUTH2_GITHUB_MAPPER_EMAIL_URL_KEY:https://api.github.com/user/emails}" | ||
118 | 120 | ||
119 | # Dashboard parameters | 121 | # Dashboard parameters |
120 | dashboard: | 122 | dashboard: |
@@ -34,6 +34,7 @@ import java.util.List; | @@ -34,6 +34,7 @@ import java.util.List; | ||
34 | public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo<OAuth2ClientRegistrationTemplateId> implements HasName { | 34 | public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo<OAuth2ClientRegistrationTemplateId> implements HasName { |
35 | 35 | ||
36 | private String providerId; | 36 | private String providerId; |
37 | + private MapperType mapperType; | ||
37 | private OAuth2BasicMapperConfig basic; | 38 | private OAuth2BasicMapperConfig basic; |
38 | private String authorizationUri; | 39 | private String authorizationUri; |
39 | private String accessTokenUri; | 40 | private String accessTokenUri; |
@@ -50,6 +51,7 @@ public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditio | @@ -50,6 +51,7 @@ public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditio | ||
50 | public OAuth2ClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { | 51 | public OAuth2ClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { |
51 | super(clientRegistrationTemplate); | 52 | super(clientRegistrationTemplate); |
52 | this.providerId = clientRegistrationTemplate.providerId; | 53 | this.providerId = clientRegistrationTemplate.providerId; |
54 | + this.mapperType = clientRegistrationTemplate.mapperType; | ||
53 | this.basic = clientRegistrationTemplate.basic; | 55 | this.basic = clientRegistrationTemplate.basic; |
54 | this.authorizationUri = clientRegistrationTemplate.authorizationUri; | 56 | this.authorizationUri = clientRegistrationTemplate.authorizationUri; |
55 | this.accessTokenUri = clientRegistrationTemplate.accessTokenUri; | 57 | this.accessTokenUri = clientRegistrationTemplate.accessTokenUri; |
@@ -190,7 +190,7 @@ public abstract class AbstractOAuth2ClientRegistrationInfoEntity<T extends OAuth | @@ -190,7 +190,7 @@ public abstract class AbstractOAuth2ClientRegistrationInfoEntity<T extends OAuth | ||
190 | .activateUser(activateUser) | 190 | .activateUser(activateUser) |
191 | .type(type) | 191 | .type(type) |
192 | .basic( | 192 | .basic( |
193 | - type == MapperType.BASIC ? | 193 | + (type == MapperType.BASIC || type == MapperType.GITHUB) ? |
194 | OAuth2BasicMapperConfig.builder() | 194 | OAuth2BasicMapperConfig.builder() |
195 | .emailAttributeKey(emailAttributeKey) | 195 | .emailAttributeKey(emailAttributeKey) |
196 | .firstNameAttributeKey(firstNameAttributeKey) | 196 | .firstNameAttributeKey(firstNameAttributeKey) |
@@ -55,6 +55,9 @@ public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2 | @@ -55,6 +55,9 @@ public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2 | ||
55 | private String jwkSetUri; | 55 | private String jwkSetUri; |
56 | @Column(name = ModelConstants.OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY) | 56 | @Column(name = ModelConstants.OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY) |
57 | private String clientAuthenticationMethod; | 57 | private String clientAuthenticationMethod; |
58 | + @Enumerated(EnumType.STRING) | ||
59 | + @Column(name = ModelConstants.OAUTH2_MAPPER_TYPE_PROPERTY) | ||
60 | + private MapperType type; | ||
58 | @Column(name = ModelConstants.OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY) | 61 | @Column(name = ModelConstants.OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY) |
59 | private String emailAttributeKey; | 62 | private String emailAttributeKey; |
60 | @Column(name = ModelConstants.OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY) | 63 | @Column(name = ModelConstants.OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY) |
@@ -106,6 +109,7 @@ public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2 | @@ -106,6 +109,7 @@ public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2 | ||
106 | this.loginButtonLabel = clientRegistrationTemplate.getLoginButtonLabel(); | 109 | this.loginButtonLabel = clientRegistrationTemplate.getLoginButtonLabel(); |
107 | this.helpLink = clientRegistrationTemplate.getHelpLink(); | 110 | this.helpLink = clientRegistrationTemplate.getHelpLink(); |
108 | this.additionalInfo = clientRegistrationTemplate.getAdditionalInfo(); | 111 | this.additionalInfo = clientRegistrationTemplate.getAdditionalInfo(); |
112 | + this.type = clientRegistrationTemplate.getMapperType(); | ||
109 | OAuth2BasicMapperConfig basicConfig = clientRegistrationTemplate.getBasic(); | 113 | OAuth2BasicMapperConfig basicConfig = clientRegistrationTemplate.getBasic(); |
110 | if (basicConfig != null) { | 114 | if (basicConfig != null) { |
111 | this.emailAttributeKey = basicConfig.getEmailAttributeKey(); | 115 | this.emailAttributeKey = basicConfig.getEmailAttributeKey(); |
@@ -126,6 +130,7 @@ public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2 | @@ -126,6 +130,7 @@ public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2 | ||
126 | clientRegistrationTemplate.setCreatedTime(createdTime); | 130 | clientRegistrationTemplate.setCreatedTime(createdTime); |
127 | clientRegistrationTemplate.setAdditionalInfo(additionalInfo); | 131 | clientRegistrationTemplate.setAdditionalInfo(additionalInfo); |
128 | 132 | ||
133 | + clientRegistrationTemplate.setMapperType(type); | ||
129 | clientRegistrationTemplate.setProviderId(providerId); | 134 | clientRegistrationTemplate.setProviderId(providerId); |
130 | clientRegistrationTemplate.setBasic( | 135 | clientRegistrationTemplate.setBasic( |
131 | OAuth2BasicMapperConfig.builder() | 136 | OAuth2BasicMapperConfig.builder() |
@@ -20,10 +20,12 @@ import lombok.extern.slf4j.Slf4j; | @@ -20,10 +20,12 @@ import lombok.extern.slf4j.Slf4j; | ||
20 | import org.springframework.boot.context.properties.ConfigurationProperties; | 20 | import org.springframework.boot.context.properties.ConfigurationProperties; |
21 | import org.springframework.context.annotation.Configuration; | 21 | import org.springframework.context.annotation.Configuration; |
22 | 22 | ||
23 | +import java.util.Map; | ||
24 | + | ||
23 | @Configuration | 25 | @Configuration |
24 | @ConfigurationProperties(prefix = "security.oauth2") | 26 | @ConfigurationProperties(prefix = "security.oauth2") |
25 | @Data | 27 | @Data |
26 | -@Slf4j | ||
27 | public class OAuth2Configuration { | 28 | public class OAuth2Configuration { |
28 | private String loginProcessingUrl; | 29 | private String loginProcessingUrl; |
30 | + private Map<String, String> githubMapper; | ||
29 | } | 31 | } |
@@ -184,6 +184,22 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se | @@ -184,6 +184,22 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se | ||
184 | throw new DataValidationException("Tenant name pattern should be specified!"); | 184 | throw new DataValidationException("Tenant name pattern should be specified!"); |
185 | } | 185 | } |
186 | } | 186 | } |
187 | + if (mapperConfig.getType() == MapperType.GITHUB) { | ||
188 | + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); | ||
189 | + if (basicConfig == null) { | ||
190 | + throw new DataValidationException("Basic config should be specified!"); | ||
191 | + } | ||
192 | + if (!StringUtils.isEmpty(basicConfig.getEmailAttributeKey())) { | ||
193 | + throw new DataValidationException("Email attribute key cannot be configured for GITHUB mapper type!"); | ||
194 | + } | ||
195 | + if (basicConfig.getTenantNameStrategy() == null) { | ||
196 | + throw new DataValidationException("Tenant name strategy should be specified!"); | ||
197 | + } | ||
198 | + if (basicConfig.getTenantNameStrategy() == TenantNameStrategyType.CUSTOM | ||
199 | + && StringUtils.isEmpty(basicConfig.getTenantNamePattern())) { | ||
200 | + throw new DataValidationException("Tenant name pattern should be specified!"); | ||
201 | + } | ||
202 | + } | ||
187 | if (mapperConfig.getType() == MapperType.CUSTOM) { | 203 | if (mapperConfig.getType() == MapperType.CUSTOM) { |
188 | OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom(); | 204 | OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom(); |
189 | if (customConfig == null) { | 205 | if (customConfig == null) { |
@@ -386,6 +386,7 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( | @@ -386,6 +386,7 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( | ||
386 | user_name_attribute_name varchar(255), | 386 | user_name_attribute_name varchar(255), |
387 | jwk_set_uri varchar(255), | 387 | jwk_set_uri varchar(255), |
388 | client_authentication_method varchar(255), | 388 | client_authentication_method varchar(255), |
389 | + type varchar(31), | ||
389 | basic_email_attribute_key varchar(31), | 390 | basic_email_attribute_key varchar(31), |
390 | basic_first_name_attribute_key varchar(31), | 391 | basic_first_name_attribute_key varchar(31), |
391 | basic_last_name_attribute_key varchar(31), | 392 | basic_last_name_attribute_key varchar(31), |
@@ -412,6 +412,7 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( | @@ -412,6 +412,7 @@ CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( | ||
412 | user_name_attribute_name varchar(255), | 412 | user_name_attribute_name varchar(255), |
413 | jwk_set_uri varchar(255), | 413 | jwk_set_uri varchar(255), |
414 | client_authentication_method varchar(255), | 414 | client_authentication_method varchar(255), |
415 | + type varchar(31), | ||
415 | basic_email_attribute_key varchar(31), | 416 | basic_email_attribute_key varchar(31), |
416 | basic_first_name_attribute_key varchar(31), | 417 | basic_first_name_attribute_key varchar(31), |
417 | basic_last_name_attribute_key varchar(31), | 418 | basic_last_name_attribute_key varchar(31), |
@@ -21,6 +21,7 @@ import org.junit.Before; | @@ -21,6 +21,7 @@ import org.junit.Before; | ||
21 | import org.junit.Test; | 21 | import org.junit.Test; |
22 | import org.springframework.beans.factory.annotation.Autowired; | 22 | import org.springframework.beans.factory.annotation.Autowired; |
23 | import org.thingsboard.server.common.data.id.TenantId; | 23 | import org.thingsboard.server.common.data.id.TenantId; |
24 | +import org.thingsboard.server.common.data.oauth2.MapperType; | ||
24 | import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; | 25 | import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; |
25 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; | 26 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; |
26 | import org.thingsboard.server.dao.exception.DataValidationException; | 27 | import org.thingsboard.server.dao.exception.DataValidationException; |
@@ -105,6 +106,7 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { | @@ -105,6 +106,7 @@ public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { | ||
105 | OAuth2ClientRegistrationTemplate clientRegistrationTemplate = new OAuth2ClientRegistrationTemplate(); | 106 | OAuth2ClientRegistrationTemplate clientRegistrationTemplate = new OAuth2ClientRegistrationTemplate(); |
106 | clientRegistrationTemplate.setProviderId(providerId); | 107 | clientRegistrationTemplate.setProviderId(providerId); |
107 | clientRegistrationTemplate.setAdditionalInfo(mapper.createObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString())); | 108 | clientRegistrationTemplate.setAdditionalInfo(mapper.createObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString())); |
109 | + clientRegistrationTemplate.setMapperType(MapperType.BASIC); | ||
108 | clientRegistrationTemplate.setBasic( | 110 | clientRegistrationTemplate.setBasic( |
109 | OAuth2BasicMapperConfig.builder() | 111 | OAuth2BasicMapperConfig.builder() |
110 | .firstNameAttributeKey("firstName") | 112 | .firstNameAttributeKey("firstName") |