Commit 8dbc550fd3bb0f0609ffdd8fbfe9e9edd3eb082f

Authored by Volodymyr Babak
Committed by Andrew Shvayka
1 parent d644dddc

Refactoring to review. Added tenantId and customerId. Added email tenant name strategy

@@ -200,10 +200,10 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt @@ -200,10 +200,10 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
200 .addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class) 200 .addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
201 .addFilterBefore(buildWsJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class) 201 .addFilterBefore(buildWsJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class)
202 .addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class); 202 .addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class);
203 - if (oauth2Configuration.isEnabled()) { 203 + if (oauth2Configuration != null && oauth2Configuration.isEnabled()) {
204 http.oauth2Login() 204 http.oauth2Login()
205 .loginPage("/oauth2Login") 205 .loginPage("/oauth2Login")
206 - .loginProcessingUrl(oauth2Configuration.getClients().values().iterator().next().getLoginProcessingUrl()) 206 + .loginProcessingUrl(oauth2Configuration.getLoginProcessingUrl())
207 .successHandler(oauth2AuthenticationSuccessHandler); 207 .successHandler(oauth2AuthenticationSuccessHandler);
208 } 208 }
209 } 209 }
application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/AbstractOAuth2ClientMapper.java renamed from application/src/main/java/org/thingsboard/server/service/security/auth/oauth2/BaseOAuth2ClientMapper.java
@@ -36,9 +36,11 @@ import org.thingsboard.server.service.security.model.UserPrincipal; @@ -36,9 +36,11 @@ import org.thingsboard.server.service.security.model.UserPrincipal;
36 36
37 import java.util.List; 37 import java.util.List;
38 import java.util.Optional; 38 import java.util.Optional;
  39 +import java.util.concurrent.locks.Lock;
  40 +import java.util.concurrent.locks.ReentrantLock;
