Commit 628ce7991da43b50aeeed7d349ea184cdfd96adc
Merge branch 'vzikratyi-tb-feature/dynamic-oauth2-new-table'
Showing
88 changed files
with
4951 additions
and
320 deletions
1 | +{ | |
2 | + "providerId": "Facebook", | |
3 | + "accessTokenUri": "https://graph.facebook.com/v2.8/oauth/access_token", | |
4 | + "authorizationUri": "https://www.facebook.com/v2.8/dialog/oauth", | |
5 | + "scope": ["email","public_profile"], | |
6 | + "jwkSetUri": null, | |
7 | + "userInfoUri": "https://graph.facebook.com/me?fields=id,name,first_name,last_name,email", | |
8 | + "clientAuthenticationMethod": "BASIC", | |
9 | + "userNameAttributeName": "email", | |
10 | + "basic": { | |
11 | + "emailAttributeKey": "email", | |
12 | + "firstNameAttributeKey": "first_name", | |
13 | + "lastNameAttributeKey": "last_name", | |
14 | + "tenantNameStrategy": "DOMAIN" | |
15 | + }, | |
16 | + "comment": null, | |
17 | + "loginButtonIcon": "mdi:facebook", | |
18 | + "loginButtonLabel": "Facebook", | |
19 | + "helpLink": "https://developers.facebook.com/docs/facebook-login/web#logindialog" | |
20 | +} | ... | ... |
1 | +{ | |
2 | + "providerId": "Github", | |
3 | + "accessTokenUri": "https://github.com/login/oauth/access_token", | |
4 | + "authorizationUri": "https://github.com/login/oauth/authorize", | |
5 | + "scope": ["read:user","user:email"], | |
6 | + "jwkSetUri": null, | |
7 | + "userInfoUri": "https://api.github.com/user", | |
8 | + "clientAuthenticationMethod": "BASIC", | |
9 | + "userNameAttributeName": "login", | |
10 | + "basic": { | |
11 | + "tenantNameStrategy": "DOMAIN" | |
12 | + }, | |
13 | + "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 | + "loginButtonIcon": "mdi:github", | |
15 | + "loginButtonLabel": "Github", | |
16 | + "helpLink": "https://docs.github.com/en/developers/apps/creating-an-oauth-app" | |
17 | +} | ... | ... |
1 | +{ | |
2 | + "providerId": "Google", | |
3 | + "additionalInfo": null, | |
4 | + "accessTokenUri": "https://oauth2.googleapis.com/token", | |
5 | + "authorizationUri": "https://accounts.google.com/o/oauth2/v2/auth", | |
6 | + "scope": ["email","openid","profile"], | |
7 | + "jwkSetUri": "https://www.googleapis.com/oauth2/v3/certs", | |
8 | + "userInfoUri": "https://openidconnect.googleapis.com/v1/userinfo", | |
9 | + "clientAuthenticationMethod": "BASIC", | |
10 | + "userNameAttributeName": "email", | |
11 | + "basic": { | |
12 | + "emailAttributeKey": "email", | |
13 | + "firstNameAttributeKey": "given_name", | |
14 | + "lastNameAttributeKey": "family_name", | |
15 | + "tenantNameStrategy": "DOMAIN" | |
16 | + }, | |
17 | + "comment": null, | |
18 | + "loginButtonIcon": "mdi:google", | |
19 | + "loginButtonLabel": "Google", | |
20 | + "helpLink": "https://developers.google.com/adwords/api/docs/guides/authentication" | |
21 | +} | ... | ... |
... | ... | @@ -14,6 +14,75 @@ |
14 | 14 | -- limitations under the License. |
15 | 15 | -- |
16 | 16 | |
17 | +CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( | |
18 | + id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY, | |
19 | + enabled boolean, | |
20 | + created_time bigint NOT NULL, | |
21 | + additional_info varchar, | |
22 | + client_id varchar(255), | |
23 | + client_secret varchar(255), | |
24 | + authorization_uri varchar(255), | |
25 | + token_uri varchar(255), | |
26 | + scope varchar(255), | |
27 | + user_info_uri varchar(255), | |
28 | + user_name_attribute_name varchar(255), | |
29 | + jwk_set_uri varchar(255), | |
30 | + client_authentication_method varchar(255), | |
31 | + login_button_label varchar(255), | |
32 | + login_button_icon varchar(255), | |
33 | + allow_user_creation boolean, | |
34 | + activate_user boolean, | |
35 | + type varchar(31), | |
36 | + basic_email_attribute_key varchar(31), | |
37 | + basic_first_name_attribute_key varchar(31), | |
38 | + basic_last_name_attribute_key varchar(31), | |
39 | + basic_tenant_name_strategy varchar(31), | |
40 | + basic_tenant_name_pattern varchar(255), | |
41 | + basic_customer_name_pattern varchar(255), | |
42 | + basic_default_dashboard_name varchar(255), | |
43 | + basic_always_full_screen boolean, | |
44 | + custom_url varchar(255), | |
45 | + custom_username varchar(255), | |
46 | + custom_password varchar(255), | |
47 | + custom_send_token boolean | |
48 | +); | |
49 | + | |
50 | +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( | |
51 | + id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, | |
52 | + created_time bigint NOT NULL, | |
53 | + domain_name varchar(255), | |
54 | + domain_scheme varchar(31), | |
55 | + client_registration_info_id uuid | |
56 | +); | |
57 | + | |
58 | +CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( | |
59 | + id uuid NOT NULL CONSTRAINT oauth2_client_registration_template_pkey PRIMARY KEY, | |
60 | + created_time bigint NOT NULL, | |
61 | + additional_info varchar, | |
62 | + provider_id varchar(255), | |
63 | + authorization_uri varchar(255), | |
64 | + token_uri varchar(255), | |
65 | + scope varchar(255), | |
66 | + user_info_uri varchar(255), | |
67 | + user_name_attribute_name varchar(255), | |
68 | + jwk_set_uri varchar(255), | |
69 | + client_authentication_method varchar(255), | |
70 | + type varchar(31), | |
71 | + basic_email_attribute_key varchar(31), | |
72 | + basic_first_name_attribute_key varchar(31), | |
73 | + basic_last_name_attribute_key varchar(31), | |
74 | + basic_tenant_name_strategy varchar(31), | |
75 | + basic_tenant_name_pattern varchar(255), | |
76 | + basic_customer_name_pattern varchar(255), | |
77 | + basic_default_dashboard_name varchar(255), | |
78 | + basic_always_full_screen boolean, | |
79 | + comment varchar, | |
80 | + login_button_icon varchar(255), | |
81 | + login_button_label varchar(255), | |
82 | + help_link varchar(255), | |
83 | + CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) | |
84 | +); | |
85 | + | |
17 | 86 | CREATE TABLE IF NOT EXISTS device_profile ( |
18 | 87 | id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY, |
19 | 88 | created_time bigint NOT NULL, | ... | ... |
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.config; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.springframework.beans.factory.annotation.Autowired; | |
20 | +import org.springframework.security.crypto.keygen.Base64StringKeyGenerator; | |
21 | +import org.springframework.security.crypto.keygen.StringKeyGenerator; | |
22 | +import org.springframework.security.oauth2.client.registration.ClientRegistration; | |
23 | +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; | |
24 | +import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver; | |
25 | +import org.springframework.security.oauth2.core.AuthorizationGrantType; | |
26 | +import org.springframework.security.oauth2.core.ClientAuthenticationMethod; | |
27 | +import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; | |
28 | +import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; | |
29 | +import org.springframework.security.oauth2.core.endpoint.PkceParameterNames; | |
30 | +import org.springframework.security.oauth2.core.oidc.OidcScopes; | |
31 | +import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; | |
32 | +import org.springframework.security.web.util.UrlUtils; | |
33 | +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; | |
34 | +import org.springframework.stereotype.Service; | |
35 | +import org.springframework.util.CollectionUtils; | |
36 | +import org.springframework.util.StringUtils; | |
37 | +import org.springframework.web.util.UriComponents; | |
38 | +import org.springframework.web.util.UriComponentsBuilder; | |
39 | +import org.thingsboard.server.dao.oauth2.OAuth2Configuration; | |
40 | +import org.thingsboard.server.utils.MiscUtils; | |
41 | + | |
42 | +import javax.servlet.http.HttpServletRequest; | |
43 | +import java.nio.charset.StandardCharsets; | |
44 | +import java.security.MessageDigest; | |
45 | +import java.security.NoSuchAlgorithmException; | |
46 | +import java.util.Base64; | |
47 | +import java.util.HashMap; | |
48 | +import java.util.Map; | |
49 | + | |
50 | +@Service | |
51 | +@Slf4j | |
52 | +public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { | |
53 | + public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization"; | |
54 | + public static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/"; | |
55 | + private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId"; | |
56 | + private static final char PATH_DELIMITER = '/'; | |
57 | + | |
58 | + private final AntPathRequestMatcher authorizationRequestMatcher = new AntPathRequestMatcher( | |
59 | + DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{" + REGISTRATION_ID_URI_VARIABLE_NAME + "}"); | |
60 | + private final StringKeyGenerator stateGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder()); | |
61 | + private final StringKeyGenerator secureKeyGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96); | |
62 | + | |
63 | + @Autowired | |
64 | + private ClientRegistrationRepository clientRegistrationRepository; | |
65 | + | |
66 | + @Autowired(required = false) | |
67 | + private OAuth2Configuration oauth2Configuration; | |
68 | + | |
69 | + | |
70 | + @Override | |
71 | + public OAuth2AuthorizationRequest resolve(HttpServletRequest request) { | |
72 | + String registrationId = this.resolveRegistrationId(request); | |
73 | + String redirectUriAction = getAction(request, "login"); | |
74 | + return resolve(request, registrationId, redirectUriAction); | |
75 | + } | |
76 | + | |
77 | + @Override | |
78 | + public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId) { | |
79 | + if (registrationId == null) { | |
80 | + return null; | |
81 | + } | |
82 | + String redirectUriAction = getAction(request, "authorize"); | |
83 | + return resolve(request, registrationId, redirectUriAction); | |
84 | + } | |
85 | + | |
86 | + private String getAction(HttpServletRequest request, String defaultAction) { | |
87 | + String action = request.getParameter("action"); | |
88 | + if (action == null) { | |
89 | + return defaultAction; | |
90 | + } | |
91 | + return action; | |
92 | + } | |
93 | + | |
94 | + private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) { | |
95 | + if (registrationId == null) { | |
96 | + return null; | |
97 | + } | |
98 | + | |
99 | + ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId); | |
100 | + if (clientRegistration == null) { | |
101 | + throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId); | |
102 | + } | |
103 | + | |
104 | + Map<String, Object> attributes = new HashMap<>(); | |
105 | + attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()); | |
106 | + | |
107 | + OAuth2AuthorizationRequest.Builder builder; | |
108 | + if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) { | |
109 | + builder = OAuth2AuthorizationRequest.authorizationCode(); | |
110 | + Map<String, Object> additionalParameters = new HashMap<>(); | |
111 | + if (!CollectionUtils.isEmpty(clientRegistration.getScopes()) && | |
112 | + clientRegistration.getScopes().contains(OidcScopes.OPENID)) { | |
113 | + // Section 3.1.2.1 Authentication Request - https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest | |
114 | + // scope | |
115 | + // REQUIRED. OpenID Connect requests MUST contain the "openid" scope value. | |
116 | + addNonceParameters(attributes, additionalParameters); | |
117 | + } | |
118 | + if (ClientAuthenticationMethod.NONE.equals(clientRegistration.getClientAuthenticationMethod())) { | |
119 | + addPkceParameters(attributes, additionalParameters); | |
120 | + } | |
121 | + builder.additionalParameters(additionalParameters); | |
122 | + } else if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) { | |
123 | + builder = OAuth2AuthorizationRequest.implicit(); | |
124 | + } else { | |
125 | + throw new IllegalArgumentException("Invalid Authorization Grant Type (" + | |
126 | + clientRegistration.getAuthorizationGrantType().getValue() + | |
127 | + ") for Client Registration with Id: " + clientRegistration.getRegistrationId()); | |
128 | + } | |
129 | + | |
130 | + String redirectUriStr = expandRedirectUri(request, clientRegistration, redirectUriAction); | |
131 | + | |
132 | + return builder | |
133 | + .clientId(clientRegistration.getClientId()) | |
134 | + .authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri()) | |
135 | + .redirectUri(redirectUriStr) | |
136 | + .scopes(clientRegistration.getScopes()) | |
137 | + .state(this.stateGenerator.generateKey()) | |
138 | + .attributes(attributes) | |
139 | + .build(); | |
140 | + } | |
141 | + | |
142 | + private String resolveRegistrationId(HttpServletRequest request) { | |
143 | + if (this.authorizationRequestMatcher.matches(request)) { | |
144 | + return this.authorizationRequestMatcher | |
145 | + .matcher(request).getVariables().get(REGISTRATION_ID_URI_VARIABLE_NAME); | |
146 | + } | |
147 | + return null; | |
148 | + } | |
149 | + | |
150 | + /** | |
151 | + * Expands the {@link ClientRegistration#getRedirectUriTemplate()} with following provided variables:<br/> | |
152 | + * - baseUrl (e.g. https://localhost/app) <br/> | |
153 | + * - baseScheme (e.g. https) <br/> | |
154 | + * - baseHost (e.g. localhost) <br/> | |
155 | + * - basePort (e.g. :8080) <br/> | |
156 | + * - basePath (e.g. /app) <br/> | |
157 | + * - registrationId (e.g. google) <br/> | |
158 | + * - action (e.g. login) <br/> | |
159 | + * <p/> | |
160 | + * Null variables are provided as empty strings. | |
161 | + * <p/> | |
162 | + * Default redirectUriTemplate is: {@link org.springframework.security.config.oauth2.client}.CommonOAuth2Provider#DEFAULT_REDIRECT_URL | |
163 | + * | |
164 | + * @return expanded URI | |
165 | + */ | |
166 | + private String expandRedirectUri(HttpServletRequest request, ClientRegistration clientRegistration, String action) { | |
167 | + Map<String, String> uriVariables = new HashMap<>(); | |
168 | + uriVariables.put("registrationId", clientRegistration.getRegistrationId()); | |
169 | + | |
170 | + UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request)) | |
171 | + .replacePath(request.getContextPath()) | |
172 | + .replaceQuery(null) | |
173 | + .fragment(null) | |
174 | + .build(); | |
175 | + String scheme = uriComponents.getScheme(); | |
176 | + uriVariables.put("baseScheme", scheme == null ? "" : scheme); | |
177 | + String host = uriComponents.getHost(); | |
178 | + uriVariables.put("baseHost", host == null ? "" : host); | |
179 | + // following logic is based on HierarchicalUriComponents#toUriString() | |
180 | + int port = uriComponents.getPort(); | |
181 | + uriVariables.put("basePort", port == -1 ? "" : ":" + port); | |
182 | + String path = uriComponents.getPath(); | |
183 | + if (StringUtils.hasLength(path)) { | |
184 | + if (path.charAt(0) != PATH_DELIMITER) { | |
185 | + path = PATH_DELIMITER + path; | |
186 | + } | |
187 | + } | |
188 | + uriVariables.put("basePath", path == null ? "" : path); | |
189 | + uriVariables.put("baseUrl", uriComponents.toUriString()); | |
190 | + | |
191 | + uriVariables.put("action", action == null ? "" : action); | |
192 | + | |
193 | + String redirectUri = getRedirectUri(request); | |
194 | + log.trace("Redirect URI - {}.", redirectUri); | |
195 | + | |
196 | + return UriComponentsBuilder.fromUriString(redirectUri) | |
197 | + .buildAndExpand(uriVariables) | |
198 | + .toUriString(); | |
199 | + } | |
200 | + | |
201 | + private String getRedirectUri(HttpServletRequest request) { | |
202 | + String loginProcessingUri = oauth2Configuration != null ? oauth2Configuration.getLoginProcessingUrl() : DEFAULT_LOGIN_PROCESSING_URI; | |
203 | + | |
204 | + String scheme = MiscUtils.getScheme(request); | |
205 | + String domainName = MiscUtils.getDomainName(request); | |
206 | + int port = MiscUtils.getPort(request); | |
207 | + String baseUrl = scheme + "://" + domainName; | |
208 | + if (needsPort(scheme, port)){ | |
209 | + baseUrl += ":" + port; | |
210 | + } | |
211 | + return baseUrl + loginProcessingUri; | |
212 | + } | |
213 | + | |
214 | + private boolean needsPort(String scheme, int port) { | |
215 | + boolean isHttpDefault = "http".equals(scheme.toLowerCase()) && port == 80; | |
216 | + boolean isHttpsDefault = "https".equals(scheme.toLowerCase()) && port == 443; | |
217 | + return !isHttpDefault && !isHttpsDefault; | |
218 | + } | |
219 | + | |
220 | + /** | |
221 | + * Creates nonce and its hash for use in OpenID Connect 1.0 Authentication Requests. | |
222 | + * | |
223 | + * @param attributes where the {@link OidcParameterNames#NONCE} is stored for the authentication request | |
224 | + * @param additionalParameters where the {@link OidcParameterNames#NONCE} hash is added for the authentication request | |
225 | + * | |
226 | + * @since 5.2 | |
227 | + * @see <a target="_blank" href="https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest">3.1.2.1. Authentication Request</a> | |
228 | + */ | |
229 | + private void addNonceParameters(Map<String, Object> attributes, Map<String, Object> additionalParameters) { | |
230 | + try { | |
231 | + String nonce = this.secureKeyGenerator.generateKey(); | |
232 | + String nonceHash = createHash(nonce); | |
233 | + attributes.put(OidcParameterNames.NONCE, nonce); | |
234 | + additionalParameters.put(OidcParameterNames.NONCE, nonceHash); | |
235 | + } catch (NoSuchAlgorithmException e) { } | |
236 | + } | |
237 | + | |
238 | + /** | |
239 | + * Creates and adds additional PKCE parameters for use in the OAuth 2.0 Authorization and Access Token Requests | |
240 | + * | |
241 | + * @param attributes where {@link PkceParameterNames#CODE_VERIFIER} is stored for the token request | |
242 | + * @param additionalParameters where {@link PkceParameterNames#CODE_CHALLENGE} and, usually, | |
243 | + * {@link PkceParameterNames#CODE_CHALLENGE_METHOD} are added to be used in the authorization request. | |
244 | + * | |
245 | + * @since 5.2 | |
246 | + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7636#section-1.1">1.1. Protocol Flow</a> | |
247 | + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7636#section-4.1">4.1. Client Creates a Code Verifier</a> | |
248 | + * @see <a target="_blank" href="https://tools.ietf.org/html/rfc7636#section-4.2">4.2. Client Creates the Code Challenge</a> | |
249 | + */ | |
250 | + private void addPkceParameters(Map<String, Object> attributes, Map<String, Object> additionalParameters) { | |
251 | + String codeVerifier = this.secureKeyGenerator.generateKey(); | |
252 | + attributes.put(PkceParameterNames.CODE_VERIFIER, codeVerifier); | |
253 | + try { | |
254 | + String codeChallenge = createHash(codeVerifier); | |
255 | + additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, codeChallenge); | |
256 | + additionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256"); | |
257 | + } catch (NoSuchAlgorithmException e) { | |
258 | + additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, codeVerifier); | |
259 | + } | |
260 | + } | |
261 | + | |
262 | + private static String createHash(String value) throws NoSuchAlgorithmException { | |
263 | + MessageDigest md = MessageDigest.getInstance("SHA-256"); | |
264 | + byte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII)); | |
265 | + return Base64.getUrlEncoder().withoutPadding().encodeToString(digest); | |
266 | + } | |
267 | +} | ... | ... |
... | ... | @@ -32,6 +32,7 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe |
32 | 32 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; |
33 | 33 | import org.springframework.security.config.http.SessionCreationPolicy; |
34 | 34 | import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; |
35 | +import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver; | |
35 | 36 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; |
36 | 37 | import org.springframework.security.web.authentication.AuthenticationSuccessHandler; |
37 | 38 | import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; |
... | ... | @@ -175,6 +176,9 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt |
175 | 176 | web.ignoring().antMatchers("/*.js","/*.css","/*.ico","/assets/**","/static/**"); |
176 | 177 | } |
177 | 178 | |
179 | + @Autowired | |
180 | + private OAuth2AuthorizationRequestResolver oAuth2AuthorizationRequestResolver; | |
181 | + | |
178 | 182 | @Override |
179 | 183 | protected void configure(HttpSecurity http) throws Exception { |
180 | 184 | http.headers().cacheControl().and().frameOptions().disable() |
... | ... | @@ -207,8 +211,10 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt |
207 | 211 | .addFilterBefore(buildRefreshTokenProcessingFilter(), UsernamePasswordAuthenticationFilter.class) |
208 | 212 | .addFilterBefore(buildWsJwtTokenAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class) |
209 | 213 | .addFilterAfter(rateLimitProcessingFilter, UsernamePasswordAuthenticationFilter.class); |
210 | - if (oauth2Configuration != null && oauth2Configuration.isEnabled()) { | |
214 | + if (oauth2Configuration != null) { | |
211 | 215 | http.oauth2Login() |
216 | + .authorizationEndpoint().authorizationRequestResolver(oAuth2AuthorizationRequestResolver) | |
217 | + .and() | |
212 | 218 | .loginPage("/oauth2Login") |
213 | 219 | .loginProcessingUrl(oauth2Configuration.getLoginProcessingUrl()) |
214 | 220 | .successHandler(oauth2AuthenticationSuccessHandler) | ... | ... |
... | ... | @@ -38,10 +38,8 @@ import org.thingsboard.server.common.data.audit.ActionType; |
38 | 38 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
39 | 39 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
40 | 40 | import org.thingsboard.server.common.data.id.TenantId; |
41 | -import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; | |
42 | 41 | import org.thingsboard.server.common.data.security.UserCredentials; |
43 | 42 | import org.thingsboard.server.dao.audit.AuditLogService; |
44 | -import org.thingsboard.server.dao.oauth2.OAuth2Service; | |
45 | 43 | import org.thingsboard.server.queue.util.TbCoreComponent; |
46 | 44 | import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; |
47 | 45 | import org.thingsboard.server.service.security.auth.rest.RestAuthenticationDetails; |
... | ... | @@ -84,9 +82,6 @@ public class AuthController extends BaseController { |
84 | 82 | @Autowired |
85 | 83 | private AuditLogService auditLogService; |
86 | 84 | |
87 | - @Autowired | |
88 | - private OAuth2Service oauth2Service; | |
89 | - | |
90 | 85 | @PreAuthorize("isAuthenticated()") |
91 | 86 | @RequestMapping(value = "/auth/user", method = RequestMethod.GET) |
92 | 87 | public @ResponseBody User getUser() throws ThingsboardException { |
... | ... | @@ -336,14 +331,4 @@ public class AuthController extends BaseController { |
336 | 331 | throw handleException(e); |
337 | 332 | } |
338 | 333 | } |
339 | - | |
340 | - @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) | |
341 | - @ResponseBody | |
342 | - public List<OAuth2ClientInfo> getOAuth2Clients() throws ThingsboardException { | |
343 | - try { | |
344 | - return oauth2Service.getOAuth2Clients(); | |
345 | - } catch (Exception e) { | |
346 | - throw handleException(e); | |
347 | - } | |
348 | - } | |
349 | 334 | } | ... | ... |
... | ... | @@ -27,22 +27,7 @@ import org.springframework.beans.factory.annotation.Value; |
27 | 27 | import org.springframework.security.core.Authentication; |
28 | 28 | import org.springframework.security.core.context.SecurityContextHolder; |
29 | 29 | import org.springframework.web.bind.annotation.ExceptionHandler; |
30 | -import org.thingsboard.server.common.data.Customer; | |
31 | -import org.thingsboard.server.common.data.Dashboard; | |
32 | -import org.thingsboard.server.common.data.DashboardInfo; | |
33 | -import org.thingsboard.server.common.data.DataConstants; | |
34 | -import org.thingsboard.server.common.data.Device; | |
35 | -import org.thingsboard.server.common.data.DeviceInfo; | |
36 | -import org.thingsboard.server.common.data.DeviceProfile; | |
37 | -import org.thingsboard.server.common.data.EntityType; | |
38 | -import org.thingsboard.server.common.data.EntityView; | |
39 | -import org.thingsboard.server.common.data.EntityViewInfo; | |
40 | -import org.thingsboard.server.common.data.HasName; | |
41 | -import org.thingsboard.server.common.data.HasTenantId; | |
42 | -import org.thingsboard.server.common.data.Tenant; | |
43 | -import org.thingsboard.server.common.data.TenantInfo; | |
44 | -import org.thingsboard.server.common.data.TenantProfile; | |
45 | -import org.thingsboard.server.common.data.User; | |
30 | +import org.thingsboard.server.common.data.*; | |
46 | 31 | import org.thingsboard.server.common.data.alarm.Alarm; |
47 | 32 | import org.thingsboard.server.common.data.alarm.AlarmInfo; |
48 | 33 | import org.thingsboard.server.common.data.asset.Asset; |
... | ... | @@ -50,6 +35,7 @@ import org.thingsboard.server.common.data.asset.AssetInfo; |
50 | 35 | import org.thingsboard.server.common.data.audit.ActionType; |
51 | 36 | import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
52 | 37 | import org.thingsboard.server.common.data.exception.ThingsboardException; |
38 | +import org.thingsboard.server.common.data.id.*; | |
53 | 39 | import org.thingsboard.server.common.data.id.AlarmId; |
54 | 40 | import org.thingsboard.server.common.data.id.AssetId; |
55 | 41 | import org.thingsboard.server.common.data.id.CustomerId; |
... | ... | @@ -93,6 +79,8 @@ import org.thingsboard.server.dao.entityview.EntityViewService; |
93 | 79 | import org.thingsboard.server.dao.exception.DataValidationException; |
94 | 80 | import org.thingsboard.server.dao.exception.IncorrectParameterException; |
95 | 81 | import org.thingsboard.server.dao.model.ModelConstants; |
82 | +import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; | |
83 | +import org.thingsboard.server.dao.oauth2.OAuth2Service; | |
96 | 84 | import org.thingsboard.server.dao.relation.RelationService; |
97 | 85 | import org.thingsboard.server.dao.rule.RuleChainService; |
98 | 86 | import org.thingsboard.server.dao.tenant.TenantProfileService; |
... | ... | @@ -176,6 +164,12 @@ public abstract class BaseController { |
176 | 164 | protected DashboardService dashboardService; |
177 | 165 | |
178 | 166 | @Autowired |
167 | + protected OAuth2Service oAuth2Service; | |
168 | + | |
169 | + @Autowired | |
170 | + protected OAuth2ConfigTemplateService oAuth2ConfigTemplateService; | |
171 | + | |
172 | + @Autowired | |
179 | 173 | protected ComponentDiscoveryService componentDescriptorService; |
180 | 174 | |
181 | 175 | @Autowired | ... | ... |
application/src/main/java/org/thingsboard/server/controller/OAuth2ConfigTemplateController.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.controller; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.springframework.http.HttpStatus; | |
20 | +import org.springframework.security.access.prepost.PreAuthorize; | |
21 | +import org.springframework.web.bind.annotation.*; | |
22 | +import org.thingsboard.server.common.data.EntityType; | |
23 | +import org.thingsboard.server.common.data.audit.ActionType; | |
24 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
25 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; | |
26 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; | |
27 | +import org.thingsboard.server.queue.util.TbCoreComponent; | |
28 | +import org.thingsboard.server.service.security.permission.Operation; | |
29 | +import org.thingsboard.server.service.security.permission.Resource; | |
30 | + | |
31 | +import java.util.List; | |
32 | + | |
33 | +@RestController | |
34 | +@TbCoreComponent | |
35 | +@RequestMapping("/api/oauth2/config/template") | |
36 | +@Slf4j | |
37 | +public class OAuth2ConfigTemplateController extends BaseController { | |
38 | + private static final String CLIENT_REGISTRATION_TEMPLATE_ID = "clientRegistrationTemplateId"; | |
39 | + | |
40 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") | |
41 | + @RequestMapping(method = RequestMethod.POST) | |
42 | + @ResponseStatus(value = HttpStatus.OK) | |
43 | + public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(@RequestBody OAuth2ClientRegistrationTemplate clientRegistrationTemplate) throws ThingsboardException { | |
44 | + try { | |
45 | + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.WRITE); | |
46 | + return oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); | |
47 | + } catch (Exception e) { | |
48 | + throw handleException(e); | |
49 | + } | |
50 | + } | |
51 | + | |
52 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") | |
53 | + @RequestMapping(value = "/{clientRegistrationTemplateId}", method = RequestMethod.DELETE) | |
54 | + @ResponseStatus(value = HttpStatus.OK) | |
55 | + public void deleteClientRegistrationTemplate(@PathVariable(CLIENT_REGISTRATION_TEMPLATE_ID) String strClientRegistrationTemplateId) throws ThingsboardException { | |
56 | + checkParameter(CLIENT_REGISTRATION_TEMPLATE_ID, strClientRegistrationTemplateId); | |
57 | + try { | |
58 | + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.DELETE); | |
59 | + OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId = new OAuth2ClientRegistrationTemplateId(toUUID(strClientRegistrationTemplateId)); | |
60 | + oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplateId); | |
61 | + } catch (Exception e) { | |
62 | + throw handleException(e); | |
63 | + } | |
64 | + } | |
65 | + | |
66 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") | |
67 | + @RequestMapping(method = RequestMethod.GET, produces = "application/json") | |
68 | + @ResponseBody | |
69 | + public List<OAuth2ClientRegistrationTemplate> getClientRegistrationTemplates() throws ThingsboardException { | |
70 | + try { | |
71 | + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.READ); | |
72 | + return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates(); | |
73 | + } catch (Exception e) { | |
74 | + throw handleException(e); | |
75 | + } | |
76 | + } | |
77 | +} | ... | ... |
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.controller; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.springframework.http.HttpStatus; | |
20 | +import org.springframework.security.access.prepost.PreAuthorize; | |
21 | +import org.springframework.web.bind.annotation.*; | |
22 | +import org.thingsboard.server.common.data.exception.ThingsboardException; | |
23 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; | |
24 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; | |
25 | +import org.thingsboard.server.common.data.oauth2.SchemeType; | |
26 | +import org.thingsboard.server.queue.util.TbCoreComponent; | |
27 | +import org.thingsboard.server.service.security.permission.Operation; | |
28 | +import org.thingsboard.server.service.security.permission.Resource; | |
29 | +import org.thingsboard.server.utils.MiscUtils; | |
30 | + | |
31 | +import javax.servlet.http.HttpServletRequest; | |
32 | +import java.util.List; | |
33 | + | |
34 | +@RestController | |
35 | +@TbCoreComponent | |
36 | +@RequestMapping("/api") | |
37 | +@Slf4j | |
38 | +public class OAuth2Controller extends BaseController { | |
39 | + @RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) | |
40 | + @ResponseBody | |
41 | + public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { | |
42 | + try { | |
43 | + return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainName(request)); | |
44 | + } catch (Exception e) { | |
45 | + throw handleException(e); | |
46 | + } | |
47 | + } | |
48 | + | |
49 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") | |
50 | + @RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") | |
51 | + @ResponseBody | |
52 | + public OAuth2ClientsParams getCurrentOAuth2Params() throws ThingsboardException { | |
53 | + try { | |
54 | + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); | |
55 | + return oAuth2Service.findOAuth2Params(); | |
56 | + } catch (Exception e) { | |
57 | + throw handleException(e); | |
58 | + } | |
59 | + } | |
60 | + | |
61 | + @PreAuthorize("hasAnyAuthority('SYS_ADMIN')") | |
62 | + @RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) | |
63 | + @ResponseStatus(value = HttpStatus.OK) | |
64 | + public OAuth2ClientsParams saveOAuth2Params(@RequestBody OAuth2ClientsParams oauth2Params) throws ThingsboardException { | |
65 | + try { | |
66 | + accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); | |
67 | + oAuth2Service.saveOAuth2Params(oauth2Params); | |
68 | + return oAuth2Service.findOAuth2Params(); | |
69 | + } catch (Exception e) { | |
70 | + throw handleException(e); | |
71 | + } | |
72 | + } | |
73 | +} | ... | ... |
... | ... | @@ -184,6 +184,7 @@ public class ThingsboardInstallService { |
184 | 184 | dataUpdateService.updateData("3.1.1"); |
185 | 185 | log.info("Updating system data..."); |
186 | 186 | systemDataLoaderService.updateSystemWidgets(); |
187 | + systemDataLoaderService.createOAuth2Templates(); | |
187 | 188 | break; |
188 | 189 | default: |
189 | 190 | throw new RuntimeException("Unable to upgrade ThingsBoard, unsupported fromVersion: " + upgradeFromVersion); |
... | ... | @@ -216,6 +217,7 @@ public class ThingsboardInstallService { |
216 | 217 | systemDataLoaderService.createDefaultTenantProfiles(); |
217 | 218 | systemDataLoaderService.createAdminSettings(); |
218 | 219 | systemDataLoaderService.loadSystemWidgets(); |
220 | + systemDataLoaderService.createOAuth2Templates(); | |
219 | 221 | // systemDataLoaderService.loadSystemPlugins(); |
220 | 222 | // systemDataLoaderService.loadSystemRules(); |
221 | 223 | ... | ... |
application/src/main/java/org/thingsboard/server/service/install/DefaultSystemDataLoaderService.java
... | ... | @@ -194,6 +194,11 @@ public class DefaultSystemDataLoaderService implements SystemDataLoaderService { |
194 | 194 | } |
195 | 195 | |
196 | 196 | @Override |
197 | + public void createOAuth2Templates() throws Exception { | |
198 | + installScripts.createOAuth2Templates(); | |
199 | + } | |
200 | + | |
201 | + @Override | |
197 | 202 | public void loadDemoData() throws Exception { |
198 | 203 | Tenant demoTenant = new Tenant(); |
199 | 204 | demoTenant.setRegion("Global"); | ... | ... |
... | ... | @@ -26,11 +26,13 @@ import org.thingsboard.server.common.data.id.CustomerId; |
26 | 26 | import org.thingsboard.server.common.data.id.EntityId; |
27 | 27 | import org.thingsboard.server.common.data.id.RuleChainId; |
28 | 28 | import org.thingsboard.server.common.data.id.TenantId; |
29 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; | |
29 | 30 | import org.thingsboard.server.common.data.rule.RuleChain; |
30 | 31 | import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
31 | 32 | import org.thingsboard.server.common.data.widget.WidgetType; |
32 | 33 | import org.thingsboard.server.common.data.widget.WidgetsBundle; |
33 | 34 | import org.thingsboard.server.dao.dashboard.DashboardService; |
35 | +import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; | |
34 | 36 | import org.thingsboard.server.dao.rule.RuleChainService; |
35 | 37 | import org.thingsboard.server.dao.widget.WidgetTypeService; |
36 | 38 | import org.thingsboard.server.dao.widget.WidgetsBundleService; |
... | ... | @@ -61,6 +63,7 @@ public class InstallScripts { |
61 | 63 | public static final String DEMO_DIR = "demo"; |
62 | 64 | public static final String RULE_CHAINS_DIR = "rule_chains"; |
63 | 65 | public static final String WIDGET_BUNDLES_DIR = "widget_bundles"; |
66 | + public static final String OAUTH2_CONFIG_TEMPLATES_DIR = "oauth2_config_templates"; | |
64 | 67 | public static final String DASHBOARDS_DIR = "dashboards"; |
65 | 68 | |
66 | 69 | public static final String JSON_EXT = ".json"; |
... | ... | @@ -80,6 +83,9 @@ public class InstallScripts { |
80 | 83 | @Autowired |
81 | 84 | private WidgetsBundleService widgetsBundleService; |
82 | 85 | |
86 | + @Autowired | |
87 | + private OAuth2ConfigTemplateService oAuth2TemplateService; | |
88 | + | |
83 | 89 | public Path getTenantRuleChainsDir() { |
84 | 90 | return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR); |
85 | 91 | } |
... | ... | @@ -228,4 +234,22 @@ public class InstallScripts { |
228 | 234 | throw new RuntimeException("Unable to load dashboard from json", e); |
229 | 235 | } |
230 | 236 | } |
237 | + | |
238 | + public void createOAuth2Templates() throws Exception { | |
239 | + Path oauth2ConfigTemplatesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, OAUTH2_CONFIG_TEMPLATES_DIR); | |
240 | + try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(oauth2ConfigTemplatesDir, path -> path.toString().endsWith(JSON_EXT))) { | |
241 | + dirStream.forEach( | |
242 | + path -> { | |
243 | + try { | |
244 | + JsonNode oauth2ConfigTemplateJson = objectMapper.readTree(path.toFile()); | |
245 | + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = objectMapper.treeToValue(oauth2ConfigTemplateJson, OAuth2ClientRegistrationTemplate.class); | |
246 | + oAuth2TemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); | |
247 | + } catch (Exception e) { | |
248 | + log.error("Unable to load oauth2 config templates from json: [{}]", path.toString()); | |
249 | + throw new RuntimeException("Unable to load oauth2 config templates from json", e); | |
250 | + } | |
251 | + } | |
252 | + ); | |
253 | + } | |
254 | + } | |
231 | 255 | } | ... | ... |
... | ... | @@ -23,6 +23,8 @@ public interface SystemDataLoaderService { |
23 | 23 | |
24 | 24 | void createAdminSettings() throws Exception; |
25 | 25 | |
26 | + void createOAuth2Templates() throws Exception; | |
27 | + | |
26 | 28 | void loadSystemWidgets() throws Exception; |
27 | 29 | |
28 | 30 | void updateSystemWidgets() throws Exception; | ... | ... |
... | ... | @@ -17,7 +17,6 @@ package org.thingsboard.server.service.security.auth.oauth2; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.databind.ObjectMapper; |
19 | 19 | import com.fasterxml.jackson.databind.node.ObjectNode; |
20 | -import com.google.common.base.Strings; | |
21 | 20 | import lombok.extern.slf4j.Slf4j; |
22 | 21 | import org.springframework.beans.factory.annotation.Autowired; |
23 | 22 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
... | ... | @@ -34,7 +33,6 @@ import org.thingsboard.server.common.data.id.IdBased; |
34 | 33 | import org.thingsboard.server.common.data.id.TenantId; |
35 | 34 | import org.thingsboard.server.common.data.page.PageData; |
36 | 35 | import org.thingsboard.server.common.data.page.PageLink; |
37 | -import org.thingsboard.server.common.data.page.TimePageLink; | |
38 | 36 | import org.thingsboard.server.common.data.security.Authority; |
39 | 37 | import org.thingsboard.server.common.data.security.UserCredentials; |
40 | 38 | import org.thingsboard.server.dao.customer.CustomerService; |
... | ... | @@ -49,7 +47,6 @@ import org.thingsboard.server.service.security.model.UserPrincipal; |
49 | 47 | import java.io.IOException; |
50 | 48 | import java.util.List; |
51 | 49 | import java.util.Optional; |
52 | -import java.util.concurrent.ExecutionException; | |
53 | 50 | import java.util.concurrent.locks.Lock; |
54 | 51 | import java.util.concurrent.locks.ReentrantLock; |
55 | 52 | ... | ... |
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,11 +16,9 @@ |
16 | 16 | package org.thingsboard.server.service.security.auth.oauth2; |
17 | 17 | |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | -import org.apache.commons.lang3.text.StrSubstitutor; | |
20 | 19 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
21 | 20 | import org.springframework.stereotype.Service; |
22 | -import org.springframework.util.StringUtils; | |
23 | -import org.thingsboard.server.dao.oauth2.OAuth2ClientMapperConfig; | |
21 | +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | |
24 | 22 | import org.thingsboard.server.dao.oauth2.OAuth2User; |
25 | 23 | import org.thingsboard.server.service.security.model.SecurityUser; |
26 | 24 | |
... | ... | @@ -30,62 +28,12 @@ import java.util.Map; |
30 | 28 | @Slf4j |
31 | 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 | - 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"; | |
38 | - | |
39 | 31 | @Override |
40 | - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig config) { | |
41 | - OAuth2User oauth2User = new OAuth2User(); | |
32 | + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) { | |
42 | 33 | Map<String, Object> attributes = token.getPrincipal().getAttributes(); |
43 | - String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); | |
44 | - oauth2User.setEmail(email); | |
45 | - oauth2User.setTenantName(getTenantName(attributes, config)); | |
46 | - if (!StringUtils.isEmpty(config.getBasic().getLastNameAttributeKey())) { | |
47 | - String lastName = getStringAttributeByKey(attributes, config.getBasic().getLastNameAttributeKey()); | |
48 | - oauth2User.setLastName(lastName); | |
49 | - } | |
50 | - if (!StringUtils.isEmpty(config.getBasic().getFirstNameAttributeKey())) { | |
51 | - String firstName = getStringAttributeByKey(attributes, config.getBasic().getFirstNameAttributeKey()); | |
52 | - oauth2User.setFirstName(firstName); | |
53 | - } | |
54 | - if (!StringUtils.isEmpty(config.getBasic().getCustomerNamePattern())) { | |
55 | - StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); | |
56 | - String customerName = sub.replace(config.getBasic().getCustomerNamePattern()); | |
57 | - oauth2User.setCustomerName(customerName); | |
58 | - } | |
59 | - oauth2User.setAlwaysFullScreen(config.getBasic().isAlwaysFullScreen()); | |
60 | - if (!StringUtils.isEmpty(config.getBasic().getDefaultDashboardName())) { | |
61 | - oauth2User.setDefaultDashboardName(config.getBasic().getDefaultDashboardName()); | |
62 | - } | |
34 | + String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); | |
35 | + OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); | |
63 | 36 | |
64 | 37 | return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser()); |
65 | 38 | } |
66 | - | |
67 | - private String getTenantName(Map<String, Object> attributes, OAuth2ClientMapperConfig config) { | |
68 | - switch (config.getBasic().getTenantNameStrategy()) { | |
69 | - case EMAIL_TENANT_STRATEGY: | |
70 | - return getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); | |
71 | - case DOMAIN_TENANT_STRATEGY: | |
72 | - String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); | |
73 | - return email.substring(email .indexOf("@") + 1); | |
74 | - case CUSTOM_TENANT_STRATEGY: | |
75 | - StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); | |
76 | - return sub.replace(config.getBasic().getTenantNamePattern()); | |
77 | - default: | |
78 | - throw new RuntimeException("Tenant Name Strategy with type " + config.getBasic().getTenantNameStrategy() + " is not supported!"); | |
79 | - } | |
80 | - } | |
81 | - | |
82 | - private String getStringAttributeByKey(Map<String, Object> attributes, String key) { | |
83 | - String result = null; | |
84 | - try { | |
85 | - result = (String) attributes.get(key); | |
86 | - } catch (Exception e) { | |
87 | - log.warn("Can't convert attribute to String by key " + key); | |
88 | - } | |
89 | - return result; | |
90 | - } | |
91 | 39 | } | ... | ... |
... | ... | @@ -23,28 +23,34 @@ import org.springframework.security.oauth2.client.authentication.OAuth2Authentic |
23 | 23 | import org.springframework.stereotype.Service; |
24 | 24 | import org.springframework.util.StringUtils; |
25 | 25 | import org.springframework.web.client.RestTemplate; |
26 | -import org.thingsboard.server.dao.oauth2.OAuth2ClientMapperConfig; | |
26 | +import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; | |
27 | +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | |
27 | 28 | import org.thingsboard.server.dao.oauth2.OAuth2User; |
28 | 29 | import org.thingsboard.server.service.security.model.SecurityUser; |
29 | 30 | |
30 | 31 | @Service(value = "customOAuth2ClientMapper") |
31 | 32 | @Slf4j |
32 | 33 | public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { |
34 | + private static final String PROVIDER_ACCESS_TOKEN = "provider-access-token"; | |
33 | 35 | |
34 | 36 | private static final ObjectMapper json = new ObjectMapper(); |
35 | 37 | |
36 | 38 | private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); |
37 | 39 | |
38 | 40 | @Override |
39 | - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig config) { | |
40 | - OAuth2User oauth2User = getOAuth2User(token, config.getCustom()); | |
41 | + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) { | |
42 | + OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom()); | |
41 | 43 | return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser()); |
42 | 44 | } |
43 | 45 | |
44 | - private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig.CustomOAuth2ClientMapperConfig custom) { | |
46 | + private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2CustomMapperConfig custom) { | |
45 | 47 | if (!StringUtils.isEmpty(custom.getUsername()) && !StringUtils.isEmpty(custom.getPassword())) { |
46 | 48 | restTemplateBuilder = restTemplateBuilder.basicAuthentication(custom.getUsername(), custom.getPassword()); |
47 | 49 | } |
50 | + if (custom.isSendToken() && !StringUtils.isEmpty(providerAccessToken)) { | |
51 | + restTemplateBuilder = restTemplateBuilder.defaultHeader(PROVIDER_ACCESS_TOKEN, providerAccessToken); | |
52 | + } | |
53 | + | |
48 | 54 | RestTemplate restTemplate = restTemplateBuilder.build(); |
49 | 55 | String request; |
50 | 56 | try { | ... | ... |
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 | +} | ... | ... |
... | ... | @@ -16,9 +16,9 @@ |
16 | 16 | package org.thingsboard.server.service.security.auth.oauth2; |
17 | 17 | |
18 | 18 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
19 | -import org.thingsboard.server.dao.oauth2.OAuth2ClientMapperConfig; | |
19 | +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | |
20 | 20 | import org.thingsboard.server.service.security.model.SecurityUser; |
21 | 21 | |
22 | 22 | public interface OAuth2ClientMapper { |
23 | - SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig config); | |
23 | + SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config); | |
24 | 24 | } | ... | ... |
... | ... | @@ -19,6 +19,7 @@ import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.springframework.beans.factory.annotation.Autowired; |
20 | 20 | import org.springframework.beans.factory.annotation.Qualifier; |
21 | 21 | import org.springframework.stereotype.Component; |
22 | +import org.thingsboard.server.common.data.oauth2.MapperType; | |
22 | 23 | |
23 | 24 | @Component |
24 | 25 | @Slf4j |
... | ... | @@ -32,14 +33,20 @@ public class OAuth2ClientMapperProvider { |
32 | 33 | @Qualifier("customOAuth2ClientMapper") |
33 | 34 | private OAuth2ClientMapper customOAuth2ClientMapper; |
34 | 35 | |
35 | - public OAuth2ClientMapper getOAuth2ClientMapperByType(String oauth2ClientType) { | |
36 | - switch (oauth2ClientType) { | |
37 | - case "custom": | |
36 | + @Autowired | |
37 | + @Qualifier("githubOAuth2ClientMapper") | |
38 | + private OAuth2ClientMapper githubOAuth2ClientMapper; | |
39 | + | |
40 | + public OAuth2ClientMapper getOAuth2ClientMapperByType(MapperType oauth2MapperType) { | |
41 | + switch (oauth2MapperType) { | |
42 | + case CUSTOM: | |
38 | 43 | return customOAuth2ClientMapper; |
39 | - case "basic": | |
44 | + case BASIC: | |
40 | 45 | return basicOAuth2ClientMapper; |
46 | + case GITHUB: | |
47 | + return githubOAuth2ClientMapper; | |
41 | 48 | default: |
42 | - throw new RuntimeException("OAuth2ClientMapper with type " + oauth2ClientType + " is not supported!"); | |
49 | + throw new RuntimeException("OAuth2ClientRegistrationMapper with type " + oauth2MapperType + " is not supported!"); | |
43 | 50 | } |
44 | 51 | } |
45 | 52 | } | ... | ... |
... | ... | @@ -16,13 +16,14 @@ |
16 | 16 | package org.thingsboard.server.service.security.auth.oauth2; |
17 | 17 | |
18 | 18 | import org.springframework.beans.factory.annotation.Autowired; |
19 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
20 | 19 | import org.springframework.security.core.Authentication; |
20 | +import org.springframework.security.oauth2.client.OAuth2AuthorizedClient; | |
21 | +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; | |
21 | 22 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
22 | 23 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; |
23 | 24 | import org.springframework.stereotype.Component; |
24 | -import org.thingsboard.server.dao.oauth2.OAuth2Client; | |
25 | -import org.thingsboard.server.dao.oauth2.OAuth2Configuration; | |
25 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
26 | +import org.thingsboard.server.dao.oauth2.OAuth2Service; | |
26 | 27 | import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; |
27 | 28 | import org.thingsboard.server.service.security.model.SecurityUser; |
28 | 29 | import org.thingsboard.server.service.security.model.token.JwtToken; |
... | ... | @@ -34,25 +35,28 @@ import javax.servlet.http.HttpServletResponse; |
34 | 35 | import java.io.IOException; |
35 | 36 | import java.net.URLEncoder; |
36 | 37 | import java.nio.charset.StandardCharsets; |
38 | +import java.util.UUID; | |
37 | 39 | |
38 | 40 | @Component(value = "oauth2AuthenticationSuccessHandler") |
39 | -@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") | |
40 | 41 | public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { |
41 | 42 | |
42 | 43 | private final JwtTokenFactory tokenFactory; |
43 | 44 | private final RefreshTokenRepository refreshTokenRepository; |
44 | 45 | private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; |
45 | - private final OAuth2Configuration oauth2Configuration; | |
46 | + private final OAuth2Service oAuth2Service; | |
47 | + private final OAuth2AuthorizedClientService oAuth2AuthorizedClientService; | |
46 | 48 | |
47 | 49 | @Autowired |
48 | 50 | public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory, |
49 | 51 | final RefreshTokenRepository refreshTokenRepository, |
50 | 52 | final OAuth2ClientMapperProvider oauth2ClientMapperProvider, |
51 | - final OAuth2Configuration oauth2Configuration) { | |
53 | + final OAuth2Service oAuth2Service, | |
54 | + final OAuth2AuthorizedClientService oAuth2AuthorizedClientService) { | |
52 | 55 | this.tokenFactory = tokenFactory; |
53 | 56 | this.refreshTokenRepository = refreshTokenRepository; |
54 | 57 | this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; |
55 | - this.oauth2Configuration = oauth2Configuration; | |
58 | + this.oAuth2Service = oAuth2Service; | |
59 | + this.oAuth2AuthorizedClientService = oAuth2AuthorizedClientService; | |
56 | 60 | } |
57 | 61 | |
58 | 62 | @Override |
... | ... | @@ -64,9 +68,13 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS |
64 | 68 | try { |
65 | 69 | OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; |
66 | 70 | |
67 | - OAuth2Client oauth2Client = oauth2Configuration.getClientByRegistrationId(token.getAuthorizedClientRegistrationId()); | |
68 | - OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(oauth2Client.getMapperConfig().getType()); | |
69 | - SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oauth2Client.getMapperConfig()); | |
71 | + OAuth2ClientRegistrationInfo clientRegistration = oAuth2Service.findClientRegistrationInfo(UUID.fromString(token.getAuthorizedClientRegistrationId())); | |
72 | + OAuth2AuthorizedClient oAuth2AuthorizedClient = oAuth2AuthorizedClientService.loadAuthorizedClient( | |
73 | + token.getAuthorizedClientRegistrationId(), | |
74 | + token.getPrincipal().getName()); | |
75 | + OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); | |
76 | + SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(), | |
77 | + clientRegistration.getMapperConfig()); | |
70 | 78 | |
71 | 79 | JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); |
72 | 80 | JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); | ... | ... |
... | ... | @@ -32,6 +32,8 @@ public enum Resource { |
32 | 32 | USER(EntityType.USER), |
33 | 33 | WIDGETS_BUNDLE(EntityType.WIDGETS_BUNDLE), |
34 | 34 | WIDGET_TYPE(EntityType.WIDGET_TYPE), |
35 | + OAUTH2_CONFIGURATION_INFO(), | |
36 | + OAUTH2_CONFIGURATION_TEMPLATE(), | |
35 | 37 | TENANT_PROFILE(EntityType.TENANT_PROFILE), |
36 | 38 | DEVICE_PROFILE(EntityType.DEVICE_PROFILE); |
37 | 39 | ... | ... |
... | ... | @@ -19,14 +19,10 @@ import org.springframework.stereotype.Component; |
19 | 19 | import org.thingsboard.server.common.data.HasTenantId; |
20 | 20 | import org.thingsboard.server.common.data.User; |
21 | 21 | import org.thingsboard.server.common.data.id.EntityId; |
22 | -import org.thingsboard.server.common.data.id.TenantId; | |
23 | 22 | import org.thingsboard.server.common.data.id.UserId; |
24 | 23 | import org.thingsboard.server.common.data.security.Authority; |
25 | 24 | import org.thingsboard.server.service.security.model.SecurityUser; |
26 | 25 | |
27 | -import java.util.HashMap; | |
28 | -import java.util.Optional; | |
29 | - | |
30 | 26 | @Component(value="sysAdminPermissions") |
31 | 27 | public class SysAdminPermissions extends AbstractPermissions { |
32 | 28 | |
... | ... | @@ -39,6 +35,8 @@ public class SysAdminPermissions extends AbstractPermissions { |
39 | 35 | put(Resource.USER, userPermissionChecker); |
40 | 36 | put(Resource.WIDGETS_BUNDLE, systemEntityPermissionChecker); |
41 | 37 | put(Resource.WIDGET_TYPE, systemEntityPermissionChecker); |
38 | + put(Resource.OAUTH2_CONFIGURATION_INFO, PermissionChecker.allowAllPermissionChecker); | |
39 | + put(Resource.OAUTH2_CONFIGURATION_TEMPLATE, PermissionChecker.allowAllPermissionChecker); | |
42 | 40 | put(Resource.TENANT_PROFILE, PermissionChecker.allowAllPermissionChecker); |
43 | 41 | } |
44 | 42 | ... | ... |
... | ... | @@ -19,13 +19,10 @@ import org.springframework.stereotype.Component; |
19 | 19 | import org.thingsboard.server.common.data.HasTenantId; |
20 | 20 | import org.thingsboard.server.common.data.User; |
21 | 21 | import org.thingsboard.server.common.data.id.EntityId; |
22 | -import org.thingsboard.server.common.data.id.TenantId; | |
23 | 22 | import org.thingsboard.server.common.data.id.UserId; |
24 | 23 | import org.thingsboard.server.common.data.security.Authority; |
25 | 24 | import org.thingsboard.server.service.security.model.SecurityUser; |
26 | 25 | |
27 | -import java.util.HashMap; | |
28 | - | |
29 | 26 | @Component(value="tenantAdminPermissions") |
30 | 27 | public class TenantAdminPermissions extends AbstractPermissions { |
31 | 28 | ... | ... |
... | ... | @@ -49,12 +49,27 @@ public class MiscUtils { |
49 | 49 | } |
50 | 50 | |
51 | 51 | public static String constructBaseUrl(HttpServletRequest request) { |
52 | - String scheme = request.getScheme(); | |
52 | + return String.format("%s://%s:%d", | |
53 | + getScheme(request), | |
54 | + getDomainName(request), | |
55 | + getPort(request)); | |
56 | + } | |
53 | 57 | |
58 | + public static String getScheme(HttpServletRequest request){ | |
59 | + String scheme = request.getScheme(); | |
54 | 60 | String forwardedProto = request.getHeader("x-forwarded-proto"); |
55 | 61 | if (forwardedProto != null) { |
56 | 62 | scheme = forwardedProto; |
57 | 63 | } |
64 | + return scheme; | |
65 | + } | |
66 | + | |
67 | + public static String getDomainName(HttpServletRequest request){ | |
68 | + return request.getServerName(); | |
69 | + } | |
70 | + | |
71 | + public static int getPort(HttpServletRequest request){ | |
72 | + String forwardedProto = request.getHeader("x-forwarded-proto"); | |
58 | 73 | |
59 | 74 | int serverPort = request.getServerPort(); |
60 | 75 | if (request.getHeader("x-forwarded-port") != null) { |
... | ... | @@ -72,11 +87,6 @@ public class MiscUtils { |
72 | 87 | break; |
73 | 88 | } |
74 | 89 | } |
75 | - | |
76 | - String baseUrl = String.format("%s://%s:%d", | |
77 | - scheme, | |
78 | - request.getServerName(), | |
79 | - serverPort); | |
80 | - return baseUrl; | |
90 | + return serverPort; | |
81 | 91 | } |
82 | 92 | } | ... | ... |
... | ... | @@ -113,62 +113,10 @@ security: |
113 | 113 | basic: |
114 | 114 | enabled: "${SECURITY_BASIC_ENABLED:false}" |
115 | 115 | oauth2: |
116 | - # Enable/disable OAuth 2 login functionality | |
117 | - # For details please refer to https://thingsboard.io/docs/user-guide/oauth-2-support/ | |
118 | - enabled: "${SECURITY_OAUTH2_ENABLED:false}" | |
119 | 116 | # Redirect URL where access code from external user management system will be processed |
120 | 117 | loginProcessingUrl: "${SECURITY_OAUTH2_LOGIN_PROCESSING_URL:/login/oauth2/code/}" |
121 | - # List of SSO clients | |
122 | - clients: | |
123 | - default: | |
124 | - # Label that going to be show on login button - 'Login with {loginButtonLabel}' | |
125 | - loginButtonLabel: "${SECURITY_OAUTH2_DEFAULT_LOGIN_BUTTON_LABEL:Default}" | |
126 | - # Icon that going to be show on login button. Material design icon ID (https://material.angularjs.org/latest/api/directive/mdIcon) | |
127 | - loginButtonIcon: "${SECURITY_OAUTH2_DEFAULT_LOGIN_BUTTON_ICON:}" | |
128 | - clientName: "${SECURITY_OAUTH2_DEFAULT_CLIENT_NAME:ClientName}" | |
129 | - clientId: "${SECURITY_OAUTH2_DEFAULT_CLIENT_ID:}" | |
130 | - clientSecret: "${SECURITY_OAUTH2_DEFAULT_CLIENT_SECRET:}" | |
131 | - accessTokenUri: "${SECURITY_OAUTH2_DEFAULT_ACCESS_TOKEN_URI:}" | |
132 | - authorizationUri: "${SECURITY_OAUTH2_DEFAULT_AUTHORIZATION_URI:}" | |
133 | - scope: "${SECURITY_OAUTH2_DEFAULT_SCOPE:}" | |
134 | - # Redirect URL that must be in sync with 'security.oauth2.loginProcessingUrl', but domain name added | |
135 | - redirectUriTemplate: "${SECURITY_OAUTH2_DEFAULT_REDIRECT_URI_TEMPLATE:http://localhost:8080/login/oauth2/code/}" | |
136 | - jwkSetUri: "${SECURITY_OAUTH2_DEFAULT_JWK_SET_URI:}" | |
137 | - # 'authorization_code', 'implicit', 'refresh_token' or 'client_credentials' | |
138 | - authorizationGrantType: "${SECURITY_OAUTH2_DEFAULT_AUTHORIZATION_GRANT_TYPE:authorization_code}" | |
139 | - clientAuthenticationMethod: "${SECURITY_OAUTH2_DEFAULT_CLIENT_AUTHENTICATION_METHOD:post}" # basic or post | |
140 | - userInfoUri: "${SECURITY_OAUTH2_DEFAULT_USER_INFO_URI:}" | |
141 | - userNameAttributeName: "${SECURITY_OAUTH2_DEFAULT_USER_NAME_ATTRIBUTE_NAME:email}" | |
142 | - mapperConfig: | |
143 | - # Allows to create user if it not exists | |
144 | - allowUserCreation: "${SECURITY_OAUTH2_DEFAULT_MAPPER_ALLOW_USER_CREATION:true}" | |
145 | - # Allows user to setup ThingsBoard internal password and login over default Login window | |
146 | - activateUser: "${SECURITY_OAUTH2_DEFAULT_MAPPER_ACTIVATE_USER:false}" | |
147 | - # Mapper type of converter from external user into internal - 'basic' or 'custom' | |
148 | - type: "${SECURITY_OAUTH2_DEFAULT_MAPPER_TYPE:basic}" | |
149 | - basic: | |
150 | - # Key from attributes of external user object to use as email | |
151 | - emailAttributeKey: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_EMAIL_ATTRIBUTE_KEY:email}" | |
152 | - firstNameAttributeKey: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_FIRST_NAME_ATTRIBUTE_KEY:}" | |
153 | - lastNameAttributeKey: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_LAST_NAME_ATTRIBUTE_KEY:}" | |
154 | - # Strategy for generating Tenant from external user object - 'domain', 'email' or 'custom' | |
155 | - # 'domain' - name of the Tenant will be extracted as domain from the email of the user | |
156 | - # 'email' - name of the Tenant will email of the user | |
157 | - # 'custom' - please configure 'tenantNamePattern' for custom mapping | |
158 | - tenantNameStrategy: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_TENANT_NAME_STRATEGY:domain}" | |
159 | - # %{attribute_key} as placeholder for attribute value of attributes of external user object | |
160 | - tenantNamePattern: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_TENANT_NAME_PATTERN:}" | |
161 | - # If this field is not empty, user will be created as a user under defined Customer | |
162 | - # %{attribute_key} as placeholder for attribute value of attributes of external user object | |
163 | - customerNamePattern: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_CUSTOMER_NAME_PATTERN:}" | |
164 | - # If this field is not empty, user will be created with default defined Dashboard | |
165 | - defaultDashboardName: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_DEFAULT_DASHBOARD_NAME:}" | |
166 | - # If this field is set 'true' along with non-empty 'defaultDashboardName', user will start from the defined Dashboard in fullscreen mode | |
167 | - alwaysFullScreen: "${SECURITY_OAUTH2_DEFAULT_MAPPER_BASIC_ALWAYS_FULL_SCREEN:false}" | |
168 | - custom: | |
169 | - url: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_URL:}" | |
170 | - username: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_USERNAME:}" | |
171 | - password: "${SECURITY_OAUTH2_DEFAULT_MAPPER_CUSTOM_PASSWORD:}" | |
118 | + githubMapper: | |
119 | + emailUrl: "${SECURITY_OAUTH2_GITHUB_MAPPER_EMAIL_URL_KEY:https://api.github.com/user/emails}" | |
172 | 120 | |
173 | 121 | # Dashboard parameters |
174 | 122 | dashboard: | ... | ... |
common/dao-api/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ConfigTemplateService.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.dao.oauth2; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; | |
19 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; | |
20 | + | |
21 | +import java.util.List; | |
22 | + | |
23 | +public interface OAuth2ConfigTemplateService { | |
24 | + OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate); | |
25 | + | |
26 | + OAuth2ClientRegistrationTemplate findClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId); | |
27 | + | |
28 | + List<OAuth2ClientRegistrationTemplate> findAllClientRegistrationTemplates(); | |
29 | + | |
30 | + void deleteClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId); | |
31 | +} | ... | ... |
... | ... | @@ -16,10 +16,20 @@ |
16 | 16 | package org.thingsboard.server.dao.oauth2; |
17 | 17 | |
18 | 18 | import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; |
19 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
20 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; | |
19 | 21 | |
20 | 22 | import java.util.List; |
23 | +import java.util.UUID; | |
21 | 24 | |
22 | 25 | public interface OAuth2Service { |
26 | + List<OAuth2ClientInfo> getOAuth2Clients(String domainScheme, String domainName); | |
23 | 27 | |
24 | - List<OAuth2ClientInfo> getOAuth2Clients(); | |
28 | + void saveOAuth2Params(OAuth2ClientsParams oauth2Params); | |
29 | + | |
30 | + OAuth2ClientsParams findOAuth2Params(); | |
31 | + | |
32 | + OAuth2ClientRegistrationInfo findClientRegistrationInfo(UUID id); | |
33 | + | |
34 | + List<OAuth2ClientRegistrationInfo> findAllClientRegistrationInfos(); | |
25 | 35 | } | ... | ... |
... | ... | @@ -24,7 +24,7 @@ public interface AdminSettingsService { |
24 | 24 | AdminSettings findAdminSettingsById(TenantId tenantId, AdminSettingsId adminSettingsId); |
25 | 25 | |
26 | 26 | AdminSettings findAdminSettingsByKey(TenantId tenantId, String key); |
27 | - | |
27 | + | |
28 | 28 | AdminSettings saveAdminSettings(TenantId tenantId, AdminSettings adminSettings); |
29 | - | |
29 | + | |
30 | 30 | } | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationId.java
renamed from
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2IntegrationId.java
... | ... | @@ -20,16 +20,14 @@ import com.fasterxml.jackson.annotation.JsonProperty; |
20 | 20 | |
21 | 21 | import java.util.UUID; |
22 | 22 | |
23 | -public class OAuth2IntegrationId extends UUIDBased { | |
24 | - | |
25 | - private static final long serialVersionUID = 1L; | |
23 | +public class OAuth2ClientRegistrationId extends UUIDBased { | |
26 | 24 | |
27 | 25 | @JsonCreator |
28 | - public OAuth2IntegrationId(@JsonProperty("id") UUID id) { | |
26 | + public OAuth2ClientRegistrationId(@JsonProperty("id") UUID id) { | |
29 | 27 | super(id); |
30 | 28 | } |
31 | 29 | |
32 | - public static OAuth2IntegrationId fromString(String oauth2IntegrationId) { | |
33 | - return new OAuth2IntegrationId(UUID.fromString(oauth2IntegrationId)); | |
30 | + public static OAuth2ClientRegistrationId fromString(String clientRegistrationId) { | |
31 | + return new OAuth2ClientRegistrationId(UUID.fromString(clientRegistrationId)); | |
34 | 32 | } |
35 | 33 | } | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/id/OAuth2ClientRegistrationInfoId.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.common.data.id; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | |
19 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
20 | + | |
21 | +import java.util.UUID; | |
22 | + | |
23 | +public class OAuth2ClientRegistrationInfoId extends UUIDBased { | |
24 | + | |
25 | + @JsonCreator | |
26 | + public OAuth2ClientRegistrationInfoId(@JsonProperty("id") UUID id) { | |
27 | + super(id); | |
28 | + } | |
29 | + | |
30 | + public static OAuth2ClientRegistrationInfoId fromString(String clientRegistrationInfoId) { | |
31 | + return new OAuth2ClientRegistrationInfoId(UUID.fromString(clientRegistrationInfoId)); | |
32 | + } | |
33 | +} | ... | ... |
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.common.data.id; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonCreator; | |
19 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
20 | + | |
21 | +import java.util.UUID; | |
22 | + | |
23 | +public class OAuth2ClientRegistrationTemplateId extends UUIDBased { | |
24 | + | |
25 | + @JsonCreator | |
26 | + public OAuth2ClientRegistrationTemplateId(@JsonProperty("id") UUID id) { | |
27 | + super(id); | |
28 | + } | |
29 | + | |
30 | + public static OAuth2ClientRegistrationTemplateId fromString(String clientRegistrationTemplateId) { | |
31 | + return new OAuth2ClientRegistrationTemplateId(UUID.fromString(clientRegistrationTemplateId)); | |
32 | + } | |
33 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/ClientRegistrationDto.java
renamed from
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2Client.java
... | ... | @@ -13,27 +13,32 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.dao.oauth2; | |
16 | +package org.thingsboard.server.common.data.oauth2; | |
17 | 17 | |
18 | -import lombok.Data; | |
18 | +import com.fasterxml.jackson.databind.JsonNode; | |
19 | +import lombok.*; | |
20 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; | |
19 | 21 | |
20 | -@Data | |
21 | -public class OAuth2Client { | |
22 | +import java.util.List; | |
22 | 23 | |
23 | - private String loginButtonLabel; | |
24 | - private String loginButtonIcon; | |
25 | - private String clientName; | |
24 | +@EqualsAndHashCode | |
25 | +@Data | |
26 | +@ToString(exclude = {"clientSecret"}) | |
27 | +@NoArgsConstructor | |
28 | +@AllArgsConstructor | |
29 | +@Builder | |
30 | +public class ClientRegistrationDto { | |
31 | + private OAuth2MapperConfig mapperConfig; | |
26 | 32 | private String clientId; |
27 | 33 | private String clientSecret; |
28 | - private String accessTokenUri; | |
29 | 34 | private String authorizationUri; |
30 | - private String scope; | |
31 | - private String redirectUriTemplate; | |
32 | - private String jwkSetUri; | |
33 | - private String authorizationGrantType; | |
34 | - private String clientAuthenticationMethod; | |
35 | + private String accessTokenUri; | |
36 | + private List<String> scope; | |
35 | 37 | private String userInfoUri; |
36 | 38 | private String userNameAttributeName; |
37 | - private OAuth2ClientMapperConfig mapperConfig; | |
38 | - | |
39 | + private String jwkSetUri; | |
40 | + private String clientAuthenticationMethod; | |
41 | + private String loginButtonLabel; | |
42 | + private String loginButtonIcon; | |
43 | + private JsonNode additionalInfo; | |
39 | 44 | } | ... | ... |
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.common.data.oauth2; | |
17 | + | |
18 | +import lombok.*; | |
19 | + | |
20 | +@EqualsAndHashCode | |
21 | +@Data | |
22 | +@ToString | |
23 | +@NoArgsConstructor | |
24 | +@AllArgsConstructor | |
25 | +@Builder | |
26 | +public class DomainInfo { | |
27 | + private SchemeType scheme; | |
28 | + private String name; | |
29 | +} | ... | ... |
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.common.data.oauth2; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | + | |
21 | +@EqualsAndHashCode(callSuper = true) | |
22 | +@Data | |
23 | +public class ExtendedOAuth2ClientRegistrationInfo extends OAuth2ClientRegistrationInfo { | |
24 | + | |
25 | + private String domainName; | |
26 | + private SchemeType domainScheme; | |
27 | + | |
28 | + public ExtendedOAuth2ClientRegistrationInfo() { | |
29 | + super(); | |
30 | + } | |
31 | + | |
32 | + public ExtendedOAuth2ClientRegistrationInfo(OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo, | |
33 | + SchemeType domainScheme, | |
34 | + String domainName) { | |
35 | + super(oAuth2ClientRegistrationInfo); | |
36 | + this.domainScheme = domainScheme; | |
37 | + this.domainName = domainName; | |
38 | + } | |
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.common.data.oauth2; | |
17 | + | |
18 | +public enum MapperType { | |
19 | + BASIC, CUSTOM, GITHUB; | |
20 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2BasicMapperConfig.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.common.data.oauth2; | |
17 | + | |
18 | +import lombok.*; | |
19 | + | |
20 | +@Builder(toBuilder = true) | |
21 | +@EqualsAndHashCode | |
22 | +@Data | |
23 | +@ToString | |
24 | +public class OAuth2BasicMapperConfig { | |
25 | + private final String emailAttributeKey; | |
26 | + private final String firstNameAttributeKey; | |
27 | + private final String lastNameAttributeKey; | |
28 | + private final TenantNameStrategyType tenantNameStrategy; | |
29 | + private final String tenantNamePattern; | |
30 | + private final String customerNamePattern; | |
31 | + private final String defaultDashboardName; | |
32 | + private final boolean alwaysFullScreen; | |
33 | +} | ... | ... |
... | ... | @@ -17,27 +17,20 @@ package org.thingsboard.server.common.data.oauth2; |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | 19 | import lombok.EqualsAndHashCode; |
20 | -import org.thingsboard.server.common.data.BaseData; | |
21 | -import org.thingsboard.server.common.data.id.OAuth2IntegrationId; | |
20 | +import lombok.NoArgsConstructor; | |
21 | +import lombok.AllArgsConstructor; | |
22 | 22 | |
23 | -@EqualsAndHashCode(callSuper = true) | |
23 | +@EqualsAndHashCode | |
24 | 24 | @Data |
25 | -public class OAuth2ClientInfo extends BaseData<OAuth2IntegrationId> { | |
25 | +@NoArgsConstructor | |
26 | +@AllArgsConstructor | |
27 | +public class OAuth2ClientInfo { | |
26 | 28 | |
27 | 29 | private String name; |
28 | 30 | private String icon; |
29 | 31 | private String url; |
30 | 32 | |
31 | - public OAuth2ClientInfo() { | |
32 | - super(); | |
33 | - } | |
34 | - | |
35 | - public OAuth2ClientInfo(OAuth2IntegrationId id) { | |
36 | - super(id); | |
37 | - } | |
38 | - | |
39 | 33 | public OAuth2ClientInfo(OAuth2ClientInfo oauth2ClientInfo) { |
40 | - super(oauth2ClientInfo); | |
41 | 34 | this.name = oauth2ClientInfo.getName(); |
42 | 35 | this.icon = oauth2ClientInfo.getIcon(); |
43 | 36 | this.url = oauth2ClientInfo.getUrl(); | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientRegistration.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.common.data.oauth2; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import lombok.NoArgsConstructor; | |
21 | +import lombok.ToString; | |
22 | +import org.thingsboard.server.common.data.BaseData; | |
23 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; | |
24 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; | |
25 | + | |
26 | +@EqualsAndHashCode(callSuper = true) | |
27 | +@Data | |
28 | +@ToString | |
29 | +@NoArgsConstructor | |
30 | +public class OAuth2ClientRegistration extends BaseData<OAuth2ClientRegistrationId> { | |
31 | + | |
32 | + private OAuth2ClientRegistrationInfoId clientRegistrationId; | |
33 | + private String domainName; | |
34 | + private SchemeType domainScheme; | |
35 | + | |
36 | + public OAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration) { | |
37 | + super(clientRegistration); | |
38 | + this.clientRegistrationId = clientRegistration.clientRegistrationId; | |
39 | + this.domainName = clientRegistration.domainName; | |
40 | + this.domainScheme = clientRegistration.domainScheme; | |
41 | + } | |
42 | +} | ... | ... |
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.common.data.oauth2; | |
17 | + | |
18 | +import com.fasterxml.jackson.annotation.JsonProperty; | |
19 | +import lombok.Data; | |
20 | +import lombok.EqualsAndHashCode; | |
21 | +import lombok.NoArgsConstructor; | |
22 | +import lombok.ToString; | |
23 | +import org.thingsboard.server.common.data.HasName; | |
24 | +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; | |
25 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; | |
26 | + | |
27 | +import java.util.List; | |
28 | + | |
29 | +@EqualsAndHashCode(callSuper = true) | |
30 | +@Data | |
31 | +@ToString(exclude = {"clientSecret"}) | |
32 | +@NoArgsConstructor | |
33 | +public class OAuth2ClientRegistrationInfo extends SearchTextBasedWithAdditionalInfo<OAuth2ClientRegistrationInfoId> implements HasName { | |
34 | + | |
35 | + private boolean enabled; | |
36 | + private OAuth2MapperConfig mapperConfig; | |
37 | + private String clientId; | |
38 | + private String clientSecret; | |
39 | + private String authorizationUri; | |
40 | + private String accessTokenUri; | |
41 | + private List<String> scope; | |
42 | + private String userInfoUri; | |
43 | + private String userNameAttributeName; | |
44 | + private String jwkSetUri; | |
45 | + private String clientAuthenticationMethod; | |
46 | + private String loginButtonLabel; | |
47 | + private String loginButtonIcon; | |
48 | + | |
49 | + public OAuth2ClientRegistrationInfo(OAuth2ClientRegistrationInfo clientRegistration) { | |
50 | + super(clientRegistration); | |
51 | + this.enabled = clientRegistration.enabled; | |
52 | + this.mapperConfig = clientRegistration.mapperConfig; | |
53 | + this.clientId = clientRegistration.clientId; | |
54 | + this.clientSecret = clientRegistration.clientSecret; | |
55 | + this.authorizationUri = clientRegistration.authorizationUri; | |
56 | + this.accessTokenUri = clientRegistration.accessTokenUri; | |
57 | + this.scope = clientRegistration.scope; | |
58 | + this.userInfoUri = clientRegistration.userInfoUri; | |
59 | + this.userNameAttributeName = clientRegistration.userNameAttributeName; | |
60 | + this.jwkSetUri = clientRegistration.jwkSetUri; | |
61 | + this.clientAuthenticationMethod = clientRegistration.clientAuthenticationMethod; | |
62 | + this.loginButtonLabel = clientRegistration.loginButtonLabel; | |
63 | + this.loginButtonIcon = clientRegistration.loginButtonIcon; | |
64 | + } | |
65 | + | |
66 | + @Override | |
67 | + @JsonProperty(access = JsonProperty.Access.READ_ONLY) | |
68 | + public String getName() { | |
69 | + return loginButtonLabel; | |
70 | + } | |
71 | + | |
72 | + @Override | |
73 | + public String getSearchText() { | |
74 | + return getName(); | |
75 | + } | |
76 | +} | ... | ... |
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.common.data.oauth2; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import lombok.NoArgsConstructor; | |
21 | +import lombok.ToString; | |
22 | +import org.thingsboard.server.common.data.HasName; | |
23 | +import org.thingsboard.server.common.data.HasTenantId; | |
24 | +import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; | |
25 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; | |
26 | +import org.thingsboard.server.common.data.id.TenantId; | |
27 | + | |
28 | +import java.util.List; | |
29 | + | |
30 | +@EqualsAndHashCode(callSuper = true) | |
31 | +@Data | |
32 | +@ToString | |
33 | +@NoArgsConstructor | |
34 | +public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo<OAuth2ClientRegistrationTemplateId> implements HasName { | |
35 | + | |
36 | + private String providerId; | |
37 | + private MapperType mapperType; | |
38 | + private OAuth2BasicMapperConfig basic; | |
39 | + private String authorizationUri; | |
40 | + private String accessTokenUri; | |
41 | + private List<String> scope; | |
42 | + private String userInfoUri; | |
43 | + private String userNameAttributeName; | |
44 | + private String jwkSetUri; | |
45 | + private String clientAuthenticationMethod; | |
46 | + private String comment; | |
47 | + private String loginButtonIcon; | |
48 | + private String loginButtonLabel; | |
49 | + private String helpLink; | |
50 | + | |
51 | + public OAuth2ClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { | |
52 | + super(clientRegistrationTemplate); | |
53 | + this.providerId = clientRegistrationTemplate.providerId; | |
54 | + this.mapperType = clientRegistrationTemplate.mapperType; | |
55 | + this.basic = clientRegistrationTemplate.basic; | |
56 | + this.authorizationUri = clientRegistrationTemplate.authorizationUri; | |
57 | + this.accessTokenUri = clientRegistrationTemplate.accessTokenUri; | |
58 | + this.scope = clientRegistrationTemplate.scope; | |
59 | + this.userInfoUri = clientRegistrationTemplate.userInfoUri; | |
60 | + this.userNameAttributeName = clientRegistrationTemplate.userNameAttributeName; | |
61 | + this.jwkSetUri = clientRegistrationTemplate.jwkSetUri; | |
62 | + this.clientAuthenticationMethod = clientRegistrationTemplate.clientAuthenticationMethod; | |
63 | + this.comment = clientRegistrationTemplate.comment; | |
64 | + this.loginButtonIcon = clientRegistrationTemplate.loginButtonIcon; | |
65 | + this.loginButtonLabel = clientRegistrationTemplate.loginButtonLabel; | |
66 | + this.helpLink = clientRegistrationTemplate.helpLink; | |
67 | + } | |
68 | + | |
69 | + @Override | |
70 | + public String getName() { | |
71 | + return providerId; | |
72 | + } | |
73 | + | |
74 | + @Override | |
75 | + public String getSearchText() { | |
76 | + return getName(); | |
77 | + } | |
78 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsDomainParams.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.common.data.oauth2; | |
17 | + | |
18 | +import lombok.*; | |
19 | + | |
20 | +import java.util.List; | |
21 | +import java.util.Set; | |
22 | + | |
23 | +@EqualsAndHashCode | |
24 | +@Data | |
25 | +@ToString | |
26 | +@Builder(toBuilder = true) | |
27 | +@NoArgsConstructor | |
28 | +@AllArgsConstructor | |
29 | +public class OAuth2ClientsDomainParams { | |
30 | + private Set<DomainInfo> domainInfos; | |
31 | + private Set<ClientRegistrationDto> clientRegistrations; | |
32 | +} | |
\ No newline at end of file | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2ClientsParams.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.common.data.oauth2; | |
17 | + | |
18 | +import lombok.*; | |
19 | +import java.util.Set; | |
20 | + | |
21 | +@EqualsAndHashCode | |
22 | +@Data | |
23 | +@ToString | |
24 | +@Builder(toBuilder = true) | |
25 | +@NoArgsConstructor | |
26 | +@AllArgsConstructor | |
27 | +public class OAuth2ClientsParams { | |
28 | + private boolean enabled; | |
29 | + private Set<OAuth2ClientsDomainParams> domainsParams; | |
30 | +} | |
\ No newline at end of file | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2CustomMapperConfig.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.common.data.oauth2; | |
17 | + | |
18 | +import lombok.*; | |
19 | + | |
20 | +@Builder(toBuilder = true) | |
21 | +@EqualsAndHashCode | |
22 | +@Data | |
23 | +@ToString(exclude = {"password"}) | |
24 | +public class OAuth2CustomMapperConfig { | |
25 | + private final String url; | |
26 | + private final String username; | |
27 | + private final String password; | |
28 | + private final boolean sendToken; | |
29 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/OAuth2MapperConfig.java
renamed from
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientMapperConfig.java
... | ... | @@ -13,35 +13,21 @@ |
13 | 13 | * See the License for the specific language governing permissions and |
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | -package org.thingsboard.server.dao.oauth2; | |
16 | +package org.thingsboard.server.common.data.oauth2; | |
17 | 17 | |
18 | +import lombok.Builder; | |
18 | 19 | import lombok.Data; |
20 | +import lombok.EqualsAndHashCode; | |
21 | +import lombok.ToString; | |
19 | 22 | |
23 | +@Builder(toBuilder = true) | |
24 | +@EqualsAndHashCode | |
20 | 25 | @Data |
21 | -public class OAuth2ClientMapperConfig { | |
22 | - | |
26 | +@ToString | |
27 | +public class OAuth2MapperConfig { | |
23 | 28 | private boolean allowUserCreation; |
24 | 29 | private boolean activateUser; |
25 | - private String type; | |
26 | - private BasicOAuth2ClientMapperConfig basic; | |
27 | - private CustomOAuth2ClientMapperConfig custom; | |
28 | - | |
29 | - @Data | |
30 | - public static class BasicOAuth2ClientMapperConfig { | |
31 | - private String emailAttributeKey; | |
32 | - private String firstNameAttributeKey; | |
33 | - private String lastNameAttributeKey; | |
34 | - private String tenantNameStrategy; | |
35 | - private String tenantNamePattern; | |
36 | - private String customerNamePattern; | |
37 | - private boolean alwaysFullScreen; | |
38 | - private String defaultDashboardName; | |
39 | - } | |
40 | - | |
41 | - @Data | |
42 | - public static class CustomOAuth2ClientMapperConfig { | |
43 | - private String url; | |
44 | - private String username; | |
45 | - private String password; | |
46 | - } | |
30 | + private MapperType type; | |
31 | + private OAuth2BasicMapperConfig basic; | |
32 | + private OAuth2CustomMapperConfig custom; | |
47 | 33 | } | ... | ... |
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.common.data.oauth2; | |
17 | + | |
18 | +public enum SchemeType { | |
19 | + HTTP, HTTPS, MIXED; | |
20 | +} | ... | ... |
common/data/src/main/java/org/thingsboard/server/common/data/oauth2/TenantNameStrategyType.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.common.data.oauth2; | |
17 | + | |
18 | +public enum TenantNameStrategyType { | |
19 | + DOMAIN, EMAIL, CUSTOM; | |
20 | +} | ... | ... |
... | ... | @@ -390,6 +390,53 @@ public class ModelConstants { |
390 | 390 | public static final String RULE_NODE_STATE_DATA_PROPERTY = "state_data"; |
391 | 391 | |
392 | 392 | /** |
393 | + * OAuth2 client registration constants. | |
394 | + */ | |
395 | + public static final String OAUTH2_TENANT_ID_PROPERTY = TENANT_ID_PROPERTY; | |
396 | + public static final String OAUTH2_CLIENT_REGISTRATION_INFO_COLUMN_FAMILY_NAME = "oauth2_client_registration_info"; | |
397 | + public static final String OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME = "oauth2_client_registration"; | |
398 | + public static final String OAUTH2_CLIENT_REGISTRATION_TO_DOMAIN_COLUMN_FAMILY_NAME = "oauth2_client_registration_to_domain"; | |
399 | + public static final String OAUTH2_CLIENT_REGISTRATION_TEMPLATE_COLUMN_FAMILY_NAME = "oauth2_client_registration_template"; | |
400 | + public static final String OAUTH2_ENABLED_PROPERTY = "enabled"; | |
401 | + public static final String OAUTH2_TEMPLATE_PROVIDER_ID_PROPERTY = "provider_id"; | |
402 | + public static final String OAUTH2_CLIENT_REGISTRATION_INFO_ID_PROPERTY = "client_registration_info_id"; | |
403 | + public static final String OAUTH2_DOMAIN_NAME_PROPERTY = "domain_name"; | |
404 | + public static final String OAUTH2_DOMAIN_SCHEME_PROPERTY = "domain_scheme"; | |
405 | + public static final String OAUTH2_CLIENT_ID_PROPERTY = "client_id"; | |
406 | + public static final String OAUTH2_CLIENT_SECRET_PROPERTY = "client_secret"; | |
407 | + public static final String OAUTH2_AUTHORIZATION_URI_PROPERTY = "authorization_uri"; | |
408 | + public static final String OAUTH2_TOKEN_URI_PROPERTY = "token_uri"; | |
409 | + public static final String OAUTH2_REDIRECT_URI_TEMPLATE_PROPERTY = "redirect_uri_template"; | |
410 | + public static final String OAUTH2_SCOPE_PROPERTY = "scope"; | |
411 | + public static final String OAUTH2_USER_INFO_URI_PROPERTY = "user_info_uri"; | |
412 | + public static final String OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY = "user_name_attribute_name"; | |
413 | + public static final String OAUTH2_JWK_SET_URI_PROPERTY = "jwk_set_uri"; | |
414 | + public static final String OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY = "client_authentication_method"; | |
415 | + public static final String OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY = "login_button_label"; | |
416 | + public static final String OAUTH2_LOGIN_BUTTON_ICON_PROPERTY = "login_button_icon"; | |
417 | + public static final String OAUTH2_ALLOW_USER_CREATION_PROPERTY = "allow_user_creation"; | |
418 | + public static final String OAUTH2_ACTIVATE_USER_PROPERTY = "activate_user"; | |
419 | + public static final String OAUTH2_MAPPER_TYPE_PROPERTY = "type"; | |
420 | + public static final String OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY = "basic_email_attribute_key"; | |
421 | + public static final String OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY = "basic_first_name_attribute_key"; | |
422 | + public static final String OAUTH2_LAST_NAME_ATTRIBUTE_KEY_PROPERTY = "basic_last_name_attribute_key"; | |
423 | + public static final String OAUTH2_TENANT_NAME_STRATEGY_PROPERTY = "basic_tenant_name_strategy"; | |
424 | + public static final String OAUTH2_TENANT_NAME_PATTERN_PROPERTY = "basic_tenant_name_pattern"; | |
425 | + public static final String OAUTH2_CUSTOMER_NAME_PATTERN_PROPERTY = "basic_customer_name_pattern"; | |
426 | + public static final String OAUTH2_DEFAULT_DASHBOARD_NAME_PROPERTY = "basic_default_dashboard_name"; | |
427 | + public static final String OAUTH2_ALWAYS_FULL_SCREEN_PROPERTY = "basic_always_full_screen"; | |
428 | + public static final String OAUTH2_MAPPER_URL_PROPERTY = "custom_url"; | |
429 | + public static final String OAUTH2_MAPPER_USERNAME_PROPERTY = "custom_username"; | |
430 | + public static final String OAUTH2_MAPPER_PASSWORD_PROPERTY = "custom_password"; | |
431 | + public static final String OAUTH2_MAPPER_SEND_TOKEN_PROPERTY = "custom_send_token"; | |
432 | + public static final String OAUTH2_TEMPLATE_COMMENT_PROPERTY = "comment"; | |
433 | + public static final String OAUTH2_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; | |
434 | + public static final String OAUTH2_TEMPLATE_ADDITIONAL_INFO_PROPERTY = ADDITIONAL_INFO_PROPERTY; | |
435 | + public static final String OAUTH2_TEMPLATE_LOGIN_BUTTON_ICON_PROPERTY = OAUTH2_LOGIN_BUTTON_ICON_PROPERTY; | |
436 | + public static final String OAUTH2_TEMPLATE_LOGIN_BUTTON_LABEL_PROPERTY = OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY; | |
437 | + public static final String OAUTH2_TEMPLATE_HELP_LINK_PROPERTY = "help_link"; | |
438 | + | |
439 | + /** | |
393 | 440 | * Cassandra attributes and timeseries constants. |
394 | 441 | */ |
395 | 442 | public static final String ATTRIBUTES_KV_CF = "attributes_kv_cf"; | ... | ... |
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.dao.model.sql; | |
17 | + | |
18 | +import com.fasterxml.jackson.databind.JsonNode; | |
19 | +import lombok.Data; | |
20 | +import lombok.EqualsAndHashCode; | |
21 | +import org.hibernate.annotations.Type; | |
22 | +import org.hibernate.annotations.TypeDef; | |
23 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; | |
24 | +import org.thingsboard.server.common.data.oauth2.*; | |
25 | +import org.thingsboard.server.dao.model.BaseSqlEntity; | |
26 | +import org.thingsboard.server.dao.model.ModelConstants; | |
27 | +import org.thingsboard.server.dao.util.mapping.JsonStringType; | |
28 | + | |
29 | +import javax.persistence.*; | |
30 | +import java.util.Arrays; | |
31 | + | |
32 | +@Data | |
33 | +@EqualsAndHashCode(callSuper = true) | |
34 | +@TypeDef(name = "json", typeClass = JsonStringType.class) | |
35 | +@MappedSuperclass | |
36 | +public abstract class AbstractOAuth2ClientRegistrationInfoEntity<T extends OAuth2ClientRegistrationInfo> extends BaseSqlEntity<T> { | |
37 | + | |
38 | + @Column(name = ModelConstants.OAUTH2_ENABLED_PROPERTY) | |
39 | + private Boolean enabled; | |
40 | + @Column(name = ModelConstants.OAUTH2_CLIENT_ID_PROPERTY) | |
41 | + private String clientId; | |
42 | + @Column(name = ModelConstants.OAUTH2_CLIENT_SECRET_PROPERTY) | |
43 | + private String clientSecret; | |
44 | + @Column(name = ModelConstants.OAUTH2_AUTHORIZATION_URI_PROPERTY) | |
45 | + private String authorizationUri; | |
46 | + @Column(name = ModelConstants.OAUTH2_TOKEN_URI_PROPERTY) | |
47 | + private String tokenUri; | |
48 | + @Column(name = ModelConstants.OAUTH2_SCOPE_PROPERTY) | |
49 | + private String scope; | |
50 | + @Column(name = ModelConstants.OAUTH2_USER_INFO_URI_PROPERTY) | |
51 | + private String userInfoUri; | |
52 | + @Column(name = ModelConstants.OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY) | |
53 | + private String userNameAttributeName; | |
54 | + @Column(name = ModelConstants.OAUTH2_JWK_SET_URI_PROPERTY) | |
55 | + private String jwkSetUri; | |
56 | + @Column(name = ModelConstants.OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY) | |
57 | + private String clientAuthenticationMethod; | |
58 | + @Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY) | |
59 | + private String loginButtonLabel; | |
60 | + @Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_ICON_PROPERTY) | |
61 | + private String loginButtonIcon; | |
62 | + @Column(name = ModelConstants.OAUTH2_ALLOW_USER_CREATION_PROPERTY) | |
63 | + private Boolean allowUserCreation; | |
64 | + @Column(name = ModelConstants.OAUTH2_ACTIVATE_USER_PROPERTY) | |
65 | + private Boolean activateUser; | |
66 | + @Enumerated(EnumType.STRING) | |
67 | + @Column(name = ModelConstants.OAUTH2_MAPPER_TYPE_PROPERTY) | |
68 | + private MapperType type; | |
69 | + @Column(name = ModelConstants.OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY) | |
70 | + private String emailAttributeKey; | |
71 | + @Column(name = ModelConstants.OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY) | |
72 | + private String firstNameAttributeKey; | |
73 | + @Column(name = ModelConstants.OAUTH2_LAST_NAME_ATTRIBUTE_KEY_PROPERTY) | |
74 | + private String lastNameAttributeKey; | |
75 | + @Enumerated(EnumType.STRING) | |
76 | + @Column(name = ModelConstants.OAUTH2_TENANT_NAME_STRATEGY_PROPERTY) | |
77 | + private TenantNameStrategyType tenantNameStrategy; | |
78 | + @Column(name = ModelConstants.OAUTH2_TENANT_NAME_PATTERN_PROPERTY) | |
79 | + private String tenantNamePattern; | |
80 | + @Column(name = ModelConstants.OAUTH2_CUSTOMER_NAME_PATTERN_PROPERTY) | |
81 | + private String customerNamePattern; | |
82 | + @Column(name = ModelConstants.OAUTH2_DEFAULT_DASHBOARD_NAME_PROPERTY) | |
83 | + private String defaultDashboardName; | |
84 | + @Column(name = ModelConstants.OAUTH2_ALWAYS_FULL_SCREEN_PROPERTY) | |
85 | + private Boolean alwaysFullScreen; | |
86 | + @Column(name = ModelConstants.OAUTH2_MAPPER_URL_PROPERTY) | |
87 | + private String url; | |
88 | + @Column(name = ModelConstants.OAUTH2_MAPPER_USERNAME_PROPERTY) | |
89 | + private String username; | |
90 | + @Column(name = ModelConstants.OAUTH2_MAPPER_PASSWORD_PROPERTY) | |
91 | + private String password; | |
92 | + @Column(name = ModelConstants.OAUTH2_MAPPER_SEND_TOKEN_PROPERTY) | |
93 | + private Boolean sendToken; | |
94 | + | |
95 | + @Type(type = "json") | |
96 | + @Column(name = ModelConstants.OAUTH2_ADDITIONAL_INFO_PROPERTY) | |
97 | + private JsonNode additionalInfo; | |
98 | + | |
99 | + public AbstractOAuth2ClientRegistrationInfoEntity() { | |
100 | + super(); | |
101 | + } | |
102 | + | |
103 | + public AbstractOAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfo clientRegistrationInfo) { | |
104 | + if (clientRegistrationInfo.getId() != null) { | |
105 | + this.setUuid(clientRegistrationInfo.getId().getId()); | |
106 | + } | |
107 | + this.createdTime = clientRegistrationInfo.getCreatedTime(); | |
108 | + this.enabled = clientRegistrationInfo.isEnabled(); | |
109 | + this.clientId = clientRegistrationInfo.getClientId(); | |
110 | + this.clientSecret = clientRegistrationInfo.getClientSecret(); | |
111 | + this.authorizationUri = clientRegistrationInfo.getAuthorizationUri(); | |
112 | + this.tokenUri = clientRegistrationInfo.getAccessTokenUri(); | |
113 | + this.scope = clientRegistrationInfo.getScope().stream().reduce((result, element) -> result + "," + element).orElse(""); | |
114 | + this.userInfoUri = clientRegistrationInfo.getUserInfoUri(); | |
115 | + this.userNameAttributeName = clientRegistrationInfo.getUserNameAttributeName(); | |
116 | + this.jwkSetUri = clientRegistrationInfo.getJwkSetUri(); | |
117 | + this.clientAuthenticationMethod = clientRegistrationInfo.getClientAuthenticationMethod(); | |
118 | + this.loginButtonLabel = clientRegistrationInfo.getLoginButtonLabel(); | |
119 | + this.loginButtonIcon = clientRegistrationInfo.getLoginButtonIcon(); | |
120 | + this.additionalInfo = clientRegistrationInfo.getAdditionalInfo(); | |
121 | + OAuth2MapperConfig mapperConfig = clientRegistrationInfo.getMapperConfig(); | |
122 | + if (mapperConfig != null) { | |
123 | + this.allowUserCreation = mapperConfig.isAllowUserCreation(); | |
124 | + this.activateUser = mapperConfig.isActivateUser(); | |
125 | + this.type = mapperConfig.getType(); | |
126 | + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); | |
127 | + if (basicConfig != null) { | |
128 | + this.emailAttributeKey = basicConfig.getEmailAttributeKey(); | |
129 | + this.firstNameAttributeKey = basicConfig.getFirstNameAttributeKey(); | |
130 | + this.lastNameAttributeKey = basicConfig.getLastNameAttributeKey(); | |
131 | + this.tenantNameStrategy = basicConfig.getTenantNameStrategy(); | |
132 | + this.tenantNamePattern = basicConfig.getTenantNamePattern(); | |
133 | + this.customerNamePattern = basicConfig.getCustomerNamePattern(); | |
134 | + this.defaultDashboardName = basicConfig.getDefaultDashboardName(); | |
135 | + this.alwaysFullScreen = basicConfig.isAlwaysFullScreen(); | |
136 | + } | |
137 | + OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom(); | |
138 | + if (customConfig != null) { | |
139 | + this.url = customConfig.getUrl(); | |
140 | + this.username = customConfig.getUsername(); | |
141 | + this.password = customConfig.getPassword(); | |
142 | + this.sendToken = customConfig.isSendToken(); | |
143 | + } | |
144 | + } | |
145 | + } | |
146 | + | |
147 | + public AbstractOAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfoEntity oAuth2ClientRegistrationInfoEntity) { | |
148 | + this.setId(oAuth2ClientRegistrationInfoEntity.getId()); | |
149 | + this.setCreatedTime(oAuth2ClientRegistrationInfoEntity.getCreatedTime()); | |
150 | + this.enabled = oAuth2ClientRegistrationInfoEntity.getEnabled(); | |
151 | + this.clientId = oAuth2ClientRegistrationInfoEntity.getClientId(); | |
152 | + this.clientSecret = oAuth2ClientRegistrationInfoEntity.getClientSecret(); | |
153 | + this.authorizationUri = oAuth2ClientRegistrationInfoEntity.getAuthorizationUri(); | |
154 | + this.tokenUri = oAuth2ClientRegistrationInfoEntity.getTokenUri(); | |
155 | + this.scope = oAuth2ClientRegistrationInfoEntity.getScope(); | |
156 | + this.userInfoUri = oAuth2ClientRegistrationInfoEntity.getUserInfoUri(); | |
157 | + this.userNameAttributeName = oAuth2ClientRegistrationInfoEntity.getUserNameAttributeName(); | |
158 | + this.jwkSetUri = oAuth2ClientRegistrationInfoEntity.getJwkSetUri(); | |
159 | + this.clientAuthenticationMethod = oAuth2ClientRegistrationInfoEntity.getClientAuthenticationMethod(); | |
160 | + this.loginButtonLabel = oAuth2ClientRegistrationInfoEntity.getLoginButtonLabel(); | |
161 | + this.loginButtonIcon = oAuth2ClientRegistrationInfoEntity.getLoginButtonIcon(); | |
162 | + this.additionalInfo = oAuth2ClientRegistrationInfoEntity.getAdditionalInfo(); | |
163 | + this.allowUserCreation = oAuth2ClientRegistrationInfoEntity.getAllowUserCreation(); | |
164 | + this.activateUser = oAuth2ClientRegistrationInfoEntity.getActivateUser(); | |
165 | + this.type = oAuth2ClientRegistrationInfoEntity.getType(); | |
166 | + this.emailAttributeKey = oAuth2ClientRegistrationInfoEntity.getEmailAttributeKey(); | |
167 | + this.firstNameAttributeKey = oAuth2ClientRegistrationInfoEntity.getFirstNameAttributeKey(); | |
168 | + this.lastNameAttributeKey = oAuth2ClientRegistrationInfoEntity.getLastNameAttributeKey(); | |
169 | + this.tenantNameStrategy = oAuth2ClientRegistrationInfoEntity.getTenantNameStrategy(); | |
170 | + this.tenantNamePattern = oAuth2ClientRegistrationInfoEntity.getTenantNamePattern(); | |
171 | + this.customerNamePattern = oAuth2ClientRegistrationInfoEntity.getCustomerNamePattern(); | |
172 | + this.defaultDashboardName = oAuth2ClientRegistrationInfoEntity.getDefaultDashboardName(); | |
173 | + this.alwaysFullScreen = oAuth2ClientRegistrationInfoEntity.getAlwaysFullScreen(); | |
174 | + this.url = oAuth2ClientRegistrationInfoEntity.getUrl(); | |
175 | + this.username = oAuth2ClientRegistrationInfoEntity.getUsername(); | |
176 | + this.password = oAuth2ClientRegistrationInfoEntity.getPassword(); | |
177 | + this.sendToken = oAuth2ClientRegistrationInfoEntity.getSendToken(); | |
178 | + } | |
179 | + | |
180 | + | |
181 | + protected OAuth2ClientRegistrationInfo toOAuth2ClientRegistrationInfo() { | |
182 | + OAuth2ClientRegistrationInfo clientRegistrationInfo = new OAuth2ClientRegistrationInfo(); | |
183 | + clientRegistrationInfo.setId(new OAuth2ClientRegistrationInfoId(id)); | |
184 | + clientRegistrationInfo.setEnabled(enabled); | |
185 | + clientRegistrationInfo.setCreatedTime(createdTime); | |
186 | + clientRegistrationInfo.setAdditionalInfo(additionalInfo); | |
187 | + clientRegistrationInfo.setMapperConfig( | |
188 | + OAuth2MapperConfig.builder() | |
189 | + .allowUserCreation(allowUserCreation) | |
190 | + .activateUser(activateUser) | |
191 | + .type(type) | |
192 | + .basic( | |
193 | + (type == MapperType.BASIC || type == MapperType.GITHUB) ? | |
194 | + OAuth2BasicMapperConfig.builder() | |
195 | + .emailAttributeKey(emailAttributeKey) | |
196 | + .firstNameAttributeKey(firstNameAttributeKey) | |
197 | + .lastNameAttributeKey(lastNameAttributeKey) | |
198 | + .tenantNameStrategy(tenantNameStrategy) | |
199 | + .tenantNamePattern(tenantNamePattern) | |
200 | + .customerNamePattern(customerNamePattern) | |
201 | + .defaultDashboardName(defaultDashboardName) | |
202 | + .alwaysFullScreen(alwaysFullScreen) | |
203 | + .build() | |
204 | + : null | |
205 | + ) | |
206 | + .custom( | |
207 | + type == MapperType.CUSTOM ? | |
208 | + OAuth2CustomMapperConfig.builder() | |
209 | + .url(url) | |
210 | + .username(username) | |
211 | + .password(password) | |
212 | + .sendToken(sendToken) | |
213 | + .build() | |
214 | + : null | |
215 | + ) | |
216 | + .build() | |
217 | + ); | |
218 | + clientRegistrationInfo.setClientId(clientId); | |
219 | + clientRegistrationInfo.setClientSecret(clientSecret); | |
220 | + clientRegistrationInfo.setAuthorizationUri(authorizationUri); | |
221 | + clientRegistrationInfo.setAccessTokenUri(tokenUri); | |
222 | + clientRegistrationInfo.setScope(Arrays.asList(scope.split(","))); | |
223 | + clientRegistrationInfo.setUserInfoUri(userInfoUri); | |
224 | + clientRegistrationInfo.setUserNameAttributeName(userNameAttributeName); | |
225 | + clientRegistrationInfo.setJwkSetUri(jwkSetUri); | |
226 | + clientRegistrationInfo.setClientAuthenticationMethod(clientAuthenticationMethod); | |
227 | + clientRegistrationInfo.setLoginButtonLabel(loginButtonLabel); | |
228 | + clientRegistrationInfo.setLoginButtonIcon(loginButtonIcon); | |
229 | + return clientRegistrationInfo; | |
230 | + } | |
231 | +} | ... | ... |
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.dao.model.sql; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; | |
21 | +import org.thingsboard.server.common.data.oauth2.SchemeType; | |
22 | + | |
23 | +@Data | |
24 | +@EqualsAndHashCode(callSuper = true) | |
25 | +public class ExtendedOAuth2ClientRegistrationInfoEntity extends AbstractOAuth2ClientRegistrationInfoEntity<ExtendedOAuth2ClientRegistrationInfo> { | |
26 | + | |
27 | + private String domainName; | |
28 | + private SchemeType domainScheme; | |
29 | + | |
30 | + public ExtendedOAuth2ClientRegistrationInfoEntity() { | |
31 | + super(); | |
32 | + } | |
33 | + | |
34 | + public ExtendedOAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfoEntity oAuth2ClientRegistrationInfoEntity, | |
35 | + String domainName, | |
36 | + SchemeType domainScheme) { | |
37 | + super(oAuth2ClientRegistrationInfoEntity); | |
38 | + this.domainName = domainName; | |
39 | + this.domainScheme = domainScheme; | |
40 | + } | |
41 | + | |
42 | + @Override | |
43 | + public ExtendedOAuth2ClientRegistrationInfo toData() { | |
44 | + return new ExtendedOAuth2ClientRegistrationInfo(super.toOAuth2ClientRegistrationInfo(), | |
45 | + domainScheme, | |
46 | + domainName); | |
47 | + } | |
48 | +} | ... | ... |
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationEntity.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.dao.model.sql; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import org.hibernate.annotations.TypeDef; | |
21 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; | |
22 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; | |
23 | +import org.thingsboard.server.common.data.oauth2.*; | |
24 | +import org.thingsboard.server.dao.model.BaseSqlEntity; | |
25 | +import org.thingsboard.server.dao.model.ModelConstants; | |
26 | +import org.thingsboard.server.dao.util.mapping.JsonStringType; | |
27 | + | |
28 | +import javax.persistence.*; | |
29 | +import java.util.Arrays; | |
30 | +import java.util.UUID; | |
31 | + | |
32 | +@Data | |
33 | +@EqualsAndHashCode(callSuper = true) | |
34 | +@Entity | |
35 | +@TypeDef(name = "json", typeClass = JsonStringType.class) | |
36 | +@Table(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME) | |
37 | +public class OAuth2ClientRegistrationEntity extends BaseSqlEntity<OAuth2ClientRegistration> { | |
38 | + | |
39 | + @Column(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_INFO_ID_PROPERTY, columnDefinition = "uuid") | |
40 | + private UUID clientRegistrationInfoId; | |
41 | + | |
42 | + @Column(name = ModelConstants.OAUTH2_DOMAIN_NAME_PROPERTY) | |
43 | + private String domainName; | |
44 | + | |
45 | + @Enumerated(EnumType.STRING) | |
46 | + @Column(name = ModelConstants.OAUTH2_DOMAIN_SCHEME_PROPERTY) | |
47 | + private SchemeType domainScheme; | |
48 | + | |
49 | + public OAuth2ClientRegistrationEntity() { | |
50 | + super(); | |
51 | + } | |
52 | + | |
53 | + public OAuth2ClientRegistrationEntity(OAuth2ClientRegistration clientRegistration) { | |
54 | + if (clientRegistration.getId() != null) { | |
55 | + this.setUuid(clientRegistration.getId().getId()); | |
56 | + } | |
57 | + if (clientRegistration.getClientRegistrationId() != null){ | |
58 | + this.clientRegistrationInfoId = clientRegistration.getClientRegistrationId().getId(); | |
59 | + } | |
60 | + this.createdTime = clientRegistration.getCreatedTime(); | |
61 | + this.domainName = clientRegistration.getDomainName(); | |
62 | + this.domainScheme = clientRegistration.getDomainScheme(); | |
63 | + } | |
64 | + | |
65 | + @Override | |
66 | + public OAuth2ClientRegistration toData() { | |
67 | + OAuth2ClientRegistration clientRegistration = new OAuth2ClientRegistration(); | |
68 | + clientRegistration.setId(new OAuth2ClientRegistrationId(id)); | |
69 | + clientRegistration.setClientRegistrationId(new OAuth2ClientRegistrationInfoId(clientRegistrationInfoId)); | |
70 | + clientRegistration.setCreatedTime(createdTime); | |
71 | + clientRegistration.setDomainName(domainName); | |
72 | + clientRegistration.setDomainScheme(domainScheme); | |
73 | + return clientRegistration; | |
74 | + } | |
75 | +} | ... | ... |
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationInfoEntity.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.dao.model.sql; | |
17 | + | |
18 | +import lombok.Data; | |
19 | +import lombok.EqualsAndHashCode; | |
20 | +import org.hibernate.annotations.TypeDef; | |
21 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
22 | +import org.thingsboard.server.dao.model.ModelConstants; | |
23 | +import org.thingsboard.server.dao.util.mapping.JsonStringType; | |
24 | + | |
25 | +import javax.persistence.Entity; | |
26 | +import javax.persistence.Table; | |
27 | + | |
28 | +@Data | |
29 | +@EqualsAndHashCode(callSuper = true) | |
30 | +@Entity | |
31 | +@TypeDef(name = "json", typeClass = JsonStringType.class) | |
32 | +@Table(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_INFO_COLUMN_FAMILY_NAME) | |
33 | +public class OAuth2ClientRegistrationInfoEntity extends AbstractOAuth2ClientRegistrationInfoEntity<OAuth2ClientRegistrationInfo> { | |
34 | + | |
35 | + public OAuth2ClientRegistrationInfoEntity() { | |
36 | + super(); | |
37 | + } | |
38 | + | |
39 | + public OAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfo clientRegistration) { | |
40 | + super(clientRegistration); | |
41 | + } | |
42 | + | |
43 | + public OAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfoEntity oAuth2ClientRegistrationInfoEntity) { | |
44 | + super(oAuth2ClientRegistrationInfoEntity); | |
45 | + } | |
46 | + | |
47 | + @Override | |
48 | + public OAuth2ClientRegistrationInfo toData() { | |
49 | + return super.toOAuth2ClientRegistrationInfo(); | |
50 | + } | |
51 | +} | ... | ... |
dao/src/main/java/org/thingsboard/server/dao/model/sql/OAuth2ClientRegistrationTemplateEntity.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.dao.model.sql; | |
17 | + | |
18 | +import com.fasterxml.jackson.databind.JsonNode; | |
19 | +import lombok.Data; | |
20 | +import lombok.EqualsAndHashCode; | |
21 | +import org.hibernate.annotations.Type; | |
22 | +import org.hibernate.annotations.TypeDef; | |
23 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; | |
24 | +import org.thingsboard.server.common.data.id.TenantId; | |
25 | +import org.thingsboard.server.common.data.oauth2.*; | |
26 | +import org.thingsboard.server.dao.model.BaseSqlEntity; | |
27 | +import org.thingsboard.server.dao.model.ModelConstants; | |
28 | +import org.thingsboard.server.dao.util.mapping.JsonStringType; | |
29 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; | |
30 | + | |
31 | +import javax.persistence.*; | |
32 | +import java.util.Arrays; | |
33 | +import java.util.UUID; | |
34 | + | |
35 | +@Data | |
36 | +@EqualsAndHashCode(callSuper = true) | |
37 | +@Entity | |
38 | +@TypeDef(name = "json", typeClass = JsonStringType.class) | |
39 | +@Table(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_TEMPLATE_COLUMN_FAMILY_NAME) | |
40 | +public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2ClientRegistrationTemplate> { | |
41 | + | |
42 | + @Column(name = ModelConstants.OAUTH2_TEMPLATE_PROVIDER_ID_PROPERTY) | |
43 | + private String providerId; | |
44 | + @Column(name = ModelConstants.OAUTH2_AUTHORIZATION_URI_PROPERTY) | |
45 | + private String authorizationUri; | |
46 | + @Column(name = ModelConstants.OAUTH2_TOKEN_URI_PROPERTY) | |
47 | + private String tokenUri; | |
48 | + @Column(name = ModelConstants.OAUTH2_SCOPE_PROPERTY) | |
49 | + private String scope; | |
50 | + @Column(name = ModelConstants.OAUTH2_USER_INFO_URI_PROPERTY) | |
51 | + private String userInfoUri; | |
52 | + @Column(name = ModelConstants.OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY) | |
53 | + private String userNameAttributeName; | |
54 | + @Column(name = ModelConstants.OAUTH2_JWK_SET_URI_PROPERTY) | |
55 | + private String jwkSetUri; | |
56 | + @Column(name = ModelConstants.OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY) | |
57 | + private String clientAuthenticationMethod; | |
58 | + @Enumerated(EnumType.STRING) | |
59 | + @Column(name = ModelConstants.OAUTH2_MAPPER_TYPE_PROPERTY) | |
60 | + private MapperType type; | |
61 | + @Column(name = ModelConstants.OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY) | |
62 | + private String emailAttributeKey; | |
63 | + @Column(name = ModelConstants.OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY) | |
64 | + private String firstNameAttributeKey; | |
65 | + @Column(name = ModelConstants.OAUTH2_LAST_NAME_ATTRIBUTE_KEY_PROPERTY) | |
66 | + private String lastNameAttributeKey; | |
67 | + @Enumerated(EnumType.STRING) | |
68 | + @Column(name = ModelConstants.OAUTH2_TENANT_NAME_STRATEGY_PROPERTY) | |
69 | + private TenantNameStrategyType tenantNameStrategy; | |
70 | + @Column(name = ModelConstants.OAUTH2_TENANT_NAME_PATTERN_PROPERTY) | |
71 | + private String tenantNamePattern; | |
72 | + @Column(name = ModelConstants.OAUTH2_CUSTOMER_NAME_PATTERN_PROPERTY) | |
73 | + private String customerNamePattern; | |
74 | + @Column(name = ModelConstants.OAUTH2_DEFAULT_DASHBOARD_NAME_PROPERTY) | |
75 | + private String defaultDashboardName; | |
76 | + @Column(name = ModelConstants.OAUTH2_ALWAYS_FULL_SCREEN_PROPERTY) | |
77 | + private Boolean alwaysFullScreen; | |
78 | + @Column(name = ModelConstants.OAUTH2_TEMPLATE_COMMENT_PROPERTY) | |
79 | + private String comment; | |
80 | + @Column(name = ModelConstants.OAUTH2_TEMPLATE_LOGIN_BUTTON_ICON_PROPERTY) | |
81 | + private String loginButtonIcon; | |
82 | + @Column(name = ModelConstants.OAUTH2_TEMPLATE_LOGIN_BUTTON_LABEL_PROPERTY) | |
83 | + private String loginButtonLabel; | |
84 | + @Column(name = ModelConstants.OAUTH2_TEMPLATE_HELP_LINK_PROPERTY) | |
85 | + private String helpLink; | |
86 | + | |
87 | + @Type(type = "json") | |
88 | + @Column(name = ModelConstants.OAUTH2_TEMPLATE_ADDITIONAL_INFO_PROPERTY) | |
89 | + private JsonNode additionalInfo; | |
90 | + | |
91 | + public OAuth2ClientRegistrationTemplateEntity() { | |
92 | + } | |
93 | + | |
94 | + public OAuth2ClientRegistrationTemplateEntity(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { | |
95 | + if (clientRegistrationTemplate.getId() != null) { | |
96 | + this.setUuid(clientRegistrationTemplate.getId().getId()); | |
97 | + } | |
98 | + this.createdTime = clientRegistrationTemplate.getCreatedTime(); | |
99 | + this.providerId = clientRegistrationTemplate.getProviderId(); | |
100 | + this.authorizationUri = clientRegistrationTemplate.getAuthorizationUri(); | |
101 | + this.tokenUri = clientRegistrationTemplate.getAccessTokenUri(); | |
102 | + this.scope = clientRegistrationTemplate.getScope().stream().reduce((result, element) -> result + "," + element).orElse(""); | |
103 | + this.userInfoUri = clientRegistrationTemplate.getUserInfoUri(); | |
104 | + this.userNameAttributeName = clientRegistrationTemplate.getUserNameAttributeName(); | |
105 | + this.jwkSetUri = clientRegistrationTemplate.getJwkSetUri(); | |
106 | + this.clientAuthenticationMethod = clientRegistrationTemplate.getClientAuthenticationMethod(); | |
107 | + this.comment = clientRegistrationTemplate.getComment(); | |
108 | + this.loginButtonIcon = clientRegistrationTemplate.getLoginButtonIcon(); | |
109 | + this.loginButtonLabel = clientRegistrationTemplate.getLoginButtonLabel(); | |
110 | + this.helpLink = clientRegistrationTemplate.getHelpLink(); | |
111 | + this.additionalInfo = clientRegistrationTemplate.getAdditionalInfo(); | |
112 | + this.type = clientRegistrationTemplate.getMapperType(); | |
113 | + OAuth2BasicMapperConfig basicConfig = clientRegistrationTemplate.getBasic(); | |
114 | + if (basicConfig != null) { | |
115 | + this.emailAttributeKey = basicConfig.getEmailAttributeKey(); | |
116 | + this.firstNameAttributeKey = basicConfig.getFirstNameAttributeKey(); | |
117 | + this.lastNameAttributeKey = basicConfig.getLastNameAttributeKey(); | |
118 | + this.tenantNameStrategy = basicConfig.getTenantNameStrategy(); | |
119 | + this.tenantNamePattern = basicConfig.getTenantNamePattern(); | |
120 | + this.customerNamePattern = basicConfig.getCustomerNamePattern(); | |
121 | + this.defaultDashboardName = basicConfig.getDefaultDashboardName(); | |
122 | + this.alwaysFullScreen = basicConfig.isAlwaysFullScreen(); | |
123 | + } | |
124 | + } | |
125 | + | |
126 | + @Override | |
127 | + public OAuth2ClientRegistrationTemplate toData() { | |
128 | + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = new OAuth2ClientRegistrationTemplate(); | |
129 | + clientRegistrationTemplate.setId(new OAuth2ClientRegistrationTemplateId(id)); | |
130 | + clientRegistrationTemplate.setCreatedTime(createdTime); | |
131 | + clientRegistrationTemplate.setAdditionalInfo(additionalInfo); | |
132 | + | |
133 | + clientRegistrationTemplate.setMapperType(type); | |
134 | + clientRegistrationTemplate.setProviderId(providerId); | |
135 | + clientRegistrationTemplate.setBasic( | |
136 | + OAuth2BasicMapperConfig.builder() | |
137 | + .emailAttributeKey(emailAttributeKey) | |
138 | + .firstNameAttributeKey(firstNameAttributeKey) | |
139 | + .lastNameAttributeKey(lastNameAttributeKey) | |
140 | + .tenantNameStrategy(tenantNameStrategy) | |
141 | + .tenantNamePattern(tenantNamePattern) | |
142 | + .customerNamePattern(customerNamePattern) | |
143 | + .defaultDashboardName(defaultDashboardName) | |
144 | + .alwaysFullScreen(alwaysFullScreen) | |
145 | + .build() | |
146 | + ); | |
147 | + clientRegistrationTemplate.setAuthorizationUri(authorizationUri); | |
148 | + clientRegistrationTemplate.setAccessTokenUri(tokenUri); | |
149 | + clientRegistrationTemplate.setScope(Arrays.asList(scope.split(","))); | |
150 | + clientRegistrationTemplate.setUserInfoUri(userInfoUri); | |
151 | + clientRegistrationTemplate.setUserNameAttributeName(userNameAttributeName); | |
152 | + clientRegistrationTemplate.setJwkSetUri(jwkSetUri); | |
153 | + clientRegistrationTemplate.setClientAuthenticationMethod(clientAuthenticationMethod); | |
154 | + clientRegistrationTemplate.setComment(comment); | |
155 | + clientRegistrationTemplate.setLoginButtonIcon(loginButtonIcon); | |
156 | + clientRegistrationTemplate.setLoginButtonLabel(loginButtonLabel); | |
157 | + clientRegistrationTemplate.setHelpLink(helpLink); | |
158 | + return clientRegistrationTemplate; | |
159 | + } | |
160 | +} | ... | ... |
dao/src/main/java/org/thingsboard/server/dao/oauth2/HybridClientRegistrationRepository.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.dao.oauth2; | |
17 | + | |
18 | +import org.springframework.beans.factory.annotation.Autowired; | |
19 | +import org.springframework.security.oauth2.client.registration.ClientRegistration; | |
20 | +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; | |
21 | +import org.springframework.security.oauth2.core.AuthorizationGrantType; | |
22 | +import org.springframework.security.oauth2.core.ClientAuthenticationMethod; | |
23 | +import org.springframework.stereotype.Component; | |
24 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
25 | + | |
26 | +import java.util.UUID; | |
27 | + | |
28 | +@Component | |
29 | +public class HybridClientRegistrationRepository implements ClientRegistrationRepository { | |
30 | + private static final String defaultRedirectUriTemplate = "{baseUrl}/login/oauth2/code/{registrationId}"; | |
31 | + | |
32 | + @Autowired | |
33 | + private OAuth2Service oAuth2Service; | |
34 | + | |
35 | + @Override | |
36 | + public ClientRegistration findByRegistrationId(String registrationId) { | |
37 | + OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo = oAuth2Service.findClientRegistrationInfo(UUID.fromString(registrationId)); | |
38 | + return oAuth2ClientRegistrationInfo == null ? | |
39 | + null : toSpringClientRegistration(oAuth2ClientRegistrationInfo); | |
40 | + } | |
41 | + | |
42 | + private ClientRegistration toSpringClientRegistration(OAuth2ClientRegistrationInfo localClientRegistration){ | |
43 | + String registrationId = localClientRegistration.getUuidId().toString(); | |
44 | + return ClientRegistration.withRegistrationId(registrationId) | |
45 | + .clientName(localClientRegistration.getName()) | |
46 | + .clientId(localClientRegistration.getClientId()) | |
47 | + .authorizationUri(localClientRegistration.getAuthorizationUri()) | |
48 | + .clientSecret(localClientRegistration.getClientSecret()) | |
49 | + .tokenUri(localClientRegistration.getAccessTokenUri()) | |
50 | + .scope(localClientRegistration.getScope()) | |
51 | + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) | |
52 | + .userInfoUri(localClientRegistration.getUserInfoUri()) | |
53 | + .userNameAttributeName(localClientRegistration.getUserNameAttributeName()) | |
54 | + .jwkSetUri(localClientRegistration.getJwkSetUri()) | |
55 | + .clientAuthenticationMethod(new ClientAuthenticationMethod(localClientRegistration.getClientAuthenticationMethod())) | |
56 | + .redirectUriTemplate(defaultRedirectUriTemplate) | |
57 | + .build(); | |
58 | + } | |
59 | +} | ... | ... |
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.dao.oauth2; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; | |
19 | +import org.thingsboard.server.dao.Dao; | |
20 | + | |
21 | +public interface OAuth2ClientRegistrationDao extends Dao<OAuth2ClientRegistration> { | |
22 | + void deleteAll(); | |
23 | +} | ... | ... |
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.dao.oauth2; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; | |
19 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
20 | +import org.thingsboard.server.common.data.oauth2.SchemeType; | |
21 | +import org.thingsboard.server.dao.Dao; | |
22 | + | |
23 | +import java.util.List; | |
24 | +import java.util.Set; | |
25 | + | |
26 | +public interface OAuth2ClientRegistrationInfoDao extends Dao<OAuth2ClientRegistrationInfo> { | |
27 | + List<OAuth2ClientRegistrationInfo> findAll(); | |
28 | + | |
29 | + List<ExtendedOAuth2ClientRegistrationInfo> findAllExtended(); | |
30 | + | |
31 | + List<OAuth2ClientRegistrationInfo> findByDomainSchemesAndDomainName(List<SchemeType> domainSchemes, String domainName); | |
32 | + | |
33 | + void deleteAll(); | |
34 | +} | ... | ... |
dao/src/main/java/org/thingsboard/server/dao/oauth2/OAuth2ClientRegistrationTemplateDao.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.dao.oauth2; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; | |
19 | +import org.thingsboard.server.dao.Dao; | |
20 | + | |
21 | +import java.util.List; | |
22 | + | |
23 | +public interface OAuth2ClientRegistrationTemplateDao extends Dao<OAuth2ClientRegistrationTemplate> { | |
24 | + List<OAuth2ClientRegistrationTemplate> findAll(); | |
25 | +} | ... | ... |
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.dao.oauth2; | |
17 | + | |
18 | +import lombok.extern.slf4j.Slf4j; | |
19 | +import org.hibernate.exception.ConstraintViolationException; | |
20 | +import org.springframework.beans.factory.annotation.Autowired; | |
21 | +import org.springframework.stereotype.Service; | |
22 | +import org.springframework.util.StringUtils; | |
23 | +import org.thingsboard.server.common.data.Tenant; | |
24 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; | |
25 | +import org.thingsboard.server.common.data.id.TenantId; | |
26 | +import org.thingsboard.server.common.data.oauth2.*; | |
27 | +import org.thingsboard.server.dao.entity.AbstractEntityService; | |
28 | +import org.thingsboard.server.dao.exception.DataValidationException; | |
29 | +import org.thingsboard.server.dao.service.DataValidator; | |
30 | + | |
31 | +import java.util.List; | |
32 | + | |
33 | +import static org.thingsboard.server.dao.service.Validator.validateId; | |
34 | +import static org.thingsboard.server.dao.service.Validator.validateString; | |
35 | + | |
36 | +@Slf4j | |
37 | +@Service | |
38 | +public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService implements OAuth2ConfigTemplateService { | |
39 | + public static final String INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID = "Incorrect clientRegistrationTemplateId "; | |
40 | + | |
41 | + @Autowired | |
42 | + private OAuth2ClientRegistrationTemplateDao clientRegistrationTemplateDao; | |
43 | + | |
44 | + @Override | |
45 | + public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { | |
46 | + log.trace("Executing saveClientRegistrationTemplate [{}]", clientRegistrationTemplate); | |
47 | + clientRegistrationTemplateValidator.validate(clientRegistrationTemplate, o -> TenantId.SYS_TENANT_ID); | |
48 | + OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate; | |
49 | + try { | |
50 | + savedClientRegistrationTemplate = clientRegistrationTemplateDao.save(TenantId.SYS_TENANT_ID, clientRegistrationTemplate); | |
51 | + } catch (Exception t) { | |
52 | + ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); | |
53 | + if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("oauth2_template_provider_id_unq_key")) { | |
54 | + throw new DataValidationException("Client registration template with such providerId already exists!"); | |
55 | + } else { | |
56 | + throw t; | |
57 | + } | |
58 | + } | |
59 | + return savedClientRegistrationTemplate; | |
60 | + } | |
61 | + | |
62 | + @Override | |
63 | + public OAuth2ClientRegistrationTemplate findClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId) { | |
64 | + log.trace("Executing findClientRegistrationTemplateById [{}]", templateId); | |
65 | + validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId); | |
66 | + return clientRegistrationTemplateDao.findById(TenantId.SYS_TENANT_ID, templateId.getId()); | |
67 | + } | |
68 | + | |
69 | + @Override | |
70 | + public List<OAuth2ClientRegistrationTemplate> findAllClientRegistrationTemplates() { | |
71 | + log.trace("Executing findAllClientRegistrationTemplates"); | |
72 | + return clientRegistrationTemplateDao.findAll(); | |
73 | + } | |
74 | + | |
75 | + @Override | |
76 | + public void deleteClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId) { | |
77 | + log.trace("Executing deleteClientRegistrationTemplateById [{}]", templateId); | |
78 | + validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId); | |
79 | + clientRegistrationTemplateDao.removeById(TenantId.SYS_TENANT_ID, templateId.getId()); | |
80 | + } | |
81 | + | |
82 | + private final DataValidator<OAuth2ClientRegistrationTemplate> clientRegistrationTemplateValidator = | |
83 | + new DataValidator<OAuth2ClientRegistrationTemplate>() { | |
84 | + | |
85 | + @Override | |
86 | + protected void validateCreate(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { | |
87 | + } | |
88 | + | |
89 | + @Override | |
90 | + protected void validateUpdate(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { | |
91 | + } | |
92 | + | |
93 | + @Override | |
94 | + protected void validateDataImpl(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { | |
95 | + if (StringUtils.isEmpty(clientRegistrationTemplate.getProviderId())) { | |
96 | + throw new DataValidationException("Provider ID should be specified!"); | |
97 | + } | |
98 | + if (clientRegistrationTemplate.getBasic() == null) { | |
99 | + throw new DataValidationException("Basic mapper config should be specified!"); | |
100 | + } | |
101 | + } | |
102 | + }; | |
103 | +} | ... | ... |
... | ... | @@ -17,66 +17,15 @@ package org.thingsboard.server.dao.oauth2; |
17 | 17 | |
18 | 18 | import lombok.Data; |
19 | 19 | import lombok.extern.slf4j.Slf4j; |
20 | -import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | |
21 | 20 | import org.springframework.boot.context.properties.ConfigurationProperties; |
22 | -import org.springframework.context.annotation.Bean; | |
23 | 21 | import org.springframework.context.annotation.Configuration; |
24 | -import org.springframework.security.oauth2.client.registration.ClientRegistration; | |
25 | -import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; | |
26 | -import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; | |
27 | -import org.springframework.security.oauth2.core.AuthorizationGrantType; | |
28 | -import org.springframework.security.oauth2.core.ClientAuthenticationMethod; | |
29 | 22 | |
30 | -import java.util.ArrayList; | |
31 | -import java.util.HashMap; | |
32 | -import java.util.List; | |
33 | 23 | import java.util.Map; |
34 | 24 | |
35 | 25 | @Configuration |
36 | -@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") | |
37 | 26 | @ConfigurationProperties(prefix = "security.oauth2") |
38 | 27 | @Data |
39 | -@Slf4j | |
40 | 28 | public class OAuth2Configuration { |
41 | - | |
42 | - private boolean enabled; | |
43 | 29 | private String loginProcessingUrl; |
44 | - private Map<String, OAuth2Client> clients = new HashMap<>(); | |
45 | - | |
46 | - @Bean | |
47 | - public ClientRegistrationRepository clientRegistrationRepository() { | |
48 | - List<ClientRegistration> result = new ArrayList<>(); | |
49 | - for (Map.Entry<String, OAuth2Client> entry : clients.entrySet()) { | |
50 | - OAuth2Client client = entry.getValue(); | |
51 | - ClientRegistration registration = ClientRegistration.withRegistrationId(entry.getKey()) | |
52 | - .clientId(client.getClientId()) | |
53 | - .authorizationUri(client.getAuthorizationUri()) | |
54 | - .clientSecret(client.getClientSecret()) | |
55 | - .tokenUri(client.getAccessTokenUri()) | |
56 | - .redirectUriTemplate(client.getRedirectUriTemplate()) | |
57 | - .scope(client.getScope().split(",")) | |
58 | - .clientName(client.getClientName()) | |
59 | - .authorizationGrantType(new AuthorizationGrantType(client.getAuthorizationGrantType())) | |
60 | - .userInfoUri(client.getUserInfoUri()) | |
61 | - .userNameAttributeName(client.getUserNameAttributeName()) | |
62 | - .jwkSetUri(client.getJwkSetUri()) | |
63 | - .clientAuthenticationMethod(new ClientAuthenticationMethod(client.getClientAuthenticationMethod())) | |
64 | - .build(); | |
65 | - result.add(registration); | |
66 | - } | |
67 | - return new InMemoryClientRegistrationRepository(result); | |
68 | - } | |
69 | - | |
70 | - public OAuth2Client getClientByRegistrationId(String registrationId) { | |
71 | - OAuth2Client result = null; | |
72 | - if (clients != null && !clients.isEmpty()) { | |
73 | - for (String key : clients.keySet()) { | |
74 | - if (key.equals(registrationId)) { | |
75 | - result = clients.get(key); | |
76 | - break; | |
77 | - } | |
78 | - } | |
79 | - } | |
80 | - return result; | |
81 | - } | |
30 | + private Map<String, String> githubMapper; | |
82 | 31 | } | ... | ... |
... | ... | @@ -18,33 +18,198 @@ package org.thingsboard.server.dao.oauth2; |
18 | 18 | import lombok.extern.slf4j.Slf4j; |
19 | 19 | import org.springframework.beans.factory.annotation.Autowired; |
20 | 20 | import org.springframework.stereotype.Service; |
21 | -import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; | |
21 | +import org.springframework.util.StringUtils; | |
22 | +import org.thingsboard.server.common.data.id.TenantId; | |
23 | +import org.thingsboard.server.common.data.oauth2.*; | |
24 | +import org.thingsboard.server.dao.entity.AbstractEntityService; | |
25 | +import org.thingsboard.server.dao.exception.DataValidationException; | |
26 | +import org.thingsboard.server.dao.exception.IncorrectParameterException; | |
22 | 27 | |
23 | -import java.util.ArrayList; | |
24 | -import java.util.Collections; | |
25 | -import java.util.List; | |
26 | -import java.util.Map; | |
28 | +import javax.transaction.Transactional; | |
29 | +import java.util.*; | |
30 | +import java.util.function.Consumer; | |
31 | +import java.util.stream.Collectors; | |
32 | + | |
33 | +import static org.thingsboard.server.dao.service.Validator.validateId; | |
34 | +import static org.thingsboard.server.dao.service.Validator.validateString; | |
27 | 35 | |
28 | 36 | @Slf4j |
29 | 37 | @Service |
30 | -public class OAuth2ServiceImpl implements OAuth2Service { | |
38 | +public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Service { | |
39 | + public static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; | |
40 | + public static final String INCORRECT_CLIENT_REGISTRATION_ID = "Incorrect clientRegistrationId "; | |
41 | + public static final String INCORRECT_DOMAIN_NAME = "Incorrect domainName "; | |
42 | + public static final String INCORRECT_DOMAIN_SCHEME = "Incorrect domainScheme "; | |
31 | 43 | |
32 | - @Autowired(required = false) | |
33 | - OAuth2Configuration oauth2Configuration; | |
44 | + @Autowired | |
45 | + private OAuth2ClientRegistrationInfoDao clientRegistrationInfoDao; | |
46 | + @Autowired | |
47 | + private OAuth2ClientRegistrationDao clientRegistrationDao; | |
34 | 48 | |
35 | 49 | @Override |
36 | - public List<OAuth2ClientInfo> getOAuth2Clients() { | |
37 | - if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) { | |
38 | - return Collections.emptyList(); | |
50 | + public List<OAuth2ClientInfo> getOAuth2Clients(String domainSchemeStr, String domainName) { | |
51 | + log.trace("Executing getOAuth2Clients [{}://{}]", domainSchemeStr, domainName); | |
52 | + if (domainSchemeStr == null) { | |
53 | + throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME); | |
39 | 54 | } |
40 | - List<OAuth2ClientInfo> result = new ArrayList<>(); | |
41 | - for (Map.Entry<String, OAuth2Client> entry : oauth2Configuration.getClients().entrySet()) { | |
42 | - OAuth2ClientInfo client = new OAuth2ClientInfo(); | |
43 | - client.setName(entry.getValue().getLoginButtonLabel()); | |
44 | - client.setUrl(String.format("/oauth2/authorization/%s", entry.getKey())); | |
45 | - client.setIcon(entry.getValue().getLoginButtonIcon()); | |
46 | - result.add(client); | |
55 | + SchemeType domainScheme; | |
56 | + try { | |
57 | + domainScheme = SchemeType.valueOf(domainSchemeStr.toUpperCase()); | |
58 | + } catch (IllegalArgumentException e){ | |
59 | + throw new IncorrectParameterException(INCORRECT_DOMAIN_SCHEME); | |
47 | 60 | } |
48 | - return result; | |
61 | + validateString(domainName, INCORRECT_DOMAIN_NAME + domainName); | |
62 | + return clientRegistrationInfoDao.findByDomainSchemesAndDomainName(Arrays.asList(domainScheme, SchemeType.MIXED), domainName).stream() | |
63 | + .filter(OAuth2ClientRegistrationInfo::isEnabled) | |
64 | + .map(OAuth2Utils::toClientInfo) | |
65 | + .collect(Collectors.toList()); | |
66 | + } | |
67 | + | |
68 | + @Override | |
69 | + @Transactional | |
70 | + public void saveOAuth2Params(OAuth2ClientsParams oauth2Params) { | |
71 | + log.trace("Executing saveOAuth2Params [{}]", oauth2Params); | |
72 | + clientParamsValidator.accept(oauth2Params); | |
73 | + clientRegistrationDao.deleteAll(); | |
74 | + clientRegistrationInfoDao.deleteAll(); | |
75 | + oauth2Params.getDomainsParams().forEach(domainParams -> { | |
76 | + domainParams.getClientRegistrations().forEach(clientRegistrationDto -> { | |
77 | + OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo = OAuth2Utils.toClientRegistrationInfo(oauth2Params.isEnabled(), clientRegistrationDto); | |
78 | + OAuth2ClientRegistrationInfo savedClientRegistrationInfo = clientRegistrationInfoDao.save(TenantId.SYS_TENANT_ID, oAuth2ClientRegistrationInfo); | |
79 | + domainParams.getDomainInfos().forEach(domainInfo -> { | |
80 | + OAuth2ClientRegistration oAuth2ClientRegistration = OAuth2Utils.toClientRegistration(savedClientRegistrationInfo.getId(), | |
81 | + domainInfo.getScheme(), domainInfo.getName()); | |
82 | + clientRegistrationDao.save(TenantId.SYS_TENANT_ID, oAuth2ClientRegistration); | |
83 | + }); | |
84 | + }); | |
85 | + }); | |
86 | + } | |
87 | + | |
88 | + @Override | |
89 | + public OAuth2ClientsParams findOAuth2Params() { | |
90 | + log.trace("Executing findOAuth2Params"); | |
91 | + List<ExtendedOAuth2ClientRegistrationInfo> extendedInfos = clientRegistrationInfoDao.findAllExtended(); | |
92 | + return OAuth2Utils.toOAuth2Params(extendedInfos); | |
93 | + } | |
94 | + | |
95 | + @Override | |
96 | + public OAuth2ClientRegistrationInfo findClientRegistrationInfo(UUID id) { | |
97 | + log.trace("Executing findClientRegistrationInfo [{}]", id); | |
98 | + validateId(id, INCORRECT_CLIENT_REGISTRATION_ID + id); | |
99 | + return clientRegistrationInfoDao.findById(null, id); | |
100 | + } | |
101 | + | |
102 | + @Override | |
103 | + public List<OAuth2ClientRegistrationInfo> findAllClientRegistrationInfos() { | |
104 | + log.trace("Executing findAllClientRegistrationInfos"); | |
105 | + return clientRegistrationInfoDao.findAll(); | |
49 | 106 | } |
107 | + | |
108 | + private final Consumer<OAuth2ClientsParams> clientParamsValidator = oauth2Params -> { | |
109 | + if (oauth2Params == null | |
110 | + || oauth2Params.getDomainsParams() == null) { | |
111 | + throw new DataValidationException("Domain params should be specified!"); | |
112 | + } | |
113 | + for (OAuth2ClientsDomainParams domainParams : oauth2Params.getDomainsParams()) { | |
114 | + if (domainParams.getDomainInfos() == null | |
115 | + || domainParams.getDomainInfos().isEmpty()) { | |
116 | + throw new DataValidationException("List of domain configuration should be specified!"); | |
117 | + } | |
118 | + for (DomainInfo domainInfo : domainParams.getDomainInfos()) { | |
119 | + if (StringUtils.isEmpty(domainInfo.getName())) { | |
120 | + throw new DataValidationException("Domain name should be specified!"); | |
121 | + } | |
122 | + if (domainInfo.getScheme() == null) { | |
123 | + throw new DataValidationException("Domain scheme should be specified!"); | |
124 | + } | |
125 | + } | |
126 | + domainParams.getDomainInfos().stream() | |
127 | + .collect(Collectors.groupingBy(DomainInfo::getName)) | |
128 | + .forEach((domainName, domainInfos) -> { | |
129 | + if (domainInfos.size() > 1 && domainInfos.stream().anyMatch(domainInfo -> domainInfo.getScheme() == SchemeType.MIXED)) { | |
130 | + throw new DataValidationException("MIXED scheme type shouldn't be combined with another scheme type!"); | |
131 | + } | |
132 | + }); | |
133 | + if (domainParams.getClientRegistrations() == null || domainParams.getClientRegistrations().isEmpty()) { | |
134 | + throw new DataValidationException("Client registrations should be specified!"); | |
135 | + } | |
136 | + for (ClientRegistrationDto clientRegistration : domainParams.getClientRegistrations()) { | |
137 | + if (StringUtils.isEmpty(clientRegistration.getClientId())) { | |
138 | + throw new DataValidationException("Client ID should be specified!"); | |
139 | + } | |
140 | + if (StringUtils.isEmpty(clientRegistration.getClientSecret())) { | |
141 | + throw new DataValidationException("Client secret should be specified!"); | |
142 | + } | |
143 | + if (StringUtils.isEmpty(clientRegistration.getAuthorizationUri())) { | |
144 | + throw new DataValidationException("Authorization uri should be specified!"); | |
145 | + } | |
146 | + if (StringUtils.isEmpty(clientRegistration.getAccessTokenUri())) { | |
147 | + throw new DataValidationException("Token uri should be specified!"); | |
148 | + } | |
149 | + if (StringUtils.isEmpty(clientRegistration.getScope())) { | |
150 | + throw new DataValidationException("Scope should be specified!"); | |
151 | + } | |
152 | + if (StringUtils.isEmpty(clientRegistration.getUserInfoUri())) { | |
153 | + throw new DataValidationException("User info uri should be specified!"); | |
154 | + } | |
155 | + if (StringUtils.isEmpty(clientRegistration.getUserNameAttributeName())) { | |
156 | + throw new DataValidationException("User name attribute name should be specified!"); | |
157 | + } | |
158 | + if (StringUtils.isEmpty(clientRegistration.getClientAuthenticationMethod())) { | |
159 | + throw new DataValidationException("Client authentication method should be specified!"); | |
160 | + } | |
161 | + if (StringUtils.isEmpty(clientRegistration.getLoginButtonLabel())) { | |
162 | + throw new DataValidationException("Login button label should be specified!"); | |
163 | + } | |
164 | + OAuth2MapperConfig mapperConfig = clientRegistration.getMapperConfig(); | |
165 | + if (mapperConfig == null) { | |
166 | + throw new DataValidationException("Mapper config should be specified!"); | |
167 | + } | |
168 | + if (mapperConfig.getType() == null) { | |
169 | + throw new DataValidationException("Mapper config type should be specified!"); | |
170 | + } | |
171 | + if (mapperConfig.getType() == MapperType.BASIC) { | |
172 | + OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); | |
173 | + if (basicConfig == null) { | |
174 | + throw new DataValidationException("Basic config should be specified!"); | |
175 | + } | |
176 | + if (StringUtils.isEmpty(basicConfig.getEmailAttributeKey())) { | |
177 | + throw new DataValidationException("Email attribute key should be specified!"); | |
178 | + } | |
179 | + if (basicConfig.getTenantNameStrategy() == null) { | |
180 | + throw new DataValidationException("Tenant name strategy should be specified!"); | |
181 | + } | |
182 | + if (basicConfig.getTenantNameStrategy() == TenantNameStrategyType.CUSTOM | |
183 | + && StringUtils.isEmpty(basicConfig.getTenantNamePattern())) { | |
184 | + throw new DataValidationException("Tenant name pattern should be specified!"); | |
185 | + } | |
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 | + } | |
203 | + if (mapperConfig.getType() == MapperType.CUSTOM) { | |
204 | + OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom(); | |
205 | + if (customConfig == null) { | |
206 | + throw new DataValidationException("Custom config should be specified!"); | |
207 | + } | |
208 | + if (StringUtils.isEmpty(customConfig.getUrl())) { | |
209 | + throw new DataValidationException("Custom mapper URL should be specified!"); | |
210 | + } | |
211 | + } | |
212 | + } | |
213 | + } | |
214 | + }; | |
50 | 215 | } | ... | ... |
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.dao.oauth2; | |
17 | + | |
18 | +import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; | |
19 | +import org.thingsboard.server.common.data.oauth2.*; | |
20 | + | |
21 | +import java.util.*; | |
22 | + | |
23 | +public class OAuth2Utils { | |
24 | + public static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; | |
25 | + | |
26 | + public static OAuth2ClientInfo toClientInfo(OAuth2ClientRegistrationInfo clientRegistrationInfo) { | |
27 | + OAuth2ClientInfo client = new OAuth2ClientInfo(); | |
28 | + client.setName(clientRegistrationInfo.getLoginButtonLabel()); | |
29 | + client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistrationInfo.getUuidId().toString())); | |
30 | + client.setIcon(clientRegistrationInfo.getLoginButtonIcon()); | |
31 | + return client; | |
32 | + } | |
33 | + | |
34 | + public static OAuth2ClientsParams toOAuth2Params(List<ExtendedOAuth2ClientRegistrationInfo> extendedOAuth2ClientRegistrationInfos) { | |
35 | + Map<OAuth2ClientRegistrationInfoId, Set<DomainInfo>> domainsByInfoId = new HashMap<>(); | |
36 | + Map<OAuth2ClientRegistrationInfoId, OAuth2ClientRegistrationInfo> infoById = new HashMap<>(); | |
37 | + for (ExtendedOAuth2ClientRegistrationInfo extendedClientRegistrationInfo : extendedOAuth2ClientRegistrationInfos) { | |
38 | + String domainName = extendedClientRegistrationInfo.getDomainName(); | |
39 | + SchemeType domainScheme = extendedClientRegistrationInfo.getDomainScheme(); | |
40 | + domainsByInfoId.computeIfAbsent(extendedClientRegistrationInfo.getId(), key -> new HashSet<>()) | |
41 | + .add(new DomainInfo(domainScheme, domainName)); | |
42 | + infoById.put(extendedClientRegistrationInfo.getId(), extendedClientRegistrationInfo); | |
43 | + } | |
44 | + Map<Set<DomainInfo>, OAuth2ClientsDomainParams> domainParamsMap = new HashMap<>(); | |
45 | + domainsByInfoId.forEach((clientRegistrationInfoId, domainInfos) -> { | |
46 | + domainParamsMap.computeIfAbsent(domainInfos, | |
47 | + key -> new OAuth2ClientsDomainParams(key, new HashSet<>()) | |
48 | + ) | |
49 | + .getClientRegistrations() | |
50 | + .add(toClientRegistrationDto(infoById.get(clientRegistrationInfoId))); | |
51 | + }); | |
52 | + boolean enabled = extendedOAuth2ClientRegistrationInfos.stream() | |
53 | + .map(OAuth2ClientRegistrationInfo::isEnabled) | |
54 | + .findFirst().orElse(false); | |
55 | + return new OAuth2ClientsParams(enabled, new HashSet<>(domainParamsMap.values())); | |
56 | + } | |
57 | + | |
58 | + public static ClientRegistrationDto toClientRegistrationDto(OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo) { | |
59 | + return ClientRegistrationDto.builder() | |
60 | + .mapperConfig(oAuth2ClientRegistrationInfo.getMapperConfig()) | |
61 | + .clientId(oAuth2ClientRegistrationInfo.getClientId()) | |
62 | + .clientSecret(oAuth2ClientRegistrationInfo.getClientSecret()) | |
63 | + .authorizationUri(oAuth2ClientRegistrationInfo.getAuthorizationUri()) | |
64 | + .accessTokenUri(oAuth2ClientRegistrationInfo.getAccessTokenUri()) | |
65 | + .scope(oAuth2ClientRegistrationInfo.getScope()) | |
66 | + .userInfoUri(oAuth2ClientRegistrationInfo.getUserInfoUri()) | |
67 | + .userNameAttributeName(oAuth2ClientRegistrationInfo.getUserNameAttributeName()) | |
68 | + .jwkSetUri(oAuth2ClientRegistrationInfo.getJwkSetUri()) | |
69 | + .clientAuthenticationMethod(oAuth2ClientRegistrationInfo.getClientAuthenticationMethod()) | |
70 | + .loginButtonLabel(oAuth2ClientRegistrationInfo.getLoginButtonLabel()) | |
71 | + .loginButtonIcon(oAuth2ClientRegistrationInfo.getLoginButtonIcon()) | |
72 | + .additionalInfo(oAuth2ClientRegistrationInfo.getAdditionalInfo()) | |
73 | + .build(); | |
74 | + } | |
75 | + | |
76 | + public static OAuth2ClientRegistrationInfo toClientRegistrationInfo(boolean enabled, ClientRegistrationDto clientRegistrationDto) { | |
77 | + OAuth2ClientRegistrationInfo clientRegistrationInfo = new OAuth2ClientRegistrationInfo(); | |
78 | + clientRegistrationInfo.setEnabled(enabled); | |
79 | + clientRegistrationInfo.setMapperConfig(clientRegistrationDto.getMapperConfig()); | |
80 | + clientRegistrationInfo.setClientId(clientRegistrationDto.getClientId()); | |
81 | + clientRegistrationInfo.setClientSecret(clientRegistrationDto.getClientSecret()); | |
82 | + clientRegistrationInfo.setAuthorizationUri(clientRegistrationDto.getAuthorizationUri()); | |
83 | + clientRegistrationInfo.setAccessTokenUri(clientRegistrationDto.getAccessTokenUri()); | |
84 | + clientRegistrationInfo.setScope(clientRegistrationDto.getScope()); | |
85 | + clientRegistrationInfo.setUserInfoUri(clientRegistrationDto.getUserInfoUri()); | |
86 | + clientRegistrationInfo.setUserNameAttributeName(clientRegistrationDto.getUserNameAttributeName()); | |
87 | + clientRegistrationInfo.setJwkSetUri(clientRegistrationDto.getJwkSetUri()); | |
88 | + clientRegistrationInfo.setClientAuthenticationMethod(clientRegistrationDto.getClientAuthenticationMethod()); | |
89 | + clientRegistrationInfo.setLoginButtonLabel(clientRegistrationDto.getLoginButtonLabel()); | |
90 | + clientRegistrationInfo.setLoginButtonIcon(clientRegistrationDto.getLoginButtonIcon()); | |
91 | + clientRegistrationInfo.setAdditionalInfo(clientRegistrationDto.getAdditionalInfo()); | |
92 | + return clientRegistrationInfo; | |
93 | + } | |
94 | + | |
95 | + public static OAuth2ClientRegistration toClientRegistration(OAuth2ClientRegistrationInfoId clientRegistrationInfoId, SchemeType domainScheme, String domainName) { | |
96 | + OAuth2ClientRegistration clientRegistration = new OAuth2ClientRegistration(); | |
97 | + clientRegistration.setClientRegistrationId(clientRegistrationInfoId); | |
98 | + clientRegistration.setDomainName(domainName); | |
99 | + clientRegistration.setDomainScheme(domainScheme); | |
100 | + return clientRegistration; | |
101 | + } | |
102 | +} | ... | ... |
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationDao.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.dao.sql.oauth2; | |
17 | + | |
18 | +import lombok.RequiredArgsConstructor; | |
19 | +import lombok.extern.slf4j.Slf4j; | |
20 | +import org.springframework.data.repository.CrudRepository; | |
21 | +import org.springframework.stereotype.Component; | |
22 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; | |
23 | +import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; | |
24 | +import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationDao; | |
25 | +import org.thingsboard.server.dao.sql.JpaAbstractDao; | |
26 | + | |
27 | +import java.util.UUID; | |
28 | + | |
29 | +@Component | |
30 | +@RequiredArgsConstructor | |
31 | +public class JpaOAuth2ClientRegistrationDao extends JpaAbstractDao<OAuth2ClientRegistrationEntity, OAuth2ClientRegistration> implements OAuth2ClientRegistrationDao { | |
32 | + private final OAuth2ClientRegistrationRepository repository; | |
33 | + | |
34 | + @Override | |
35 | + protected Class<OAuth2ClientRegistrationEntity> getEntityClass() { | |
36 | + return OAuth2ClientRegistrationEntity.class; | |
37 | + } | |
38 | + | |
39 | + @Override | |
40 | + protected CrudRepository<OAuth2ClientRegistrationEntity, UUID> getCrudRepository() { | |
41 | + return repository; | |
42 | + } | |
43 | + | |
44 | + @Override | |
45 | + public void deleteAll() { | |
46 | + repository.deleteAll(); | |
47 | + } | |
48 | +} | ... | ... |
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationInfoDao.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.dao.sql.oauth2; | |
17 | + | |
18 | +import lombok.RequiredArgsConstructor; | |
19 | +import org.springframework.data.repository.CrudRepository; | |
20 | +import org.springframework.stereotype.Component; | |
21 | +import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; | |
22 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | |
23 | +import org.thingsboard.server.common.data.oauth2.SchemeType; | |
24 | +import org.thingsboard.server.dao.DaoUtil; | |
25 | +import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationInfoEntity; | |
26 | +import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationInfoDao; | |
27 | +import org.thingsboard.server.dao.sql.JpaAbstractDao; | |
28 | + | |
29 | +import java.util.ArrayList; | |
30 | +import java.util.List; | |
31 | +import java.util.UUID; | |
32 | +import java.util.stream.Collectors; | |
33 | + | |
34 | +@Component | |
35 | +@RequiredArgsConstructor | |
36 | +public class JpaOAuth2ClientRegistrationInfoDao extends JpaAbstractDao<OAuth2ClientRegistrationInfoEntity, OAuth2ClientRegistrationInfo> implements OAuth2ClientRegistrationInfoDao { | |
37 | + private final OAuth2ClientRegistrationInfoRepository repository; | |
38 | + | |
39 | + @Override | |
40 | + protected Class<OAuth2ClientRegistrationInfoEntity> getEntityClass() { | |
41 | + return OAuth2ClientRegistrationInfoEntity.class; | |
42 | + } | |
43 | + | |
44 | + @Override | |
45 | + protected CrudRepository<OAuth2ClientRegistrationInfoEntity, UUID> getCrudRepository() { | |
46 | + return repository; | |
47 | + } | |
48 | + | |
49 | + @Override | |
50 | + public List<OAuth2ClientRegistrationInfo> findAll() { | |
51 | + Iterable<OAuth2ClientRegistrationInfoEntity> entities = repository.findAll(); | |
52 | + List<OAuth2ClientRegistrationInfo> result = new ArrayList<>(); | |
53 | + entities.forEach(entity -> { | |
54 | + result.add(DaoUtil.getData(entity)); | |
55 | + }); | |
56 | + return result; | |
57 | + } | |
58 | + | |
59 | + @Override | |
60 | + public List<ExtendedOAuth2ClientRegistrationInfo> findAllExtended() { | |
61 | + return repository.findAllExtended().stream() | |
62 | + .map(DaoUtil::getData) | |
63 | + .collect(Collectors.toList()); | |
64 | + } | |
65 | + | |
66 | + @Override | |
67 | + public List<OAuth2ClientRegistrationInfo> findByDomainSchemesAndDomainName(List<SchemeType> domainSchemes, String domainName) { | |
68 | + List<OAuth2ClientRegistrationInfoEntity> entities = repository.findAllByDomainSchemesAndName(domainSchemes, domainName); | |
69 | + return entities.stream().map(DaoUtil::getData).collect(Collectors.toList()); | |
70 | + } | |
71 | + | |
72 | + @Override | |
73 | + public void deleteAll() { | |
74 | + repository.deleteAll(); | |
75 | + } | |
76 | +} | ... | ... |
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/JpaOAuth2ClientRegistrationTemplateDao.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.dao.sql.oauth2; | |
17 | + | |
18 | +import lombok.RequiredArgsConstructor; | |
19 | +import org.springframework.data.repository.CrudRepository; | |
20 | +import org.springframework.stereotype.Component; | |
21 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; | |
22 | +import org.thingsboard.server.dao.DaoUtil; | |
23 | +import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationTemplateEntity; | |
24 | +import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationTemplateDao; | |
25 | +import org.thingsboard.server.dao.sql.JpaAbstractDao; | |
26 | + | |
27 | +import java.util.ArrayList; | |
28 | +import java.util.List; | |
29 | +import java.util.UUID; | |
30 | + | |
31 | +@Component | |
32 | +@RequiredArgsConstructor | |
33 | +public class JpaOAuth2ClientRegistrationTemplateDao extends JpaAbstractDao<OAuth2ClientRegistrationTemplateEntity, OAuth2ClientRegistrationTemplate> implements OAuth2ClientRegistrationTemplateDao { | |
34 | + private final OAuth2ClientRegistrationTemplateRepository repository; | |
35 | + | |
36 | + @Override | |
37 | + protected Class<OAuth2ClientRegistrationTemplateEntity> getEntityClass() { | |
38 | + return OAuth2ClientRegistrationTemplateEntity.class; | |
39 | + } | |
40 | + | |
41 | + @Override | |
42 | + protected CrudRepository<OAuth2ClientRegistrationTemplateEntity, UUID> getCrudRepository() { | |
43 | + return repository; | |
44 | + } | |
45 | + | |
46 | + @Override | |
47 | + public List<OAuth2ClientRegistrationTemplate> findAll() { | |
48 | + Iterable<OAuth2ClientRegistrationTemplateEntity> entities = repository.findAll(); | |
49 | + List<OAuth2ClientRegistrationTemplate> result = new ArrayList<>(); | |
50 | + entities.forEach(entity -> result.add(DaoUtil.getData(entity))); | |
51 | + return result; | |
52 | + } | |
53 | +} | ... | ... |
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationInfoRepository.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.dao.sql.oauth2; | |
17 | + | |
18 | +import org.springframework.data.jpa.repository.Query; | |
19 | +import org.springframework.data.repository.CrudRepository; | |
20 | +import org.springframework.data.repository.query.Param; | |
21 | +import org.thingsboard.server.common.data.oauth2.SchemeType; | |
22 | +import org.thingsboard.server.dao.model.sql.ExtendedOAuth2ClientRegistrationInfoEntity; | |
23 | +import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationInfoEntity; | |
24 | + | |
25 | +import java.util.List; | |
26 | +import java.util.UUID; | |
27 | + | |
28 | +public interface OAuth2ClientRegistrationInfoRepository extends CrudRepository<OAuth2ClientRegistrationInfoEntity, UUID> { | |
29 | + @Query("SELECT new OAuth2ClientRegistrationInfoEntity(cr_info) " + | |
30 | + "FROM OAuth2ClientRegistrationInfoEntity cr_info " + | |
31 | + "LEFT JOIN OAuth2ClientRegistrationEntity cr on cr_info.id = cr.clientRegistrationInfoId " + | |
32 | + "WHERE cr.domainName = :domainName " + | |
33 | + "AND cr.domainScheme IN (:domainSchemes)") | |
34 | + List<OAuth2ClientRegistrationInfoEntity> findAllByDomainSchemesAndName(@Param("domainSchemes") List<SchemeType> domainSchemes, | |
35 | + @Param("domainName") String domainName); | |
36 | + | |
37 | + @Query("SELECT new org.thingsboard.server.dao.model.sql.ExtendedOAuth2ClientRegistrationInfoEntity(cr_info, cr.domainName, cr.domainScheme) " + | |
38 | + "FROM OAuth2ClientRegistrationInfoEntity cr_info " + | |
39 | + "LEFT JOIN OAuth2ClientRegistrationEntity cr on cr_info.id = cr.clientRegistrationInfoId ") | |
40 | + List<ExtendedOAuth2ClientRegistrationInfoEntity> findAllExtended(); | |
41 | +} | ... | ... |
dao/src/main/java/org/thingsboard/server/dao/sql/oauth2/OAuth2ClientRegistrationRepository.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.dao.sql.oauth2; | |
17 | + | |
18 | +import org.springframework.data.repository.CrudRepository; | |
19 | +import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; | |
20 | + | |
21 | +import java.util.UUID; | |
22 | + | |
23 | +public interface OAuth2ClientRegistrationRepository extends CrudRepository<OAuth2ClientRegistrationEntity, UUID> { | |
24 | +} | ... | ... |
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.dao.sql.oauth2; | |
17 | + | |
18 | +import org.springframework.data.repository.CrudRepository; | |
19 | +import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationTemplateEntity; | |
20 | + | |
21 | +import java.util.UUID; | |
22 | + | |
23 | +public interface OAuth2ClientRegistrationTemplateRepository extends CrudRepository<OAuth2ClientRegistrationTemplateEntity, UUID> { | |
24 | +} | ... | ... |
... | ... | @@ -331,3 +331,73 @@ CREATE TABLE IF NOT EXISTS ts_kv_dictionary ( |
331 | 331 | key_id int GENERATED BY DEFAULT AS IDENTITY(start with 0 increment by 1) UNIQUE, |
332 | 332 | CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) |
333 | 333 | ); |
334 | + | |
335 | + | |
336 | +CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( | |
337 | + id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY, | |
338 | + enabled boolean, | |
339 | + created_time bigint NOT NULL, | |
340 | + additional_info varchar, | |
341 | + client_id varchar(255), | |
342 | + client_secret varchar(255), | |
343 | + authorization_uri varchar(255), | |
344 | + token_uri varchar(255), | |
345 | + scope varchar(255), | |
346 | + user_info_uri varchar(255), | |
347 | + user_name_attribute_name varchar(255), | |
348 | + jwk_set_uri varchar(255), | |
349 | + client_authentication_method varchar(255), | |
350 | + login_button_label varchar(255), | |
351 | + login_button_icon varchar(255), | |
352 | + allow_user_creation boolean, | |
353 | + activate_user boolean, | |
354 | + type varchar(31), | |
355 | + basic_email_attribute_key varchar(31), | |
356 | + basic_first_name_attribute_key varchar(31), | |
357 | + basic_last_name_attribute_key varchar(31), | |
358 | + basic_tenant_name_strategy varchar(31), | |
359 | + basic_tenant_name_pattern varchar(255), | |
360 | + basic_customer_name_pattern varchar(255), | |
361 | + basic_default_dashboard_name varchar(255), | |
362 | + basic_always_full_screen boolean, | |
363 | + custom_url varchar(255), | |
364 | + custom_username varchar(255), | |
365 | + custom_password varchar(255), | |
366 | + custom_send_token boolean | |
367 | +); | |
368 | + | |
369 | +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( | |
370 | + id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, | |
371 | + created_time bigint NOT NULL, | |
372 | + domain_name varchar(255), | |
373 | + domain_scheme varchar(31), | |
374 | + client_registration_info_id uuid | |
375 | +); | |
376 | + | |
377 | +CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( | |
378 | + id uuid NOT NULL CONSTRAINT oauth2_client_registration_template_pkey PRIMARY KEY, | |
379 | + created_time bigint NOT NULL, | |
380 | + additional_info varchar, | |
381 | + provider_id varchar(255), | |
382 | + authorization_uri varchar(255), | |
383 | + token_uri varchar(255), | |
384 | + scope varchar(255), | |
385 | + user_info_uri varchar(255), | |
386 | + user_name_attribute_name varchar(255), | |
387 | + jwk_set_uri varchar(255), | |
388 | + client_authentication_method varchar(255), | |
389 | + type varchar(31), | |
390 | + basic_email_attribute_key varchar(31), | |
391 | + basic_first_name_attribute_key varchar(31), | |
392 | + basic_last_name_attribute_key varchar(31), | |
393 | + basic_tenant_name_strategy varchar(31), | |
394 | + basic_tenant_name_pattern varchar(255), | |
395 | + basic_customer_name_pattern varchar(255), | |
396 | + basic_default_dashboard_name varchar(255), | |
397 | + basic_always_full_screen boolean, | |
398 | + comment varchar, | |
399 | + login_button_icon varchar(255), | |
400 | + login_button_label varchar(255), | |
401 | + help_link varchar(255), | |
402 | + CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) | |
403 | +); | ... | ... |
... | ... | @@ -359,6 +359,75 @@ CREATE TABLE IF NOT EXISTS ts_kv_dictionary |
359 | 359 | CONSTRAINT ts_key_id_pkey PRIMARY KEY (key) |
360 | 360 | ); |
361 | 361 | |
362 | +CREATE TABLE IF NOT EXISTS oauth2_client_registration_info ( | |
363 | + id uuid NOT NULL CONSTRAINT oauth2_client_registration_info_pkey PRIMARY KEY, | |
364 | + enabled boolean, | |
365 | + created_time bigint NOT NULL, | |
366 | + additional_info varchar, | |
367 | + client_id varchar(255), | |
368 | + client_secret varchar(255), | |
369 | + authorization_uri varchar(255), | |
370 | + token_uri varchar(255), | |
371 | + scope varchar(255), | |
372 | + user_info_uri varchar(255), | |
373 | + user_name_attribute_name varchar(255), | |
374 | + jwk_set_uri varchar(255), | |
375 | + client_authentication_method varchar(255), | |
376 | + login_button_label varchar(255), | |
377 | + login_button_icon varchar(255), | |
378 | + allow_user_creation boolean, | |
379 | + activate_user boolean, | |
380 | + type varchar(31), | |
381 | + basic_email_attribute_key varchar(31), | |
382 | + basic_first_name_attribute_key varchar(31), | |
383 | + basic_last_name_attribute_key varchar(31), | |
384 | + basic_tenant_name_strategy varchar(31), | |
385 | + basic_tenant_name_pattern varchar(255), | |
386 | + basic_customer_name_pattern varchar(255), | |
387 | + basic_default_dashboard_name varchar(255), | |
388 | + basic_always_full_screen boolean, | |
389 | + custom_url varchar(255), | |
390 | + custom_username varchar(255), | |
391 | + custom_password varchar(255), | |
392 | + custom_send_token boolean | |
393 | +); | |
394 | + | |
395 | +CREATE TABLE IF NOT EXISTS oauth2_client_registration ( | |
396 | + id uuid NOT NULL CONSTRAINT oauth2_client_registration_pkey PRIMARY KEY, | |
397 | + created_time bigint NOT NULL, | |
398 | + domain_name varchar(255), | |
399 | + domain_scheme varchar(31), | |
400 | + client_registration_info_id uuid | |
401 | +); | |
402 | + | |
403 | +CREATE TABLE IF NOT EXISTS oauth2_client_registration_template ( | |
404 | + id uuid NOT NULL CONSTRAINT oauth2_client_registration_template_pkey PRIMARY KEY, | |
405 | + created_time bigint NOT NULL, | |
406 | + additional_info varchar, | |
407 | + provider_id varchar(255), | |
408 | + authorization_uri varchar(255), | |
409 | + token_uri varchar(255), | |
410 | + scope varchar(255), | |
411 | + user_info_uri varchar(255), | |
412 | + user_name_attribute_name varchar(255), | |
413 | + jwk_set_uri varchar(255), | |
414 | + client_authentication_method varchar(255), | |
415 | + type varchar(31), | |
416 | + basic_email_attribute_key varchar(31), | |
417 | + basic_first_name_attribute_key varchar(31), | |
418 | + basic_last_name_attribute_key varchar(31), | |
419 | + basic_tenant_name_strategy varchar(31), | |
420 | + basic_tenant_name_pattern varchar(255), | |
421 | + basic_customer_name_pattern varchar(255), | |
422 | + basic_default_dashboard_name varchar(255), | |
423 | + basic_always_full_screen boolean, | |
424 | + comment varchar, | |
425 | + login_button_icon varchar(255), | |
426 | + login_button_label varchar(255), | |
427 | + help_link varchar(255), | |
428 | + CONSTRAINT oauth2_template_provider_id_unq_key UNIQUE (provider_id) | |
429 | +); | |
430 | + | |
362 | 431 | CREATE OR REPLACE PROCEDURE cleanup_events_by_ttl(IN ttl bigint, IN debug_ttl bigint, INOUT deleted bigint) |
363 | 432 | LANGUAGE plpgsql AS |
364 | 433 | $$ | ... | ... |
dao/src/test/java/org/thingsboard/server/dao/service/BaseOAuth2ConfigTemplateServiceTest.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.dao.service; | |
17 | + | |
18 | +import org.junit.After; | |
19 | +import org.junit.Assert; | |
20 | +import org.junit.Before; | |
21 | +import org.junit.Test; | |
22 | +import org.springframework.beans.factory.annotation.Autowired; | |
23 | +import org.thingsboard.server.common.data.id.TenantId; | |
24 | +import org.thingsboard.server.common.data.oauth2.MapperType; | |
25 | +import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; | |
26 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; | |
27 | +import org.thingsboard.server.dao.exception.DataValidationException; | |
28 | +import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; | |
29 | + | |
30 | +import java.util.Arrays; | |
31 | +import java.util.UUID; | |
32 | + | |
33 | +public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { | |
34 | + | |
35 | + @Autowired | |
36 | + protected OAuth2ConfigTemplateService oAuth2ConfigTemplateService; | |
37 | + | |
38 | + @Before | |
39 | + public void beforeRun() throws Exception { | |
40 | + Assert.assertTrue(oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().isEmpty()); | |
41 | + } | |
42 | + | |
43 | + @After | |
44 | + public void after() throws Exception { | |
45 | + oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().forEach(clientRegistrationTemplate -> { | |
46 | + oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplate.getId()); | |
47 | + }); | |
48 | + | |
49 | + Assert.assertTrue(oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().isEmpty()); | |
50 | + } | |
51 | + | |
52 | + | |
53 | + @Test(expected = DataValidationException.class) | |
54 | + public void testSaveDuplicateProviderId() { | |
55 | + OAuth2ClientRegistrationTemplate first = validClientRegistrationTemplate("providerId"); | |
56 | + OAuth2ClientRegistrationTemplate second = validClientRegistrationTemplate("providerId"); | |
57 | + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(first); | |
58 | + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(second); | |
59 | + } | |
60 | + | |
61 | + @Test | |
62 | + public void testCreateNewTemplate() { | |
63 | + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = validClientRegistrationTemplate(UUID.randomUUID().toString()); | |
64 | + OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); | |
65 | + | |
66 | + Assert.assertNotNull(savedClientRegistrationTemplate); | |
67 | + Assert.assertNotNull(savedClientRegistrationTemplate.getId()); | |
68 | + clientRegistrationTemplate.setId(savedClientRegistrationTemplate.getId()); | |
69 | + clientRegistrationTemplate.setCreatedTime(savedClientRegistrationTemplate.getCreatedTime()); | |
70 | + Assert.assertEquals(clientRegistrationTemplate, savedClientRegistrationTemplate); | |
71 | + } | |
72 | + | |
73 | + @Test | |
74 | + public void testFindTemplate() { | |
75 | + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = validClientRegistrationTemplate(UUID.randomUUID().toString()); | |
76 | + OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); | |
77 | + | |
78 | + OAuth2ClientRegistrationTemplate foundClientRegistrationTemplate = oAuth2ConfigTemplateService.findClientRegistrationTemplateById(savedClientRegistrationTemplate.getId()); | |
79 | + Assert.assertEquals(savedClientRegistrationTemplate, foundClientRegistrationTemplate); | |
80 | + } | |
81 | + | |
82 | + @Test | |
83 | + public void testFindAll() { | |
84 | + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); | |
85 | + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); | |
86 | + | |
87 | + Assert.assertEquals(2, oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().size()); | |
88 | + } | |
89 | + | |
90 | + @Test | |
91 | + public void testDeleteTemplate() { | |
92 | + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); | |
93 | + oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); | |
94 | + OAuth2ClientRegistrationTemplate saved = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); | |
95 | + | |
96 | + Assert.assertEquals(3, oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().size()); | |
97 | + Assert.assertNotNull(oAuth2ConfigTemplateService.findClientRegistrationTemplateById(saved.getId())); | |
98 | + | |
99 | + oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(saved.getId()); | |
100 | + | |
101 | + Assert.assertEquals(2, oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().size()); | |
102 | + Assert.assertNull(oAuth2ConfigTemplateService.findClientRegistrationTemplateById(saved.getId())); | |
103 | + } | |
104 | + | |
105 | + private OAuth2ClientRegistrationTemplate validClientRegistrationTemplate(String providerId) { | |
106 | + OAuth2ClientRegistrationTemplate clientRegistrationTemplate = new OAuth2ClientRegistrationTemplate(); | |
107 | + clientRegistrationTemplate.setProviderId(providerId); | |
108 | + clientRegistrationTemplate.setAdditionalInfo(mapper.createObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString())); | |
109 | + clientRegistrationTemplate.setMapperType(MapperType.BASIC); | |
110 | + clientRegistrationTemplate.setBasic( | |
111 | + OAuth2BasicMapperConfig.builder() | |
112 | + .firstNameAttributeKey("firstName") | |
113 | + .lastNameAttributeKey("lastName") | |
114 | + .emailAttributeKey("email") | |
115 | + .tenantNamePattern("tenant") | |
116 | + .defaultDashboardName("Test") | |
117 | + .alwaysFullScreen(true) | |
118 | + .build() | |
119 | + ); | |
120 | + clientRegistrationTemplate.setAuthorizationUri("authorizationUri"); | |
121 | + clientRegistrationTemplate.setAccessTokenUri("tokenUri"); | |
122 | + clientRegistrationTemplate.setScope(Arrays.asList("scope1", "scope2")); | |
123 | + clientRegistrationTemplate.setUserInfoUri("userInfoUri"); | |
124 | + clientRegistrationTemplate.setUserNameAttributeName("userNameAttributeName"); | |
125 | + clientRegistrationTemplate.setJwkSetUri("jwkSetUri"); | |
126 | + clientRegistrationTemplate.setClientAuthenticationMethod("clientAuthenticationMethod"); | |
127 | + clientRegistrationTemplate.setComment("comment"); | |
128 | + clientRegistrationTemplate.setLoginButtonIcon("icon"); | |
129 | + clientRegistrationTemplate.setLoginButtonLabel("label"); | |
130 | + clientRegistrationTemplate.setHelpLink("helpLink"); | |
131 | + return clientRegistrationTemplate; | |
132 | + } | |
133 | +} | ... | ... |
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.dao.service; | |
17 | + | |
18 | +import com.google.common.collect.Sets; | |
19 | +import org.junit.After; | |
20 | +import org.junit.Assert; | |
21 | +import org.junit.Before; | |
22 | +import org.junit.Test; | |
23 | +import org.springframework.beans.factory.annotation.Autowired; | |
24 | +import org.thingsboard.server.common.data.oauth2.*; | |
25 | +import org.thingsboard.server.dao.exception.DataValidationException; | |
26 | +import org.thingsboard.server.dao.oauth2.OAuth2Service; | |
27 | + | |
28 | +import java.util.*; | |
29 | +import java.util.stream.Collectors; | |
30 | + | |
31 | +public class BaseOAuth2ServiceTest extends AbstractServiceTest { | |
32 | + private static final OAuth2ClientsParams EMPTY_PARAMS = new OAuth2ClientsParams(false, new HashSet<>()); | |
33 | + | |
34 | + @Autowired | |
35 | + protected OAuth2Service oAuth2Service; | |
36 | + | |
37 | + @Before | |
38 | + public void beforeRun() { | |
39 | + Assert.assertTrue(oAuth2Service.findAllClientRegistrationInfos().isEmpty()); | |
40 | + } | |
41 | + | |
42 | + @After | |
43 | + public void after() { | |
44 | + oAuth2Service.saveOAuth2Params(EMPTY_PARAMS); | |
45 | + Assert.assertTrue(oAuth2Service.findAllClientRegistrationInfos().isEmpty()); | |
46 | + Assert.assertTrue(oAuth2Service.findOAuth2Params().getDomainsParams().isEmpty()); | |
47 | + } | |
48 | + | |
49 | + @Test(expected = DataValidationException.class) | |
50 | + public void testSaveHttpAndMixedDomainsTogether() { | |
51 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
52 | + OAuth2ClientsDomainParams.builder() | |
53 | + .domainInfos(Sets.newHashSet( | |
54 | + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), | |
55 | + DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), | |
56 | + DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() | |
57 | + )) | |
58 | + .clientRegistrations(Sets.newHashSet( | |
59 | + validClientRegistrationDto(), | |
60 | + validClientRegistrationDto(), | |
61 | + validClientRegistrationDto() | |
62 | + )) | |
63 | + .build() | |
64 | + )); | |
65 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
66 | + } | |
67 | + | |
68 | + @Test(expected = DataValidationException.class) | |
69 | + public void testSaveHttpsAndMixedDomainsTogether() { | |
70 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
71 | + OAuth2ClientsDomainParams.builder() | |
72 | + .domainInfos(Sets.newHashSet( | |
73 | + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(), | |
74 | + DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), | |
75 | + DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() | |
76 | + )) | |
77 | + .clientRegistrations(Sets.newHashSet( | |
78 | + validClientRegistrationDto(), | |
79 | + validClientRegistrationDto(), | |
80 | + validClientRegistrationDto() | |
81 | + )) | |
82 | + .build() | |
83 | + )); | |
84 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
85 | + } | |
86 | + | |
87 | + @Test | |
88 | + public void testCreateAndFindParams() { | |
89 | + OAuth2ClientsParams clientsParams = createDefaultClientsParams(); | |
90 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
91 | + OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); | |
92 | + Assert.assertNotNull(foundClientsParams); | |
93 | + // TODO ask if it's safe to check equality on AdditionalProperties | |
94 | + Assert.assertEquals(clientsParams, foundClientsParams); | |
95 | + } | |
96 | + | |
97 | + @Test | |
98 | + public void testDisableParams() { | |
99 | + OAuth2ClientsParams clientsParams = createDefaultClientsParams(); | |
100 | + clientsParams.setEnabled(true); | |
101 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
102 | + OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); | |
103 | + Assert.assertNotNull(foundClientsParams); | |
104 | + Assert.assertEquals(clientsParams, foundClientsParams); | |
105 | + | |
106 | + clientsParams.setEnabled(false); | |
107 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
108 | + OAuth2ClientsParams foundDisabledClientsParams = oAuth2Service.findOAuth2Params(); | |
109 | + Assert.assertEquals(clientsParams, foundDisabledClientsParams); | |
110 | + } | |
111 | + | |
112 | + @Test | |
113 | + public void testClearDomainParams() { | |
114 | + OAuth2ClientsParams clientsParams = createDefaultClientsParams(); | |
115 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
116 | + OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); | |
117 | + Assert.assertNotNull(foundClientsParams); | |
118 | + Assert.assertEquals(clientsParams, foundClientsParams); | |
119 | + | |
120 | + oAuth2Service.saveOAuth2Params(EMPTY_PARAMS); | |
121 | + OAuth2ClientsParams foundAfterClearClientsParams = oAuth2Service.findOAuth2Params(); | |
122 | + Assert.assertNotNull(foundAfterClearClientsParams); | |
123 | + Assert.assertEquals(EMPTY_PARAMS, foundAfterClearClientsParams); | |
124 | + } | |
125 | + | |
126 | + @Test | |
127 | + public void testUpdateClientsParams() { | |
128 | + OAuth2ClientsParams clientsParams = createDefaultClientsParams(); | |
129 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
130 | + OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); | |
131 | + Assert.assertNotNull(foundClientsParams); | |
132 | + Assert.assertEquals(clientsParams, foundClientsParams); | |
133 | + | |
134 | + OAuth2ClientsParams newClientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
135 | + OAuth2ClientsDomainParams.builder() | |
136 | + .domainInfos(Sets.newHashSet( | |
137 | + DomainInfo.builder().name("another-domain").scheme(SchemeType.HTTPS).build() | |
138 | + )) | |
139 | + .clientRegistrations(Sets.newHashSet( | |
140 | + validClientRegistrationDto() | |
141 | + )) | |
142 | + .build(), | |
143 | + OAuth2ClientsDomainParams.builder() | |
144 | + .domainInfos(Sets.newHashSet( | |
145 | + DomainInfo.builder().name("test-domain").scheme(SchemeType.MIXED).build() | |
146 | + )) | |
147 | + .clientRegistrations(Sets.newHashSet( | |
148 | + validClientRegistrationDto() | |
149 | + )) | |
150 | + .build() | |
151 | + )); | |
152 | + oAuth2Service.saveOAuth2Params(newClientsParams); | |
153 | + OAuth2ClientsParams foundAfterUpdateClientsParams = oAuth2Service.findOAuth2Params(); | |
154 | + Assert.assertNotNull(foundAfterUpdateClientsParams); | |
155 | + Assert.assertEquals(newClientsParams, foundAfterUpdateClientsParams); | |
156 | + } | |
157 | + | |
158 | + @Test | |
159 | + public void testGetOAuth2Clients() { | |
160 | + Set<ClientRegistrationDto> firstGroup = Sets.newHashSet( | |
161 | + validClientRegistrationDto(), | |
162 | + validClientRegistrationDto(), | |
163 | + validClientRegistrationDto(), | |
164 | + validClientRegistrationDto() | |
165 | + ); | |
166 | + Set<ClientRegistrationDto> secondGroup = Sets.newHashSet( | |
167 | + validClientRegistrationDto(), | |
168 | + validClientRegistrationDto() | |
169 | + ); | |
170 | + Set<ClientRegistrationDto> thirdGroup = Sets.newHashSet( | |
171 | + validClientRegistrationDto() | |
172 | + ); | |
173 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
174 | + OAuth2ClientsDomainParams.builder() | |
175 | + .domainInfos(Sets.newHashSet( | |
176 | + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), | |
177 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), | |
178 | + DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() | |
179 | + )) | |
180 | + .clientRegistrations(firstGroup) | |
181 | + .build(), | |
182 | + OAuth2ClientsDomainParams.builder() | |
183 | + .domainInfos(Sets.newHashSet( | |
184 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), | |
185 | + DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() | |
186 | + )) | |
187 | + .clientRegistrations(secondGroup) | |
188 | + .build(), | |
189 | + OAuth2ClientsDomainParams.builder() | |
190 | + .domainInfos(Sets.newHashSet( | |
191 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), | |
192 | + DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() | |
193 | + )) | |
194 | + .clientRegistrations(thirdGroup) | |
195 | + .build() | |
196 | + )); | |
197 | + | |
198 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
199 | + OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); | |
200 | + Assert.assertNotNull(foundClientsParams); | |
201 | + Assert.assertEquals(clientsParams, foundClientsParams); | |
202 | + | |
203 | + List<OAuth2ClientInfo> firstGroupClientInfos = firstGroup.stream() | |
204 | + .map(clientRegistrationDto -> new OAuth2ClientInfo( | |
205 | + clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) | |
206 | + .collect(Collectors.toList()); | |
207 | + List<OAuth2ClientInfo> secondGroupClientInfos = secondGroup.stream() | |
208 | + .map(clientRegistrationDto -> new OAuth2ClientInfo( | |
209 | + clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) | |
210 | + .collect(Collectors.toList()); | |
211 | + List<OAuth2ClientInfo> thirdGroupClientInfos = thirdGroup.stream() | |
212 | + .map(clientRegistrationDto -> new OAuth2ClientInfo( | |
213 | + clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) | |
214 | + .collect(Collectors.toList()); | |
215 | + | |
216 | + List<OAuth2ClientInfo> nonExistentDomainClients = oAuth2Service.getOAuth2Clients("http", "non-existent-domain"); | |
217 | + Assert.assertTrue(nonExistentDomainClients.isEmpty()); | |
218 | + | |
219 | + List<OAuth2ClientInfo> firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain"); | |
220 | + Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); | |
221 | + firstGroupClientInfos.forEach(firstGroupClientInfo -> { | |
222 | + Assert.assertTrue( | |
223 | + firstDomainHttpClients.stream().anyMatch(clientInfo -> | |
224 | + clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) | |
225 | + && clientInfo.getName().equals(firstGroupClientInfo.getName())) | |
226 | + ); | |
227 | + }); | |
228 | + | |
229 | + List<OAuth2ClientInfo> firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain"); | |
230 | + Assert.assertTrue(firstDomainHttpsClients.isEmpty()); | |
231 | + | |
232 | + List<OAuth2ClientInfo> fourthDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "fourth-domain"); | |
233 | + Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpClients.size()); | |
234 | + secondGroupClientInfos.forEach(secondGroupClientInfo -> { | |
235 | + Assert.assertTrue( | |
236 | + fourthDomainHttpClients.stream().anyMatch(clientInfo -> | |
237 | + clientInfo.getIcon().equals(secondGroupClientInfo.getIcon()) | |
238 | + && clientInfo.getName().equals(secondGroupClientInfo.getName())) | |
239 | + ); | |
240 | + }); | |
241 | + List<OAuth2ClientInfo> fourthDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "fourth-domain"); | |
242 | + Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpsClients.size()); | |
243 | + secondGroupClientInfos.forEach(secondGroupClientInfo -> { | |
244 | + Assert.assertTrue( | |
245 | + fourthDomainHttpsClients.stream().anyMatch(clientInfo -> | |
246 | + clientInfo.getIcon().equals(secondGroupClientInfo.getIcon()) | |
247 | + && clientInfo.getName().equals(secondGroupClientInfo.getName())) | |
248 | + ); | |
249 | + }); | |
250 | + | |
251 | + List<OAuth2ClientInfo> secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); | |
252 | + Assert.assertEquals(firstGroupClientInfos.size() + secondGroupClientInfos.size(), secondDomainHttpClients.size()); | |
253 | + firstGroupClientInfos.forEach(firstGroupClientInfo -> { | |
254 | + Assert.assertTrue( | |
255 | + secondDomainHttpClients.stream().anyMatch(clientInfo -> | |
256 | + clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) | |
257 | + && clientInfo.getName().equals(firstGroupClientInfo.getName())) | |
258 | + ); | |
259 | + }); | |
260 | + secondGroupClientInfos.forEach(secondGroupClientInfo -> { | |
261 | + Assert.assertTrue( | |
262 | + secondDomainHttpClients.stream().anyMatch(clientInfo -> | |
263 | + clientInfo.getIcon().equals(secondGroupClientInfo.getIcon()) | |
264 | + && clientInfo.getName().equals(secondGroupClientInfo.getName())) | |
265 | + ); | |
266 | + }); | |
267 | + | |
268 | + List<OAuth2ClientInfo> secondDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "second-domain"); | |
269 | + Assert.assertEquals(firstGroupClientInfos.size() + thirdGroupClientInfos.size(), secondDomainHttpsClients.size()); | |
270 | + firstGroupClientInfos.forEach(firstGroupClientInfo -> { | |
271 | + Assert.assertTrue( | |
272 | + secondDomainHttpsClients.stream().anyMatch(clientInfo -> | |
273 | + clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) | |
274 | + && clientInfo.getName().equals(firstGroupClientInfo.getName())) | |
275 | + ); | |
276 | + }); | |
277 | + thirdGroupClientInfos.forEach(thirdGroupClientInfo -> { | |
278 | + Assert.assertTrue( | |
279 | + secondDomainHttpsClients.stream().anyMatch(clientInfo -> | |
280 | + clientInfo.getIcon().equals(thirdGroupClientInfo.getIcon()) | |
281 | + && clientInfo.getName().equals(thirdGroupClientInfo.getName())) | |
282 | + ); | |
283 | + }); | |
284 | + } | |
285 | + | |
286 | + @Test | |
287 | + public void testGetOAuth2ClientsForHttpAndHttps() { | |
288 | + Set<ClientRegistrationDto> firstGroup = Sets.newHashSet( | |
289 | + validClientRegistrationDto(), | |
290 | + validClientRegistrationDto(), | |
291 | + validClientRegistrationDto(), | |
292 | + validClientRegistrationDto() | |
293 | + ); | |
294 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
295 | + OAuth2ClientsDomainParams.builder() | |
296 | + .domainInfos(Sets.newHashSet( | |
297 | + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), | |
298 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), | |
299 | + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build() | |
300 | + )) | |
301 | + .clientRegistrations(firstGroup) | |
302 | + .build() | |
303 | + )); | |
304 | + | |
305 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
306 | + OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); | |
307 | + Assert.assertNotNull(foundClientsParams); | |
308 | + Assert.assertEquals(clientsParams, foundClientsParams); | |
309 | + | |
310 | + List<OAuth2ClientInfo> firstGroupClientInfos = firstGroup.stream() | |
311 | + .map(clientRegistrationDto -> new OAuth2ClientInfo( | |
312 | + clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) | |
313 | + .collect(Collectors.toList()); | |
314 | + | |
315 | + List<OAuth2ClientInfo> firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain"); | |
316 | + Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); | |
317 | + firstGroupClientInfos.forEach(firstGroupClientInfo -> { | |
318 | + Assert.assertTrue( | |
319 | + firstDomainHttpClients.stream().anyMatch(clientInfo -> | |
320 | + clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) | |
321 | + && clientInfo.getName().equals(firstGroupClientInfo.getName())) | |
322 | + ); | |
323 | + }); | |
324 | + | |
325 | + List<OAuth2ClientInfo> firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain"); | |
326 | + Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpsClients.size()); | |
327 | + firstGroupClientInfos.forEach(firstGroupClientInfo -> { | |
328 | + Assert.assertTrue( | |
329 | + firstDomainHttpsClients.stream().anyMatch(clientInfo -> | |
330 | + clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) | |
331 | + && clientInfo.getName().equals(firstGroupClientInfo.getName())) | |
332 | + ); | |
333 | + }); | |
334 | + } | |
335 | + | |
336 | + @Test | |
337 | + public void testGetDisabledOAuth2Clients() { | |
338 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
339 | + OAuth2ClientsDomainParams.builder() | |
340 | + .domainInfos(Sets.newHashSet( | |
341 | + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), | |
342 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), | |
343 | + DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() | |
344 | + )) | |
345 | + .clientRegistrations(Sets.newHashSet( | |
346 | + validClientRegistrationDto(), | |
347 | + validClientRegistrationDto(), | |
348 | + validClientRegistrationDto() | |
349 | + )) | |
350 | + .build(), | |
351 | + OAuth2ClientsDomainParams.builder() | |
352 | + .domainInfos(Sets.newHashSet( | |
353 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), | |
354 | + DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() | |
355 | + )) | |
356 | + .clientRegistrations(Sets.newHashSet( | |
357 | + validClientRegistrationDto(), | |
358 | + validClientRegistrationDto() | |
359 | + )) | |
360 | + .build() | |
361 | + )); | |
362 | + | |
363 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
364 | + | |
365 | + List<OAuth2ClientInfo> secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); | |
366 | + Assert.assertEquals(5, secondDomainHttpClients.size()); | |
367 | + | |
368 | + clientsParams.setEnabled(false); | |
369 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
370 | + | |
371 | + List<OAuth2ClientInfo> secondDomainHttpDisabledClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); | |
372 | + Assert.assertEquals(0, secondDomainHttpDisabledClients.size()); | |
373 | + } | |
374 | + | |
375 | + @Test | |
376 | + public void testFindAllClientRegistrationInfos() { | |
377 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
378 | + OAuth2ClientsDomainParams.builder() | |
379 | + .domainInfos(Sets.newHashSet( | |
380 | + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), | |
381 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), | |
382 | + DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() | |
383 | + )) | |
384 | + .clientRegistrations(Sets.newHashSet( | |
385 | + validClientRegistrationDto(), | |
386 | + validClientRegistrationDto(), | |
387 | + validClientRegistrationDto() | |
388 | + )) | |
389 | + .build(), | |
390 | + OAuth2ClientsDomainParams.builder() | |
391 | + .domainInfos(Sets.newHashSet( | |
392 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), | |
393 | + DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() | |
394 | + )) | |
395 | + .clientRegistrations(Sets.newHashSet( | |
396 | + validClientRegistrationDto(), | |
397 | + validClientRegistrationDto() | |
398 | + )) | |
399 | + .build(), | |
400 | + OAuth2ClientsDomainParams.builder() | |
401 | + .domainInfos(Sets.newHashSet( | |
402 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), | |
403 | + DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() | |
404 | + )) | |
405 | + .clientRegistrations(Sets.newHashSet( | |
406 | + validClientRegistrationDto() | |
407 | + )) | |
408 | + .build() | |
409 | + )); | |
410 | + | |
411 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
412 | + List<OAuth2ClientRegistrationInfo> foundClientRegistrationInfos = oAuth2Service.findAllClientRegistrationInfos(); | |
413 | + Assert.assertEquals(6, foundClientRegistrationInfos.size()); | |
414 | + clientsParams.getDomainsParams().stream() | |
415 | + .flatMap(domainParams -> domainParams.getClientRegistrations().stream()) | |
416 | + .forEach(clientRegistrationDto -> | |
417 | + Assert.assertTrue( | |
418 | + foundClientRegistrationInfos.stream() | |
419 | + .anyMatch(clientRegistrationInfo -> clientRegistrationInfo.getClientId().equals(clientRegistrationDto.getClientId())) | |
420 | + ) | |
421 | + ); | |
422 | + } | |
423 | + | |
424 | + @Test | |
425 | + public void testFindClientRegistrationById() { | |
426 | + OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( | |
427 | + OAuth2ClientsDomainParams.builder() | |
428 | + .domainInfos(Sets.newHashSet( | |
429 | + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), | |
430 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), | |
431 | + DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() | |
432 | + )) | |
433 | + .clientRegistrations(Sets.newHashSet( | |
434 | + validClientRegistrationDto(), | |
435 | + validClientRegistrationDto(), | |
436 | + validClientRegistrationDto() | |
437 | + )) | |
438 | + .build(), | |
439 | + OAuth2ClientsDomainParams.builder() | |
440 | + .domainInfos(Sets.newHashSet( | |
441 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), | |
442 | + DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() | |
443 | + )) | |
444 | + .clientRegistrations(Sets.newHashSet( | |
445 | + validClientRegistrationDto(), | |
446 | + validClientRegistrationDto() | |
447 | + )) | |
448 | + .build(), | |
449 | + OAuth2ClientsDomainParams.builder() | |
450 | + .domainInfos(Sets.newHashSet( | |
451 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), | |
452 | + DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() | |
453 | + )) | |
454 | + .clientRegistrations(Sets.newHashSet( | |
455 | + validClientRegistrationDto() | |
456 | + )) | |
457 | + .build() | |
458 | + )); | |
459 | + | |
460 | + oAuth2Service.saveOAuth2Params(clientsParams); | |
461 | + List<OAuth2ClientRegistrationInfo> clientRegistrationInfos = oAuth2Service.findAllClientRegistrationInfos(); | |
462 | + clientRegistrationInfos.forEach(clientRegistrationInfo -> { | |
463 | + OAuth2ClientRegistrationInfo foundClientRegistrationInfo = oAuth2Service.findClientRegistrationInfo(clientRegistrationInfo.getUuidId()); | |
464 | + Assert.assertEquals(clientRegistrationInfo, foundClientRegistrationInfo); | |
465 | + }); | |
466 | + } | |
467 | + | |
468 | + private OAuth2ClientsParams createDefaultClientsParams() { | |
469 | + return new OAuth2ClientsParams(true, Sets.newHashSet( | |
470 | + OAuth2ClientsDomainParams.builder() | |
471 | + .domainInfos(Sets.newHashSet( | |
472 | + DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), | |
473 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), | |
474 | + DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() | |
475 | + )) | |
476 | + .clientRegistrations(Sets.newHashSet( | |
477 | + validClientRegistrationDto(), | |
478 | + validClientRegistrationDto(), | |
479 | + validClientRegistrationDto(), | |
480 | + validClientRegistrationDto() | |
481 | + )) | |
482 | + .build(), | |
483 | + OAuth2ClientsDomainParams.builder() | |
484 | + .domainInfos(Sets.newHashSet( | |
485 | + DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), | |
486 | + DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() | |
487 | + )) | |
488 | + .clientRegistrations(Sets.newHashSet( | |
489 | + validClientRegistrationDto(), | |
490 | + validClientRegistrationDto() | |
491 | + )) | |
492 | + .build() | |
493 | + )); | |
494 | + } | |
495 | + | |
496 | + private ClientRegistrationDto validClientRegistrationDto() { | |
497 | + return ClientRegistrationDto.builder() | |
498 | + .clientId(UUID.randomUUID().toString()) | |
499 | + .clientSecret(UUID.randomUUID().toString()) | |
500 | + .authorizationUri(UUID.randomUUID().toString()) | |
501 | + .accessTokenUri(UUID.randomUUID().toString()) | |
502 | + .scope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString())) | |
503 | + .userInfoUri(UUID.randomUUID().toString()) | |
504 | + .userNameAttributeName(UUID.randomUUID().toString()) | |
505 | + .jwkSetUri(UUID.randomUUID().toString()) | |
506 | + .clientAuthenticationMethod(UUID.randomUUID().toString()) | |
507 | + .loginButtonLabel(UUID.randomUUID().toString()) | |
508 | + .loginButtonIcon(UUID.randomUUID().toString()) | |
509 | + .additionalInfo(mapper.createObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString())) | |
510 | + .mapperConfig( | |
511 | + OAuth2MapperConfig.builder() | |
512 | + .allowUserCreation(true) | |
513 | + .activateUser(true) | |
514 | + .type(MapperType.CUSTOM) | |
515 | + .custom( | |
516 | + OAuth2CustomMapperConfig.builder() | |
517 | + .url(UUID.randomUUID().toString()) | |
518 | + .build() | |
519 | + ) | |
520 | + .build() | |
521 | + ) | |
522 | + .build(); | |
523 | + } | |
524 | +} | ... | ... |
dao/src/test/java/org/thingsboard/server/dao/service/sql/OAuth2ConfigTemplateServiceSqlTest.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.dao.service.sql; | |
17 | + | |
18 | +import org.thingsboard.server.dao.service.BaseOAuth2ConfigTemplateServiceTest; | |
19 | +import org.thingsboard.server.dao.service.DaoSqlTest; | |
20 | + | |
21 | +@DaoSqlTest | |
22 | +public class OAuth2ConfigTemplateServiceSqlTest extends BaseOAuth2ConfigTemplateServiceTest { | |
23 | +} | ... | ... |
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.dao.service.sql; | |
17 | + | |
18 | +import org.thingsboard.server.dao.service.BaseOAuth2ServiceTest; | |
19 | +import org.thingsboard.server.dao.service.DaoSqlTest; | |
20 | + | |
21 | +@DaoSqlTest | |
22 | +public class OAuth2ServiceSqlTest extends BaseOAuth2ServiceTest { | |
23 | +} | ... | ... |
... | ... | @@ -24,4 +24,7 @@ DROP TABLE IF EXISTS tenant_profile; |
24 | 24 | DROP TABLE IF EXISTS rule_node_state; |
25 | 25 | DROP TABLE IF EXISTS rule_node; |
26 | 26 | DROP TABLE IF EXISTS rule_chain; |
27 | +DROP TABLE IF EXISTS oauth2_client_registration; | |
28 | +DROP TABLE IF EXISTS oauth2_client_registration_info; | |
29 | +DROP TABLE IF EXISTS oauth2_client_registration_template; | |
27 | 30 | DROP FUNCTION IF EXISTS to_uuid; | ... | ... |
... | ... | @@ -25,3 +25,6 @@ DROP TABLE IF EXISTS rule_node_state; |
25 | 25 | DROP TABLE IF EXISTS rule_node; |
26 | 26 | DROP TABLE IF EXISTS rule_chain; |
27 | 27 | DROP TABLE IF EXISTS tb_schema_settings; |
28 | +DROP TABLE IF EXISTS oauth2_client_registration; | |
29 | +DROP TABLE IF EXISTS oauth2_client_registration_info; | |
30 | +DROP TABLE IF EXISTS oauth2_client_registration_template; | ... | ... |
... | ... | @@ -25,3 +25,6 @@ DROP TABLE IF EXISTS entity_view; |
25 | 25 | DROP TABLE IF EXISTS device_profile; |
26 | 26 | DROP TABLE IF EXISTS tenant_profile; |
27 | 27 | DROP TABLE IF EXISTS tb_schema_settings; |
28 | +DROP TABLE IF EXISTS oauth2_client_registration; | |
29 | +DROP TABLE IF EXISTS oauth2_client_registration_info; | |
30 | +DROP TABLE IF EXISTS oauth2_client_registration_template; | ... | ... |
... | ... | @@ -18,7 +18,7 @@ import { Injectable, NgZone } from '@angular/core'; |
18 | 18 | import { JwtHelperService } from '@auth0/angular-jwt'; |
19 | 19 | import { HttpClient } from '@angular/common/http'; |
20 | 20 | |
21 | -import { forkJoin, Observable, of, throwError, ReplaySubject } from 'rxjs'; | |
21 | +import { forkJoin, Observable, of, ReplaySubject, throwError } from 'rxjs'; | |
22 | 22 | import { catchError, map, mergeMap, tap } from 'rxjs/operators'; |
23 | 23 | |
24 | 24 | import { LoginRequest, LoginResponse, OAuth2Client, PublicLoginRequest } from '@shared/models/login.models'; | ... | ... |
... | ... | @@ -18,7 +18,14 @@ import { Injectable } from '@angular/core'; |
18 | 18 | import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; |
19 | 19 | import { Observable } from 'rxjs'; |
20 | 20 | import { HttpClient } from '@angular/common/http'; |
21 | -import { AdminSettings, MailServerSettings, SecuritySettings, UpdateMessage } from '@shared/models/settings.models'; | |
21 | +import { | |
22 | + AdminSettings, | |
23 | + ClientProviderTemplated, | |
24 | + MailServerSettings, | |
25 | + OAuth2Settings, | |
26 | + SecuritySettings, | |
27 | + UpdateMessage | |
28 | +} from '@shared/models/settings.models'; | |
22 | 29 | |
23 | 30 | @Injectable({ |
24 | 31 | providedIn: 'root' |
... | ... | @@ -53,6 +60,19 @@ export class AdminService { |
53 | 60 | defaultHttpOptionsFromConfig(config)); |
54 | 61 | } |
55 | 62 | |
63 | + public getOAuth2Settings(config?: RequestConfig): Observable<OAuth2Settings> { | |
64 | + return this.http.get<OAuth2Settings>(`/api/oauth2/config`, defaultHttpOptionsFromConfig(config)); | |
65 | + } | |
66 | + | |
67 | + public getOAuth2Template(config?: RequestConfig): Observable<Array<ClientProviderTemplated>> { | |
68 | + return this.http.get<Array<ClientProviderTemplated>>(`/api/oauth2/config/template`, defaultHttpOptionsFromConfig(config)); | |
69 | + } | |
70 | + | |
71 | + public saveOAuth2Settings(OAuth2Setting: OAuth2Settings, config?: RequestConfig): Observable<OAuth2Settings> { | |
72 | + return this.http.post<OAuth2Settings>('/api/oauth2/config', OAuth2Setting, | |
73 | + defaultHttpOptionsFromConfig(config)); | |
74 | + } | |
75 | + | |
56 | 76 | public checkUpdates(config?: RequestConfig): Observable<UpdateMessage> { |
57 | 77 | return this.http.get<UpdateMessage>(`/api/admin/updates`, defaultHttpOptionsFromConfig(config)); |
58 | 78 | } | ... | ... |
... | ... | @@ -108,7 +108,7 @@ export class MenuService { |
108 | 108 | name: 'admin.system-settings', |
109 | 109 | type: 'toggle', |
110 | 110 | path: '/settings', |
111 | - height: '120px', | |
111 | + height: '160px', | |
112 | 112 | icon: 'settings', |
113 | 113 | pages: [ |
114 | 114 | { |
... | ... | @@ -131,6 +131,13 @@ export class MenuService { |
131 | 131 | type: 'link', |
132 | 132 | path: '/settings/security-settings', |
133 | 133 | icon: 'security' |
134 | + }, | |
135 | + { | |
136 | + id: guid(), | |
137 | + name: 'admin.oauth2.oauth2', | |
138 | + type: 'link', | |
139 | + path: '/settings/oauth2', | |
140 | + icon: 'security' | |
134 | 141 | } |
135 | 142 | ] |
136 | 143 | } | ... | ... |
... | ... | @@ -22,6 +22,7 @@ import { ConfirmOnExitGuard } from '@core/guards/confirm-on-exit.guard'; |
22 | 22 | import { Authority } from '@shared/models/authority.enum'; |
23 | 23 | import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component'; |
24 | 24 | import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; |
25 | +import { OAuth2SettingsComponent } from '@home/pages/admin/oauth2-settings.component'; | |
25 | 26 | |
26 | 27 | const routes: Routes = [ |
27 | 28 | { |
... | ... | @@ -77,6 +78,19 @@ const routes: Routes = [ |
77 | 78 | icon: 'security' |
78 | 79 | } |
79 | 80 | } |
81 | + }, | |
82 | + { | |
83 | + path: 'oauth2', | |
84 | + component: OAuth2SettingsComponent, | |
85 | + canDeactivate: [ConfirmOnExitGuard], | |
86 | + data: { | |
87 | + auth: [Authority.SYS_ADMIN], | |
88 | + title: 'admin.oauth2.oauth2', | |
89 | + breadcrumb: { | |
90 | + label: 'admin.oauth2.oauth2', | |
91 | + icon: 'security' | |
92 | + } | |
93 | + } | |
80 | 94 | } |
81 | 95 | ] |
82 | 96 | } | ... | ... |
... | ... | @@ -23,13 +23,15 @@ import { MailServerComponent } from '@modules/home/pages/admin/mail-server.compo |
23 | 23 | import { GeneralSettingsComponent } from '@modules/home/pages/admin/general-settings.component'; |
24 | 24 | import { SecuritySettingsComponent } from '@modules/home/pages/admin/security-settings.component'; |
25 | 25 | import { HomeComponentsModule } from '@modules/home/components/home-components.module'; |
26 | +import { OAuth2SettingsComponent } from '@modules/home/pages/admin/oauth2-settings.component'; | |
26 | 27 | |
27 | 28 | @NgModule({ |
28 | 29 | declarations: |
29 | 30 | [ |
30 | 31 | GeneralSettingsComponent, |
31 | 32 | MailServerComponent, |
32 | - SecuritySettingsComponent | |
33 | + SecuritySettingsComponent, | |
34 | + OAuth2SettingsComponent | |
33 | 35 | ], |
34 | 36 | imports: [ |
35 | 37 | CommonModule, | ... | ... |
1 | +<!-- | |
2 | + | |
3 | + Copyright © 2016-2020 The Thingsboard Authors | |
4 | + | |
5 | + Licensed under the Apache License, Version 2.0 (the "License"); | |
6 | + you may not use this file except in compliance with the License. | |
7 | + You may obtain a copy of the License at | |
8 | + | |
9 | + http://www.apache.org/licenses/LICENSE-2.0 | |
10 | + | |
11 | + Unless required by applicable law or agreed to in writing, software | |
12 | + distributed under the License is distributed on an "AS IS" BASIS, | |
13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
14 | + See the License for the specific language governing permissions and | |
15 | + limitations under the License. | |
16 | + | |
17 | +--> | |
18 | +<div> | |
19 | + <mat-card class="settings-card"> | |
20 | + <mat-card-title> | |
21 | + <div fxLayout="row"> | |
22 | + <span class="mat-headline" translate>admin.oauth2.oauth2</span> | |
23 | + <span fxFlex></span> | |
24 | + <div tb-help="oauth2Settings"></div> | |
25 | + </div> | |
26 | + </mat-card-title> | |
27 | + <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> | |
28 | + </mat-progress-bar> | |
29 | + <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> | |
30 | + <mat-card-content style="padding-top: 16px;"> | |
31 | + <form [formGroup]="oauth2SettingsForm" (ngSubmit)="save()"> | |
32 | + <fieldset [disabled]="isLoading$ | async"> | |
33 | + <mat-checkbox formControlName="enabled"> | |
34 | + {{ 'admin.oauth2.enable' | translate }} | |
35 | + </mat-checkbox> | |
36 | + <section *ngIf="oauth2SettingsForm.get('enabled').value && !(isLoading$ | async)" style="margin-top: 1em;"> | |
37 | + <ng-container formArrayName="domainsParams"> | |
38 | + <div class="container"> | |
39 | + <mat-accordion multi> | |
40 | + <ng-container *ngFor="let domain of domainsParams.controls; let i = index; trackBy: trackByParams"> | |
41 | + <mat-expansion-panel [formGroupName]="i"> | |
42 | + <mat-expansion-panel-header> | |
43 | + <mat-panel-title fxLayoutAlign="start center"> | |
44 | + {{ domainListTittle(domain) }} | |
45 | + </mat-panel-title> | |
46 | + <mat-panel-description fxLayoutAlign="end center"> | |
47 | + <button mat-icon-button | |
48 | + type="button" | |
49 | + (click)="deleteDomain($event, i)" | |
50 | + matTooltip="{{ 'action.delete' | translate }}" | |
51 | + matTooltipPosition="above"> | |
52 | + <mat-icon>delete</mat-icon> | |
53 | + </button> | |
54 | + </mat-panel-description> | |
55 | + </mat-expansion-panel-header> | |
56 | + | |
57 | + <ng-template matExpansionPanelContent> | |
58 | + <ng-container formArrayName="domainInfos"> | |
59 | + <section *ngFor="let domainInfo of clientDomainInfos(domain).controls; let n = index; trackBy: trackByParams" | |
60 | + class="domains-list"> | |
61 | + <div [formGroupName]="n" fxLayout="row" fxLayoutGap="8px"> | |
62 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px"> | |
63 | + <div fxLayout="column" fxFlex.sm="60" fxFlex.gt-sm="50"> | |
64 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px"> | |
65 | + <mat-form-field fxFlex="30" fxFlex.xs class="mat-block"> | |
66 | + <mat-label translate>admin.oauth2.protocol</mat-label> | |
67 | + <mat-select formControlName="scheme"> | |
68 | + <mat-option *ngFor="let protocol of protocols" [value]="protocol"> | |
69 | + {{ domainSchemaTranslations.get(protocol) | translate | uppercase }} | |
70 | + </mat-option> | |
71 | + </mat-select> | |
72 | + </mat-form-field> | |
73 | + <mat-form-field fxFlex class="mat-block"> | |
74 | + <mat-label translate>admin.domain-name</mat-label> | |
75 | + <input matInput formControlName="name" required> | |
76 | + <mat-error *ngIf="domainInfo.get('name').hasError('pattern')"> | |
77 | + {{ 'admin.error-verification-url' | translate }} | |
78 | + </mat-error> | |
79 | + </mat-form-field> | |
80 | + </div> | |
81 | + <mat-error *ngIf="domainInfo.hasError('unique')"> | |
82 | + {{ 'admin.domain-name-unique' | translate }} | |
83 | + </mat-error> | |
84 | + </div> | |
85 | + | |
86 | + <div fxFlex fxLayout="column"> | |
87 | + <mat-form-field fxFlex class="mat-block"> | |
88 | + <mat-label translate>admin.oauth2.redirect-uri-template</mat-label> | |
89 | + <input matInput [value]="redirectURI(domainInfo)" readonly> | |
90 | + <button mat-icon-button color="primary" matSuffix type="button" | |
91 | + ngxClipboard cbContent="{{ redirectURI(domainInfo) }}" | |
92 | + matTooltip="{{ 'admin.oauth2.copy-redirect-uri' | translate }}" | |
93 | + matTooltipPosition="above"> | |
94 | + <mat-icon class="material-icons" svgIcon="mdi:clipboard-arrow-left"></mat-icon> | |
95 | + </button> | |
96 | + </mat-form-field> | |
97 | + | |
98 | + <mat-form-field fxFlex *ngIf="domainInfo.get('scheme').value === 'MIXED'" class="mat-block"> | |
99 | + <mat-label></mat-label> | |
100 | + <input matInput [value]="redirectURIMixed(domainInfo)" readonly> | |
101 | + <button mat-icon-button color="primary" matSuffix type="button" | |
102 | + ngxClipboard cbContent="{{ redirectURIMixed(domainInfo) }}" | |
103 | + matTooltip="{{ 'admin.oauth2.copy-redirect-uri' | translate }}" | |
104 | + matTooltipPosition="above"> | |
105 | + <mat-icon class="material-icons" svgIcon="mdi:clipboard-arrow-left"></mat-icon> | |
106 | + </button> | |
107 | + </mat-form-field> | |
108 | + </div> | |
109 | + </div> | |
110 | + | |
111 | + <div fxLayout="column" fxLayoutAlign="center start"> | |
112 | + <button type="button" mat-icon-button color="primary" | |
113 | + (click)="removeDomain($event, domain, n)" | |
114 | + [disabled]="clientDomainInfos(domain).controls.length < 2" | |
115 | + matTooltip="{{ 'admin.oauth2.delete-domain' | translate }}" | |
116 | + matTooltipPosition="above"> | |
117 | + <mat-icon>close</mat-icon> | |
118 | + </button> | |
119 | + </div> | |
120 | + </div> | |
121 | + </section> | |
122 | + <div fxLayout="row" fxLayoutAlign="end center" style="margin-bottom: 1.25em"> | |
123 | + <button mat-button mat-raised-button color="primary" | |
124 | + [disabled]="(isLoading$ | async)" | |
125 | + (click)="addDomainInfo(domain)" | |
126 | + type="button"> | |
127 | + {{'admin.oauth2.add-domain' | translate}} | |
128 | + </button> | |
129 | + </div> | |
130 | + </ng-container> | |
131 | + | |
132 | + <ng-container formArrayName="clientRegistrations"> | |
133 | + <div class="container"> | |
134 | + <mat-expansion-panel *ngFor="let registration of clientDomainProviders(domain).controls; let j = index; trackBy: trackByParams" | |
135 | + class="registration-card mat-elevation-z0"> | |
136 | + <mat-expansion-panel-header> | |
137 | + <mat-panel-title fxLayoutAlign="start center"> | |
138 | + {{ getProviderName(registration) }} | |
139 | + </mat-panel-title> | |
140 | + <mat-panel-description fxLayoutAlign="end center"> | |
141 | + <button mat-icon-button | |
142 | + type="button" | |
143 | + [disabled]="clientDomainProviders(domain).controls.length < 2" | |
144 | + (click)="deleteProvider($event, domain, j)" | |
145 | + matTooltip="{{ 'admin.oauth2.delete-provider' | translate }}" | |
146 | + matTooltipPosition="above"> | |
147 | + <mat-icon>delete</mat-icon> | |
148 | + </button> | |
149 | + </mat-panel-description> | |
150 | + </mat-expansion-panel-header> | |
151 | + | |
152 | + <ng-template matExpansionPanelContent> | |
153 | + <section [formGroupName]="j"> | |
154 | + <section formGroupName="additionalInfo" fxLayout="row"> | |
155 | + <mat-form-field fxFlex class="mat-block"> | |
156 | + <mat-label translate>admin.oauth2.login-provider</mat-label> | |
157 | + <mat-select formControlName="providerName"> | |
158 | + <mat-option *ngFor="let provider of templateProvider" [value]="provider"> | |
159 | + {{ provider }} | |
160 | + </mat-option> | |
161 | + </mat-select> | |
162 | + </mat-form-field> | |
163 | + <div [tb-help]="getHelpLink(registration)" *ngIf="getProviderName(registration) !== 'Custom'"></div> | |
164 | + </section> | |
165 | + | |
166 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> | |
167 | + <mat-form-field fxFlex class="mat-block"> | |
168 | + <mat-label translate>admin.oauth2.client-id</mat-label> | |
169 | + <input matInput formControlName="clientId" required> | |
170 | + <mat-error *ngIf="registration.get('clientId').hasError('required')"> | |
171 | + {{ 'admin.oauth2.client-id-required' | translate }} | |
172 | + </mat-error> | |
173 | + </mat-form-field> | |
174 | + | |
175 | + <mat-form-field fxFlex class="mat-block"> | |
176 | + <mat-label translate>admin.oauth2.client-secret</mat-label> | |
177 | + <input matInput formControlName="clientSecret" required> | |
178 | + <mat-error *ngIf="registration.get('clientSecret').hasError('required')"> | |
179 | + {{ 'admin.oauth2.client-secret-required' | translate }} | |
180 | + </mat-error> | |
181 | + </mat-form-field> | |
182 | + </div> | |
183 | + | |
184 | + <mat-expansion-panel class="mat-elevation-z0 custom-settings" | |
185 | + [disabled]="getProviderName(registration) === 'Custom'" | |
186 | + [expanded]="getProviderName(registration) === 'Custom'"> | |
187 | + <mat-expansion-panel-header [fxHide]="getProviderName(registration) === 'Custom'"> | |
188 | + <mat-panel-description fxLayoutAlign="end center"> | |
189 | + {{ 'admin.oauth2.custom-setting' | translate }} | |
190 | + </mat-panel-description> | |
191 | + </mat-expansion-panel-header> | |
192 | + <ng-template matExpansionPanelContent> | |
193 | + <mat-tab-group dynamicHeight> | |
194 | + <mat-tab label="{{ 'admin.oauth2.general' | translate }}"> | |
195 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" style="margin-top: 16px;"> | |
196 | + <mat-form-field fxFlex class="mat-block"> | |
197 | + <mat-label translate>admin.oauth2.access-token-uri</mat-label> | |
198 | + <input matInput formControlName="accessTokenUri" required> | |
199 | + <button mat-icon-button matSuffix | |
200 | + type="button" | |
201 | + (click)="toggleEditMode(registration, 'accessTokenUri')" | |
202 | + *ngIf="getProviderName(registration) !== 'Custom'"> | |
203 | + <mat-icon class="material-icons">create</mat-icon> | |
204 | + </button> | |
205 | + <mat-error *ngIf="registration.get('accessTokenUri').hasError('required')"> | |
206 | + {{ 'admin.oauth2.access-token-uri-required' | translate }} | |
207 | + </mat-error> | |
208 | + <mat-error *ngIf="registration.get('accessTokenUri').hasError('pattern')"> | |
209 | + {{ 'admin.oauth2.uri-pattern-error' | translate }} | |
210 | + </mat-error> | |
211 | + </mat-form-field> | |
212 | + | |
213 | + <mat-form-field fxFlex class="mat-block"> | |
214 | + <mat-label translate>admin.oauth2.authorization-uri</mat-label> | |
215 | + <input matInput formControlName="authorizationUri" required> | |
216 | + <button mat-icon-button matSuffix | |
217 | + type="button" | |
218 | + (click)="toggleEditMode(registration, 'authorizationUri')" | |
219 | + *ngIf="getProviderName(registration) !== 'Custom'"> | |
220 | + <mat-icon class="material-icons">create</mat-icon> | |
221 | + </button> | |
222 | + <mat-error *ngIf="registration.get('authorizationUri').hasError('required')"> | |
223 | + {{ 'admin.oauth2.authorization-uri-required' | translate }} | |
224 | + </mat-error> | |
225 | + <mat-error *ngIf="registration.get('authorizationUri').hasError('pattern')"> | |
226 | + {{ 'admin.oauth2.uri-pattern-error' | translate }} | |
227 | + </mat-error> | |
228 | + </mat-form-field> | |
229 | + </div> | |
230 | + | |
231 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> | |
232 | + <mat-form-field fxFlex class="mat-block" appearance="legacy"> | |
233 | + <mat-label translate>admin.oauth2.jwk-set-uri</mat-label> | |
234 | + <input matInput formControlName="jwkSetUri"> | |
235 | + <button mat-icon-button matSuffix | |
236 | + type="button" aria-label="Clear" | |
237 | + (click)="toggleEditMode(registration, 'jwkSetUri')" | |
238 | + *ngIf="getProviderName(registration) !== 'Custom'"> | |
239 | + <mat-icon class="material-icons">create</mat-icon> | |
240 | + </button> | |
241 | + <mat-error *ngIf="registration.get('jwkSetUri').hasError('pattern')"> | |
242 | + {{ 'admin.oauth2.uri-pattern-error' | translate }} | |
243 | + </mat-error> | |
244 | + </mat-form-field> | |
245 | + | |
246 | + <mat-form-field fxFlex class="mat-block"> | |
247 | + <mat-label translate>admin.oauth2.user-info-uri</mat-label> | |
248 | + <input matInput formControlName="userInfoUri" required> | |
249 | + <button mat-icon-button matSuffix | |
250 | + type="button" | |
251 | + (click)="toggleEditMode(registration, 'userInfoUri')" | |
252 | + *ngIf="getProviderName(registration) !== 'Custom'"> | |
253 | + <mat-icon class="material-icons">create</mat-icon> | |
254 | + </button> | |
255 | + <mat-error *ngIf="registration.get('userInfoUri').hasError('required')"> | |
256 | + {{ 'admin.oauth2.user-info-uri-required' | translate }} | |
257 | + </mat-error> | |
258 | + <mat-error *ngIf="registration.get('userInfoUri').hasError('pattern')"> | |
259 | + {{ 'admin.oauth2.uri-pattern-error' | translate }} | |
260 | + </mat-error> | |
261 | + </mat-form-field> | |
262 | + </div> | |
263 | + | |
264 | + <mat-form-field fxFlex class="mat-block"> | |
265 | + <mat-label translate>admin.oauth2.client-authentication-method</mat-label> | |
266 | + <mat-select formControlName="clientAuthenticationMethod"> | |
267 | + <mat-option *ngFor="let clientAuthenticationMethod of clientAuthenticationMethods" | |
268 | + [value]="clientAuthenticationMethod"> | |
269 | + {{ clientAuthenticationMethod | uppercase }} | |
270 | + </mat-option> | |
271 | + </mat-select> | |
272 | + </mat-form-field> | |
273 | + | |
274 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" *ngIf="getProviderName(registration) === 'Custom'"> | |
275 | + <mat-form-field fxFlex class="mat-block" floatLabel="always"> | |
276 | + <mat-label translate>admin.oauth2.login-button-label</mat-label> | |
277 | + <input matInput formControlName="loginButtonLabel" | |
278 | + placeholder="{{ 'admin.oauth2.login-button-label-1' | translate }}" | |
279 | + required> | |
280 | + <mat-error *ngIf="registration.get('loginButtonLabel').hasError('required')"> | |
281 | + {{ 'admin.oauth2.login-button-label-required' | translate }} | |
282 | + </mat-error> | |
283 | + </mat-form-field> | |
284 | + | |
285 | + <mat-form-field fxFlex class="mat-block"> | |
286 | + <mat-label translate>admin.oauth2.login-button-icon</mat-label> | |
287 | + <input matInput formControlName="loginButtonIcon"> | |
288 | + </mat-form-field> | |
289 | + </div> | |
290 | + | |
291 | + <section formGroupName="mapperConfig"> | |
292 | + <div fxLayout="column" fxLayoutGap="8px" style="margin-bottom: 8px;"> | |
293 | + <mat-checkbox formControlName="allowUserCreation"> | |
294 | + {{ 'admin.oauth2.allow-user-creation' | translate }} | |
295 | + </mat-checkbox> | |
296 | + <mat-checkbox formControlName="activateUser"> | |
297 | + {{ 'admin.oauth2.activate-user' | translate }} | |
298 | + </mat-checkbox> | |
299 | + </div> | |
300 | + </section> | |
301 | + | |
302 | + <mat-form-field fxFlex class="mat-block"> | |
303 | + <mat-label translate>admin.oauth2.scope</mat-label> | |
304 | + <mat-chip-list #scopeList> | |
305 | + <mat-chip *ngFor="let scope of registration.get('scope').value; let k = index; trackBy: trackByParams" | |
306 | + removable (removed)="removeScope(k, registration)"> | |
307 | + {{scope}} | |
308 | + <mat-icon matChipRemove>cancel</mat-icon> | |
309 | + </mat-chip> | |
310 | + <input [matChipInputFor]="scopeList" | |
311 | + [matChipInputSeparatorKeyCodes]="separatorKeysCodes" | |
312 | + matChipInputAddOnBlur | |
313 | + (matChipInputTokenEnd)="addScope($event, registration)"> | |
314 | + </mat-chip-list> | |
315 | + <mat-error *ngIf="registration.get('scope').hasError('required')"> | |
316 | + {{ 'admin.oauth2.scope-required' | translate }} | |
317 | + </mat-error> | |
318 | + </mat-form-field> | |
319 | + | |
320 | + </mat-tab> | |
321 | + <mat-tab label="{{ 'admin.oauth2.mapper' | translate }}"> | |
322 | + <mat-form-field class="mat-block" style="margin-top: 16px;"> | |
323 | + <mat-label translate>admin.oauth2.user-name-attribute-name</mat-label> | |
324 | + <input matInput formControlName="userNameAttributeName" required> | |
325 | + <mat-error | |
326 | + *ngIf="registration.get('userNameAttributeName').hasError('required')"> | |
327 | + {{ 'admin.oauth2.user-name-attribute-name-required' | translate }} | |
328 | + </mat-error> | |
329 | + </mat-form-field> | |
330 | + | |
331 | + <section formGroupName="mapperConfig"> | |
332 | + <mat-form-field fxFlex class="mat-block"> | |
333 | + <mat-label translate>admin.oauth2.type</mat-label> | |
334 | + <mat-select formControlName="type"> | |
335 | + <mat-option *ngFor="let converterTypeExternalUser of converterTypesExternalUser" | |
336 | + [value]="converterTypeExternalUser"> | |
337 | + {{ converterTypeExternalUser }} | |
338 | + </mat-option> | |
339 | + </mat-select> | |
340 | + </mat-form-field> | |
341 | + | |
342 | + <section formGroupName="basic" | |
343 | + *ngIf="registration.get('mapperConfig.type').value === 'BASIC'"> | |
344 | + <mat-form-field class="mat-block"> | |
345 | + <mat-label translate>admin.oauth2.email-attribute-key</mat-label> | |
346 | + <input matInput formControlName="emailAttributeKey" required> | |
347 | + <mat-error | |
348 | + *ngIf="registration.get('mapperConfig.basic.emailAttributeKey').hasError('required')"> | |
349 | + {{ 'admin.oauth2.email-attribute-key-required' | translate }} | |
350 | + </mat-error> | |
351 | + </mat-form-field> | |
352 | + | |
353 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> | |
354 | + <mat-form-field fxFlex class="mat-block"> | |
355 | + <mat-label translate>admin.oauth2.first-name-attribute-key</mat-label> | |
356 | + <input matInput formControlName="firstNameAttributeKey"> | |
357 | + </mat-form-field> | |
358 | + | |
359 | + <mat-form-field fxFlex class="mat-block"> | |
360 | + <mat-label translate>admin.oauth2.last-name-attribute-key</mat-label> | |
361 | + <input matInput formControlName="lastNameAttributeKey"> | |
362 | + </mat-form-field> | |
363 | + </div> | |
364 | + | |
365 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> | |
366 | + <mat-form-field fxFlex class="mat-block"> | |
367 | + <mat-label translate>admin.oauth2.tenant-name-strategy</mat-label> | |
368 | + <mat-select formControlName="tenantNameStrategy"> | |
369 | + <mat-option *ngFor="let tenantNameStrategy of tenantNameStrategies" | |
370 | + [value]="tenantNameStrategy"> | |
371 | + {{ tenantNameStrategy }} | |
372 | + </mat-option> | |
373 | + </mat-select> | |
374 | + </mat-form-field> | |
375 | + | |
376 | + <mat-form-field fxFlex class="mat-block" [fxShow]="registration.get('mapperConfig.basic.tenantNameStrategy').value === 'CUSTOM'"> | |
377 | + <mat-label translate>admin.oauth2.tenant-name-pattern</mat-label> | |
378 | + <input matInput | |
379 | + formControlName="tenantNamePattern" | |
380 | + [required]="registration.get('mapperConfig.basic.tenantNameStrategy').value === 'CUSTOM'"> | |
381 | + <mat-error | |
382 | + *ngIf="registration.get('mapperConfig.basic.tenantNamePattern').hasError('required')"> | |
383 | + {{ 'admin.oauth2.tenant-name-pattern-required' | translate }} | |
384 | + </mat-error> | |
385 | + </mat-form-field> | |
386 | + </div> | |
387 | + | |
388 | + <mat-form-field fxFlex class="mat-block"> | |
389 | + <mat-label translate>admin.oauth2.customer-name-pattern</mat-label> | |
390 | + <input matInput formControlName="customerNamePattern"> | |
391 | + </mat-form-field> | |
392 | + | |
393 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> | |
394 | + <mat-form-field fxFlex class="mat-block"> | |
395 | + <mat-label translate>admin.oauth2.default-dashboard-name</mat-label> | |
396 | + <input matInput formControlName="defaultDashboardName"> | |
397 | + </mat-form-field> | |
398 | + | |
399 | + <mat-checkbox fxFlex formControlName="alwaysFullScreen" class="checkbox-row"> | |
400 | + {{ 'admin.oauth2.always-fullscreen' | translate}} | |
401 | + </mat-checkbox> | |
402 | + </div> | |
403 | + </section> | |
404 | + | |
405 | + <section formGroupName="custom" | |
406 | + *ngIf="registration.get('mapperConfig.type').value === 'CUSTOM'"> | |
407 | + <mat-form-field class="mat-block"> | |
408 | + <mat-label translate>admin.oauth2.url</mat-label> | |
409 | + <input matInput formControlName="url" required> | |
410 | + <mat-error | |
411 | + *ngIf="registration.get('mapperConfig.custom.url').hasError('required')"> | |
412 | + {{ 'admin.oauth2.url-required' | translate }} | |
413 | + </mat-error> | |
414 | + <mat-error | |
415 | + *ngIf="registration.get('mapperConfig.custom.url').hasError('pattern')"> | |
416 | + {{ 'admin.oauth2.url-pattern' | translate }} | |
417 | + </mat-error> | |
418 | + </mat-form-field> | |
419 | + | |
420 | + <div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> | |
421 | + <mat-form-field fxFlex class="mat-block"> | |
422 | + <mat-label translate>common.username</mat-label> | |
423 | + <input matInput formControlName="username" autocomplete="new-username"> | |
424 | + </mat-form-field> | |
425 | + | |
426 | + <mat-form-field fxFlex class="mat-block"> | |
427 | + <mat-label translate>common.password</mat-label> | |
428 | + <input matInput type="password" formControlName="password" autocomplete="new-password"> | |
429 | + </mat-form-field> | |
430 | + </div> | |
431 | + </section> | |
432 | + </section> | |
433 | + </mat-tab> | |
434 | + </mat-tab-group> | |
435 | + </ng-template> | |
436 | + </mat-expansion-panel> | |
437 | + | |
438 | + </section> | |
439 | + </ng-template> | |
440 | + </mat-expansion-panel> | |
441 | + </div> | |
442 | + </ng-container> | |
443 | + | |
444 | + <div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> | |
445 | + <button mat-button mat-raised-button color="primary" | |
446 | + [disabled]="(isLoading$ | async)" | |
447 | + (click)="addProvider(domain)" | |
448 | + type="button"> | |
449 | + {{'admin.oauth2.add-provider' | translate}} | |
450 | + </button> | |
451 | + </div> | |
452 | + </ng-template> | |
453 | + | |
454 | + </mat-expansion-panel> | |
455 | + </ng-container> | |
456 | + </mat-accordion> | |
457 | + </div> | |
458 | + </ng-container> | |
459 | + </section> | |
460 | + </fieldset> | |
461 | + <div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> | |
462 | + <button type="button" mat-raised-button color="primary" | |
463 | + [disabled]="isLoading$ | async" | |
464 | + *ngIf="oauth2SettingsForm.get('enabled').value" | |
465 | + (click)="addDomain()"> | |
466 | + <mat-icon>add</mat-icon> | |
467 | + <span translate>action.add</span> | |
468 | + </button> | |
469 | + <button mat-button mat-raised-button color="primary" | |
470 | + [disabled]="(isLoading$ | async) || oauth2SettingsForm.invalid || !oauth2SettingsForm.dirty" | |
471 | + type="submit"> | |
472 | + {{'action.save' | translate}} | |
473 | + </button> | |
474 | + </div> | |
475 | + </form> | |
476 | + </mat-card-content> | |
477 | + </mat-card> | |
478 | +</div> | ... | ... |
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 | +:host{ | |
17 | + .checkbox-row { | |
18 | + margin-top: 1em; | |
19 | + } | |
20 | + | |
21 | + .registration-card{ | |
22 | + margin-bottom: 0.5em; | |
23 | + border: 1px solid rgba(0, 0, 0, 0.2); | |
24 | + | |
25 | + .custom-settings{ | |
26 | + font-size: 16px; | |
27 | + .mat-expansion-panel-header{ | |
28 | + padding: 0 2px; | |
29 | + } | |
30 | + } | |
31 | + } | |
32 | + | |
33 | + .container{ | |
34 | + margin-bottom: 1em; | |
35 | + | |
36 | + .tb-highlight{ | |
37 | + margin: 0; | |
38 | + } | |
39 | + | |
40 | + .tb-hint{ | |
41 | + padding-bottom: 0; | |
42 | + } | |
43 | + } | |
44 | +} | |
45 | + | |
46 | +:host ::ng-deep{ | |
47 | + .registration-card{ | |
48 | + .custom-settings{ | |
49 | + .mat-expansion-panel-body{ | |
50 | + padding: 0 2px 1em; | |
51 | + } | |
52 | + .mat-tab-label{ | |
53 | + text-transform: none; | |
54 | + } | |
55 | + .mat-form-field-suffix .mat-icon-button .mat-icon{ | |
56 | + font-size: 18px; | |
57 | + } | |
58 | + } | |
59 | + } | |
60 | + .domains-list{ | |
61 | + margin-bottom: 1.5em; | |
62 | + .mat-form-field-suffix .mat-icon-button .mat-icon{ | |
63 | + font-size: 24px; | |
64 | + } | |
65 | + } | |
66 | +} | ... | ... |
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 | + | |
17 | +import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; | |
18 | +import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; | |
19 | +import { | |
20 | + ClientAuthenticationMethod, | |
21 | + ClientProviderTemplated, | |
22 | + ClientRegistration, | |
23 | + DomainInfo, | |
24 | + DomainSchema, | |
25 | + domainSchemaTranslations, | |
26 | + DomainsParam, | |
27 | + MapperConfig, | |
28 | + MapperConfigBasic, | |
29 | + MapperConfigCustom, | |
30 | + MapperConfigType, | |
31 | + OAuth2Settings, | |
32 | + TenantNameStrategy | |
33 | +} from '@shared/models/settings.models'; | |
34 | +import { Store } from '@ngrx/store'; | |
35 | +import { AppState } from '@core/core.state'; | |
36 | +import { AdminService } from '@core/http/admin.service'; | |
37 | +import { PageComponent } from '@shared/components/page.component'; | |
38 | +import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; | |
39 | +import { COMMA, ENTER } from '@angular/cdk/keycodes'; | |
40 | +import { MatChipInputEvent } from '@angular/material/chips'; | |
41 | +import { WINDOW } from '@core/services/window.service'; | |
42 | +import { forkJoin, Subscription } from 'rxjs'; | |
43 | +import { DialogService } from '@core/services/dialog.service'; | |
44 | +import { TranslateService } from '@ngx-translate/core'; | |
45 | +import { isDefined } from '@core/utils'; | |
46 | + | |
47 | +@Component({ | |
48 | + selector: 'tb-oauth2-settings', | |
49 | + templateUrl: './oauth2-settings.component.html', | |
50 | + styleUrls: ['./oauth2-settings.component.scss', './settings-card.scss'] | |
51 | +}) | |
52 | +export class OAuth2SettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy { | |
53 | + | |
54 | + private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.,?+=&%@\-/]*)?$/; | |
55 | + private subscriptions: Subscription[] = []; | |
56 | + private templates = new Map<string, ClientProviderTemplated>(); | |
57 | + private defaultProvider = { | |
58 | + additionalInfo: { | |
59 | + providerName: 'Custom' | |
60 | + }, | |
61 | + clientAuthenticationMethod: ClientAuthenticationMethod.POST, | |
62 | + userNameAttributeName: 'email', | |
63 | + mapperConfig: { | |
64 | + allowUserCreation: true, | |
65 | + activateUser: false, | |
66 | + type: MapperConfigType.BASIC, | |
67 | + basic: { | |
68 | + emailAttributeKey: 'email', | |
69 | + tenantNameStrategy: TenantNameStrategy.DOMAIN, | |
70 | + alwaysFullScreen: false | |
71 | + } | |
72 | + } | |
73 | + }; | |
74 | + | |
75 | + readonly separatorKeysCodes: number[] = [ENTER, COMMA]; | |
76 | + | |
77 | + oauth2SettingsForm: FormGroup; | |
78 | + oauth2Settings: OAuth2Settings; | |
79 | + | |
80 | + clientAuthenticationMethods = Object.keys(ClientAuthenticationMethod); | |
81 | + converterTypesExternalUser = Object.keys(MapperConfigType); | |
82 | + tenantNameStrategies = Object.keys(TenantNameStrategy); | |
83 | + protocols = Object.keys(DomainSchema); | |
84 | + domainSchemaTranslations = domainSchemaTranslations; | |
85 | + | |
86 | + templateProvider = ['Custom']; | |
87 | + | |
88 | + constructor(protected store: Store<AppState>, | |
89 | + private adminService: AdminService, | |
90 | + private fb: FormBuilder, | |
91 | + private dialogService: DialogService, | |
92 | + private translate: TranslateService, | |
93 | + @Inject(WINDOW) private window: Window) { | |
94 | + super(store); | |
95 | + } | |
96 | + | |
97 | + ngOnInit(): void { | |
98 | + this.buildOAuth2SettingsForm(); | |
99 | + forkJoin([ | |
100 | + this.adminService.getOAuth2Template(), | |
101 | + this.adminService.getOAuth2Settings() | |
102 | + ]).subscribe( | |
103 | + ([templates, oauth2Settings]) => { | |
104 | + this.initTemplates(templates); | |
105 | + this.oauth2Settings = oauth2Settings; | |
106 | + this.initOAuth2Settings(this.oauth2Settings); | |
107 | + } | |
108 | + ); | |
109 | + } | |
110 | + | |
111 | + ngOnDestroy() { | |
112 | + super.ngOnDestroy(); | |
113 | + this.subscriptions.forEach((subscription) => { | |
114 | + subscription.unsubscribe(); | |
115 | + }); | |
116 | + } | |
117 | + | |
118 | + private initTemplates(templates: ClientProviderTemplated[]): void { | |
119 | + templates.map(provider => this.templates.set(provider.name, provider)); | |
120 | + this.templateProvider.push(...Array.from(this.templates.keys())); | |
121 | + this.templateProvider.sort(); | |
122 | + } | |
123 | + | |
124 | + get domainsParams(): FormArray { | |
125 | + return this.oauth2SettingsForm.get('domainsParams') as FormArray; | |
126 | + } | |
127 | + | |
128 | + private formBasicGroup(mapperConfigBasic?: MapperConfigBasic): FormGroup { | |
129 | + let tenantNamePattern; | |
130 | + if (mapperConfigBasic?.tenantNamePattern) { | |
131 | + tenantNamePattern = mapperConfigBasic.tenantNamePattern; | |
132 | + } else { | |
133 | + tenantNamePattern = {value: null, disabled: true}; | |
134 | + } | |
135 | + const basicGroup = this.fb.group({ | |
136 | + emailAttributeKey: [mapperConfigBasic?.emailAttributeKey ? mapperConfigBasic.emailAttributeKey : 'email', Validators.required], | |
137 | + firstNameAttributeKey: [mapperConfigBasic?.firstNameAttributeKey ? mapperConfigBasic.firstNameAttributeKey : ''], | |
138 | + lastNameAttributeKey: [mapperConfigBasic?.lastNameAttributeKey ? mapperConfigBasic.lastNameAttributeKey : ''], | |
139 | + tenantNameStrategy: [mapperConfigBasic?.tenantNameStrategy ? mapperConfigBasic.tenantNameStrategy : TenantNameStrategy.DOMAIN], | |
140 | + tenantNamePattern: [tenantNamePattern, Validators.required], | |
141 | + customerNamePattern: [mapperConfigBasic?.customerNamePattern ? mapperConfigBasic.customerNamePattern : null], | |
142 | + defaultDashboardName: [mapperConfigBasic?.defaultDashboardName ? mapperConfigBasic.defaultDashboardName : null], | |
143 | + alwaysFullScreen: [mapperConfigBasic?.alwaysFullScreen ? mapperConfigBasic.alwaysFullScreen : false] | |
144 | + }); | |
145 | + | |
146 | + this.subscriptions.push(basicGroup.get('tenantNameStrategy').valueChanges.subscribe((domain) => { | |
147 | + if (domain === 'CUSTOM') { | |
148 | + basicGroup.get('tenantNamePattern').enable(); | |
149 | + } else { | |
150 | + basicGroup.get('tenantNamePattern').disable(); | |
151 | + } | |
152 | + })); | |
153 | + | |
154 | + return basicGroup; | |
155 | + } | |
156 | + | |
157 | + private formCustomGroup(mapperConfigCustom?: MapperConfigCustom): FormGroup { | |
158 | + return this.fb.group({ | |
159 | + url: [mapperConfigCustom?.url ? mapperConfigCustom.url : null, [Validators.required, Validators.pattern(this.URL_REGEXP)]], | |
160 | + username: [mapperConfigCustom?.username ? mapperConfigCustom.username : null], | |
161 | + password: [mapperConfigCustom?.password ? mapperConfigCustom.password : null] | |
162 | + }); | |
163 | + } | |
164 | + | |
165 | + private buildOAuth2SettingsForm(): void { | |
166 | + this.oauth2SettingsForm = this.fb.group({ | |
167 | + domainsParams: this.fb.array([]), | |
168 | + enabled: [false] | |
169 | + }); | |
170 | + } | |
171 | + | |
172 | + private initOAuth2Settings(oauth2Settings: OAuth2Settings): void { | |
173 | + if (oauth2Settings) { | |
174 | + this.oauth2SettingsForm.patchValue({enabled: oauth2Settings.enabled}, {emitEvent: false}); | |
175 | + oauth2Settings.domainsParams.forEach((domain) => { | |
176 | + this.domainsParams.push(this.buildDomainsForm(domain)); | |
177 | + }); | |
178 | + } | |
179 | + } | |
180 | + | |
181 | + private uniqueDomainValidator(control: FormGroup): { [key: string]: boolean } | null { | |
182 | + if (control.parent?.value) { | |
183 | + const domain = control.value.name; | |
184 | + const listProtocols = control.parent.getRawValue() | |
185 | + .filter((domainInfo) => domainInfo.name === domain) | |
186 | + .map((domainInfo) => domainInfo.scheme); | |
187 | + if (listProtocols.length > 1 && listProtocols.indexOf(DomainSchema.MIXED) > -1 || | |
188 | + new Set(listProtocols).size !== listProtocols.length) { | |
189 | + return {unique: true}; | |
190 | + } | |
191 | + } | |
192 | + return null; | |
193 | + } | |
194 | + | |
195 | + public domainListTittle(control: AbstractControl): string { | |
196 | + const domainInfos = control.get('domainInfos').value as DomainInfo[]; | |
197 | + if (domainInfos.length) { | |
198 | + const domainList = new Set<string>(); | |
199 | + domainInfos.forEach((domain) => { | |
200 | + domainList.add(domain.name); | |
201 | + }); | |
202 | + return Array.from(domainList).join(', '); | |
203 | + } | |
204 | + return this.translate.instant('admin.oauth2.new-domain'); | |
205 | + } | |
206 | + | |
207 | + private buildDomainsForm(domainParams?: DomainsParam): FormGroup { | |
208 | + const formDomain = this.fb.group({ | |
209 | + domainInfos: this.fb.array([], Validators.required), | |
210 | + clientRegistrations: this.fb.array([], Validators.required) | |
211 | + }); | |
212 | + | |
213 | + if (domainParams) { | |
214 | + domainParams.domainInfos.forEach((domain) => { | |
215 | + this.clientDomainInfos(formDomain).push(this.buildDomainForm(domain)); | |
216 | + }); | |
217 | + domainParams.clientRegistrations.forEach((registration) => { | |
218 | + this.clientDomainProviders(formDomain).push(this.buildProviderForm(registration)); | |
219 | + }); | |
220 | + } else { | |
221 | + this.clientDomainProviders(formDomain).push(this.buildProviderForm()); | |
222 | + this.clientDomainInfos(formDomain).push(this.buildDomainForm()); | |
223 | + } | |
224 | + | |
225 | + return formDomain; | |
226 | + } | |
227 | + | |
228 | + private buildDomainForm(domainInfo?: DomainInfo): FormGroup { | |
229 | + const domain = this.fb.group({ | |
230 | + name: [domainInfo ? domainInfo.name : this.window.location.hostname, [ | |
231 | + Validators.required, | |
232 | + Validators.pattern('((?![:/]).)*$')]], | |
233 | + scheme: [domainInfo?.scheme ? domainInfo.scheme : DomainSchema.HTTPS, Validators.required] | |
234 | + }, {validators: this.uniqueDomainValidator}); | |
235 | + return domain; | |
236 | + } | |
237 | + | |
238 | + private buildProviderForm(registrationData?: ClientRegistration): FormGroup { | |
239 | + let additionalInfo = null; | |
240 | + if (registrationData?.additionalInfo) { | |
241 | + additionalInfo = JSON.parse(registrationData.additionalInfo); | |
242 | + if (this.templateProvider.indexOf(additionalInfo.providerName) === -1) { | |
243 | + additionalInfo.providerName = 'Custom'; | |
244 | + } | |
245 | + } | |
246 | + let defaultProviderName = 'Custom'; | |
247 | + if (this.templateProvider.indexOf('Google')) { | |
248 | + defaultProviderName = 'Google'; | |
249 | + } | |
250 | + | |
251 | + const clientRegistration = this.fb.group({ | |
252 | + id: this.fb.group({ | |
253 | + id: [registrationData?.id?.id ? registrationData.id.id : null], | |
254 | + entityType: [registrationData?.id?.entityType ? registrationData.id.entityType : null] | |
255 | + }), | |
256 | + additionalInfo: this.fb.group({ | |
257 | + providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : defaultProviderName, Validators.required] | |
258 | + }), | |
259 | + loginButtonLabel: [registrationData?.loginButtonLabel ? registrationData.loginButtonLabel : null, Validators.required], | |
260 | + loginButtonIcon: [registrationData?.loginButtonIcon ? registrationData.loginButtonIcon : null], | |
261 | + clientId: [registrationData?.clientId ? registrationData.clientId : '', Validators.required], | |
262 | + clientSecret: [registrationData?.clientSecret ? registrationData.clientSecret : '', Validators.required], | |
263 | + accessTokenUri: [registrationData?.accessTokenUri ? registrationData.accessTokenUri : '', | |
264 | + [Validators.required, | |
265 | + Validators.pattern(this.URL_REGEXP)]], | |
266 | + authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '', | |
267 | + [Validators.required, | |
268 | + Validators.pattern(this.URL_REGEXP)]], | |
269 | + scope: this.fb.array(registrationData?.scope ? registrationData.scope : [], Validators.required), | |
270 | + jwkSetUri: [registrationData?.jwkSetUri ? registrationData.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], | |
271 | + userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '', | |
272 | + [Validators.required, | |
273 | + Validators.pattern(this.URL_REGEXP)]], | |
274 | + clientAuthenticationMethod: [ | |
275 | + registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : ClientAuthenticationMethod.POST, | |
276 | + Validators.required], | |
277 | + userNameAttributeName: [ | |
278 | + registrationData?.userNameAttributeName ? registrationData.userNameAttributeName : 'email', Validators.required], | |
279 | + mapperConfig: this.fb.group({ | |
280 | + allowUserCreation: [registrationData?.mapperConfig?.allowUserCreation ? registrationData.mapperConfig.allowUserCreation : true], | |
281 | + activateUser: [registrationData?.mapperConfig?.activateUser ? registrationData.mapperConfig.activateUser : false], | |
282 | + type: [registrationData?.mapperConfig?.type ? registrationData.mapperConfig.type : MapperConfigType.BASIC, Validators.required] | |
283 | + } | |
284 | + ) | |
285 | + }); | |
286 | + | |
287 | + if (registrationData) { | |
288 | + this.changeMapperConfigType(clientRegistration, registrationData.mapperConfig.type, registrationData.mapperConfig); | |
289 | + } else { | |
290 | + this.changeMapperConfigType(clientRegistration, MapperConfigType.BASIC); | |
291 | + this.setProviderDefaultValue(defaultProviderName, clientRegistration); | |
292 | + } | |
293 | + | |
294 | + this.subscriptions.push(clientRegistration.get('mapperConfig.type').valueChanges.subscribe((value) => { | |
295 | + this.changeMapperConfigType(clientRegistration, value); | |
296 | + })); | |
297 | + | |
298 | + this.subscriptions.push(clientRegistration.get('additionalInfo.providerName').valueChanges.subscribe((provider) => { | |
299 | + (clientRegistration.get('scope') as FormArray).clear(); | |
300 | + this.setProviderDefaultValue(provider, clientRegistration); | |
301 | + })); | |
302 | + | |
303 | + return clientRegistration; | |
304 | + } | |
305 | + | |
306 | + private setProviderDefaultValue(provider: string, clientRegistration: FormGroup) { | |
307 | + if (provider === 'Custom') { | |
308 | + const defaultSettings = {...this.defaultProvider, ...{id: clientRegistration.get('id').value}}; | |
309 | + clientRegistration.reset(defaultSettings, {emitEvent: false}); | |
310 | + clientRegistration.get('accessTokenUri').enable(); | |
311 | + clientRegistration.get('authorizationUri').enable(); | |
312 | + clientRegistration.get('jwkSetUri').enable(); | |
313 | + clientRegistration.get('userInfoUri').enable(); | |
314 | + } else { | |
315 | + const template = this.templates.get(provider); | |
316 | + delete template.id; | |
317 | + delete template.additionalInfo; | |
318 | + template.clientId = ''; | |
319 | + template.clientSecret = ''; | |
320 | + template.scope.forEach(() => { | |
321 | + (clientRegistration.get('scope') as FormArray).push(this.fb.control('')); | |
322 | + }); | |
323 | + clientRegistration.get('accessTokenUri').disable(); | |
324 | + clientRegistration.get('authorizationUri').disable(); | |
325 | + clientRegistration.get('jwkSetUri').disable(); | |
326 | + clientRegistration.get('userInfoUri').disable(); | |
327 | + clientRegistration.patchValue(template, {emitEvent: false}); | |
328 | + } | |
329 | + } | |
330 | + | |
331 | + private changeMapperConfigType(control: AbstractControl, type: MapperConfigType, predefinedValue?: MapperConfig) { | |
332 | + const mapperConfig = control.get('mapperConfig') as FormGroup; | |
333 | + if (type === MapperConfigType.BASIC) { | |
334 | + mapperConfig.removeControl('custom'); | |
335 | + mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic)); | |
336 | + } else { | |
337 | + mapperConfig.removeControl('basic'); | |
338 | + mapperConfig.addControl('custom', this.formCustomGroup(predefinedValue?.custom)); | |
339 | + } | |
340 | + } | |
341 | + | |
342 | + save(): void { | |
343 | + const setting = this.prepareFormValue(this.oauth2SettingsForm.getRawValue()); | |
344 | + this.adminService.saveOAuth2Settings(setting).subscribe( | |
345 | + (oauth2Settings) => { | |
346 | + this.oauth2Settings = oauth2Settings; | |
347 | + this.oauth2SettingsForm.markAsPristine(); | |
348 | + this.oauth2SettingsForm.markAsUntouched(); | |
349 | + } | |
350 | + ); | |
351 | + } | |
352 | + | |
353 | + private prepareFormValue(formValue: OAuth2Settings): OAuth2Settings{ | |
354 | + formValue.domainsParams.forEach((setting, index) => { | |
355 | + setting.clientRegistrations.forEach((registration) => { | |
356 | + registration.additionalInfo = JSON.stringify(registration.additionalInfo); | |
357 | + if (registration.id.id === null) { | |
358 | + delete registration.id; | |
359 | + } | |
360 | + }); | |
361 | + }); | |
362 | + return formValue; | |
363 | + } | |
364 | + | |
365 | + confirmForm(): FormGroup { | |
366 | + return this.oauth2SettingsForm; | |
367 | + } | |
368 | + | |
369 | + addScope(event: MatChipInputEvent, control: AbstractControl): void { | |
370 | + const input = event.input; | |
371 | + const value = event.value; | |
372 | + const controller = control.get('scope') as FormArray; | |
373 | + if ((value.trim() !== '')) { | |
374 | + controller.push(this.fb.control(value.trim())); | |
375 | + controller.markAsDirty(); | |
376 | + } | |
377 | + | |
378 | + if (input) { | |
379 | + input.value = ''; | |
380 | + } | |
381 | + } | |
382 | + | |
383 | + removeScope(i: number, control: AbstractControl): void { | |
384 | + const controller = control.get('scope') as FormArray; | |
385 | + controller.removeAt(i); | |
386 | + controller.markAsTouched(); | |
387 | + controller.markAsDirty(); | |
388 | + } | |
389 | + | |
390 | + addDomain(): void { | |
391 | + this.domainsParams.push(this.buildDomainsForm()); | |
392 | + } | |
393 | + | |
394 | + deleteDomain($event: Event, index: number): void { | |
395 | + if ($event) { | |
396 | + $event.stopPropagation(); | |
397 | + $event.preventDefault(); | |
398 | + } | |
399 | + | |
400 | + const domainName = this.domainListTittle(this.domainsParams.at(index)); | |
401 | + this.dialogService.confirm( | |
402 | + this.translate.instant('admin.oauth2.delete-domain-title', {domainName: domainName || ''}), | |
403 | + this.translate.instant('admin.oauth2.delete-domain-text'), null, | |
404 | + this.translate.instant('action.delete') | |
405 | + ).subscribe((data) => { | |
406 | + if (data) { | |
407 | + this.domainsParams.removeAt(index); | |
408 | + this.domainsParams.markAsTouched(); | |
409 | + this.domainsParams.markAsDirty(); | |
410 | + } | |
411 | + }); | |
412 | + } | |
413 | + | |
414 | + clientDomainProviders(control: AbstractControl): FormArray { | |
415 | + return control.get('clientRegistrations') as FormArray; | |
416 | + } | |
417 | + | |
418 | + clientDomainInfos(control: AbstractControl): FormArray { | |
419 | + return control.get('domainInfos') as FormArray; | |
420 | + } | |
421 | + | |
422 | + addProvider(control: AbstractControl): void { | |
423 | + this.clientDomainProviders(control).push(this.buildProviderForm()); | |
424 | + } | |
425 | + | |
426 | + deleteProvider($event: Event, control: AbstractControl, index: number): void { | |
427 | + if ($event) { | |
428 | + $event.stopPropagation(); | |
429 | + $event.preventDefault(); | |
430 | + } | |
431 | + | |
432 | + const providerName = this.clientDomainProviders(control).at(index).get('additionalInfo.providerName').value; | |
433 | + this.dialogService.confirm( | |
434 | + this.translate.instant('admin.oauth2.delete-registration-title', {name: providerName || ''}), | |
435 | + this.translate.instant('admin.oauth2.delete-registration-text'), null, | |
436 | + this.translate.instant('action.delete') | |
437 | + ).subscribe((data) => { | |
438 | + if (data) { | |
439 | + this.clientDomainProviders(control).removeAt(index); | |
440 | + this.clientDomainProviders(control).markAsTouched(); | |
441 | + this.clientDomainProviders(control).markAsDirty(); | |
442 | + } | |
443 | + }); | |
444 | + } | |
445 | + | |
446 | + toggleEditMode(control: AbstractControl, path: string) { | |
447 | + control.get(path).disabled ? control.get(path).enable() : control.get(path).disable(); | |
448 | + } | |
449 | + | |
450 | + getProviderName(controller: AbstractControl): string { | |
451 | + return controller.get('additionalInfo.providerName').value; | |
452 | + } | |
453 | + | |
454 | + getHelpLink(controller: AbstractControl): string { | |
455 | + const provider = controller.get('additionalInfo.providerName').value; | |
456 | + if (provider === null || provider === 'Custom') { | |
457 | + return ''; | |
458 | + } | |
459 | + return this.templates.get(provider).helpLink; | |
460 | + } | |
461 | + | |
462 | + addDomainInfo(control: AbstractControl): void { | |
463 | + this.clientDomainInfos(control).push(this.buildDomainForm({ | |
464 | + name: '', | |
465 | + scheme: DomainSchema.HTTPS | |
466 | + })); | |
467 | + } | |
468 | + | |
469 | + removeDomain($event: Event, control: AbstractControl, index: number): void { | |
470 | + if ($event) { | |
471 | + $event.stopPropagation(); | |
472 | + $event.preventDefault(); | |
473 | + } | |
474 | + this.clientDomainInfos(control).removeAt(index); | |
475 | + this.clientDomainInfos(control).markAsTouched(); | |
476 | + this.clientDomainInfos(control).markAsDirty(); | |
477 | + } | |
478 | + | |
479 | + redirectURI(control: AbstractControl, schema?: DomainSchema): string { | |
480 | + const domainInfo = control.value as DomainInfo; | |
481 | + if (domainInfo.name !== '') { | |
482 | + let protocol; | |
483 | + if (isDefined(schema)) { | |
484 | + protocol = schema.toLowerCase(); | |
485 | + } else { | |
486 | + protocol = domainInfo.scheme === DomainSchema.MIXED ? DomainSchema.HTTPS.toLowerCase() : domainInfo.scheme.toLowerCase(); | |
487 | + } | |
488 | + return `${protocol}://${domainInfo.name}/login/oauth2/code/`; | |
489 | + } | |
490 | + return ''; | |
491 | + } | |
492 | + | |
493 | + redirectURIMixed(control: AbstractControl): string { | |
494 | + return this.redirectURI(control, DomainSchema.HTTP); | |
495 | + } | |
496 | + | |
497 | + trackByParams(index: number): number { | |
498 | + return index; | |
499 | + } | |
500 | +} | ... | ... |
... | ... | @@ -60,6 +60,7 @@ export const HelpLinks = { |
60 | 60 | linksMap: { |
61 | 61 | outgoingMailSettings: helpBaseUrl + '/docs/user-guide/ui/mail-settings', |
62 | 62 | securitySettings: helpBaseUrl + '/docs/user-guide/ui/security-settings', |
63 | + oauth2Settings: helpBaseUrl + '/docs/user-guide/oauth-2-support/', | |
63 | 64 | ruleEngine: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/overview/', |
64 | 65 | ruleNodeCheckRelation: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#check-relation-filter-node', |
65 | 66 | ruleNodeCheckExistenceFields: helpBaseUrl + '/docs/user-guide/rule-engine-2-0/filter-nodes/#check-existence-fields-node', |
... | ... | @@ -123,7 +124,7 @@ export const HelpLinks = { |
123 | 124 | widgetsConfigLatest: helpBaseUrl + '/docs/user-guide/ui/dashboards#latest', |
124 | 125 | widgetsConfigRpc: helpBaseUrl + '/docs/user-guide/ui/dashboards#rpc', |
125 | 126 | widgetsConfigAlarm: helpBaseUrl + '/docs/user-guide/ui/dashboards#alarm', |
126 | - widgetsConfigStatic: helpBaseUrl + '/docs/user-guide/ui/dashboards#static' | |
127 | + widgetsConfigStatic: helpBaseUrl + '/docs/user-guide/ui/dashboards#static', | |
127 | 128 | } |
128 | 129 | }; |
129 | 130 | ... | ... |
... | ... | @@ -14,6 +14,9 @@ |
14 | 14 | /// limitations under the License. |
15 | 15 | /// |
16 | 16 | |
17 | +import { EntityId } from '@shared/models/id/entity-id'; | |
18 | +import { TenantId } from '@shared/models/id/tenant-id'; | |
19 | + | |
17 | 20 | 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])$/; |
18 | 21 | |
19 | 22 | export interface AdminSettings<T> { |
... | ... | @@ -60,3 +63,99 @@ export interface UpdateMessage { |
60 | 63 | message: string; |
61 | 64 | updateAvailable: boolean; |
62 | 65 | } |
66 | + | |
67 | +export interface OAuth2Settings { | |
68 | + enabled: boolean; | |
69 | + domainsParams: DomainsParam[]; | |
70 | +} | |
71 | + | |
72 | +export interface DomainsParam { | |
73 | + clientRegistrations: ClientRegistration[]; | |
74 | + domainInfos: DomainInfo[]; | |
75 | +} | |
76 | + | |
77 | +export interface DomainInfo { | |
78 | + name: string; | |
79 | + scheme: DomainSchema; | |
80 | +} | |
81 | + | |
82 | +export enum DomainSchema{ | |
83 | + HTTP = 'HTTP', | |
84 | + HTTPS = 'HTTPS', | |
85 | + MIXED = 'MIXED' | |
86 | +} | |
87 | + | |
88 | +export const domainSchemaTranslations = new Map<DomainSchema, string>( | |
89 | + [ | |
90 | + [DomainSchema.HTTP, 'admin.oauth2.domain-schema-http'], | |
91 | + [DomainSchema.HTTPS, 'admin.oauth2.domain-schema-https'], | |
92 | + [DomainSchema.MIXED, 'admin.oauth2.domain-schema-mixed'] | |
93 | + ] | |
94 | +); | |
95 | + | |
96 | +export enum MapperConfigType{ | |
97 | + BASIC = 'BASIC', | |
98 | + CUSTOM = 'CUSTOM' | |
99 | +} | |
100 | + | |
101 | +export enum TenantNameStrategy{ | |
102 | + DOMAIN = 'DOMAIN', | |
103 | + EMAIL = 'EMAIL', | |
104 | + CUSTOM = 'CUSTOM' | |
105 | +} | |
106 | + | |
107 | +export interface ClientProviderTemplated extends ClientRegistration{ | |
108 | + comment: string; | |
109 | + createdTime: number; | |
110 | + helpLink: string; | |
111 | + name: string; | |
112 | + providerId: string; | |
113 | + tenantId: TenantId; | |
114 | +} | |
115 | + | |
116 | +export interface ClientRegistration { | |
117 | + loginButtonLabel: string; | |
118 | + loginButtonIcon: string; | |
119 | + clientId: string; | |
120 | + clientSecret: string; | |
121 | + accessTokenUri: string; | |
122 | + authorizationUri: string; | |
123 | + scope: string[]; | |
124 | + jwkSetUri?: string; | |
125 | + userInfoUri: string; | |
126 | + clientAuthenticationMethod: ClientAuthenticationMethod; | |
127 | + userNameAttributeName: string; | |
128 | + mapperConfig: MapperConfig; | |
129 | + id?: EntityId; | |
130 | + additionalInfo: string; | |
131 | +} | |
132 | + | |
133 | +export enum ClientAuthenticationMethod { | |
134 | + BASIC = 'BASIC', | |
135 | + POST = 'POST' | |
136 | +} | |
137 | + | |
138 | +export interface MapperConfig { | |
139 | + allowUserCreation: boolean; | |
140 | + activateUser: boolean; | |
141 | + type: MapperConfigType; | |
142 | + basic?: MapperConfigBasic; | |
143 | + custom?: MapperConfigCustom; | |
144 | +} | |
145 | + | |
146 | +export interface MapperConfigBasic { | |
147 | + emailAttributeKey: string; | |
148 | + firstNameAttributeKey?: string; | |
149 | + lastNameAttributeKey?: string; | |
150 | + tenantNameStrategy: TenantNameStrategy; | |
151 | + tenantNamePattern?: string; | |
152 | + customerNamePattern?: string; | |
153 | + defaultDashboardName?: string; | |
154 | + alwaysFullScreen?: boolean; | |
155 | +} | |
156 | + | |
157 | +export interface MapperConfigCustom { | |
158 | + url: string; | |
159 | + username?: string; | |
160 | + password?: string; | |
161 | +} | ... | ... |
... | ... | @@ -120,8 +120,74 @@ |
120 | 120 | "general-policy": "General policy", |
121 | 121 | "max-failed-login-attempts": "Maximum number of failed login attempts, before account is locked", |
122 | 122 | "minimum-max-failed-login-attempts-range": "Maximum number of failed login attempts can't be negative", |
123 | - "user-lockout-notification-email": "In case user account lockout, send notification to email" | |
124 | - }, | |
123 | + "user-lockout-notification-email": "In case user account lockout, send notification to email", | |
124 | + "domain-name": "Domain name", | |
125 | + "domain-name-unique": "Domain name and protocol need to unique.", | |
126 | + "error-verification-url": "A domain name shouldn't contain symbols '/' and ':'. Example: thingsboard.io", | |
127 | + "oauth2": { | |
128 | + "access-token-uri": "Access token URI", | |
129 | + "access-token-uri-required": "Access token URI is required.", | |
130 | + "activate-user": "Activate user", | |
131 | + "add-domain": "Add domain", | |
132 | + "delete-domain": "Delete domain", | |
133 | + "add-provider": "Add provider", | |
134 | + "delete-provider": "Delete provider", | |
135 | + "allow-user-creation": "Allow user creation", | |
136 | + "always-fullscreen": "Always fullscreen", | |
137 | + "authorization-uri": "Authorization URI", | |
138 | + "authorization-uri-required": "Authorization URI is required.", | |
139 | + "client-authentication-method": "Client authentication method", | |
140 | + "client-id": "Client ID", | |
141 | + "client-id-required": "Client ID is required.", | |
142 | + "client-secret": "Client secret", | |
143 | + "client-secret-required": "Client secret is required.", | |
144 | + "custom-setting": "Custom settings", | |
145 | + "customer-name-pattern": "Customer name pattern", | |
146 | + "default-dashboard-name": "Default dashboard name", | |
147 | + "delete-domain-text": "Be careful, after the confirmation a domain and all provider data will be unavailable.", | |
148 | + "delete-domain-title": "Are you sure you want to delete settings the domain '{{domainName}}'?", | |
149 | + "delete-registration-text": "Be careful, after the confirmation a provider data will be unavailable.", | |
150 | + "delete-registration-title": "Are you sure you want to delete the provider '{{name}}'?", | |
151 | + "email-attribute-key": "Email attribute key", | |
152 | + "email-attribute-key-required": "Email attribute key is required.", | |
153 | + "first-name-attribute-key": "First name attribute key", | |
154 | + "general": "General", | |
155 | + "jwk-set-uri": "JSON Web Key URI", | |
156 | + "last-name-attribute-key": "Last name attribute key", | |
157 | + "login-button-icon": "Login button icon", | |
158 | + "login-button-label": "Provider label", | |
159 | + "login-button-label-1": "Login with $(Provider label)", | |
160 | + "login-button-label-required": "Label is required.", | |
161 | + "login-provider": "Login provider", | |
162 | + "mapper": "Mapper", | |
163 | + "new-domain": "New domain", | |
164 | + "oauth2": "OAuth2", | |
165 | + "redirect-uri-template": "Redirect URI template", | |
166 | + "copy-redirect-uri": "Copy redirect URI", | |
167 | + "registration-id": "Registration ID", | |
168 | + "registration-id-required": "Registration ID is required.", | |
169 | + "registration-id-unique": "Registration ID need to unique for the system.", | |
170 | + "scope": "Scope", | |
171 | + "scope-required": "Scope is required.", | |
172 | + "tenant-name-pattern": "Tenant name pattern", | |
173 | + "tenant-name-pattern-required": "Tenant name pattern is required.", | |
174 | + "tenant-name-strategy": "Tenant name strategy", | |
175 | + "type": "Mapper type", | |
176 | + "uri-pattern-error": "Invalid URI format.", | |
177 | + "url": "URL", | |
178 | + "url-pattern": "Invalid URL format.", | |
179 | + "url-required": "URL is required.", | |
180 | + "user-info-uri": "User info URI", | |
181 | + "user-info-uri-required": "User info URI is required.", | |
182 | + "user-name-attribute-name": "User name attribute key", | |
183 | + "user-name-attribute-name-required": "User name attribute key is required", | |
184 | + "protocol": "Protocol", | |
185 | + "domain-schema-http": "HTTP", | |
186 | + "domain-schema-https": "HTTPS", | |
187 | + "domain-schema-mixed": "HTTP+HTTPS", | |
188 | + "enable": "Enable OAuth2 settings" | |
189 | + } | |
190 | + }, | |
125 | 191 | "alarm": { |
126 | 192 | "alarm": "Alarm", |
127 | 193 | "alarms": "Alarms", | ... | ... |