Commit ac055397b01341838a14754efeec46f6d63a71a0

Authored by vzikratyi
1 parent e2791a28

Added GITHUB mapper type

@@ -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),
  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:
@@ -16,5 +16,5 @@ @@ -16,5 +16,5 @@
16 package org.thingsboard.server.common.data.oauth2; 16 package org.thingsboard.server.common.data.oauth2;
17 17
18 public enum MapperType { 18 public enum MapperType {
19 - BASIC, CUSTOM; 19 + BASIC, CUSTOM, GITHUB;
20 } 20 }
@@ -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")