39 41
40 @Slf4j 42 @Slf4j
41 -public abstract class BaseOAuth2ClientMapper { 43 +public abstract class AbstractOAuth2ClientMapper {
42 44
43 @Autowired 45 @Autowired
44 private UserService userService; 46 private UserService userService;
@@ -49,6 +51,8 @@ public abstract class BaseOAuth2ClientMapper { @@ -49,6 +51,8 @@ public abstract class BaseOAuth2ClientMapper {
49 @Autowired 51 @Autowired
50 private CustomerService customerService; 52 private CustomerService customerService;
51 53
  54 + private final Lock userCreationLock = new ReentrantLock();
  55 +
52 protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, boolean allowUserCreation) { 56 protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, boolean allowUserCreation) {
53 UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail()); 57 UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail());
54 58
@@ -59,18 +63,30 @@ public abstract class BaseOAuth2ClientMapper { @@ -59,18 +63,30 @@ public abstract class BaseOAuth2ClientMapper {
59 } 63 }
60 64
61 if (user == null) { 65 if (user == null) {
62 - user = new User();  
63 - if (StringUtils.isEmpty(oauth2User.getCustomerName())) {  
64 - user.setAuthority(Authority.TENANT_ADMIN);  
65 - } else {  
66 - user.setAuthority(Authority.CUSTOMER_USER); 66 + userCreationLock.lock();
  67 + try {
  68 + user = userService.findUserByEmail(TenantId.SYS_TENANT_ID, oauth2User.getEmail());
  69 + if (user == null) {
  70 + user = new User();
  71 + if (oauth2User.getCustomerId() == null && StringUtils.isEmpty(oauth2User.getCustomerName())) {
  72 + user.setAuthority(Authority.TENANT_ADMIN);
  73 + } else {
  74 + user.setAuthority(Authority.CUSTOMER_USER);
  75 + }
  76 + TenantId tenantId = oauth2User.getTenantId() != null ?
  77 + oauth2User.getTenantId() : getTenantId(oauth2User.getTenantName());
  78 + user.setTenantId(tenantId);
  79 + CustomerId customerId = oauth2User.getCustomerId() != null ?
  80 + oauth2User.getCustomerId() : getCustomerId(user.getTenantId(), oauth2User.getCustomerName());
  81 + user.setCustomerId(customerId);
  82 + user.setEmail(oauth2User.getEmail());
  83 + user.setFirstName(oauth2User.getFirstName());
  84 + user.setLastName(oauth2User.getLastName());
  85 + user = userService.saveUser(user);
  86 + }
  87 + } finally {
  88 + userCreationLock.unlock();
67 } 89 }
68 - user.setTenantId(getTenantId(oauth2User.getTenantName()));  
69 - user.setCustomerId(getCustomerId(user.getTenantId(), oauth2User.getCustomerName()));  
70 - user.setEmail(oauth2User.getEmail());  
71 - user.setFirstName(oauth2User.getFirstName());  
72 - user.setLastName(oauth2User.getLastName());  
73 - user = userService.saveUser(user);  
74 } 90 }
75 91
76 try { 92 try {
@@ -78,7 +94,7 @@ public abstract class BaseOAuth2ClientMapper { @@ -78,7 +94,7 @@ public abstract class BaseOAuth2ClientMapper {
78 return (SecurityUser) new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities()).getPrincipal(); 94 return (SecurityUser) new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities()).getPrincipal();
79 } catch (Exception e) { 95 } catch (Exception e) {
80 log.error("Can't get or create security user from oauth2 user", e); 96 log.error("Can't get or create security user from oauth2 user", e);
81 - throw e; 97 + throw new RuntimeException("Can't get or create security user from oauth2 user", e);
82 } 98 }
83 } 99 }
84 100
@@ -28,7 +28,13 @@ import java.util.Map; @@ -28,7 +28,13 @@ import java.util.Map;
28 28
29 @Service(value = "basicOAuth2ClientMapper") 29 @Service(value = "basicOAuth2ClientMapper")
30 @Slf4j 30 @Slf4j
31 -public class BasicOAuth2ClientMapper extends BaseOAuth2ClientMapper implements OAuth2ClientMapper { 31 +public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
  32 +
  33 + private static final String START_PLACEHOLDER_PREFIX = "%{";
  34 + private static final String END_PLACEHOLDER_PREFIX = "}";
  35 + private static final String EMAIL_TENANT_STRATEGY = "email";
  36 + private static final String DOMAIN_TENANT_STRATEGY = "domain";
  37 + private static final String CUSTOM_TENANT_STRATEGY = "custom";
32 38
33 @Override 39 @Override
34 public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig config) { 40 public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig config) {
@@ -46,7 +52,7 @@ public class BasicOAuth2ClientMapper extends BaseOAuth2ClientMapper implements O @@ -46,7 +52,7 @@ public class BasicOAuth2ClientMapper extends BaseOAuth2ClientMapper implements O
46 oauth2User.setFirstName(firstName); 52 oauth2User.setFirstName(firstName);
47 } 53 }
48 if (!StringUtils.isEmpty(config.getBasic().getCustomerNameStrategyPattern())) { 54 if (!StringUtils.isEmpty(config.getBasic().getCustomerNameStrategyPattern())) {
49 - StrSubstitutor sub = new StrSubstitutor(attributes, "${", "}"); 55 + StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX);
50 String customerName = sub.replace(config.getBasic().getCustomerNameStrategyPattern()); 56 String customerName = sub.replace(config.getBasic().getCustomerNameStrategyPattern());
51 oauth2User.setCustomerName(customerName); 57 oauth2User.setCustomerName(customerName);
52 } 58 }
@@ -55,11 +61,13 @@ public class BasicOAuth2ClientMapper extends BaseOAuth2ClientMapper implements O @@ -55,11 +61,13 @@ public class BasicOAuth2ClientMapper extends BaseOAuth2ClientMapper implements O
55 61
56 private String getTenantName(Map<String, Object> attributes, OAuth2ClientMapperConfig config) { 62 private String getTenantName(Map<String, Object> attributes, OAuth2ClientMapperConfig config) {
57 switch (config.getBasic().getTenantNameStrategy()) { 63 switch (config.getBasic().getTenantNameStrategy()) {
58 - case "domain": 64 + case EMAIL_TENANT_STRATEGY:
  65 + return getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey());
  66 + case DOMAIN_TENANT_STRATEGY:
59 String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); 67 String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey());
60 return email.substring(email .indexOf("@") + 1); 68 return email.substring(email .indexOf("@") + 1);
61 - case "custom":  
62 - StrSubstitutor sub = new StrSubstitutor(attributes, "${", "}"); 69 + case CUSTOM_TENANT_STRATEGY:
  70 + StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX);
63 return sub.replace(config.getBasic().getTenantNameStrategyPattern()); 71 return sub.replace(config.getBasic().getTenantNameStrategyPattern());
64 default: 72 default:
65 throw new RuntimeException("Tenant Name Strategy with type " + config.getBasic().getTenantNameStrategy() + " is not supported!"); 73 throw new RuntimeException("Tenant Name Strategy with type " + config.getBasic().getTenantNameStrategy() + " is not supported!");
@@ -15,6 +15,8 @@ @@ -15,6 +15,8 @@
15 */ 15 */
16 package org.thingsboard.server.service.security.auth.oauth2; 16 package org.thingsboard.server.service.security.auth.oauth2;
17 17
  18 +import com.fasterxml.jackson.core.JsonProcessingException;
  19 +import com.fasterxml.jackson.databind.ObjectMapper;
18 import lombok.extern.slf4j.Slf4j; 20 import lombok.extern.slf4j.Slf4j;
19 import org.springframework.boot.web.client.RestTemplateBuilder; 21 import org.springframework.boot.web.client.RestTemplateBuilder;
20 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; 22 import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
@@ -27,7 +29,9 @@ import org.thingsboard.server.service.security.model.SecurityUser; @@ -27,7 +29,9 @@ import org.thingsboard.server.service.security.model.SecurityUser;
27 29
28 @Service(value = "customOAuth2ClientMapper") 30 @Service(value = "customOAuth2ClientMapper")
29 @Slf4j 31 @Slf4j
30 -public class CustomOAuth2ClientMapper extends BaseOAuth2ClientMapper implements OAuth2ClientMapper { 32 +public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
  33 +
  34 + private static final ObjectMapper json = new ObjectMapper();
31 35
32 private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); 36 private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
33 37
@@ -42,6 +46,18 @@ public class CustomOAuth2ClientMapper extends BaseOAuth2ClientMapper implements @@ -42,6 +46,18 @@ public class CustomOAuth2ClientMapper extends BaseOAuth2ClientMapper implements
42 restTemplateBuilder = restTemplateBuilder.basicAuthentication(custom.getUsername(), custom.getPassword()); 46 restTemplateBuilder = restTemplateBuilder.basicAuthentication(custom.getUsername(), custom.getPassword());
43 } 47 }
44 RestTemplate restTemplate = restTemplateBuilder.build(); 48 RestTemplate restTemplate = restTemplateBuilder.build();
45 - return restTemplate.postForEntity(custom.getUrl(), token.getPrincipal(), OAuth2User.class).getBody(); 49 + String request;
  50 + try {
  51 + request = json.writeValueAsString(token.getPrincipal());
  52 + } catch (JsonProcessingException e) {
  53 + log.error("Can't convert principal to JSON string", e);
  54 + throw new RuntimeException("Can't convert principal to JSON string", e);
  55 + }
  56 + try {
  57 + return restTemplate.postForEntity(custom.getUrl(), request, OAuth2User.class).getBody();
  58 + } catch (Exception e) {
  59 + log.error("Can't connect to custom mapper endpoint", e);
  60 + throw new RuntimeException("Can't connect to custom mapper endpoint", e);
  61 + }
46 } 62 }
47 } 63 }
@@ -98,72 +98,40 @@ security: @@ -98,72 +98,40 @@ security:
98 # Time allowed to claim the device in milliseconds 98 # Time allowed to claim the device in milliseconds
99 duration: "${SECURITY_CLAIM_DURATION:60000}" # 1 minute, note this value must equal claimDevices.timeToLiveInMinutes value 99 duration: "${SECURITY_CLAIM_DURATION:60000}" # 1 minute, note this value must equal claimDevices.timeToLiveInMinutes value
100 basic: 100 basic:
101 - enabled: false 101 + enabled: "${SECURITY_BASIC_ENABLED:false}"
102 oauth2: 102 oauth2:
103 - enabled: true 103 + enabled: "${SECURITY_OAUTH2_ENABLED:false}"
  104 + loginProcessingUrl: "${SECURITY_OAUTH2_LOGIN_PROCESSING_URL:/login/oauth2/code/}"
104 clients: 105 clients:
105 - schwarz:  
106 - registrationId: A  
107 - loginButtonLabel: Auth0 #  
108 - loginButtonIcon:  
109 - clientName: Test app  
110 - clientId: dVH9reqyqiXIG7M2wmamb0ySue8zaM4g  
111 - clientSecret: EYAfAGxwkwoeYnb2o2cDgaWZB5k97OStpZQPPvcMMD-SVH2BuughTGeBazXtF5I6  
112 - accessTokenUri: https://dev-r9m8ht0k.auth0.com/oauth/token  
113 - authorizationUri: https://dev-r9m8ht0k.auth0.com/authorize  
114 - scope: openid,profile,email  
115 - redirectUriTemplate: http://localhost:8080/login/oauth2/code/  
116 - loginProcessingUrl: /login/oauth2/code/  
117 - jwkSetUri: https://dev-r9m8ht0k.auth0.com/.well-known/jwks.json  
118 - authorizationGrantType: authorization_code # authorization_code, implicit, refresh_token, client_credentials  
119 - clientAuthenticationMethod: post # basic, post  
120 - userInfoUri: https://dev-r9m8ht0k.auth0.com/userinfo  
121 - userNameAttributeName: email 106 + default:
  107 + loginButtonLabel: "${SECURITY_OAUTH2_DEFAULT_LOGIN_BUTTON_LABEL:Default}" # Label that going to be show on login screen
  108 + loginButtonIcon: "${SECURITY_OAUTH2_DEFAULT_LOGIN_BUTTON_ICON:}" # Icon that going to be show on login screen. Material design icon ID (https://material.angularjs.org/latest/api/directive/mdIcon)
  109 + clientName: "${SECURITY_OAUTH2_DEFAULT_CLIENT_NAME:ClientName}"
  110 + clientId: "${SECURITY_OAUTH2_DEFAULT_CLIENT_ID:}"
  111 + clientSecret: "${SECURITY_OAUTH2_DEFAULT_CLIENT_SECRET:}"
  112 + accessTokenUri: "${SECURITY_OAUTH2_DEFAULT_ACCESS_TOKEN_URI:}"
  113 + authorizationUri: "${SECURITY_OAUTH2_DEFAULT_AUTHORIZATION_URI:}"
  114 + scope: "${SECURITY_OAUTH2_DEFAULT_SCOPE:}"
  115 + redirectUriTemplate: "${SECURITY_OAUTH2_DEFAULT_REDIRECT_URI_TEMPLATE:http://localhost:8080/login/oauth2/code/}" # Must be in sync with security.oauth2.loginProcessingUrl
  116 + jwkSetUri: "${SECURITY_OAUTH2_DEFAULT_JWK_SET_URI:}"
  117 + authorizationGrantType: "${SECURITY_OAUTH2_DEFAULT_AUTHORIZATION_GRANT_TYPE:authorization_code}" # authorization_code, implicit, refresh_token or client_credentials
  118 + clientAuthenticationMethod: "${SECURITY_OAUTH2_DEFAULT_CLIENT_AUTHENTICATION_METHOD:post}" # basic or post
  119 + userInfoUri: "${SECURITY_OAUTH2_DEFAULT_USER_INFO_URI:}"
  120 + userNameAttributeName: "${SECURITY_OAUTH2_DEFAULT_USER_NAME_ATTRIBUTE_NAME:email}"
122 mapperConfig: 121 mapperConfig:
123 - type: custom # basic or custom 122 + type: "${SECURITY_OAUTH2_DEFAULT_MAPPER_TYPE:basic}" # basic or custom
124 basic: 123 basic:
125 - allowUserCreation: true # required  
126 - emailAttributeKey: email # required  
127 - firstNameAttributeKey:  
128 - lastNameAttributeKey:  
129 - tenantNameStrategy: domain # domain or custom  
130 - tenantNameStrategyPattern:  
131 - customerNameStrategyPattern: 124 + allowUserCreation: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_ALLOW_USER_CREATION:true}" # Allows to create user if it not exists
  125 + emailAttributeKey: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_EMAIL_ATTRIBUTE_KEY:email}" # Attribute key to use as email for the user
  126 + firstNameAttributeKey: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_FIRST_NAME_ATTRIBUTE_KEY:}"
  127 + lastNameAttributeKey: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_LAST_NAME_ATTRIBUTE_KEY:}"
  128 + tenantNameStrategy: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_TENANT_NAME_STRATEGY:domain}" # domain, email or custom
  129 + tenantNameStrategyPattern: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_TENANT_NAME_STRATEGY_PATTERN:}"
  130 + customerNameStrategyPattern: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_CUSTOMER_NAME_STRATEGY_PATTERN:}"
132 custom: 131 custom:
133 - url: http://localhost:9090/oauth2/mapper  
134 - username: admin  
135 - password: bababa  
136 - auth0:  
137 - registrationId: B  
138 - loginButtonLabel: Schwarz #  
139 - loginButtonIcon: mdi:google  
140 - clientName: Thingsboard Dev Test Q  
141 - clientId: 5f5c0998-1d9b-4679-9610-6108fb91af2a  
142 - clientSecret: h_kXVb7Ee1LgDDinix_nkAh_owWX7YCO783NNteF9AIOqlTWu2L03YoFjv5KL8yRVyx4uYAE-r_N3tFbupE8Kw  
143 - accessTokenUri: https://federation-q.auth.schwarz/nidp/oauth/nam/token  
144 - authorizationUri: https://federation-q.auth.schwarz/nidp/oauth/nam/authz  
145 - scope: openid,profile,email,siam  
146 - redirectUriTemplate: http://localhost:8080/login/oauth2/code/  
147 - loginProcessingUrl: /login/oauth2/code/  
148 - jwkSetUri: https://federation-q.auth.schwarz/nidp/oauth/nam/keys  
149 - authorizationGrantType: authorization_code # authorization_code, implicit, refresh_token, client_credentials  
150 - clientAuthenticationMethod: post # basic, post  
151 - userInfoUri: https://federation-q.auth.schwarz/nidp/oauth/nam/userinfo  
152 - userNameAttributeName: mail  
153 - mapperConfig:  
154 - type: basic # simple or custom  
155 - basic:  
156 - allowUserCreation: true # required  
157 - emailAttributeKey: CloudLoginName # required  
158 - firstNameAttributeKey: givenName  
159 - lastNameAttributeKey: sn  
160 - tenantNameStrategy: custom # domain or custom  
161 - tenantNameStrategyPattern: LOL ${region}  
162 - customerNameStrategyPattern: GGG ${countrycode}  
163 - custom:  
164 - url: http://localhost:9090/oauth2/mapper  
165 - username: test  
166 - password: test 132 + url: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_URL:}"
  133 + username: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_USERNAME:}"
  134 + password: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_PASSWORD:}"
167 135
168 # Dashboard parameters 136 # Dashboard parameters
169 dashboard: 137 dashboard:
@@ -16,11 +16,15 @@ @@ -16,11 +16,15 @@
16 package org.thingsboard.server.dao.oauth2; 16 package org.thingsboard.server.dao.oauth2;
17 17
18 import lombok.Data; 18 import lombok.Data;
  19 +import org.thingsboard.server.common.data.id.CustomerId;
  20 +import org.thingsboard.server.common.data.id.TenantId;
19 21
20 @Data 22 @Data
21 public class OAuth2User { 23 public class OAuth2User {
22 private String tenantName; 24 private String tenantName;
  25 + private TenantId tenantId;
23 private String customerName; 26 private String customerName;
  27 + private CustomerId customerId;
24 private String email; 28 private String email;
25 private String firstName; 29 private String firstName;
26 private String lastName; 30 private String lastName;
@@ -20,7 +20,6 @@ import lombok.Data; @@ -20,7 +20,6 @@ import lombok.Data;
20 @Data 20 @Data
21 public class OAuth2Client { 21 public class OAuth2Client {
22 22
23 - private String registrationId;  
24 private String loginButtonLabel; 23 private String loginButtonLabel;
25 private String loginButtonIcon; 24 private String loginButtonIcon;
26 private String clientName; 25 private String clientName;
@@ -31,7 +30,6 @@ public class OAuth2Client { @@ -31,7 +30,6 @@ public class OAuth2Client {
31 private String scope; 30 private String scope;
32 private String redirectUriTemplate; 31 private String redirectUriTemplate;
33 private String jwkSetUri; 32 private String jwkSetUri;
34 - private String loginProcessingUrl;  
35 private String authorizationGrantType; 33 private String authorizationGrantType;
36 private String clientAuthenticationMethod; 34 private String clientAuthenticationMethod;
37 private String userInfoUri; 35 private String userInfoUri;
@@ -21,8 +21,8 @@ import lombok.Data; @@ -21,8 +21,8 @@ import lombok.Data;
21 public class OAuth2ClientMapperConfig { 21 public class OAuth2ClientMapperConfig {
22 22
23 private String type; 23 private String type;
24 - private CustomOAuth2ClientMapperConfig custom;  
25 private BasicOAuth2ClientMapperConfig basic; 24 private BasicOAuth2ClientMapperConfig basic;
  25 + private CustomOAuth2ClientMapperConfig custom;
26 26
27 @Data 27 @Data
28 public static class BasicOAuth2ClientMapperConfig { 28 public static class BasicOAuth2ClientMapperConfig {
@@ -40,13 +40,15 @@ import java.util.Map; @@ -40,13 +40,15 @@ import java.util.Map;
40 public class OAuth2Configuration { 40 public class OAuth2Configuration {
41 41
42 private boolean enabled; 42 private boolean enabled;
  43 + private String loginProcessingUrl;
43 private Map<String, OAuth2Client> clients = new HashMap<>(); 44 private Map<String, OAuth2Client> clients = new HashMap<>();
44 45
45 @Bean 46 @Bean
46 public ClientRegistrationRepository clientRegistrationRepository() { 47 public ClientRegistrationRepository clientRegistrationRepository() {
47 List<ClientRegistration> result = new ArrayList<>(); 48 List<ClientRegistration> result = new ArrayList<>();
48 - for (OAuth2Client client : clients.values()) {  
49 - ClientRegistration registration = ClientRegistration.withRegistrationId(client.getRegistrationId()) 49 + for (Map.Entry<String, OAuth2Client> entry : clients.entrySet()) {
  50 + OAuth2Client client = entry.getValue();
  51 + ClientRegistration registration = ClientRegistration.withRegistrationId(entry.getKey())
50 .clientId(client.getClientId()) 52 .clientId(client.getClientId())
51 .authorizationUri(client.getAuthorizationUri()) 53 .authorizationUri(client.getAuthorizationUri())
52 .clientSecret(client.getClientSecret()) 54 .clientSecret(client.getClientSecret())
@@ -68,9 +70,9 @@ public class OAuth2Configuration { @@ -68,9 +70,9 @@ public class OAuth2Configuration {
68 public OAuth2Client getClientByRegistrationId(String registrationId) { 70 public OAuth2Client getClientByRegistrationId(String registrationId) {
69 OAuth2Client result = null; 71 OAuth2Client result = null;
70 if (clients != null && !clients.isEmpty()) { 72 if (clients != null && !clients.isEmpty()) {
71 - for (OAuth2Client client : clients.values()) {  
72 - if (client.getRegistrationId().equals(registrationId)) {  
73 - result = client; 73 + for (String key : clients.keySet()) {
  74 + if (key.equals(registrationId)) {
  75 + result = clients.get(key);
74 break; 76 break;
75 } 77 }
76 } 78 }
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo;
23 import java.util.ArrayList; 23 import java.util.ArrayList;
24 import java.util.Collections; 24 import java.util.Collections;
25 import java.util.List; 25 import java.util.List;
  26 +import java.util.Map;
26 27
27 @Slf4j 28 @Slf4j
28 @Service 29 @Service
@@ -33,15 +34,15 @@ public class OAuth2ServiceImpl implements OAuth2Service { @@ -33,15 +34,15 @@ public class OAuth2ServiceImpl implements OAuth2Service {
33 34
34 @Override 35 @Override
35 public List<OAuth2ClientInfo> getOAuth2Clients() { 36 public List<OAuth2ClientInfo> getOAuth2Clients() {
36 - if (!oauth2Configuration.isEnabled()) { 37 + if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) {
37 return Collections.emptyList(); 38 return Collections.emptyList();
38 } 39 }
39 List<OAuth2ClientInfo> result = new ArrayList<>(); 40 List<OAuth2ClientInfo> result = new ArrayList<>();
40 - for (OAuth2Client c : oauth2Configuration.getClients().values()) { 41 + for (Map.Entry<String, OAuth2Client> entry : oauth2Configuration.getClients().entrySet()) {
41 OAuth2ClientInfo client = new OAuth2ClientInfo(); 42 OAuth2ClientInfo client = new OAuth2ClientInfo();
42 - client.setName(c.getLoginButtonLabel());  
43 - client.setUrl(String.format("/oauth2/authorization/%s", c.getRegistrationId()));  
44 - client.setIcon(c.getLoginButtonIcon()); 43 + client.setName(entry.getValue().getLoginButtonLabel());
  44 + client.setUrl(String.format("/oauth2/authorization/%s", entry.getKey()));
  45 + client.setIcon(entry.getValue().getLoginButtonIcon());
45 result.add(client); 46 result.add(client);
46 } 47 }
47 return result; 48 return result;