89 changed files with 4973 additions and 342 deletions
@ -0,0 +1,20 @@ |
|||
{ |
|||
"providerId": "Facebook", |
|||
"accessTokenUri": "https://graph.facebook.com/v2.8/oauth/access_token", |
|||
"authorizationUri": "https://www.facebook.com/v2.8/dialog/oauth", |
|||
"scope": ["email","public_profile"], |
|||
"jwkSetUri": null, |
|||
"userInfoUri": "https://graph.facebook.com/me?fields=id,name,first_name,last_name,email", |
|||
"clientAuthenticationMethod": "BASIC", |
|||
"userNameAttributeName": "email", |
|||
"basic": { |
|||
"emailAttributeKey": "email", |
|||
"firstNameAttributeKey": "first_name", |
|||
"lastNameAttributeKey": "last_name", |
|||
"tenantNameStrategy": "DOMAIN" |
|||
}, |
|||
"comment": null, |
|||
"loginButtonIcon": "mdi:facebook", |
|||
"loginButtonLabel": "Facebook", |
|||
"helpLink": "https://developers.facebook.com/docs/facebook-login/web#logindialog" |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
{ |
|||
"providerId": "Github", |
|||
"accessTokenUri": "https://github.com/login/oauth/access_token", |
|||
"authorizationUri": "https://github.com/login/oauth/authorize", |
|||
"scope": ["read:user","user:email"], |
|||
"jwkSetUri": null, |
|||
"userInfoUri": "https://api.github.com/user", |
|||
"clientAuthenticationMethod": "BASIC", |
|||
"userNameAttributeName": "login", |
|||
"basic": { |
|||
"tenantNameStrategy": "DOMAIN" |
|||
}, |
|||
"comment": "In order to log into ThingsBoard you need to have user's email. You may configure and use Custom OAuth2 Mapper to get email information. Please refer to <a href=\"https://docs.github.com/en/rest/reference/users#list-email-addresses-for-the-authenticated-user\">Github Documentation</a>", |
|||
"loginButtonIcon": "mdi:github", |
|||
"loginButtonLabel": "Github", |
|||
"helpLink": "https://docs.github.com/en/developers/apps/creating-an-oauth-app" |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
{ |
|||
"providerId": "Google", |
|||
"additionalInfo": null, |
|||
"accessTokenUri": "https://oauth2.googleapis.com/token", |
|||
"authorizationUri": "https://accounts.google.com/o/oauth2/v2/auth", |
|||
"scope": ["email","openid","profile"], |
|||
"jwkSetUri": "https://www.googleapis.com/oauth2/v3/certs", |
|||
"userInfoUri": "https://openidconnect.googleapis.com/v1/userinfo", |
|||
"clientAuthenticationMethod": "BASIC", |
|||
"userNameAttributeName": "email", |
|||
"basic": { |
|||
"emailAttributeKey": "email", |
|||
"firstNameAttributeKey": "given_name", |
|||
"lastNameAttributeKey": "family_name", |
|||
"tenantNameStrategy": "DOMAIN" |
|||
}, |
|||
"comment": null, |
|||
"loginButtonIcon": "mdi:google", |
|||
"loginButtonLabel": "Google", |
|||
"helpLink": "https://developers.google.com/adwords/api/docs/guides/authentication" |
|||
} |
|||
@ -0,0 +1,267 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.config; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.security.crypto.keygen.Base64StringKeyGenerator; |
|||
import org.springframework.security.crypto.keygen.StringKeyGenerator; |
|||
import org.springframework.security.oauth2.client.registration.ClientRegistration; |
|||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; |
|||
import org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestResolver; |
|||
import org.springframework.security.oauth2.core.AuthorizationGrantType; |
|||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod; |
|||
import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest; |
|||
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; |
|||
import org.springframework.security.oauth2.core.endpoint.PkceParameterNames; |
|||
import org.springframework.security.oauth2.core.oidc.OidcScopes; |
|||
import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames; |
|||
import org.springframework.security.web.util.UrlUtils; |
|||
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.util.CollectionUtils; |
|||
import org.springframework.util.StringUtils; |
|||
import org.springframework.web.util.UriComponents; |
|||
import org.springframework.web.util.UriComponentsBuilder; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2Configuration; |
|||
import org.thingsboard.server.utils.MiscUtils; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import java.nio.charset.StandardCharsets; |
|||
import java.security.MessageDigest; |
|||
import java.security.NoSuchAlgorithmException; |
|||
import java.util.Base64; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
@Service |
|||
@Slf4j |
|||
public class CustomOAuth2AuthorizationRequestResolver implements OAuth2AuthorizationRequestResolver { |
|||
public static final String DEFAULT_AUTHORIZATION_REQUEST_BASE_URI = "/oauth2/authorization"; |
|||
public static final String DEFAULT_LOGIN_PROCESSING_URI = "/login/oauth2/code/"; |
|||
private static final String REGISTRATION_ID_URI_VARIABLE_NAME = "registrationId"; |
|||
private static final char PATH_DELIMITER = '/'; |
|||
|
|||
private final AntPathRequestMatcher authorizationRequestMatcher = new AntPathRequestMatcher( |
|||
DEFAULT_AUTHORIZATION_REQUEST_BASE_URI + "/{" + REGISTRATION_ID_URI_VARIABLE_NAME + "}"); |
|||
private final StringKeyGenerator stateGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder()); |
|||
private final StringKeyGenerator secureKeyGenerator = new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96); |
|||
|
|||
@Autowired |
|||
private ClientRegistrationRepository clientRegistrationRepository; |
|||
|
|||
@Autowired(required = false) |
|||
private OAuth2Configuration oauth2Configuration; |
|||
|
|||
|
|||
@Override |
|||
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) { |
|||
String registrationId = this.resolveRegistrationId(request); |
|||
String redirectUriAction = getAction(request, "login"); |
|||
return resolve(request, registrationId, redirectUriAction); |
|||
} |
|||
|
|||
@Override |
|||
public OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId) { |
|||
if (registrationId == null) { |
|||
return null; |
|||
} |
|||
String redirectUriAction = getAction(request, "authorize"); |
|||
return resolve(request, registrationId, redirectUriAction); |
|||
} |
|||
|
|||
private String getAction(HttpServletRequest request, String defaultAction) { |
|||
String action = request.getParameter("action"); |
|||
if (action == null) { |
|||
return defaultAction; |
|||
} |
|||
return action; |
|||
} |
|||
|
|||
private OAuth2AuthorizationRequest resolve(HttpServletRequest request, String registrationId, String redirectUriAction) { |
|||
if (registrationId == null) { |
|||
return null; |
|||
} |
|||
|
|||
ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId); |
|||
if (clientRegistration == null) { |
|||
throw new IllegalArgumentException("Invalid Client Registration with Id: " + registrationId); |
|||
} |
|||
|
|||
Map<String, Object> attributes = new HashMap<>(); |
|||
attributes.put(OAuth2ParameterNames.REGISTRATION_ID, clientRegistration.getRegistrationId()); |
|||
|
|||
OAuth2AuthorizationRequest.Builder builder; |
|||
if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(clientRegistration.getAuthorizationGrantType())) { |
|||
builder = OAuth2AuthorizationRequest.authorizationCode(); |
|||
Map<String, Object> additionalParameters = new HashMap<>(); |
|||
if (!CollectionUtils.isEmpty(clientRegistration.getScopes()) && |
|||
clientRegistration.getScopes().contains(OidcScopes.OPENID)) { |
|||
// Section 3.1.2.1 Authentication Request - https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
|||
// scope
|
|||
// REQUIRED. OpenID Connect requests MUST contain the "openid" scope value.
|
|||
addNonceParameters(attributes, additionalParameters); |
|||
} |
|||
if (ClientAuthenticationMethod.NONE.equals(clientRegistration.getClientAuthenticationMethod())) { |
|||
addPkceParameters(attributes, additionalParameters); |
|||
} |
|||
builder.additionalParameters(additionalParameters); |
|||
} else if (AuthorizationGrantType.IMPLICIT.equals(clientRegistration.getAuthorizationGrantType())) { |
|||
builder = OAuth2AuthorizationRequest.implicit(); |
|||
} else { |
|||
throw new IllegalArgumentException("Invalid Authorization Grant Type (" + |
|||
clientRegistration.getAuthorizationGrantType().getValue() + |
|||
") for Client Registration with Id: " + clientRegistration.getRegistrationId()); |
|||
} |
|||
|
|||
String redirectUriStr = expandRedirectUri(request, clientRegistration, redirectUriAction); |
|||
|
|||
return builder |
|||
.clientId(clientRegistration.getClientId()) |
|||
.authorizationUri(clientRegistration.getProviderDetails().getAuthorizationUri()) |
|||
.redirectUri(redirectUriStr) |
|||
.scopes(clientRegistration.getScopes()) |
|||
.state(this.stateGenerator.generateKey()) |
|||
.attributes(attributes) |
|||
.build(); |
|||
} |
|||
|
|||
private String resolveRegistrationId(HttpServletRequest request) { |
|||
if (this.authorizationRequestMatcher.matches(request)) { |
|||
return this.authorizationRequestMatcher |
|||
.matcher(request).getVariables().get(REGISTRATION_ID_URI_VARIABLE_NAME); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* Expands the {@link ClientRegistration#getRedirectUriTemplate()} with following provided variables:<br/> |
|||
* - baseUrl (e.g. https://localhost/app) <br/>
|
|||
* - baseScheme (e.g. https) <br/> |
|||
* - baseHost (e.g. localhost) <br/> |
|||
* - basePort (e.g. :8080) <br/> |
|||
* - basePath (e.g. /app) <br/> |
|||
* - registrationId (e.g. google) <br/> |
|||
* - action (e.g. login) <br/> |
|||
* <p/> |
|||
* Null variables are provided as empty strings. |
|||
* <p/> |
|||
* Default redirectUriTemplate is: {@link org.springframework.security.config.oauth2.client}.CommonOAuth2Provider#DEFAULT_REDIRECT_URL |
|||
* |
|||
* @return expanded URI |
|||
*/ |
|||
private String expandRedirectUri(HttpServletRequest request, ClientRegistration clientRegistration, String action) { |
|||
Map<String, String> uriVariables = new HashMap<>(); |
|||
uriVariables.put("registrationId", clientRegistration.getRegistrationId()); |
|||
|
|||
UriComponents uriComponents = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request)) |
|||
.replacePath(request.getContextPath()) |
|||
.replaceQuery(null) |
|||
.fragment(null) |
|||
.build(); |
|||
String scheme = uriComponents.getScheme(); |
|||
uriVariables.put("baseScheme", scheme == null ? "" : scheme); |
|||
String host = uriComponents.getHost(); |
|||
uriVariables.put("baseHost", host == null ? "" : host); |
|||
// following logic is based on HierarchicalUriComponents#toUriString()
|
|||
int port = uriComponents.getPort(); |
|||
uriVariables.put("basePort", port == -1 ? "" : ":" + port); |
|||
String path = uriComponents.getPath(); |
|||
if (StringUtils.hasLength(path)) { |
|||
if (path.charAt(0) != PATH_DELIMITER) { |
|||
path = PATH_DELIMITER + path; |
|||
} |
|||
} |
|||
uriVariables.put("basePath", path == null ? "" : path); |
|||
uriVariables.put("baseUrl", uriComponents.toUriString()); |
|||
|
|||
uriVariables.put("action", action == null ? "" : action); |
|||
|
|||
String redirectUri = getRedirectUri(request); |
|||
log.trace("Redirect URI - {}.", redirectUri); |
|||
|
|||
return UriComponentsBuilder.fromUriString(redirectUri) |
|||
.buildAndExpand(uriVariables) |
|||
.toUriString(); |
|||
} |
|||
|
|||
private String getRedirectUri(HttpServletRequest request) { |
|||
String loginProcessingUri = oauth2Configuration != null ? oauth2Configuration.getLoginProcessingUrl() : DEFAULT_LOGIN_PROCESSING_URI; |
|||
|
|||
String scheme = MiscUtils.getScheme(request); |
|||
String domainName = MiscUtils.getDomainName(request); |
|||
int port = MiscUtils.getPort(request); |
|||
String baseUrl = scheme + "://" + domainName; |
|||
if (needsPort(scheme, port)){ |
|||
baseUrl += ":" + port; |
|||
} |
|||
return baseUrl + loginProcessingUri; |
|||
} |
|||
|
|||
private boolean needsPort(String scheme, int port) { |
|||
boolean isHttpDefault = "http".equals(scheme.toLowerCase()) && port == 80; |
|||
boolean isHttpsDefault = "https".equals(scheme.toLowerCase()) && port == 443; |
|||
return !isHttpDefault && !isHttpsDefault; |
|||
} |
|||
|
|||
/** |
|||
* Creates nonce and its hash for use in OpenID Connect 1.0 Authentication Requests. |
|||
* |
|||
* @param attributes where the {@link OidcParameterNames#NONCE} is stored for the authentication request |
|||
* @param additionalParameters where the {@link OidcParameterNames#NONCE} hash is added for the authentication request |
|||
* |
|||
* @since 5.2 |
|||
* @see <a target="_blank" href="https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest">3.1.2.1. Authentication Request</a> |
|||
*/ |
|||
private void addNonceParameters(Map<String, Object> attributes, Map<String, Object> additionalParameters) { |
|||
try { |
|||
String nonce = this.secureKeyGenerator.generateKey(); |
|||
String nonceHash = createHash(nonce); |
|||
attributes.put(OidcParameterNames.NONCE, nonce); |
|||
additionalParameters.put(OidcParameterNames.NONCE, nonceHash); |
|||
} catch (NoSuchAlgorithmException e) { } |
|||
} |
|||
|
|||
/** |
|||
* Creates and adds additional PKCE parameters for use in the OAuth 2.0 Authorization and Access Token Requests |
|||
* |
|||
* @param attributes where {@link PkceParameterNames#CODE_VERIFIER} is stored for the token request |
|||
* @param additionalParameters where {@link PkceParameterNames#CODE_CHALLENGE} and, usually, |
|||
* {@link PkceParameterNames#CODE_CHALLENGE_METHOD} are added to be used in the authorization request. |
|||
* |
|||
* @since 5.2 |
|||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7636#section-1.1">1.1. Protocol Flow</a> |
|||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7636#section-4.1">4.1. Client Creates a Code Verifier</a> |
|||
* @see <a target="_blank" href="https://tools.ietf.org/html/rfc7636#section-4.2">4.2. Client Creates the Code Challenge</a> |
|||
*/ |
|||
private void addPkceParameters(Map<String, Object> attributes, Map<String, Object> additionalParameters) { |
|||
String codeVerifier = this.secureKeyGenerator.generateKey(); |
|||
attributes.put(PkceParameterNames.CODE_VERIFIER, codeVerifier); |
|||
try { |
|||
String codeChallenge = createHash(codeVerifier); |
|||
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, codeChallenge); |
|||
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256"); |
|||
} catch (NoSuchAlgorithmException e) { |
|||
additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, codeVerifier); |
|||
} |
|||
} |
|||
|
|||
private static String createHash(String value) throws NoSuchAlgorithmException { |
|||
MessageDigest md = MessageDigest.getInstance("SHA-256"); |
|||
byte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII)); |
|||
return Base64.getUrlEncoder().withoutPadding().encodeToString(digest); |
|||
} |
|||
} |
|||
@ -0,0 +1,77 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.controller; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.security.access.prepost.PreAuthorize; |
|||
import org.springframework.web.bind.annotation.*; |
|||
import org.thingsboard.server.common.data.EntityType; |
|||
import org.thingsboard.server.common.data.audit.ActionType; |
|||
import org.thingsboard.server.common.data.exception.ThingsboardException; |
|||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
import org.thingsboard.server.service.security.permission.Operation; |
|||
import org.thingsboard.server.service.security.permission.Resource; |
|||
|
|||
import java.util.List; |
|||
|
|||
@RestController |
|||
@TbCoreComponent |
|||
@RequestMapping("/api/oauth2/config/template") |
|||
@Slf4j |
|||
public class OAuth2ConfigTemplateController extends BaseController { |
|||
private static final String CLIENT_REGISTRATION_TEMPLATE_ID = "clientRegistrationTemplateId"; |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
|||
@RequestMapping(method = RequestMethod.POST) |
|||
@ResponseStatus(value = HttpStatus.OK) |
|||
public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(@RequestBody OAuth2ClientRegistrationTemplate clientRegistrationTemplate) throws ThingsboardException { |
|||
try { |
|||
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.WRITE); |
|||
return oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
|||
@RequestMapping(value = "/{clientRegistrationTemplateId}", method = RequestMethod.DELETE) |
|||
@ResponseStatus(value = HttpStatus.OK) |
|||
public void deleteClientRegistrationTemplate(@PathVariable(CLIENT_REGISTRATION_TEMPLATE_ID) String strClientRegistrationTemplateId) throws ThingsboardException { |
|||
checkParameter(CLIENT_REGISTRATION_TEMPLATE_ID, strClientRegistrationTemplateId); |
|||
try { |
|||
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.DELETE); |
|||
OAuth2ClientRegistrationTemplateId clientRegistrationTemplateId = new OAuth2ClientRegistrationTemplateId(toUUID(strClientRegistrationTemplateId)); |
|||
oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplateId); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN', 'TENANT_ADMIN')") |
|||
@RequestMapping(method = RequestMethod.GET, produces = "application/json") |
|||
@ResponseBody |
|||
public List<OAuth2ClientRegistrationTemplate> getClientRegistrationTemplates() throws ThingsboardException { |
|||
try { |
|||
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_TEMPLATE, Operation.READ); |
|||
return oAuth2ConfigTemplateService.findAllClientRegistrationTemplates(); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,73 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.controller; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.http.HttpStatus; |
|||
import org.springframework.security.access.prepost.PreAuthorize; |
|||
import org.springframework.web.bind.annotation.*; |
|||
import org.thingsboard.server.common.data.exception.ThingsboardException; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientsParams; |
|||
import org.thingsboard.server.common.data.oauth2.SchemeType; |
|||
import org.thingsboard.server.queue.util.TbCoreComponent; |
|||
import org.thingsboard.server.service.security.permission.Operation; |
|||
import org.thingsboard.server.service.security.permission.Resource; |
|||
import org.thingsboard.server.utils.MiscUtils; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import java.util.List; |
|||
|
|||
@RestController |
|||
@TbCoreComponent |
|||
@RequestMapping("/api") |
|||
@Slf4j |
|||
public class OAuth2Controller extends BaseController { |
|||
@RequestMapping(value = "/noauth/oauth2Clients", method = RequestMethod.POST) |
|||
@ResponseBody |
|||
public List<OAuth2ClientInfo> getOAuth2Clients(HttpServletRequest request) throws ThingsboardException { |
|||
try { |
|||
return oAuth2Service.getOAuth2Clients(MiscUtils.getScheme(request), MiscUtils.getDomainName(request)); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
|||
@RequestMapping(value = "/oauth2/config", method = RequestMethod.GET, produces = "application/json") |
|||
@ResponseBody |
|||
public OAuth2ClientsParams getCurrentOAuth2Params() throws ThingsboardException { |
|||
try { |
|||
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.READ); |
|||
return oAuth2Service.findOAuth2Params(); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
|
|||
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
|||
@RequestMapping(value = "/oauth2/config", method = RequestMethod.POST) |
|||
@ResponseStatus(value = HttpStatus.OK) |
|||
public OAuth2ClientsParams saveOAuth2Params(@RequestBody OAuth2ClientsParams oauth2Params) throws ThingsboardException { |
|||
try { |
|||
accessControlService.checkPermission(getCurrentUser(), Resource.OAUTH2_CONFIGURATION_INFO, Operation.WRITE); |
|||
oAuth2Service.saveOAuth2Params(oauth2Params); |
|||
return oAuth2Service.findOAuth2Params(); |
|||
} catch (Exception e) { |
|||
throw handleException(e); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.security.auth.oauth2; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.lang3.text.StrSubstitutor; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2User; |
|||
|
|||
import java.util.Map; |
|||
|
|||
@Slf4j |
|||
public class BasicMapperUtils { |
|||
private static final String START_PLACEHOLDER_PREFIX = "%{"; |
|||
private static final String END_PLACEHOLDER_PREFIX = "}"; |
|||
|
|||
public static OAuth2User getOAuth2User(String email, Map<String, Object> attributes, OAuth2MapperConfig config) { |
|||
OAuth2User oauth2User = new OAuth2User(); |
|||
oauth2User.setEmail(email); |
|||
oauth2User.setTenantName(getTenantName(email, attributes, config)); |
|||
if (!StringUtils.isEmpty(config.getBasic().getLastNameAttributeKey())) { |
|||
String lastName = getStringAttributeByKey(attributes, config.getBasic().getLastNameAttributeKey()); |
|||
oauth2User.setLastName(lastName); |
|||
} |
|||
if (!StringUtils.isEmpty(config.getBasic().getFirstNameAttributeKey())) { |
|||
String firstName = getStringAttributeByKey(attributes, config.getBasic().getFirstNameAttributeKey()); |
|||
oauth2User.setFirstName(firstName); |
|||
} |
|||
if (!StringUtils.isEmpty(config.getBasic().getCustomerNamePattern())) { |
|||
StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); |
|||
String customerName = sub.replace(config.getBasic().getCustomerNamePattern()); |
|||
oauth2User.setCustomerName(customerName); |
|||
} |
|||
oauth2User.setAlwaysFullScreen(config.getBasic().isAlwaysFullScreen()); |
|||
if (!StringUtils.isEmpty(config.getBasic().getDefaultDashboardName())) { |
|||
oauth2User.setDefaultDashboardName(config.getBasic().getDefaultDashboardName()); |
|||
} |
|||
return oauth2User; |
|||
} |
|||
|
|||
public static String getTenantName(String email, Map<String, Object> attributes, OAuth2MapperConfig config) { |
|||
switch (config.getBasic().getTenantNameStrategy()) { |
|||
case EMAIL: |
|||
return email; |
|||
case DOMAIN: |
|||
return email.substring(email .indexOf("@") + 1); |
|||
case CUSTOM: |
|||
StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); |
|||
return sub.replace(config.getBasic().getTenantNamePattern()); |
|||
default: |
|||
throw new RuntimeException("Tenant Name Strategy with type " + config.getBasic().getTenantNameStrategy() + " is not supported!"); |
|||
} |
|||
} |
|||
|
|||
public static String getStringAttributeByKey(Map<String, Object> attributes, String key) { |
|||
String result = null; |
|||
try { |
|||
result = (String) attributes.get(key); |
|||
} catch (Exception e) { |
|||
log.warn("Can't convert attribute to String by key " + key); |
|||
} |
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.service.security.auth.oauth2; |
|||
|
|||
import lombok.Data; |
|||
import lombok.ToString; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.web.client.RestTemplateBuilder; |
|||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.web.client.RestTemplate; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2Configuration; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2User; |
|||
import org.thingsboard.server.service.security.model.SecurityUser; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Map; |
|||
import java.util.Optional; |
|||
|
|||
@Service(value = "githubOAuth2ClientMapper") |
|||
@Slf4j |
|||
public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { |
|||
private static final String EMAIL_URL_KEY = "emailUrl"; |
|||
|
|||
private static final String AUTHORIZATION = "Authorization"; |
|||
|
|||
private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); |
|||
|
|||
@Autowired |
|||
private OAuth2Configuration oAuth2Configuration; |
|||
|
|||
@Override |
|||
public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) { |
|||
Map<String, String> githubMapperConfig = oAuth2Configuration.getGithubMapper(); |
|||
String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken); |
|||
Map<String, Object> attributes = token.getPrincipal().getAttributes(); |
|||
OAuth2User oAuth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); |
|||
return getOrCreateSecurityUserFromOAuth2User(oAuth2User, config.isAllowUserCreation(), config.isActivateUser()); |
|||
} |
|||
|
|||
private synchronized String getEmail(String emailUrl, String oauth2Token) { |
|||
restTemplateBuilder = restTemplateBuilder.defaultHeader(AUTHORIZATION, "token " + oauth2Token); |
|||
|
|||
RestTemplate restTemplate = restTemplateBuilder.build(); |
|||
GithubEmailsResponse githubEmailsResponse; |
|||
try { |
|||
githubEmailsResponse = restTemplate.getForEntity(emailUrl, GithubEmailsResponse.class).getBody(); |
|||
if (githubEmailsResponse == null){ |
|||
throw new RuntimeException("Empty Github response!"); |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("There was an error during connection to Github API", e); |
|||
throw new RuntimeException("Unable to login. Please contact your Administrator!"); |
|||
} |
|||
Optional<String> emailOpt = githubEmailsResponse.stream() |
|||
.filter(GithubEmailResponse::isPrimary) |
|||
.map(GithubEmailResponse::getEmail) |
|||
.findAny(); |
|||
if (emailOpt.isPresent()){ |
|||
return emailOpt.get(); |
|||
} else { |
|||
log.error("Could not find primary email from {}.", githubEmailsResponse); |
|||
throw new RuntimeException("Unable to login. Please contact your Administrator!"); |
|||
} |
|||
} |
|||
private static class GithubEmailsResponse extends ArrayList<GithubEmailResponse> {} |
|||
|
|||
@Data |
|||
@ToString |
|||
private static class GithubEmailResponse { |
|||
private String email; |
|||
private boolean verified; |
|||
private boolean primary; |
|||
private String visibility; |
|||
} |
|||
} |
|||
@ -0,0 +1,31 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.oauth2; |
|||
|
|||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface OAuth2ConfigTemplateService { |
|||
OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate); |
|||
|
|||
OAuth2ClientRegistrationTemplate findClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId); |
|||
|
|||
List<OAuth2ClientRegistrationTemplate> findAllClientRegistrationTemplates(); |
|||
|
|||
void deleteClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId); |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.id; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonCreator; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
public class OAuth2ClientRegistrationInfoId extends UUIDBased { |
|||
|
|||
@JsonCreator |
|||
public OAuth2ClientRegistrationInfoId(@JsonProperty("id") UUID id) { |
|||
super(id); |
|||
} |
|||
|
|||
public static OAuth2ClientRegistrationInfoId fromString(String clientRegistrationInfoId) { |
|||
return new OAuth2ClientRegistrationInfoId(UUID.fromString(clientRegistrationInfoId)); |
|||
} |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.id; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonCreator; |
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
public class OAuth2ClientRegistrationTemplateId extends UUIDBased { |
|||
|
|||
@JsonCreator |
|||
public OAuth2ClientRegistrationTemplateId(@JsonProperty("id") UUID id) { |
|||
super(id); |
|||
} |
|||
|
|||
public static OAuth2ClientRegistrationTemplateId fromString(String clientRegistrationTemplateId) { |
|||
return new OAuth2ClientRegistrationTemplateId(UUID.fromString(clientRegistrationTemplateId)); |
|||
} |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
import lombok.*; |
|||
|
|||
@EqualsAndHashCode |
|||
@Data |
|||
@ToString |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
@Builder |
|||
public class DomainInfo { |
|||
private SchemeType scheme; |
|||
private String name; |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
|
|||
@EqualsAndHashCode(callSuper = true) |
|||
@Data |
|||
public class ExtendedOAuth2ClientRegistrationInfo extends OAuth2ClientRegistrationInfo { |
|||
|
|||
private String domainName; |
|||
private SchemeType domainScheme; |
|||
|
|||
public ExtendedOAuth2ClientRegistrationInfo() { |
|||
super(); |
|||
} |
|||
|
|||
public ExtendedOAuth2ClientRegistrationInfo(OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo, |
|||
SchemeType domainScheme, |
|||
String domainName) { |
|||
super(oAuth2ClientRegistrationInfo); |
|||
this.domainScheme = domainScheme; |
|||
this.domainName = domainName; |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
public enum MapperType { |
|||
BASIC, CUSTOM, GITHUB; |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
import lombok.*; |
|||
|
|||
@Builder(toBuilder = true) |
|||
@EqualsAndHashCode |
|||
@Data |
|||
@ToString |
|||
public class OAuth2BasicMapperConfig { |
|||
private final String emailAttributeKey; |
|||
private final String firstNameAttributeKey; |
|||
private final String lastNameAttributeKey; |
|||
private final TenantNameStrategyType tenantNameStrategy; |
|||
private final String tenantNamePattern; |
|||
private final String customerNamePattern; |
|||
private final String defaultDashboardName; |
|||
private final boolean alwaysFullScreen; |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.BaseData; |
|||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; |
|||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; |
|||
|
|||
@EqualsAndHashCode(callSuper = true) |
|||
@Data |
|||
@ToString |
|||
@NoArgsConstructor |
|||
public class OAuth2ClientRegistration extends BaseData<OAuth2ClientRegistrationId> { |
|||
|
|||
private OAuth2ClientRegistrationInfoId clientRegistrationId; |
|||
private String domainName; |
|||
private SchemeType domainScheme; |
|||
|
|||
public OAuth2ClientRegistration(OAuth2ClientRegistration clientRegistration) { |
|||
super(clientRegistration); |
|||
this.clientRegistrationId = clientRegistration.clientRegistrationId; |
|||
this.domainName = clientRegistration.domainName; |
|||
this.domainScheme = clientRegistration.domainScheme; |
|||
} |
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonProperty; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.HasName; |
|||
import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; |
|||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; |
|||
|
|||
import java.util.List; |
|||
|
|||
@EqualsAndHashCode(callSuper = true) |
|||
@Data |
|||
@ToString(exclude = {"clientSecret"}) |
|||
@NoArgsConstructor |
|||
public class OAuth2ClientRegistrationInfo extends SearchTextBasedWithAdditionalInfo<OAuth2ClientRegistrationInfoId> implements HasName { |
|||
|
|||
private boolean enabled; |
|||
private OAuth2MapperConfig mapperConfig; |
|||
private String clientId; |
|||
private String clientSecret; |
|||
private String authorizationUri; |
|||
private String accessTokenUri; |
|||
private List<String> scope; |
|||
private String userInfoUri; |
|||
private String userNameAttributeName; |
|||
private String jwkSetUri; |
|||
private String clientAuthenticationMethod; |
|||
private String loginButtonLabel; |
|||
private String loginButtonIcon; |
|||
|
|||
public OAuth2ClientRegistrationInfo(OAuth2ClientRegistrationInfo clientRegistration) { |
|||
super(clientRegistration); |
|||
this.enabled = clientRegistration.enabled; |
|||
this.mapperConfig = clientRegistration.mapperConfig; |
|||
this.clientId = clientRegistration.clientId; |
|||
this.clientSecret = clientRegistration.clientSecret; |
|||
this.authorizationUri = clientRegistration.authorizationUri; |
|||
this.accessTokenUri = clientRegistration.accessTokenUri; |
|||
this.scope = clientRegistration.scope; |
|||
this.userInfoUri = clientRegistration.userInfoUri; |
|||
this.userNameAttributeName = clientRegistration.userNameAttributeName; |
|||
this.jwkSetUri = clientRegistration.jwkSetUri; |
|||
this.clientAuthenticationMethod = clientRegistration.clientAuthenticationMethod; |
|||
this.loginButtonLabel = clientRegistration.loginButtonLabel; |
|||
this.loginButtonIcon = clientRegistration.loginButtonIcon; |
|||
} |
|||
|
|||
@Override |
|||
@JsonProperty(access = JsonProperty.Access.READ_ONLY) |
|||
public String getName() { |
|||
return loginButtonLabel; |
|||
} |
|||
|
|||
@Override |
|||
public String getSearchText() { |
|||
return getName(); |
|||
} |
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.NoArgsConstructor; |
|||
import lombok.ToString; |
|||
import org.thingsboard.server.common.data.HasName; |
|||
import org.thingsboard.server.common.data.HasTenantId; |
|||
import org.thingsboard.server.common.data.SearchTextBasedWithAdditionalInfo; |
|||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
|
|||
import java.util.List; |
|||
|
|||
@EqualsAndHashCode(callSuper = true) |
|||
@Data |
|||
@ToString |
|||
@NoArgsConstructor |
|||
public class OAuth2ClientRegistrationTemplate extends SearchTextBasedWithAdditionalInfo<OAuth2ClientRegistrationTemplateId> implements HasName { |
|||
|
|||
private String providerId; |
|||
private MapperType mapperType; |
|||
private OAuth2BasicMapperConfig basic; |
|||
private String authorizationUri; |
|||
private String accessTokenUri; |
|||
private List<String> scope; |
|||
private String userInfoUri; |
|||
private String userNameAttributeName; |
|||
private String jwkSetUri; |
|||
private String clientAuthenticationMethod; |
|||
private String comment; |
|||
private String loginButtonIcon; |
|||
private String loginButtonLabel; |
|||
private String helpLink; |
|||
|
|||
public OAuth2ClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { |
|||
super(clientRegistrationTemplate); |
|||
this.providerId = clientRegistrationTemplate.providerId; |
|||
this.mapperType = clientRegistrationTemplate.mapperType; |
|||
this.basic = clientRegistrationTemplate.basic; |
|||
this.authorizationUri = clientRegistrationTemplate.authorizationUri; |
|||
this.accessTokenUri = clientRegistrationTemplate.accessTokenUri; |
|||
this.scope = clientRegistrationTemplate.scope; |
|||
this.userInfoUri = clientRegistrationTemplate.userInfoUri; |
|||
this.userNameAttributeName = clientRegistrationTemplate.userNameAttributeName; |
|||
this.jwkSetUri = clientRegistrationTemplate.jwkSetUri; |
|||
this.clientAuthenticationMethod = clientRegistrationTemplate.clientAuthenticationMethod; |
|||
this.comment = clientRegistrationTemplate.comment; |
|||
this.loginButtonIcon = clientRegistrationTemplate.loginButtonIcon; |
|||
this.loginButtonLabel = clientRegistrationTemplate.loginButtonLabel; |
|||
this.helpLink = clientRegistrationTemplate.helpLink; |
|||
} |
|||
|
|||
@Override |
|||
public String getName() { |
|||
return providerId; |
|||
} |
|||
|
|||
@Override |
|||
public String getSearchText() { |
|||
return getName(); |
|||
} |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
import lombok.*; |
|||
|
|||
import java.util.List; |
|||
import java.util.Set; |
|||
|
|||
@EqualsAndHashCode |
|||
@Data |
|||
@ToString |
|||
@Builder(toBuilder = true) |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class OAuth2ClientsDomainParams { |
|||
private Set<DomainInfo> domainInfos; |
|||
private Set<ClientRegistrationDto> clientRegistrations; |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
import lombok.*; |
|||
import java.util.Set; |
|||
|
|||
@EqualsAndHashCode |
|||
@Data |
|||
@ToString |
|||
@Builder(toBuilder = true) |
|||
@NoArgsConstructor |
|||
@AllArgsConstructor |
|||
public class OAuth2ClientsParams { |
|||
private boolean enabled; |
|||
private Set<OAuth2ClientsDomainParams> domainsParams; |
|||
} |
|||
@ -0,0 +1,29 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
import lombok.*; |
|||
|
|||
@Builder(toBuilder = true) |
|||
@EqualsAndHashCode |
|||
@Data |
|||
@ToString(exclude = {"password"}) |
|||
public class OAuth2CustomMapperConfig { |
|||
private final String url; |
|||
private final String username; |
|||
private final String password; |
|||
private final boolean sendToken; |
|||
} |
|||
@ -0,0 +1,33 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
import lombok.Builder; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import lombok.ToString; |
|||
|
|||
@Builder(toBuilder = true) |
|||
@EqualsAndHashCode |
|||
@Data |
|||
@ToString |
|||
public class OAuth2MapperConfig { |
|||
private boolean allowUserCreation; |
|||
private boolean activateUser; |
|||
private MapperType type; |
|||
private OAuth2BasicMapperConfig basic; |
|||
private OAuth2CustomMapperConfig custom; |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
public enum SchemeType { |
|||
HTTP, HTTPS, MIXED; |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.common.data.oauth2; |
|||
|
|||
public enum TenantNameStrategyType { |
|||
DOMAIN, EMAIL, CUSTOM; |
|||
} |
|||
@ -0,0 +1,231 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.model.sql; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.hibernate.annotations.Type; |
|||
import org.hibernate.annotations.TypeDef; |
|||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; |
|||
import org.thingsboard.server.common.data.oauth2.*; |
|||
import org.thingsboard.server.dao.model.BaseSqlEntity; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.util.mapping.JsonStringType; |
|||
|
|||
import javax.persistence.*; |
|||
import java.util.Arrays; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@TypeDef(name = "json", typeClass = JsonStringType.class) |
|||
@MappedSuperclass |
|||
public abstract class AbstractOAuth2ClientRegistrationInfoEntity<T extends OAuth2ClientRegistrationInfo> extends BaseSqlEntity<T> { |
|||
|
|||
@Column(name = ModelConstants.OAUTH2_ENABLED_PROPERTY) |
|||
private Boolean enabled; |
|||
@Column(name = ModelConstants.OAUTH2_CLIENT_ID_PROPERTY) |
|||
private String clientId; |
|||
@Column(name = ModelConstants.OAUTH2_CLIENT_SECRET_PROPERTY) |
|||
private String clientSecret; |
|||
@Column(name = ModelConstants.OAUTH2_AUTHORIZATION_URI_PROPERTY) |
|||
private String authorizationUri; |
|||
@Column(name = ModelConstants.OAUTH2_TOKEN_URI_PROPERTY) |
|||
private String tokenUri; |
|||
@Column(name = ModelConstants.OAUTH2_SCOPE_PROPERTY) |
|||
private String scope; |
|||
@Column(name = ModelConstants.OAUTH2_USER_INFO_URI_PROPERTY) |
|||
private String userInfoUri; |
|||
@Column(name = ModelConstants.OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY) |
|||
private String userNameAttributeName; |
|||
@Column(name = ModelConstants.OAUTH2_JWK_SET_URI_PROPERTY) |
|||
private String jwkSetUri; |
|||
@Column(name = ModelConstants.OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY) |
|||
private String clientAuthenticationMethod; |
|||
@Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_LABEL_PROPERTY) |
|||
private String loginButtonLabel; |
|||
@Column(name = ModelConstants.OAUTH2_LOGIN_BUTTON_ICON_PROPERTY) |
|||
private String loginButtonIcon; |
|||
@Column(name = ModelConstants.OAUTH2_ALLOW_USER_CREATION_PROPERTY) |
|||
private Boolean allowUserCreation; |
|||
@Column(name = ModelConstants.OAUTH2_ACTIVATE_USER_PROPERTY) |
|||
private Boolean activateUser; |
|||
@Enumerated(EnumType.STRING) |
|||
@Column(name = ModelConstants.OAUTH2_MAPPER_TYPE_PROPERTY) |
|||
private MapperType type; |
|||
@Column(name = ModelConstants.OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY) |
|||
private String emailAttributeKey; |
|||
@Column(name = ModelConstants.OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY) |
|||
private String firstNameAttributeKey; |
|||
@Column(name = ModelConstants.OAUTH2_LAST_NAME_ATTRIBUTE_KEY_PROPERTY) |
|||
private String lastNameAttributeKey; |
|||
@Enumerated(EnumType.STRING) |
|||
@Column(name = ModelConstants.OAUTH2_TENANT_NAME_STRATEGY_PROPERTY) |
|||
private TenantNameStrategyType tenantNameStrategy; |
|||
@Column(name = ModelConstants.OAUTH2_TENANT_NAME_PATTERN_PROPERTY) |
|||
private String tenantNamePattern; |
|||
@Column(name = ModelConstants.OAUTH2_CUSTOMER_NAME_PATTERN_PROPERTY) |
|||
private String customerNamePattern; |
|||
@Column(name = ModelConstants.OAUTH2_DEFAULT_DASHBOARD_NAME_PROPERTY) |
|||
private String defaultDashboardName; |
|||
@Column(name = ModelConstants.OAUTH2_ALWAYS_FULL_SCREEN_PROPERTY) |
|||
private Boolean alwaysFullScreen; |
|||
@Column(name = ModelConstants.OAUTH2_MAPPER_URL_PROPERTY) |
|||
private String url; |
|||
@Column(name = ModelConstants.OAUTH2_MAPPER_USERNAME_PROPERTY) |
|||
private String username; |
|||
@Column(name = ModelConstants.OAUTH2_MAPPER_PASSWORD_PROPERTY) |
|||
private String password; |
|||
@Column(name = ModelConstants.OAUTH2_MAPPER_SEND_TOKEN_PROPERTY) |
|||
private Boolean sendToken; |
|||
|
|||
@Type(type = "json") |
|||
@Column(name = ModelConstants.OAUTH2_ADDITIONAL_INFO_PROPERTY) |
|||
private JsonNode additionalInfo; |
|||
|
|||
public AbstractOAuth2ClientRegistrationInfoEntity() { |
|||
super(); |
|||
} |
|||
|
|||
public AbstractOAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfo clientRegistrationInfo) { |
|||
if (clientRegistrationInfo.getId() != null) { |
|||
this.setUuid(clientRegistrationInfo.getId().getId()); |
|||
} |
|||
this.createdTime = clientRegistrationInfo.getCreatedTime(); |
|||
this.enabled = clientRegistrationInfo.isEnabled(); |
|||
this.clientId = clientRegistrationInfo.getClientId(); |
|||
this.clientSecret = clientRegistrationInfo.getClientSecret(); |
|||
this.authorizationUri = clientRegistrationInfo.getAuthorizationUri(); |
|||
this.tokenUri = clientRegistrationInfo.getAccessTokenUri(); |
|||
this.scope = clientRegistrationInfo.getScope().stream().reduce((result, element) -> result + "," + element).orElse(""); |
|||
this.userInfoUri = clientRegistrationInfo.getUserInfoUri(); |
|||
this.userNameAttributeName = clientRegistrationInfo.getUserNameAttributeName(); |
|||
this.jwkSetUri = clientRegistrationInfo.getJwkSetUri(); |
|||
this.clientAuthenticationMethod = clientRegistrationInfo.getClientAuthenticationMethod(); |
|||
this.loginButtonLabel = clientRegistrationInfo.getLoginButtonLabel(); |
|||
this.loginButtonIcon = clientRegistrationInfo.getLoginButtonIcon(); |
|||
this.additionalInfo = clientRegistrationInfo.getAdditionalInfo(); |
|||
OAuth2MapperConfig mapperConfig = clientRegistrationInfo.getMapperConfig(); |
|||
if (mapperConfig != null) { |
|||
this.allowUserCreation = mapperConfig.isAllowUserCreation(); |
|||
this.activateUser = mapperConfig.isActivateUser(); |
|||
this.type = mapperConfig.getType(); |
|||
OAuth2BasicMapperConfig basicConfig = mapperConfig.getBasic(); |
|||
if (basicConfig != null) { |
|||
this.emailAttributeKey = basicConfig.getEmailAttributeKey(); |
|||
this.firstNameAttributeKey = basicConfig.getFirstNameAttributeKey(); |
|||
this.lastNameAttributeKey = basicConfig.getLastNameAttributeKey(); |
|||
this.tenantNameStrategy = basicConfig.getTenantNameStrategy(); |
|||
this.tenantNamePattern = basicConfig.getTenantNamePattern(); |
|||
this.customerNamePattern = basicConfig.getCustomerNamePattern(); |
|||
this.defaultDashboardName = basicConfig.getDefaultDashboardName(); |
|||
this.alwaysFullScreen = basicConfig.isAlwaysFullScreen(); |
|||
} |
|||
OAuth2CustomMapperConfig customConfig = mapperConfig.getCustom(); |
|||
if (customConfig != null) { |
|||
this.url = customConfig.getUrl(); |
|||
this.username = customConfig.getUsername(); |
|||
this.password = customConfig.getPassword(); |
|||
this.sendToken = customConfig.isSendToken(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public AbstractOAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfoEntity oAuth2ClientRegistrationInfoEntity) { |
|||
this.setId(oAuth2ClientRegistrationInfoEntity.getId()); |
|||
this.setCreatedTime(oAuth2ClientRegistrationInfoEntity.getCreatedTime()); |
|||
this.enabled = oAuth2ClientRegistrationInfoEntity.getEnabled(); |
|||
this.clientId = oAuth2ClientRegistrationInfoEntity.getClientId(); |
|||
this.clientSecret = oAuth2ClientRegistrationInfoEntity.getClientSecret(); |
|||
this.authorizationUri = oAuth2ClientRegistrationInfoEntity.getAuthorizationUri(); |
|||
this.tokenUri = oAuth2ClientRegistrationInfoEntity.getTokenUri(); |
|||
this.scope = oAuth2ClientRegistrationInfoEntity.getScope(); |
|||
this.userInfoUri = oAuth2ClientRegistrationInfoEntity.getUserInfoUri(); |
|||
this.userNameAttributeName = oAuth2ClientRegistrationInfoEntity.getUserNameAttributeName(); |
|||
this.jwkSetUri = oAuth2ClientRegistrationInfoEntity.getJwkSetUri(); |
|||
this.clientAuthenticationMethod = oAuth2ClientRegistrationInfoEntity.getClientAuthenticationMethod(); |
|||
this.loginButtonLabel = oAuth2ClientRegistrationInfoEntity.getLoginButtonLabel(); |
|||
this.loginButtonIcon = oAuth2ClientRegistrationInfoEntity.getLoginButtonIcon(); |
|||
this.additionalInfo = oAuth2ClientRegistrationInfoEntity.getAdditionalInfo(); |
|||
this.allowUserCreation = oAuth2ClientRegistrationInfoEntity.getAllowUserCreation(); |
|||
this.activateUser = oAuth2ClientRegistrationInfoEntity.getActivateUser(); |
|||
this.type = oAuth2ClientRegistrationInfoEntity.getType(); |
|||
this.emailAttributeKey = oAuth2ClientRegistrationInfoEntity.getEmailAttributeKey(); |
|||
this.firstNameAttributeKey = oAuth2ClientRegistrationInfoEntity.getFirstNameAttributeKey(); |
|||
this.lastNameAttributeKey = oAuth2ClientRegistrationInfoEntity.getLastNameAttributeKey(); |
|||
this.tenantNameStrategy = oAuth2ClientRegistrationInfoEntity.getTenantNameStrategy(); |
|||
this.tenantNamePattern = oAuth2ClientRegistrationInfoEntity.getTenantNamePattern(); |
|||
this.customerNamePattern = oAuth2ClientRegistrationInfoEntity.getCustomerNamePattern(); |
|||
this.defaultDashboardName = oAuth2ClientRegistrationInfoEntity.getDefaultDashboardName(); |
|||
this.alwaysFullScreen = oAuth2ClientRegistrationInfoEntity.getAlwaysFullScreen(); |
|||
this.url = oAuth2ClientRegistrationInfoEntity.getUrl(); |
|||
this.username = oAuth2ClientRegistrationInfoEntity.getUsername(); |
|||
this.password = oAuth2ClientRegistrationInfoEntity.getPassword(); |
|||
this.sendToken = oAuth2ClientRegistrationInfoEntity.getSendToken(); |
|||
} |
|||
|
|||
|
|||
protected OAuth2ClientRegistrationInfo toOAuth2ClientRegistrationInfo() { |
|||
OAuth2ClientRegistrationInfo clientRegistrationInfo = new OAuth2ClientRegistrationInfo(); |
|||
clientRegistrationInfo.setId(new OAuth2ClientRegistrationInfoId(id)); |
|||
clientRegistrationInfo.setEnabled(enabled); |
|||
clientRegistrationInfo.setCreatedTime(createdTime); |
|||
clientRegistrationInfo.setAdditionalInfo(additionalInfo); |
|||
clientRegistrationInfo.setMapperConfig( |
|||
OAuth2MapperConfig.builder() |
|||
.allowUserCreation(allowUserCreation) |
|||
.activateUser(activateUser) |
|||
.type(type) |
|||
.basic( |
|||
(type == MapperType.BASIC || type == MapperType.GITHUB) ? |
|||
OAuth2BasicMapperConfig.builder() |
|||
.emailAttributeKey(emailAttributeKey) |
|||
.firstNameAttributeKey(firstNameAttributeKey) |
|||
.lastNameAttributeKey(lastNameAttributeKey) |
|||
.tenantNameStrategy(tenantNameStrategy) |
|||
.tenantNamePattern(tenantNamePattern) |
|||
.customerNamePattern(customerNamePattern) |
|||
.defaultDashboardName(defaultDashboardName) |
|||
.alwaysFullScreen(alwaysFullScreen) |
|||
.build() |
|||
: null |
|||
) |
|||
.custom( |
|||
type == MapperType.CUSTOM ? |
|||
OAuth2CustomMapperConfig.builder() |
|||
.url(url) |
|||
.username(username) |
|||
.password(password) |
|||
.sendToken(sendToken) |
|||
.build() |
|||
: null |
|||
) |
|||
.build() |
|||
); |
|||
clientRegistrationInfo.setClientId(clientId); |
|||
clientRegistrationInfo.setClientSecret(clientSecret); |
|||
clientRegistrationInfo.setAuthorizationUri(authorizationUri); |
|||
clientRegistrationInfo.setAccessTokenUri(tokenUri); |
|||
clientRegistrationInfo.setScope(Arrays.asList(scope.split(","))); |
|||
clientRegistrationInfo.setUserInfoUri(userInfoUri); |
|||
clientRegistrationInfo.setUserNameAttributeName(userNameAttributeName); |
|||
clientRegistrationInfo.setJwkSetUri(jwkSetUri); |
|||
clientRegistrationInfo.setClientAuthenticationMethod(clientAuthenticationMethod); |
|||
clientRegistrationInfo.setLoginButtonLabel(loginButtonLabel); |
|||
clientRegistrationInfo.setLoginButtonIcon(loginButtonIcon); |
|||
return clientRegistrationInfo; |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.model.sql; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; |
|||
import org.thingsboard.server.common.data.oauth2.SchemeType; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
public class ExtendedOAuth2ClientRegistrationInfoEntity extends AbstractOAuth2ClientRegistrationInfoEntity<ExtendedOAuth2ClientRegistrationInfo> { |
|||
|
|||
private String domainName; |
|||
private SchemeType domainScheme; |
|||
|
|||
public ExtendedOAuth2ClientRegistrationInfoEntity() { |
|||
super(); |
|||
} |
|||
|
|||
public ExtendedOAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfoEntity oAuth2ClientRegistrationInfoEntity, |
|||
String domainName, |
|||
SchemeType domainScheme) { |
|||
super(oAuth2ClientRegistrationInfoEntity); |
|||
this.domainName = domainName; |
|||
this.domainScheme = domainScheme; |
|||
} |
|||
|
|||
@Override |
|||
public ExtendedOAuth2ClientRegistrationInfo toData() { |
|||
return new ExtendedOAuth2ClientRegistrationInfo(super.toOAuth2ClientRegistrationInfo(), |
|||
domainScheme, |
|||
domainName); |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.model.sql; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.hibernate.annotations.TypeDef; |
|||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationId; |
|||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; |
|||
import org.thingsboard.server.common.data.oauth2.*; |
|||
import org.thingsboard.server.dao.model.BaseSqlEntity; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.util.mapping.JsonStringType; |
|||
|
|||
import javax.persistence.*; |
|||
import java.util.Arrays; |
|||
import java.util.UUID; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@Entity |
|||
@TypeDef(name = "json", typeClass = JsonStringType.class) |
|||
@Table(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_COLUMN_FAMILY_NAME) |
|||
public class OAuth2ClientRegistrationEntity extends BaseSqlEntity<OAuth2ClientRegistration> { |
|||
|
|||
@Column(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_INFO_ID_PROPERTY, columnDefinition = "uuid") |
|||
private UUID clientRegistrationInfoId; |
|||
|
|||
@Column(name = ModelConstants.OAUTH2_DOMAIN_NAME_PROPERTY) |
|||
private String domainName; |
|||
|
|||
@Enumerated(EnumType.STRING) |
|||
@Column(name = ModelConstants.OAUTH2_DOMAIN_SCHEME_PROPERTY) |
|||
private SchemeType domainScheme; |
|||
|
|||
public OAuth2ClientRegistrationEntity() { |
|||
super(); |
|||
} |
|||
|
|||
public OAuth2ClientRegistrationEntity(OAuth2ClientRegistration clientRegistration) { |
|||
if (clientRegistration.getId() != null) { |
|||
this.setUuid(clientRegistration.getId().getId()); |
|||
} |
|||
if (clientRegistration.getClientRegistrationId() != null){ |
|||
this.clientRegistrationInfoId = clientRegistration.getClientRegistrationId().getId(); |
|||
} |
|||
this.createdTime = clientRegistration.getCreatedTime(); |
|||
this.domainName = clientRegistration.getDomainName(); |
|||
this.domainScheme = clientRegistration.getDomainScheme(); |
|||
} |
|||
|
|||
@Override |
|||
public OAuth2ClientRegistration toData() { |
|||
OAuth2ClientRegistration clientRegistration = new OAuth2ClientRegistration(); |
|||
clientRegistration.setId(new OAuth2ClientRegistrationId(id)); |
|||
clientRegistration.setClientRegistrationId(new OAuth2ClientRegistrationInfoId(clientRegistrationInfoId)); |
|||
clientRegistration.setCreatedTime(createdTime); |
|||
clientRegistration.setDomainName(domainName); |
|||
clientRegistration.setDomainScheme(domainScheme); |
|||
return clientRegistration; |
|||
} |
|||
} |
|||
@ -0,0 +1,51 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.model.sql; |
|||
|
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.hibernate.annotations.TypeDef; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.util.mapping.JsonStringType; |
|||
|
|||
import javax.persistence.Entity; |
|||
import javax.persistence.Table; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@Entity |
|||
@TypeDef(name = "json", typeClass = JsonStringType.class) |
|||
@Table(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_INFO_COLUMN_FAMILY_NAME) |
|||
public class OAuth2ClientRegistrationInfoEntity extends AbstractOAuth2ClientRegistrationInfoEntity<OAuth2ClientRegistrationInfo> { |
|||
|
|||
public OAuth2ClientRegistrationInfoEntity() { |
|||
super(); |
|||
} |
|||
|
|||
public OAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfo clientRegistration) { |
|||
super(clientRegistration); |
|||
} |
|||
|
|||
public OAuth2ClientRegistrationInfoEntity(OAuth2ClientRegistrationInfoEntity oAuth2ClientRegistrationInfoEntity) { |
|||
super(oAuth2ClientRegistrationInfoEntity); |
|||
} |
|||
|
|||
@Override |
|||
public OAuth2ClientRegistrationInfo toData() { |
|||
return super.toOAuth2ClientRegistrationInfo(); |
|||
} |
|||
} |
|||
@ -0,0 +1,160 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.model.sql; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.Data; |
|||
import lombok.EqualsAndHashCode; |
|||
import org.hibernate.annotations.Type; |
|||
import org.hibernate.annotations.TypeDef; |
|||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.oauth2.*; |
|||
import org.thingsboard.server.dao.model.BaseSqlEntity; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.util.mapping.JsonStringType; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; |
|||
|
|||
import javax.persistence.*; |
|||
import java.util.Arrays; |
|||
import java.util.UUID; |
|||
|
|||
@Data |
|||
@EqualsAndHashCode(callSuper = true) |
|||
@Entity |
|||
@TypeDef(name = "json", typeClass = JsonStringType.class) |
|||
@Table(name = ModelConstants.OAUTH2_CLIENT_REGISTRATION_TEMPLATE_COLUMN_FAMILY_NAME) |
|||
public class OAuth2ClientRegistrationTemplateEntity extends BaseSqlEntity<OAuth2ClientRegistrationTemplate> { |
|||
|
|||
@Column(name = ModelConstants.OAUTH2_TEMPLATE_PROVIDER_ID_PROPERTY) |
|||
private String providerId; |
|||
@Column(name = ModelConstants.OAUTH2_AUTHORIZATION_URI_PROPERTY) |
|||
private String authorizationUri; |
|||
@Column(name = ModelConstants.OAUTH2_TOKEN_URI_PROPERTY) |
|||
private String tokenUri; |
|||
@Column(name = ModelConstants.OAUTH2_SCOPE_PROPERTY) |
|||
private String scope; |
|||
@Column(name = ModelConstants.OAUTH2_USER_INFO_URI_PROPERTY) |
|||
private String userInfoUri; |
|||
@Column(name = ModelConstants.OAUTH2_USER_NAME_ATTRIBUTE_NAME_PROPERTY) |
|||
private String userNameAttributeName; |
|||
@Column(name = ModelConstants.OAUTH2_JWK_SET_URI_PROPERTY) |
|||
private String jwkSetUri; |
|||
@Column(name = ModelConstants.OAUTH2_CLIENT_AUTHENTICATION_METHOD_PROPERTY) |
|||
private String clientAuthenticationMethod; |
|||
@Enumerated(EnumType.STRING) |
|||
@Column(name = ModelConstants.OAUTH2_MAPPER_TYPE_PROPERTY) |
|||
private MapperType type; |
|||
@Column(name = ModelConstants.OAUTH2_EMAIL_ATTRIBUTE_KEY_PROPERTY) |
|||
private String emailAttributeKey; |
|||
@Column(name = ModelConstants.OAUTH2_FIRST_NAME_ATTRIBUTE_KEY_PROPERTY) |
|||
private String firstNameAttributeKey; |
|||
@Column(name = ModelConstants.OAUTH2_LAST_NAME_ATTRIBUTE_KEY_PROPERTY) |
|||
private String lastNameAttributeKey; |
|||
@Enumerated(EnumType.STRING) |
|||
@Column(name = ModelConstants.OAUTH2_TENANT_NAME_STRATEGY_PROPERTY) |
|||
private TenantNameStrategyType tenantNameStrategy; |
|||
@Column(name = ModelConstants.OAUTH2_TENANT_NAME_PATTERN_PROPERTY) |
|||
private String tenantNamePattern; |
|||
@Column(name = ModelConstants.OAUTH2_CUSTOMER_NAME_PATTERN_PROPERTY) |
|||
private String customerNamePattern; |
|||
@Column(name = ModelConstants.OAUTH2_DEFAULT_DASHBOARD_NAME_PROPERTY) |
|||
private String defaultDashboardName; |
|||
@Column(name = ModelConstants.OAUTH2_ALWAYS_FULL_SCREEN_PROPERTY) |
|||
private Boolean alwaysFullScreen; |
|||
@Column(name = ModelConstants.OAUTH2_TEMPLATE_COMMENT_PROPERTY) |
|||
private String comment; |
|||
@Column(name = ModelConstants.OAUTH2_TEMPLATE_LOGIN_BUTTON_ICON_PROPERTY) |
|||
private String loginButtonIcon; |
|||
@Column(name = ModelConstants.OAUTH2_TEMPLATE_LOGIN_BUTTON_LABEL_PROPERTY) |
|||
private String loginButtonLabel; |
|||
@Column(name = ModelConstants.OAUTH2_TEMPLATE_HELP_LINK_PROPERTY) |
|||
private String helpLink; |
|||
|
|||
@Type(type = "json") |
|||
@Column(name = ModelConstants.OAUTH2_TEMPLATE_ADDITIONAL_INFO_PROPERTY) |
|||
private JsonNode additionalInfo; |
|||
|
|||
public OAuth2ClientRegistrationTemplateEntity() { |
|||
} |
|||
|
|||
public OAuth2ClientRegistrationTemplateEntity(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { |
|||
if (clientRegistrationTemplate.getId() != null) { |
|||
this.setUuid(clientRegistrationTemplate.getId().getId()); |
|||
} |
|||
this.createdTime = clientRegistrationTemplate.getCreatedTime(); |
|||
this.providerId = clientRegistrationTemplate.getProviderId(); |
|||
this.authorizationUri = clientRegistrationTemplate.getAuthorizationUri(); |
|||
this.tokenUri = clientRegistrationTemplate.getAccessTokenUri(); |
|||
this.scope = clientRegistrationTemplate.getScope().stream().reduce((result, element) -> result + "," + element).orElse(""); |
|||
this.userInfoUri = clientRegistrationTemplate.getUserInfoUri(); |
|||
this.userNameAttributeName = clientRegistrationTemplate.getUserNameAttributeName(); |
|||
this.jwkSetUri = clientRegistrationTemplate.getJwkSetUri(); |
|||
this.clientAuthenticationMethod = clientRegistrationTemplate.getClientAuthenticationMethod(); |
|||
this.comment = clientRegistrationTemplate.getComment(); |
|||
this.loginButtonIcon = clientRegistrationTemplate.getLoginButtonIcon(); |
|||
this.loginButtonLabel = clientRegistrationTemplate.getLoginButtonLabel(); |
|||
this.helpLink = clientRegistrationTemplate.getHelpLink(); |
|||
this.additionalInfo = clientRegistrationTemplate.getAdditionalInfo(); |
|||
this.type = clientRegistrationTemplate.getMapperType(); |
|||
OAuth2BasicMapperConfig basicConfig = clientRegistrationTemplate.getBasic(); |
|||
if (basicConfig != null) { |
|||
this.emailAttributeKey = basicConfig.getEmailAttributeKey(); |
|||
this.firstNameAttributeKey = basicConfig.getFirstNameAttributeKey(); |
|||
this.lastNameAttributeKey = basicConfig.getLastNameAttributeKey(); |
|||
this.tenantNameStrategy = basicConfig.getTenantNameStrategy(); |
|||
this.tenantNamePattern = basicConfig.getTenantNamePattern(); |
|||
this.customerNamePattern = basicConfig.getCustomerNamePattern(); |
|||
this.defaultDashboardName = basicConfig.getDefaultDashboardName(); |
|||
this.alwaysFullScreen = basicConfig.isAlwaysFullScreen(); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public OAuth2ClientRegistrationTemplate toData() { |
|||
OAuth2ClientRegistrationTemplate clientRegistrationTemplate = new OAuth2ClientRegistrationTemplate(); |
|||
clientRegistrationTemplate.setId(new OAuth2ClientRegistrationTemplateId(id)); |
|||
clientRegistrationTemplate.setCreatedTime(createdTime); |
|||
clientRegistrationTemplate.setAdditionalInfo(additionalInfo); |
|||
|
|||
clientRegistrationTemplate.setMapperType(type); |
|||
clientRegistrationTemplate.setProviderId(providerId); |
|||
clientRegistrationTemplate.setBasic( |
|||
OAuth2BasicMapperConfig.builder() |
|||
.emailAttributeKey(emailAttributeKey) |
|||
.firstNameAttributeKey(firstNameAttributeKey) |
|||
.lastNameAttributeKey(lastNameAttributeKey) |
|||
.tenantNameStrategy(tenantNameStrategy) |
|||
.tenantNamePattern(tenantNamePattern) |
|||
.customerNamePattern(customerNamePattern) |
|||
.defaultDashboardName(defaultDashboardName) |
|||
.alwaysFullScreen(alwaysFullScreen) |
|||
.build() |
|||
); |
|||
clientRegistrationTemplate.setAuthorizationUri(authorizationUri); |
|||
clientRegistrationTemplate.setAccessTokenUri(tokenUri); |
|||
clientRegistrationTemplate.setScope(Arrays.asList(scope.split(","))); |
|||
clientRegistrationTemplate.setUserInfoUri(userInfoUri); |
|||
clientRegistrationTemplate.setUserNameAttributeName(userNameAttributeName); |
|||
clientRegistrationTemplate.setJwkSetUri(jwkSetUri); |
|||
clientRegistrationTemplate.setClientAuthenticationMethod(clientAuthenticationMethod); |
|||
clientRegistrationTemplate.setComment(comment); |
|||
clientRegistrationTemplate.setLoginButtonIcon(loginButtonIcon); |
|||
clientRegistrationTemplate.setLoginButtonLabel(loginButtonLabel); |
|||
clientRegistrationTemplate.setHelpLink(helpLink); |
|||
return clientRegistrationTemplate; |
|||
} |
|||
} |
|||
@ -0,0 +1,59 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.oauth2; |
|||
|
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.security.oauth2.client.registration.ClientRegistration; |
|||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; |
|||
import org.springframework.security.oauth2.core.AuthorizationGrantType; |
|||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
@Component |
|||
public class HybridClientRegistrationRepository implements ClientRegistrationRepository { |
|||
private static final String defaultRedirectUriTemplate = "{baseUrl}/login/oauth2/code/{registrationId}"; |
|||
|
|||
@Autowired |
|||
private OAuth2Service oAuth2Service; |
|||
|
|||
@Override |
|||
public ClientRegistration findByRegistrationId(String registrationId) { |
|||
OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo = oAuth2Service.findClientRegistrationInfo(UUID.fromString(registrationId)); |
|||
return oAuth2ClientRegistrationInfo == null ? |
|||
null : toSpringClientRegistration(oAuth2ClientRegistrationInfo); |
|||
} |
|||
|
|||
private ClientRegistration toSpringClientRegistration(OAuth2ClientRegistrationInfo localClientRegistration){ |
|||
String registrationId = localClientRegistration.getUuidId().toString(); |
|||
return ClientRegistration.withRegistrationId(registrationId) |
|||
.clientName(localClientRegistration.getName()) |
|||
.clientId(localClientRegistration.getClientId()) |
|||
.authorizationUri(localClientRegistration.getAuthorizationUri()) |
|||
.clientSecret(localClientRegistration.getClientSecret()) |
|||
.tokenUri(localClientRegistration.getAccessTokenUri()) |
|||
.scope(localClientRegistration.getScope()) |
|||
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) |
|||
.userInfoUri(localClientRegistration.getUserInfoUri()) |
|||
.userNameAttributeName(localClientRegistration.getUserNameAttributeName()) |
|||
.jwkSetUri(localClientRegistration.getJwkSetUri()) |
|||
.clientAuthenticationMethod(new ClientAuthenticationMethod(localClientRegistration.getClientAuthenticationMethod())) |
|||
.redirectUriTemplate(defaultRedirectUriTemplate) |
|||
.build(); |
|||
} |
|||
} |
|||
@ -1,47 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.oauth2; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class OAuth2ClientMapperConfig { |
|||
|
|||
private boolean allowUserCreation; |
|||
private boolean activateUser; |
|||
private String type; |
|||
private BasicOAuth2ClientMapperConfig basic; |
|||
private CustomOAuth2ClientMapperConfig custom; |
|||
|
|||
@Data |
|||
public static class BasicOAuth2ClientMapperConfig { |
|||
private String emailAttributeKey; |
|||
private String firstNameAttributeKey; |
|||
private String lastNameAttributeKey; |
|||
private String tenantNameStrategy; |
|||
private String tenantNamePattern; |
|||
private String customerNamePattern; |
|||
private boolean alwaysFullScreen; |
|||
private String defaultDashboardName; |
|||
} |
|||
|
|||
@Data |
|||
public static class CustomOAuth2ClientMapperConfig { |
|||
private String url; |
|||
private String username; |
|||
private String password; |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.oauth2; |
|||
|
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; |
|||
import org.thingsboard.server.dao.Dao; |
|||
|
|||
public interface OAuth2ClientRegistrationDao extends Dao<OAuth2ClientRegistration> { |
|||
void deleteAll(); |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.oauth2; |
|||
|
|||
import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; |
|||
import org.thingsboard.server.common.data.oauth2.SchemeType; |
|||
import org.thingsboard.server.dao.Dao; |
|||
|
|||
import java.util.List; |
|||
import java.util.Set; |
|||
|
|||
public interface OAuth2ClientRegistrationInfoDao extends Dao<OAuth2ClientRegistrationInfo> { |
|||
List<OAuth2ClientRegistrationInfo> findAll(); |
|||
|
|||
List<ExtendedOAuth2ClientRegistrationInfo> findAllExtended(); |
|||
|
|||
List<OAuth2ClientRegistrationInfo> findByDomainSchemesAndDomainName(List<SchemeType> domainSchemes, String domainName); |
|||
|
|||
void deleteAll(); |
|||
} |
|||
@ -0,0 +1,25 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.oauth2; |
|||
|
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; |
|||
import org.thingsboard.server.dao.Dao; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface OAuth2ClientRegistrationTemplateDao extends Dao<OAuth2ClientRegistrationTemplate> { |
|||
List<OAuth2ClientRegistrationTemplate> findAll(); |
|||
} |
|||
@ -0,0 +1,103 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.oauth2; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.hibernate.exception.ConstraintViolationException; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationTemplateId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.oauth2.*; |
|||
import org.thingsboard.server.dao.entity.AbstractEntityService; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.service.DataValidator; |
|||
|
|||
import java.util.List; |
|||
|
|||
import static org.thingsboard.server.dao.service.Validator.validateId; |
|||
import static org.thingsboard.server.dao.service.Validator.validateString; |
|||
|
|||
@Slf4j |
|||
@Service |
|||
public class OAuth2ConfigTemplateServiceImpl extends AbstractEntityService implements OAuth2ConfigTemplateService { |
|||
public static final String INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID = "Incorrect clientRegistrationTemplateId "; |
|||
|
|||
@Autowired |
|||
private OAuth2ClientRegistrationTemplateDao clientRegistrationTemplateDao; |
|||
|
|||
@Override |
|||
public OAuth2ClientRegistrationTemplate saveClientRegistrationTemplate(OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { |
|||
log.trace("Executing saveClientRegistrationTemplate [{}]", clientRegistrationTemplate); |
|||
clientRegistrationTemplateValidator.validate(clientRegistrationTemplate, o -> TenantId.SYS_TENANT_ID); |
|||
OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate; |
|||
try { |
|||
savedClientRegistrationTemplate = clientRegistrationTemplateDao.save(TenantId.SYS_TENANT_ID, clientRegistrationTemplate); |
|||
} catch (Exception t) { |
|||
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); |
|||
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("oauth2_template_provider_id_unq_key")) { |
|||
throw new DataValidationException("Client registration template with such providerId already exists!"); |
|||
} else { |
|||
throw t; |
|||
} |
|||
} |
|||
return savedClientRegistrationTemplate; |
|||
} |
|||
|
|||
@Override |
|||
public OAuth2ClientRegistrationTemplate findClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId) { |
|||
log.trace("Executing findClientRegistrationTemplateById [{}]", templateId); |
|||
validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId); |
|||
return clientRegistrationTemplateDao.findById(TenantId.SYS_TENANT_ID, templateId.getId()); |
|||
} |
|||
|
|||
@Override |
|||
public List<OAuth2ClientRegistrationTemplate> findAllClientRegistrationTemplates() { |
|||
log.trace("Executing findAllClientRegistrationTemplates"); |
|||
return clientRegistrationTemplateDao.findAll(); |
|||
} |
|||
|
|||
@Override |
|||
public void deleteClientRegistrationTemplateById(OAuth2ClientRegistrationTemplateId templateId) { |
|||
log.trace("Executing deleteClientRegistrationTemplateById [{}]", templateId); |
|||
validateId(templateId, INCORRECT_CLIENT_REGISTRATION_TEMPLATE_ID + templateId); |
|||
clientRegistrationTemplateDao.removeById(TenantId.SYS_TENANT_ID, templateId.getId()); |
|||
} |
|||
|
|||
private final DataValidator<OAuth2ClientRegistrationTemplate> clientRegistrationTemplateValidator = |
|||
new DataValidator<OAuth2ClientRegistrationTemplate>() { |
|||
|
|||
@Override |
|||
protected void validateCreate(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { |
|||
} |
|||
|
|||
@Override |
|||
protected void validateUpdate(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { |
|||
} |
|||
|
|||
@Override |
|||
protected void validateDataImpl(TenantId tenantId, OAuth2ClientRegistrationTemplate clientRegistrationTemplate) { |
|||
if (StringUtils.isEmpty(clientRegistrationTemplate.getProviderId())) { |
|||
throw new DataValidationException("Provider ID should be specified!"); |
|||
} |
|||
if (clientRegistrationTemplate.getBasic() == null) { |
|||
throw new DataValidationException("Basic mapper config should be specified!"); |
|||
} |
|||
} |
|||
}; |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.oauth2; |
|||
|
|||
import org.thingsboard.server.common.data.id.OAuth2ClientRegistrationInfoId; |
|||
import org.thingsboard.server.common.data.oauth2.*; |
|||
|
|||
import java.util.*; |
|||
|
|||
public class OAuth2Utils { |
|||
public static final String OAUTH2_AUTHORIZATION_PATH_TEMPLATE = "/oauth2/authorization/%s"; |
|||
|
|||
public static OAuth2ClientInfo toClientInfo(OAuth2ClientRegistrationInfo clientRegistrationInfo) { |
|||
OAuth2ClientInfo client = new OAuth2ClientInfo(); |
|||
client.setName(clientRegistrationInfo.getLoginButtonLabel()); |
|||
client.setUrl(String.format(OAUTH2_AUTHORIZATION_PATH_TEMPLATE, clientRegistrationInfo.getUuidId().toString())); |
|||
client.setIcon(clientRegistrationInfo.getLoginButtonIcon()); |
|||
return client; |
|||
} |
|||
|
|||
public static OAuth2ClientsParams toOAuth2Params(List<ExtendedOAuth2ClientRegistrationInfo> extendedOAuth2ClientRegistrationInfos) { |
|||
Map<OAuth2ClientRegistrationInfoId, Set<DomainInfo>> domainsByInfoId = new HashMap<>(); |
|||
Map<OAuth2ClientRegistrationInfoId, OAuth2ClientRegistrationInfo> infoById = new HashMap<>(); |
|||
for (ExtendedOAuth2ClientRegistrationInfo extendedClientRegistrationInfo : extendedOAuth2ClientRegistrationInfos) { |
|||
String domainName = extendedClientRegistrationInfo.getDomainName(); |
|||
SchemeType domainScheme = extendedClientRegistrationInfo.getDomainScheme(); |
|||
domainsByInfoId.computeIfAbsent(extendedClientRegistrationInfo.getId(), key -> new HashSet<>()) |
|||
.add(new DomainInfo(domainScheme, domainName)); |
|||
infoById.put(extendedClientRegistrationInfo.getId(), extendedClientRegistrationInfo); |
|||
} |
|||
Map<Set<DomainInfo>, OAuth2ClientsDomainParams> domainParamsMap = new HashMap<>(); |
|||
domainsByInfoId.forEach((clientRegistrationInfoId, domainInfos) -> { |
|||
domainParamsMap.computeIfAbsent(domainInfos, |
|||
key -> new OAuth2ClientsDomainParams(key, new HashSet<>()) |
|||
) |
|||
.getClientRegistrations() |
|||
.add(toClientRegistrationDto(infoById.get(clientRegistrationInfoId))); |
|||
}); |
|||
boolean enabled = extendedOAuth2ClientRegistrationInfos.stream() |
|||
.map(OAuth2ClientRegistrationInfo::isEnabled) |
|||
.findFirst().orElse(false); |
|||
return new OAuth2ClientsParams(enabled, new HashSet<>(domainParamsMap.values())); |
|||
} |
|||
|
|||
public static ClientRegistrationDto toClientRegistrationDto(OAuth2ClientRegistrationInfo oAuth2ClientRegistrationInfo) { |
|||
return ClientRegistrationDto.builder() |
|||
.mapperConfig(oAuth2ClientRegistrationInfo.getMapperConfig()) |
|||
.clientId(oAuth2ClientRegistrationInfo.getClientId()) |
|||
.clientSecret(oAuth2ClientRegistrationInfo.getClientSecret()) |
|||
.authorizationUri(oAuth2ClientRegistrationInfo.getAuthorizationUri()) |
|||
.accessTokenUri(oAuth2ClientRegistrationInfo.getAccessTokenUri()) |
|||
.scope(oAuth2ClientRegistrationInfo.getScope()) |
|||
.userInfoUri(oAuth2ClientRegistrationInfo.getUserInfoUri()) |
|||
.userNameAttributeName(oAuth2ClientRegistrationInfo.getUserNameAttributeName()) |
|||
.jwkSetUri(oAuth2ClientRegistrationInfo.getJwkSetUri()) |
|||
.clientAuthenticationMethod(oAuth2ClientRegistrationInfo.getClientAuthenticationMethod()) |
|||
.loginButtonLabel(oAuth2ClientRegistrationInfo.getLoginButtonLabel()) |
|||
.loginButtonIcon(oAuth2ClientRegistrationInfo.getLoginButtonIcon()) |
|||
.additionalInfo(oAuth2ClientRegistrationInfo.getAdditionalInfo()) |
|||
.build(); |
|||
} |
|||
|
|||
public static OAuth2ClientRegistrationInfo toClientRegistrationInfo(boolean enabled, ClientRegistrationDto clientRegistrationDto) { |
|||
OAuth2ClientRegistrationInfo clientRegistrationInfo = new OAuth2ClientRegistrationInfo(); |
|||
clientRegistrationInfo.setEnabled(enabled); |
|||
clientRegistrationInfo.setMapperConfig(clientRegistrationDto.getMapperConfig()); |
|||
clientRegistrationInfo.setClientId(clientRegistrationDto.getClientId()); |
|||
clientRegistrationInfo.setClientSecret(clientRegistrationDto.getClientSecret()); |
|||
clientRegistrationInfo.setAuthorizationUri(clientRegistrationDto.getAuthorizationUri()); |
|||
clientRegistrationInfo.setAccessTokenUri(clientRegistrationDto.getAccessTokenUri()); |
|||
clientRegistrationInfo.setScope(clientRegistrationDto.getScope()); |
|||
clientRegistrationInfo.setUserInfoUri(clientRegistrationDto.getUserInfoUri()); |
|||
clientRegistrationInfo.setUserNameAttributeName(clientRegistrationDto.getUserNameAttributeName()); |
|||
clientRegistrationInfo.setJwkSetUri(clientRegistrationDto.getJwkSetUri()); |
|||
clientRegistrationInfo.setClientAuthenticationMethod(clientRegistrationDto.getClientAuthenticationMethod()); |
|||
clientRegistrationInfo.setLoginButtonLabel(clientRegistrationDto.getLoginButtonLabel()); |
|||
clientRegistrationInfo.setLoginButtonIcon(clientRegistrationDto.getLoginButtonIcon()); |
|||
clientRegistrationInfo.setAdditionalInfo(clientRegistrationDto.getAdditionalInfo()); |
|||
return clientRegistrationInfo; |
|||
} |
|||
|
|||
public static OAuth2ClientRegistration toClientRegistration(OAuth2ClientRegistrationInfoId clientRegistrationInfoId, SchemeType domainScheme, String domainName) { |
|||
OAuth2ClientRegistration clientRegistration = new OAuth2ClientRegistration(); |
|||
clientRegistration.setClientRegistrationId(clientRegistrationInfoId); |
|||
clientRegistration.setDomainName(domainName); |
|||
clientRegistration.setDomainScheme(domainScheme); |
|||
return clientRegistration; |
|||
} |
|||
} |
|||
@ -0,0 +1,48 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.sql.oauth2; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.data.repository.CrudRepository; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistration; |
|||
import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationDao; |
|||
import org.thingsboard.server.dao.sql.JpaAbstractDao; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
@Component |
|||
@RequiredArgsConstructor |
|||
public class JpaOAuth2ClientRegistrationDao extends JpaAbstractDao<OAuth2ClientRegistrationEntity, OAuth2ClientRegistration> implements OAuth2ClientRegistrationDao { |
|||
private final OAuth2ClientRegistrationRepository repository; |
|||
|
|||
@Override |
|||
protected Class<OAuth2ClientRegistrationEntity> getEntityClass() { |
|||
return OAuth2ClientRegistrationEntity.class; |
|||
} |
|||
|
|||
@Override |
|||
protected CrudRepository<OAuth2ClientRegistrationEntity, UUID> getCrudRepository() { |
|||
return repository; |
|||
} |
|||
|
|||
@Override |
|||
public void deleteAll() { |
|||
repository.deleteAll(); |
|||
} |
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.sql.oauth2; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import org.springframework.data.repository.CrudRepository; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.oauth2.ExtendedOAuth2ClientRegistrationInfo; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; |
|||
import org.thingsboard.server.common.data.oauth2.SchemeType; |
|||
import org.thingsboard.server.dao.DaoUtil; |
|||
import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationInfoEntity; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationInfoDao; |
|||
import org.thingsboard.server.dao.sql.JpaAbstractDao; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
import java.util.stream.Collectors; |
|||
|
|||
@Component |
|||
@RequiredArgsConstructor |
|||
public class JpaOAuth2ClientRegistrationInfoDao extends JpaAbstractDao<OAuth2ClientRegistrationInfoEntity, OAuth2ClientRegistrationInfo> implements OAuth2ClientRegistrationInfoDao { |
|||
private final OAuth2ClientRegistrationInfoRepository repository; |
|||
|
|||
@Override |
|||
protected Class<OAuth2ClientRegistrationInfoEntity> getEntityClass() { |
|||
return OAuth2ClientRegistrationInfoEntity.class; |
|||
} |
|||
|
|||
@Override |
|||
protected CrudRepository<OAuth2ClientRegistrationInfoEntity, UUID> getCrudRepository() { |
|||
return repository; |
|||
} |
|||
|
|||
@Override |
|||
public List<OAuth2ClientRegistrationInfo> findAll() { |
|||
Iterable<OAuth2ClientRegistrationInfoEntity> entities = repository.findAll(); |
|||
List<OAuth2ClientRegistrationInfo> result = new ArrayList<>(); |
|||
entities.forEach(entity -> { |
|||
result.add(DaoUtil.getData(entity)); |
|||
}); |
|||
return result; |
|||
} |
|||
|
|||
@Override |
|||
public List<ExtendedOAuth2ClientRegistrationInfo> findAllExtended() { |
|||
return repository.findAllExtended().stream() |
|||
.map(DaoUtil::getData) |
|||
.collect(Collectors.toList()); |
|||
} |
|||
|
|||
@Override |
|||
public List<OAuth2ClientRegistrationInfo> findByDomainSchemesAndDomainName(List<SchemeType> domainSchemes, String domainName) { |
|||
List<OAuth2ClientRegistrationInfoEntity> entities = repository.findAllByDomainSchemesAndName(domainSchemes, domainName); |
|||
return entities.stream().map(DaoUtil::getData).collect(Collectors.toList()); |
|||
} |
|||
|
|||
@Override |
|||
public void deleteAll() { |
|||
repository.deleteAll(); |
|||
} |
|||
} |
|||
@ -0,0 +1,53 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.sql.oauth2; |
|||
|
|||
import lombok.RequiredArgsConstructor; |
|||
import org.springframework.data.repository.CrudRepository; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; |
|||
import org.thingsboard.server.dao.DaoUtil; |
|||
import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationTemplateEntity; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2ClientRegistrationTemplateDao; |
|||
import org.thingsboard.server.dao.sql.JpaAbstractDao; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
|
|||
@Component |
|||
@RequiredArgsConstructor |
|||
public class JpaOAuth2ClientRegistrationTemplateDao extends JpaAbstractDao<OAuth2ClientRegistrationTemplateEntity, OAuth2ClientRegistrationTemplate> implements OAuth2ClientRegistrationTemplateDao { |
|||
private final OAuth2ClientRegistrationTemplateRepository repository; |
|||
|
|||
@Override |
|||
protected Class<OAuth2ClientRegistrationTemplateEntity> getEntityClass() { |
|||
return OAuth2ClientRegistrationTemplateEntity.class; |
|||
} |
|||
|
|||
@Override |
|||
protected CrudRepository<OAuth2ClientRegistrationTemplateEntity, UUID> getCrudRepository() { |
|||
return repository; |
|||
} |
|||
|
|||
@Override |
|||
public List<OAuth2ClientRegistrationTemplate> findAll() { |
|||
Iterable<OAuth2ClientRegistrationTemplateEntity> entities = repository.findAll(); |
|||
List<OAuth2ClientRegistrationTemplate> result = new ArrayList<>(); |
|||
entities.forEach(entity -> result.add(DaoUtil.getData(entity))); |
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,41 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.sql.oauth2; |
|||
|
|||
import org.springframework.data.jpa.repository.Query; |
|||
import org.springframework.data.repository.CrudRepository; |
|||
import org.springframework.data.repository.query.Param; |
|||
import org.thingsboard.server.common.data.oauth2.SchemeType; |
|||
import org.thingsboard.server.dao.model.sql.ExtendedOAuth2ClientRegistrationInfoEntity; |
|||
import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationInfoEntity; |
|||
|
|||
import java.util.List; |
|||
import java.util.UUID; |
|||
|
|||
public interface OAuth2ClientRegistrationInfoRepository extends CrudRepository<OAuth2ClientRegistrationInfoEntity, UUID> { |
|||
@Query("SELECT new OAuth2ClientRegistrationInfoEntity(cr_info) " + |
|||
"FROM OAuth2ClientRegistrationInfoEntity cr_info " + |
|||
"LEFT JOIN OAuth2ClientRegistrationEntity cr on cr_info.id = cr.clientRegistrationInfoId " + |
|||
"WHERE cr.domainName = :domainName " + |
|||
"AND cr.domainScheme IN (:domainSchemes)") |
|||
List<OAuth2ClientRegistrationInfoEntity> findAllByDomainSchemesAndName(@Param("domainSchemes") List<SchemeType> domainSchemes, |
|||
@Param("domainName") String domainName); |
|||
|
|||
@Query("SELECT new org.thingsboard.server.dao.model.sql.ExtendedOAuth2ClientRegistrationInfoEntity(cr_info, cr.domainName, cr.domainScheme) " + |
|||
"FROM OAuth2ClientRegistrationInfoEntity cr_info " + |
|||
"LEFT JOIN OAuth2ClientRegistrationEntity cr on cr_info.id = cr.clientRegistrationInfoId ") |
|||
List<ExtendedOAuth2ClientRegistrationInfoEntity> findAllExtended(); |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.sql.oauth2; |
|||
|
|||
import org.springframework.data.repository.CrudRepository; |
|||
import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationEntity; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
public interface OAuth2ClientRegistrationRepository extends CrudRepository<OAuth2ClientRegistrationEntity, UUID> { |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.sql.oauth2; |
|||
|
|||
import org.springframework.data.repository.CrudRepository; |
|||
import org.thingsboard.server.dao.model.sql.OAuth2ClientRegistrationTemplateEntity; |
|||
|
|||
import java.util.UUID; |
|||
|
|||
public interface OAuth2ClientRegistrationTemplateRepository extends CrudRepository<OAuth2ClientRegistrationTemplateEntity, UUID> { |
|||
} |
|||
@ -0,0 +1,133 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service; |
|||
|
|||
import org.junit.After; |
|||
import org.junit.Assert; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.oauth2.MapperType; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2BasicMapperConfig; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationTemplate; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2ConfigTemplateService; |
|||
|
|||
import java.util.Arrays; |
|||
import java.util.UUID; |
|||
|
|||
public class BaseOAuth2ConfigTemplateServiceTest extends AbstractServiceTest { |
|||
|
|||
@Autowired |
|||
protected OAuth2ConfigTemplateService oAuth2ConfigTemplateService; |
|||
|
|||
@Before |
|||
public void beforeRun() throws Exception { |
|||
Assert.assertTrue(oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().isEmpty()); |
|||
} |
|||
|
|||
@After |
|||
public void after() throws Exception { |
|||
oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().forEach(clientRegistrationTemplate -> { |
|||
oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(clientRegistrationTemplate.getId()); |
|||
}); |
|||
|
|||
Assert.assertTrue(oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().isEmpty()); |
|||
} |
|||
|
|||
|
|||
@Test(expected = DataValidationException.class) |
|||
public void testSaveDuplicateProviderId() { |
|||
OAuth2ClientRegistrationTemplate first = validClientRegistrationTemplate("providerId"); |
|||
OAuth2ClientRegistrationTemplate second = validClientRegistrationTemplate("providerId"); |
|||
oAuth2ConfigTemplateService.saveClientRegistrationTemplate(first); |
|||
oAuth2ConfigTemplateService.saveClientRegistrationTemplate(second); |
|||
} |
|||
|
|||
@Test |
|||
public void testCreateNewTemplate() { |
|||
OAuth2ClientRegistrationTemplate clientRegistrationTemplate = validClientRegistrationTemplate(UUID.randomUUID().toString()); |
|||
OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); |
|||
|
|||
Assert.assertNotNull(savedClientRegistrationTemplate); |
|||
Assert.assertNotNull(savedClientRegistrationTemplate.getId()); |
|||
clientRegistrationTemplate.setId(savedClientRegistrationTemplate.getId()); |
|||
clientRegistrationTemplate.setCreatedTime(savedClientRegistrationTemplate.getCreatedTime()); |
|||
Assert.assertEquals(clientRegistrationTemplate, savedClientRegistrationTemplate); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindTemplate() { |
|||
OAuth2ClientRegistrationTemplate clientRegistrationTemplate = validClientRegistrationTemplate(UUID.randomUUID().toString()); |
|||
OAuth2ClientRegistrationTemplate savedClientRegistrationTemplate = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(clientRegistrationTemplate); |
|||
|
|||
OAuth2ClientRegistrationTemplate foundClientRegistrationTemplate = oAuth2ConfigTemplateService.findClientRegistrationTemplateById(savedClientRegistrationTemplate.getId()); |
|||
Assert.assertEquals(savedClientRegistrationTemplate, foundClientRegistrationTemplate); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindAll() { |
|||
oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); |
|||
oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); |
|||
|
|||
Assert.assertEquals(2, oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().size()); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeleteTemplate() { |
|||
oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); |
|||
oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); |
|||
OAuth2ClientRegistrationTemplate saved = oAuth2ConfigTemplateService.saveClientRegistrationTemplate(validClientRegistrationTemplate(UUID.randomUUID().toString())); |
|||
|
|||
Assert.assertEquals(3, oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().size()); |
|||
Assert.assertNotNull(oAuth2ConfigTemplateService.findClientRegistrationTemplateById(saved.getId())); |
|||
|
|||
oAuth2ConfigTemplateService.deleteClientRegistrationTemplateById(saved.getId()); |
|||
|
|||
Assert.assertEquals(2, oAuth2ConfigTemplateService.findAllClientRegistrationTemplates().size()); |
|||
Assert.assertNull(oAuth2ConfigTemplateService.findClientRegistrationTemplateById(saved.getId())); |
|||
} |
|||
|
|||
private OAuth2ClientRegistrationTemplate validClientRegistrationTemplate(String providerId) { |
|||
OAuth2ClientRegistrationTemplate clientRegistrationTemplate = new OAuth2ClientRegistrationTemplate(); |
|||
clientRegistrationTemplate.setProviderId(providerId); |
|||
clientRegistrationTemplate.setAdditionalInfo(mapper.createObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString())); |
|||
clientRegistrationTemplate.setMapperType(MapperType.BASIC); |
|||
clientRegistrationTemplate.setBasic( |
|||
OAuth2BasicMapperConfig.builder() |
|||
.firstNameAttributeKey("firstName") |
|||
.lastNameAttributeKey("lastName") |
|||
.emailAttributeKey("email") |
|||
.tenantNamePattern("tenant") |
|||
.defaultDashboardName("Test") |
|||
.alwaysFullScreen(true) |
|||
.build() |
|||
); |
|||
clientRegistrationTemplate.setAuthorizationUri("authorizationUri"); |
|||
clientRegistrationTemplate.setAccessTokenUri("tokenUri"); |
|||
clientRegistrationTemplate.setScope(Arrays.asList("scope1", "scope2")); |
|||
clientRegistrationTemplate.setUserInfoUri("userInfoUri"); |
|||
clientRegistrationTemplate.setUserNameAttributeName("userNameAttributeName"); |
|||
clientRegistrationTemplate.setJwkSetUri("jwkSetUri"); |
|||
clientRegistrationTemplate.setClientAuthenticationMethod("clientAuthenticationMethod"); |
|||
clientRegistrationTemplate.setComment("comment"); |
|||
clientRegistrationTemplate.setLoginButtonIcon("icon"); |
|||
clientRegistrationTemplate.setLoginButtonLabel("label"); |
|||
clientRegistrationTemplate.setHelpLink("helpLink"); |
|||
return clientRegistrationTemplate; |
|||
} |
|||
} |
|||
@ -0,0 +1,524 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service; |
|||
|
|||
import com.google.common.collect.Sets; |
|||
import org.junit.After; |
|||
import org.junit.Assert; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.thingsboard.server.common.data.oauth2.*; |
|||
import org.thingsboard.server.dao.exception.DataValidationException; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2Service; |
|||
|
|||
import java.util.*; |
|||
import java.util.stream.Collectors; |
|||
|
|||
public class BaseOAuth2ServiceTest extends AbstractServiceTest { |
|||
private static final OAuth2ClientsParams EMPTY_PARAMS = new OAuth2ClientsParams(false, new HashSet<>()); |
|||
|
|||
@Autowired |
|||
protected OAuth2Service oAuth2Service; |
|||
|
|||
@Before |
|||
public void beforeRun() { |
|||
Assert.assertTrue(oAuth2Service.findAllClientRegistrationInfos().isEmpty()); |
|||
} |
|||
|
|||
@After |
|||
public void after() { |
|||
oAuth2Service.saveOAuth2Params(EMPTY_PARAMS); |
|||
Assert.assertTrue(oAuth2Service.findAllClientRegistrationInfos().isEmpty()); |
|||
Assert.assertTrue(oAuth2Service.findOAuth2Params().getDomainsParams().isEmpty()); |
|||
} |
|||
|
|||
@Test(expected = DataValidationException.class) |
|||
public void testSaveHttpAndMixedDomainsTogether() { |
|||
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
|||
DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), |
|||
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build() |
|||
)); |
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
} |
|||
|
|||
@Test(expected = DataValidationException.class) |
|||
public void testSaveHttpsAndMixedDomainsTogether() { |
|||
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build(), |
|||
DomainInfo.builder().name("first-domain").scheme(SchemeType.MIXED).build(), |
|||
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build() |
|||
)); |
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
} |
|||
|
|||
@Test |
|||
public void testCreateAndFindParams() { |
|||
OAuth2ClientsParams clientsParams = createDefaultClientsParams(); |
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); |
|||
Assert.assertNotNull(foundClientsParams); |
|||
// TODO ask if it's safe to check equality on AdditionalProperties
|
|||
Assert.assertEquals(clientsParams, foundClientsParams); |
|||
} |
|||
|
|||
@Test |
|||
public void testDisableParams() { |
|||
OAuth2ClientsParams clientsParams = createDefaultClientsParams(); |
|||
clientsParams.setEnabled(true); |
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); |
|||
Assert.assertNotNull(foundClientsParams); |
|||
Assert.assertEquals(clientsParams, foundClientsParams); |
|||
|
|||
clientsParams.setEnabled(false); |
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
OAuth2ClientsParams foundDisabledClientsParams = oAuth2Service.findOAuth2Params(); |
|||
Assert.assertEquals(clientsParams, foundDisabledClientsParams); |
|||
} |
|||
|
|||
@Test |
|||
public void testClearDomainParams() { |
|||
OAuth2ClientsParams clientsParams = createDefaultClientsParams(); |
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); |
|||
Assert.assertNotNull(foundClientsParams); |
|||
Assert.assertEquals(clientsParams, foundClientsParams); |
|||
|
|||
oAuth2Service.saveOAuth2Params(EMPTY_PARAMS); |
|||
OAuth2ClientsParams foundAfterClearClientsParams = oAuth2Service.findOAuth2Params(); |
|||
Assert.assertNotNull(foundAfterClearClientsParams); |
|||
Assert.assertEquals(EMPTY_PARAMS, foundAfterClearClientsParams); |
|||
} |
|||
|
|||
@Test |
|||
public void testUpdateClientsParams() { |
|||
OAuth2ClientsParams clientsParams = createDefaultClientsParams(); |
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); |
|||
Assert.assertNotNull(foundClientsParams); |
|||
Assert.assertEquals(clientsParams, foundClientsParams); |
|||
|
|||
OAuth2ClientsParams newClientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("another-domain").scheme(SchemeType.HTTPS).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build(), |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("test-domain").scheme(SchemeType.MIXED).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build() |
|||
)); |
|||
oAuth2Service.saveOAuth2Params(newClientsParams); |
|||
OAuth2ClientsParams foundAfterUpdateClientsParams = oAuth2Service.findOAuth2Params(); |
|||
Assert.assertNotNull(foundAfterUpdateClientsParams); |
|||
Assert.assertEquals(newClientsParams, foundAfterUpdateClientsParams); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetOAuth2Clients() { |
|||
Set<ClientRegistrationDto> firstGroup = Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
); |
|||
Set<ClientRegistrationDto> secondGroup = Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
); |
|||
Set<ClientRegistrationDto> thirdGroup = Sets.newHashSet( |
|||
validClientRegistrationDto() |
|||
); |
|||
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
|||
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
|||
)) |
|||
.clientRegistrations(firstGroup) |
|||
.build(), |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), |
|||
DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() |
|||
)) |
|||
.clientRegistrations(secondGroup) |
|||
.build(), |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), |
|||
DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() |
|||
)) |
|||
.clientRegistrations(thirdGroup) |
|||
.build() |
|||
)); |
|||
|
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); |
|||
Assert.assertNotNull(foundClientsParams); |
|||
Assert.assertEquals(clientsParams, foundClientsParams); |
|||
|
|||
List<OAuth2ClientInfo> firstGroupClientInfos = firstGroup.stream() |
|||
.map(clientRegistrationDto -> new OAuth2ClientInfo( |
|||
clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) |
|||
.collect(Collectors.toList()); |
|||
List<OAuth2ClientInfo> secondGroupClientInfos = secondGroup.stream() |
|||
.map(clientRegistrationDto -> new OAuth2ClientInfo( |
|||
clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) |
|||
.collect(Collectors.toList()); |
|||
List<OAuth2ClientInfo> thirdGroupClientInfos = thirdGroup.stream() |
|||
.map(clientRegistrationDto -> new OAuth2ClientInfo( |
|||
clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) |
|||
.collect(Collectors.toList()); |
|||
|
|||
List<OAuth2ClientInfo> nonExistentDomainClients = oAuth2Service.getOAuth2Clients("http", "non-existent-domain"); |
|||
Assert.assertTrue(nonExistentDomainClients.isEmpty()); |
|||
|
|||
List<OAuth2ClientInfo> firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain"); |
|||
Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); |
|||
firstGroupClientInfos.forEach(firstGroupClientInfo -> { |
|||
Assert.assertTrue( |
|||
firstDomainHttpClients.stream().anyMatch(clientInfo -> |
|||
clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) |
|||
&& clientInfo.getName().equals(firstGroupClientInfo.getName())) |
|||
); |
|||
}); |
|||
|
|||
List<OAuth2ClientInfo> firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain"); |
|||
Assert.assertTrue(firstDomainHttpsClients.isEmpty()); |
|||
|
|||
List<OAuth2ClientInfo> fourthDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "fourth-domain"); |
|||
Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpClients.size()); |
|||
secondGroupClientInfos.forEach(secondGroupClientInfo -> { |
|||
Assert.assertTrue( |
|||
fourthDomainHttpClients.stream().anyMatch(clientInfo -> |
|||
clientInfo.getIcon().equals(secondGroupClientInfo.getIcon()) |
|||
&& clientInfo.getName().equals(secondGroupClientInfo.getName())) |
|||
); |
|||
}); |
|||
List<OAuth2ClientInfo> fourthDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "fourth-domain"); |
|||
Assert.assertEquals(secondGroupClientInfos.size(), fourthDomainHttpsClients.size()); |
|||
secondGroupClientInfos.forEach(secondGroupClientInfo -> { |
|||
Assert.assertTrue( |
|||
fourthDomainHttpsClients.stream().anyMatch(clientInfo -> |
|||
clientInfo.getIcon().equals(secondGroupClientInfo.getIcon()) |
|||
&& clientInfo.getName().equals(secondGroupClientInfo.getName())) |
|||
); |
|||
}); |
|||
|
|||
List<OAuth2ClientInfo> secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); |
|||
Assert.assertEquals(firstGroupClientInfos.size() + secondGroupClientInfos.size(), secondDomainHttpClients.size()); |
|||
firstGroupClientInfos.forEach(firstGroupClientInfo -> { |
|||
Assert.assertTrue( |
|||
secondDomainHttpClients.stream().anyMatch(clientInfo -> |
|||
clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) |
|||
&& clientInfo.getName().equals(firstGroupClientInfo.getName())) |
|||
); |
|||
}); |
|||
secondGroupClientInfos.forEach(secondGroupClientInfo -> { |
|||
Assert.assertTrue( |
|||
secondDomainHttpClients.stream().anyMatch(clientInfo -> |
|||
clientInfo.getIcon().equals(secondGroupClientInfo.getIcon()) |
|||
&& clientInfo.getName().equals(secondGroupClientInfo.getName())) |
|||
); |
|||
}); |
|||
|
|||
List<OAuth2ClientInfo> secondDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "second-domain"); |
|||
Assert.assertEquals(firstGroupClientInfos.size() + thirdGroupClientInfos.size(), secondDomainHttpsClients.size()); |
|||
firstGroupClientInfos.forEach(firstGroupClientInfo -> { |
|||
Assert.assertTrue( |
|||
secondDomainHttpsClients.stream().anyMatch(clientInfo -> |
|||
clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) |
|||
&& clientInfo.getName().equals(firstGroupClientInfo.getName())) |
|||
); |
|||
}); |
|||
thirdGroupClientInfos.forEach(thirdGroupClientInfo -> { |
|||
Assert.assertTrue( |
|||
secondDomainHttpsClients.stream().anyMatch(clientInfo -> |
|||
clientInfo.getIcon().equals(thirdGroupClientInfo.getIcon()) |
|||
&& clientInfo.getName().equals(thirdGroupClientInfo.getName())) |
|||
); |
|||
}); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetOAuth2ClientsForHttpAndHttps() { |
|||
Set<ClientRegistrationDto> firstGroup = Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
); |
|||
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
|||
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTPS).build() |
|||
)) |
|||
.clientRegistrations(firstGroup) |
|||
.build() |
|||
)); |
|||
|
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
OAuth2ClientsParams foundClientsParams = oAuth2Service.findOAuth2Params(); |
|||
Assert.assertNotNull(foundClientsParams); |
|||
Assert.assertEquals(clientsParams, foundClientsParams); |
|||
|
|||
List<OAuth2ClientInfo> firstGroupClientInfos = firstGroup.stream() |
|||
.map(clientRegistrationDto -> new OAuth2ClientInfo( |
|||
clientRegistrationDto.getLoginButtonLabel(), clientRegistrationDto.getLoginButtonIcon(), null)) |
|||
.collect(Collectors.toList()); |
|||
|
|||
List<OAuth2ClientInfo> firstDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "first-domain"); |
|||
Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpClients.size()); |
|||
firstGroupClientInfos.forEach(firstGroupClientInfo -> { |
|||
Assert.assertTrue( |
|||
firstDomainHttpClients.stream().anyMatch(clientInfo -> |
|||
clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) |
|||
&& clientInfo.getName().equals(firstGroupClientInfo.getName())) |
|||
); |
|||
}); |
|||
|
|||
List<OAuth2ClientInfo> firstDomainHttpsClients = oAuth2Service.getOAuth2Clients("https", "first-domain"); |
|||
Assert.assertEquals(firstGroupClientInfos.size(), firstDomainHttpsClients.size()); |
|||
firstGroupClientInfos.forEach(firstGroupClientInfo -> { |
|||
Assert.assertTrue( |
|||
firstDomainHttpsClients.stream().anyMatch(clientInfo -> |
|||
clientInfo.getIcon().equals(firstGroupClientInfo.getIcon()) |
|||
&& clientInfo.getName().equals(firstGroupClientInfo.getName())) |
|||
); |
|||
}); |
|||
} |
|||
|
|||
@Test |
|||
public void testGetDisabledOAuth2Clients() { |
|||
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
|||
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build(), |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), |
|||
DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build() |
|||
)); |
|||
|
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
|
|||
List<OAuth2ClientInfo> secondDomainHttpClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); |
|||
Assert.assertEquals(5, secondDomainHttpClients.size()); |
|||
|
|||
clientsParams.setEnabled(false); |
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
|
|||
List<OAuth2ClientInfo> secondDomainHttpDisabledClients = oAuth2Service.getOAuth2Clients("http", "second-domain"); |
|||
Assert.assertEquals(0, secondDomainHttpDisabledClients.size()); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindAllClientRegistrationInfos() { |
|||
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
|||
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build(), |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), |
|||
DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build(), |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), |
|||
DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build() |
|||
)); |
|||
|
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
List<OAuth2ClientRegistrationInfo> foundClientRegistrationInfos = oAuth2Service.findAllClientRegistrationInfos(); |
|||
Assert.assertEquals(6, foundClientRegistrationInfos.size()); |
|||
clientsParams.getDomainsParams().stream() |
|||
.flatMap(domainParams -> domainParams.getClientRegistrations().stream()) |
|||
.forEach(clientRegistrationDto -> |
|||
Assert.assertTrue( |
|||
foundClientRegistrationInfos.stream() |
|||
.anyMatch(clientRegistrationInfo -> clientRegistrationInfo.getClientId().equals(clientRegistrationDto.getClientId())) |
|||
) |
|||
); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindClientRegistrationById() { |
|||
OAuth2ClientsParams clientsParams = new OAuth2ClientsParams(true, Sets.newHashSet( |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
|||
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build(), |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTP).build(), |
|||
DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build(), |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.HTTPS).build(), |
|||
DomainInfo.builder().name("fifth-domain").scheme(SchemeType.HTTP).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build() |
|||
)); |
|||
|
|||
oAuth2Service.saveOAuth2Params(clientsParams); |
|||
List<OAuth2ClientRegistrationInfo> clientRegistrationInfos = oAuth2Service.findAllClientRegistrationInfos(); |
|||
clientRegistrationInfos.forEach(clientRegistrationInfo -> { |
|||
OAuth2ClientRegistrationInfo foundClientRegistrationInfo = oAuth2Service.findClientRegistrationInfo(clientRegistrationInfo.getUuidId()); |
|||
Assert.assertEquals(clientRegistrationInfo, foundClientRegistrationInfo); |
|||
}); |
|||
} |
|||
|
|||
private OAuth2ClientsParams createDefaultClientsParams() { |
|||
return new OAuth2ClientsParams(true, Sets.newHashSet( |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("first-domain").scheme(SchemeType.HTTP).build(), |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
|||
DomainInfo.builder().name("third-domain").scheme(SchemeType.HTTPS).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build(), |
|||
OAuth2ClientsDomainParams.builder() |
|||
.domainInfos(Sets.newHashSet( |
|||
DomainInfo.builder().name("second-domain").scheme(SchemeType.MIXED).build(), |
|||
DomainInfo.builder().name("fourth-domain").scheme(SchemeType.MIXED).build() |
|||
)) |
|||
.clientRegistrations(Sets.newHashSet( |
|||
validClientRegistrationDto(), |
|||
validClientRegistrationDto() |
|||
)) |
|||
.build() |
|||
)); |
|||
} |
|||
|
|||
private ClientRegistrationDto validClientRegistrationDto() { |
|||
return ClientRegistrationDto.builder() |
|||
.clientId(UUID.randomUUID().toString()) |
|||
.clientSecret(UUID.randomUUID().toString()) |
|||
.authorizationUri(UUID.randomUUID().toString()) |
|||
.accessTokenUri(UUID.randomUUID().toString()) |
|||
.scope(Arrays.asList(UUID.randomUUID().toString(), UUID.randomUUID().toString())) |
|||
.userInfoUri(UUID.randomUUID().toString()) |
|||
.userNameAttributeName(UUID.randomUUID().toString()) |
|||
.jwkSetUri(UUID.randomUUID().toString()) |
|||
.clientAuthenticationMethod(UUID.randomUUID().toString()) |
|||
.loginButtonLabel(UUID.randomUUID().toString()) |
|||
.loginButtonIcon(UUID.randomUUID().toString()) |
|||
.additionalInfo(mapper.createObjectNode().put(UUID.randomUUID().toString(), UUID.randomUUID().toString())) |
|||
.mapperConfig( |
|||
OAuth2MapperConfig.builder() |
|||
.allowUserCreation(true) |
|||
.activateUser(true) |
|||
.type(MapperType.CUSTOM) |
|||
.custom( |
|||
OAuth2CustomMapperConfig.builder() |
|||
.url(UUID.randomUUID().toString()) |
|||
.build() |
|||
) |
|||
.build() |
|||
) |
|||
.build(); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.BaseOAuth2ConfigTemplateServiceTest; |
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
|
|||
@DaoSqlTest |
|||
public class OAuth2ConfigTemplateServiceSqlTest extends BaseOAuth2ConfigTemplateServiceTest { |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0
|
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
package org.thingsboard.server.dao.service.sql; |
|||
|
|||
import org.thingsboard.server.dao.service.BaseOAuth2ServiceTest; |
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
|
|||
@DaoSqlTest |
|||
public class OAuth2ServiceSqlTest extends BaseOAuth2ServiceTest { |
|||
} |
|||
@ -0,0 +1,478 @@ |
|||
<!-- |
|||
|
|||
Copyright © 2016-2020 The Thingsboard Authors |
|||
|
|||
Licensed under the Apache License, Version 2.0 (the "License"); |
|||
you may not use this file except in compliance with the License. |
|||
You may obtain a copy of the License at |
|||
|
|||
http://www.apache.org/licenses/LICENSE-2.0 |
|||
|
|||
Unless required by applicable law or agreed to in writing, software |
|||
distributed under the License is distributed on an "AS IS" BASIS, |
|||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
See the License for the specific language governing permissions and |
|||
limitations under the License. |
|||
|
|||
--> |
|||
<div> |
|||
<mat-card class="settings-card"> |
|||
<mat-card-title> |
|||
<div fxLayout="row"> |
|||
<span class="mat-headline" translate>admin.oauth2.oauth2</span> |
|||
<span fxFlex></span> |
|||
<div tb-help="oauth2Settings"></div> |
|||
</div> |
|||
</mat-card-title> |
|||
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async"> |
|||
</mat-progress-bar> |
|||
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div> |
|||
<mat-card-content style="padding-top: 16px;"> |
|||
<form [formGroup]="oauth2SettingsForm" (ngSubmit)="save()"> |
|||
<fieldset [disabled]="isLoading$ | async"> |
|||
<mat-checkbox formControlName="enabled"> |
|||
{{ 'admin.oauth2.enable' | translate }} |
|||
</mat-checkbox> |
|||
<section *ngIf="oauth2SettingsForm.get('enabled').value && !(isLoading$ | async)" style="margin-top: 1em;"> |
|||
<ng-container formArrayName="domainsParams"> |
|||
<div class="container"> |
|||
<mat-accordion multi> |
|||
<ng-container *ngFor="let domain of domainsParams.controls; let i = index; trackBy: trackByParams"> |
|||
<mat-expansion-panel [formGroupName]="i"> |
|||
<mat-expansion-panel-header> |
|||
<mat-panel-title fxLayoutAlign="start center"> |
|||
{{ domainListTittle(domain) }} |
|||
</mat-panel-title> |
|||
<mat-panel-description fxLayoutAlign="end center"> |
|||
<button mat-icon-button |
|||
type="button" |
|||
(click)="deleteDomain($event, i)" |
|||
matTooltip="{{ 'action.delete' | translate }}" |
|||
matTooltipPosition="above"> |
|||
<mat-icon>delete</mat-icon> |
|||
</button> |
|||
</mat-panel-description> |
|||
</mat-expansion-panel-header> |
|||
|
|||
<ng-template matExpansionPanelContent> |
|||
<ng-container formArrayName="domainInfos"> |
|||
<section *ngFor="let domainInfo of clientDomainInfos(domain).controls; let n = index; trackBy: trackByParams" |
|||
class="domains-list"> |
|||
<div [formGroupName]="n" fxLayout="row" fxLayoutGap="8px"> |
|||
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px"> |
|||
<div fxLayout="column" fxFlex.sm="60" fxFlex.gt-sm="50"> |
|||
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap="8px"> |
|||
<mat-form-field fxFlex="30" fxFlex.xs class="mat-block"> |
|||
<mat-label translate>admin.oauth2.protocol</mat-label> |
|||
<mat-select formControlName="scheme"> |
|||
<mat-option *ngFor="let protocol of protocols" [value]="protocol"> |
|||
{{ domainSchemaTranslations.get(protocol) | translate | uppercase }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.domain-name</mat-label> |
|||
<input matInput formControlName="name" required> |
|||
<mat-error *ngIf="domainInfo.get('name').hasError('pattern')"> |
|||
{{ 'admin.error-verification-url' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
</div> |
|||
<mat-error *ngIf="domainInfo.hasError('unique')"> |
|||
{{ 'admin.domain-name-unique' | translate }} |
|||
</mat-error> |
|||
</div> |
|||
|
|||
<div fxFlex fxLayout="column"> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.redirect-uri-template</mat-label> |
|||
<input matInput [value]="redirectURI(domainInfo)" readonly> |
|||
<button mat-icon-button color="primary" matSuffix type="button" |
|||
ngxClipboard cbContent="{{ redirectURI(domainInfo) }}" |
|||
matTooltip="{{ 'admin.oauth2.copy-redirect-uri' | translate }}" |
|||
matTooltipPosition="above"> |
|||
<mat-icon class="material-icons" svgIcon="mdi:clipboard-arrow-left"></mat-icon> |
|||
</button> |
|||
</mat-form-field> |
|||
|
|||
<mat-form-field fxFlex *ngIf="domainInfo.get('scheme').value === 'MIXED'" class="mat-block"> |
|||
<mat-label></mat-label> |
|||
<input matInput [value]="redirectURIMixed(domainInfo)" readonly> |
|||
<button mat-icon-button color="primary" matSuffix type="button" |
|||
ngxClipboard cbContent="{{ redirectURIMixed(domainInfo) }}" |
|||
matTooltip="{{ 'admin.oauth2.copy-redirect-uri' | translate }}" |
|||
matTooltipPosition="above"> |
|||
<mat-icon class="material-icons" svgIcon="mdi:clipboard-arrow-left"></mat-icon> |
|||
</button> |
|||
</mat-form-field> |
|||
</div> |
|||
</div> |
|||
|
|||
<div fxLayout="column" fxLayoutAlign="center start"> |
|||
<button type="button" mat-icon-button color="primary" |
|||
(click)="removeDomain($event, domain, n)" |
|||
[disabled]="clientDomainInfos(domain).controls.length < 2" |
|||
matTooltip="{{ 'admin.oauth2.delete-domain' | translate }}" |
|||
matTooltipPosition="above"> |
|||
<mat-icon>close</mat-icon> |
|||
</button> |
|||
</div> |
|||
</div> |
|||
</section> |
|||
<div fxLayout="row" fxLayoutAlign="end center" style="margin-bottom: 1.25em"> |
|||
<button mat-button mat-raised-button color="primary" |
|||
[disabled]="(isLoading$ | async)" |
|||
(click)="addDomainInfo(domain)" |
|||
type="button"> |
|||
{{'admin.oauth2.add-domain' | translate}} |
|||
</button> |
|||
</div> |
|||
</ng-container> |
|||
|
|||
<ng-container formArrayName="clientRegistrations"> |
|||
<div class="container"> |
|||
<mat-expansion-panel *ngFor="let registration of clientDomainProviders(domain).controls; let j = index; trackBy: trackByParams" |
|||
class="registration-card mat-elevation-z0"> |
|||
<mat-expansion-panel-header> |
|||
<mat-panel-title fxLayoutAlign="start center"> |
|||
{{ getProviderName(registration) }} |
|||
</mat-panel-title> |
|||
<mat-panel-description fxLayoutAlign="end center"> |
|||
<button mat-icon-button |
|||
type="button" |
|||
[disabled]="clientDomainProviders(domain).controls.length < 2" |
|||
(click)="deleteProvider($event, domain, j)" |
|||
matTooltip="{{ 'admin.oauth2.delete-provider' | translate }}" |
|||
matTooltipPosition="above"> |
|||
<mat-icon>delete</mat-icon> |
|||
</button> |
|||
</mat-panel-description> |
|||
</mat-expansion-panel-header> |
|||
|
|||
<ng-template matExpansionPanelContent> |
|||
<section [formGroupName]="j"> |
|||
<section formGroupName="additionalInfo" fxLayout="row"> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.login-provider</mat-label> |
|||
<mat-select formControlName="providerName"> |
|||
<mat-option *ngFor="let provider of templateProvider" [value]="provider"> |
|||
{{ provider }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
<div [tb-help]="getHelpLink(registration)" *ngIf="getProviderName(registration) !== 'Custom'"></div> |
|||
</section> |
|||
|
|||
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.client-id</mat-label> |
|||
<input matInput formControlName="clientId" required> |
|||
<mat-error *ngIf="registration.get('clientId').hasError('required')"> |
|||
{{ 'admin.oauth2.client-id-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
|
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.client-secret</mat-label> |
|||
<input matInput formControlName="clientSecret" required> |
|||
<mat-error *ngIf="registration.get('clientSecret').hasError('required')"> |
|||
{{ 'admin.oauth2.client-secret-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
</div> |
|||
|
|||
<mat-expansion-panel class="mat-elevation-z0 custom-settings" |
|||
[disabled]="getProviderName(registration) === 'Custom'" |
|||
[expanded]="getProviderName(registration) === 'Custom'"> |
|||
<mat-expansion-panel-header [fxHide]="getProviderName(registration) === 'Custom'"> |
|||
<mat-panel-description fxLayoutAlign="end center"> |
|||
{{ 'admin.oauth2.custom-setting' | translate }} |
|||
</mat-panel-description> |
|||
</mat-expansion-panel-header> |
|||
<ng-template matExpansionPanelContent> |
|||
<mat-tab-group dynamicHeight> |
|||
<mat-tab label="{{ 'admin.oauth2.general' | translate }}"> |
|||
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" style="margin-top: 16px;"> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.access-token-uri</mat-label> |
|||
<input matInput formControlName="accessTokenUri" required> |
|||
<button mat-icon-button matSuffix |
|||
type="button" |
|||
(click)="toggleEditMode(registration, 'accessTokenUri')" |
|||
*ngIf="getProviderName(registration) !== 'Custom'"> |
|||
<mat-icon class="material-icons">create</mat-icon> |
|||
</button> |
|||
<mat-error *ngIf="registration.get('accessTokenUri').hasError('required')"> |
|||
{{ 'admin.oauth2.access-token-uri-required' | translate }} |
|||
</mat-error> |
|||
<mat-error *ngIf="registration.get('accessTokenUri').hasError('pattern')"> |
|||
{{ 'admin.oauth2.uri-pattern-error' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
|
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.authorization-uri</mat-label> |
|||
<input matInput formControlName="authorizationUri" required> |
|||
<button mat-icon-button matSuffix |
|||
type="button" |
|||
(click)="toggleEditMode(registration, 'authorizationUri')" |
|||
*ngIf="getProviderName(registration) !== 'Custom'"> |
|||
<mat-icon class="material-icons">create</mat-icon> |
|||
</button> |
|||
<mat-error *ngIf="registration.get('authorizationUri').hasError('required')"> |
|||
{{ 'admin.oauth2.authorization-uri-required' | translate }} |
|||
</mat-error> |
|||
<mat-error *ngIf="registration.get('authorizationUri').hasError('pattern')"> |
|||
{{ 'admin.oauth2.uri-pattern-error' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
</div> |
|||
|
|||
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> |
|||
<mat-form-field fxFlex class="mat-block" appearance="legacy"> |
|||
<mat-label translate>admin.oauth2.jwk-set-uri</mat-label> |
|||
<input matInput formControlName="jwkSetUri"> |
|||
<button mat-icon-button matSuffix |
|||
type="button" aria-label="Clear" |
|||
(click)="toggleEditMode(registration, 'jwkSetUri')" |
|||
*ngIf="getProviderName(registration) !== 'Custom'"> |
|||
<mat-icon class="material-icons">create</mat-icon> |
|||
</button> |
|||
<mat-error *ngIf="registration.get('jwkSetUri').hasError('pattern')"> |
|||
{{ 'admin.oauth2.uri-pattern-error' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
|
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.user-info-uri</mat-label> |
|||
<input matInput formControlName="userInfoUri" required> |
|||
<button mat-icon-button matSuffix |
|||
type="button" |
|||
(click)="toggleEditMode(registration, 'userInfoUri')" |
|||
*ngIf="getProviderName(registration) !== 'Custom'"> |
|||
<mat-icon class="material-icons">create</mat-icon> |
|||
</button> |
|||
<mat-error *ngIf="registration.get('userInfoUri').hasError('required')"> |
|||
{{ 'admin.oauth2.user-info-uri-required' | translate }} |
|||
</mat-error> |
|||
<mat-error *ngIf="registration.get('userInfoUri').hasError('pattern')"> |
|||
{{ 'admin.oauth2.uri-pattern-error' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
</div> |
|||
|
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.client-authentication-method</mat-label> |
|||
<mat-select formControlName="clientAuthenticationMethod"> |
|||
<mat-option *ngFor="let clientAuthenticationMethod of clientAuthenticationMethods" |
|||
[value]="clientAuthenticationMethod"> |
|||
{{ clientAuthenticationMethod | uppercase }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
|
|||
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px" *ngIf="getProviderName(registration) === 'Custom'"> |
|||
<mat-form-field fxFlex class="mat-block" floatLabel="always"> |
|||
<mat-label translate>admin.oauth2.login-button-label</mat-label> |
|||
<input matInput formControlName="loginButtonLabel" |
|||
placeholder="{{ 'admin.oauth2.login-button-label-1' | translate }}" |
|||
required> |
|||
<mat-error *ngIf="registration.get('loginButtonLabel').hasError('required')"> |
|||
{{ 'admin.oauth2.login-button-label-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
|
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.login-button-icon</mat-label> |
|||
<input matInput formControlName="loginButtonIcon"> |
|||
</mat-form-field> |
|||
</div> |
|||
|
|||
<section formGroupName="mapperConfig"> |
|||
<div fxLayout="column" fxLayoutGap="8px" style="margin-bottom: 8px;"> |
|||
<mat-checkbox formControlName="allowUserCreation"> |
|||
{{ 'admin.oauth2.allow-user-creation' | translate }} |
|||
</mat-checkbox> |
|||
<mat-checkbox formControlName="activateUser"> |
|||
{{ 'admin.oauth2.activate-user' | translate }} |
|||
</mat-checkbox> |
|||
</div> |
|||
</section> |
|||
|
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.scope</mat-label> |
|||
<mat-chip-list #scopeList> |
|||
<mat-chip *ngFor="let scope of registration.get('scope').value; let k = index; trackBy: trackByParams" |
|||
removable (removed)="removeScope(k, registration)"> |
|||
{{scope}} |
|||
<mat-icon matChipRemove>cancel</mat-icon> |
|||
</mat-chip> |
|||
<input [matChipInputFor]="scopeList" |
|||
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" |
|||
matChipInputAddOnBlur |
|||
(matChipInputTokenEnd)="addScope($event, registration)"> |
|||
</mat-chip-list> |
|||
<mat-error *ngIf="registration.get('scope').hasError('required')"> |
|||
{{ 'admin.oauth2.scope-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
|
|||
</mat-tab> |
|||
<mat-tab label="{{ 'admin.oauth2.mapper' | translate }}"> |
|||
<mat-form-field class="mat-block" style="margin-top: 16px;"> |
|||
<mat-label translate>admin.oauth2.user-name-attribute-name</mat-label> |
|||
<input matInput formControlName="userNameAttributeName" required> |
|||
<mat-error |
|||
*ngIf="registration.get('userNameAttributeName').hasError('required')"> |
|||
{{ 'admin.oauth2.user-name-attribute-name-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
|
|||
<section formGroupName="mapperConfig"> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.type</mat-label> |
|||
<mat-select formControlName="type"> |
|||
<mat-option *ngFor="let converterTypeExternalUser of converterTypesExternalUser" |
|||
[value]="converterTypeExternalUser"> |
|||
{{ converterTypeExternalUser }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
|
|||
<section formGroupName="basic" |
|||
*ngIf="registration.get('mapperConfig.type').value === 'BASIC'"> |
|||
<mat-form-field class="mat-block"> |
|||
<mat-label translate>admin.oauth2.email-attribute-key</mat-label> |
|||
<input matInput formControlName="emailAttributeKey" required> |
|||
<mat-error |
|||
*ngIf="registration.get('mapperConfig.basic.emailAttributeKey').hasError('required')"> |
|||
{{ 'admin.oauth2.email-attribute-key-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
|
|||
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.first-name-attribute-key</mat-label> |
|||
<input matInput formControlName="firstNameAttributeKey"> |
|||
</mat-form-field> |
|||
|
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.last-name-attribute-key</mat-label> |
|||
<input matInput formControlName="lastNameAttributeKey"> |
|||
</mat-form-field> |
|||
</div> |
|||
|
|||
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.tenant-name-strategy</mat-label> |
|||
<mat-select formControlName="tenantNameStrategy"> |
|||
<mat-option *ngFor="let tenantNameStrategy of tenantNameStrategies" |
|||
[value]="tenantNameStrategy"> |
|||
{{ tenantNameStrategy }} |
|||
</mat-option> |
|||
</mat-select> |
|||
</mat-form-field> |
|||
|
|||
<mat-form-field fxFlex class="mat-block" [fxShow]="registration.get('mapperConfig.basic.tenantNameStrategy').value === 'CUSTOM'"> |
|||
<mat-label translate>admin.oauth2.tenant-name-pattern</mat-label> |
|||
<input matInput |
|||
formControlName="tenantNamePattern" |
|||
[required]="registration.get('mapperConfig.basic.tenantNameStrategy').value === 'CUSTOM'"> |
|||
<mat-error |
|||
*ngIf="registration.get('mapperConfig.basic.tenantNamePattern').hasError('required')"> |
|||
{{ 'admin.oauth2.tenant-name-pattern-required' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
</div> |
|||
|
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.customer-name-pattern</mat-label> |
|||
<input matInput formControlName="customerNamePattern"> |
|||
</mat-form-field> |
|||
|
|||
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>admin.oauth2.default-dashboard-name</mat-label> |
|||
<input matInput formControlName="defaultDashboardName"> |
|||
</mat-form-field> |
|||
|
|||
<mat-checkbox fxFlex formControlName="alwaysFullScreen" class="checkbox-row"> |
|||
{{ 'admin.oauth2.always-fullscreen' | translate}} |
|||
</mat-checkbox> |
|||
</div> |
|||
</section> |
|||
|
|||
<section formGroupName="custom" |
|||
*ngIf="registration.get('mapperConfig.type').value === 'CUSTOM'"> |
|||
<mat-form-field class="mat-block"> |
|||
<mat-label translate>admin.oauth2.url</mat-label> |
|||
<input matInput formControlName="url" required> |
|||
<mat-error |
|||
*ngIf="registration.get('mapperConfig.custom.url').hasError('required')"> |
|||
{{ 'admin.oauth2.url-required' | translate }} |
|||
</mat-error> |
|||
<mat-error |
|||
*ngIf="registration.get('mapperConfig.custom.url').hasError('pattern')"> |
|||
{{ 'admin.oauth2.url-pattern' | translate }} |
|||
</mat-error> |
|||
</mat-form-field> |
|||
|
|||
<div fxLayout="row" fxLayout.xs="column" fxLayoutGap.gt-xs="8px"> |
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>common.username</mat-label> |
|||
<input matInput formControlName="username" autocomplete="new-username"> |
|||
</mat-form-field> |
|||
|
|||
<mat-form-field fxFlex class="mat-block"> |
|||
<mat-label translate>common.password</mat-label> |
|||
<input matInput type="password" formControlName="password" autocomplete="new-password"> |
|||
</mat-form-field> |
|||
</div> |
|||
</section> |
|||
</section> |
|||
</mat-tab> |
|||
</mat-tab-group> |
|||
</ng-template> |
|||
</mat-expansion-panel> |
|||
|
|||
</section> |
|||
</ng-template> |
|||
</mat-expansion-panel> |
|||
</div> |
|||
</ng-container> |
|||
|
|||
<div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> |
|||
<button mat-button mat-raised-button color="primary" |
|||
[disabled]="(isLoading$ | async)" |
|||
(click)="addProvider(domain)" |
|||
type="button"> |
|||
{{'admin.oauth2.add-provider' | translate}} |
|||
</button> |
|||
</div> |
|||
</ng-template> |
|||
|
|||
</mat-expansion-panel> |
|||
</ng-container> |
|||
</mat-accordion> |
|||
</div> |
|||
</ng-container> |
|||
</section> |
|||
</fieldset> |
|||
<div fxLayout="row" fxLayoutAlign="end center" fxLayoutGap="8px"> |
|||
<button type="button" mat-raised-button color="primary" |
|||
[disabled]="isLoading$ | async" |
|||
*ngIf="oauth2SettingsForm.get('enabled').value" |
|||
(click)="addDomain()"> |
|||
<mat-icon>add</mat-icon> |
|||
<span translate>action.add</span> |
|||
</button> |
|||
<button mat-button mat-raised-button color="primary" |
|||
[disabled]="(isLoading$ | async) || oauth2SettingsForm.invalid || !oauth2SettingsForm.dirty" |
|||
type="submit"> |
|||
{{'action.save' | translate}} |
|||
</button> |
|||
</div> |
|||
</form> |
|||
</mat-card-content> |
|||
</mat-card> |
|||
</div> |
|||
@ -0,0 +1,66 @@ |
|||
/** |
|||
* Copyright © 2016-2020 The Thingsboard Authors |
|||
* |
|||
* Licensed under the Apache License, Version 2.0 (the "License"); |
|||
* you may not use this file except in compliance with the License. |
|||
* You may obtain a copy of the License at |
|||
* |
|||
* http://www.apache.org/licenses/LICENSE-2.0 |
|||
* |
|||
* Unless required by applicable law or agreed to in writing, software |
|||
* distributed under the License is distributed on an "AS IS" BASIS, |
|||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|||
* See the License for the specific language governing permissions and |
|||
* limitations under the License. |
|||
*/ |
|||
:host{ |
|||
.checkbox-row { |
|||
margin-top: 1em; |
|||
} |
|||
|
|||
.registration-card{ |
|||
margin-bottom: 0.5em; |
|||
border: 1px solid rgba(0, 0, 0, 0.2); |
|||
|
|||
.custom-settings{ |
|||
font-size: 16px; |
|||
.mat-expansion-panel-header{ |
|||
padding: 0 2px; |
|||
} |
|||
} |
|||
} |
|||
|
|||
.container{ |
|||
margin-bottom: 1em; |
|||
|
|||
.tb-highlight{ |
|||
margin: 0; |
|||
} |
|||
|
|||
.tb-hint{ |
|||
padding-bottom: 0; |
|||
} |
|||
} |
|||
} |
|||
|
|||
:host ::ng-deep{ |
|||
.registration-card{ |
|||
.custom-settings{ |
|||
.mat-expansion-panel-body{ |
|||
padding: 0 2px 1em; |
|||
} |
|||
.mat-tab-label{ |
|||
text-transform: none; |
|||
} |
|||
.mat-form-field-suffix .mat-icon-button .mat-icon{ |
|||
font-size: 18px; |
|||
} |
|||
} |
|||
} |
|||
.domains-list{ |
|||
margin-bottom: 1.5em; |
|||
.mat-form-field-suffix .mat-icon-button .mat-icon{ |
|||
font-size: 24px; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,500 @@ |
|||
///
|
|||
/// Copyright © 2016-2020 The Thingsboard Authors
|
|||
///
|
|||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
|||
/// you may not use this file except in compliance with the License.
|
|||
/// You may obtain a copy of the License at
|
|||
///
|
|||
/// http://www.apache.org/licenses/LICENSE-2.0
|
|||
///
|
|||
/// Unless required by applicable law or agreed to in writing, software
|
|||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
|||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
/// See the License for the specific language governing permissions and
|
|||
/// limitations under the License.
|
|||
///
|
|||
|
|||
import { Component, Inject, OnDestroy, OnInit } from '@angular/core'; |
|||
import { AbstractControl, FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms'; |
|||
import { |
|||
ClientAuthenticationMethod, |
|||
ClientProviderTemplated, |
|||
ClientRegistration, |
|||
DomainInfo, |
|||
DomainSchema, |
|||
domainSchemaTranslations, |
|||
DomainsParam, |
|||
MapperConfig, |
|||
MapperConfigBasic, |
|||
MapperConfigCustom, |
|||
MapperConfigType, |
|||
OAuth2Settings, |
|||
TenantNameStrategy |
|||
} from '@shared/models/settings.models'; |
|||
import { Store } from '@ngrx/store'; |
|||
import { AppState } from '@core/core.state'; |
|||
import { AdminService } from '@core/http/admin.service'; |
|||
import { PageComponent } from '@shared/components/page.component'; |
|||
import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard'; |
|||
import { COMMA, ENTER } from '@angular/cdk/keycodes'; |
|||
import { MatChipInputEvent } from '@angular/material/chips'; |
|||
import { WINDOW } from '@core/services/window.service'; |
|||
import { forkJoin, Subscription } from 'rxjs'; |
|||
import { DialogService } from '@core/services/dialog.service'; |
|||
import { TranslateService } from '@ngx-translate/core'; |
|||
import { isDefined } from '@core/utils'; |
|||
|
|||
@Component({ |
|||
selector: 'tb-oauth2-settings', |
|||
templateUrl: './oauth2-settings.component.html', |
|||
styleUrls: ['./oauth2-settings.component.scss', './settings-card.scss'] |
|||
}) |
|||
export class OAuth2SettingsComponent extends PageComponent implements OnInit, HasConfirmForm, OnDestroy { |
|||
|
|||
private URL_REGEXP = /^[A-Za-z][A-Za-z\d.+-]*:\/*(?:\w+(?::\w+)?@)?[^\s/]+(?::\d+)?(?:\/[\w#!:.,?+=&%@\-/]*)?$/; |
|||
private subscriptions: Subscription[] = []; |
|||
private templates = new Map<string, ClientProviderTemplated>(); |
|||
private defaultProvider = { |
|||
additionalInfo: { |
|||
providerName: 'Custom' |
|||
}, |
|||
clientAuthenticationMethod: ClientAuthenticationMethod.POST, |
|||
userNameAttributeName: 'email', |
|||
mapperConfig: { |
|||
allowUserCreation: true, |
|||
activateUser: false, |
|||
type: MapperConfigType.BASIC, |
|||
basic: { |
|||
emailAttributeKey: 'email', |
|||
tenantNameStrategy: TenantNameStrategy.DOMAIN, |
|||
alwaysFullScreen: false |
|||
} |
|||
} |
|||
}; |
|||
|
|||
readonly separatorKeysCodes: number[] = [ENTER, COMMA]; |
|||
|
|||
oauth2SettingsForm: FormGroup; |
|||
oauth2Settings: OAuth2Settings; |
|||
|
|||
clientAuthenticationMethods = Object.keys(ClientAuthenticationMethod); |
|||
converterTypesExternalUser = Object.keys(MapperConfigType); |
|||
tenantNameStrategies = Object.keys(TenantNameStrategy); |
|||
protocols = Object.keys(DomainSchema); |
|||
domainSchemaTranslations = domainSchemaTranslations; |
|||
|
|||
templateProvider = ['Custom']; |
|||
|
|||
constructor(protected store: Store<AppState>, |
|||
private adminService: AdminService, |
|||
private fb: FormBuilder, |
|||
private dialogService: DialogService, |
|||
private translate: TranslateService, |
|||
@Inject(WINDOW) private window: Window) { |
|||
super(store); |
|||
} |
|||
|
|||
ngOnInit(): void { |
|||
this.buildOAuth2SettingsForm(); |
|||
forkJoin([ |
|||
this.adminService.getOAuth2Template(), |
|||
this.adminService.getOAuth2Settings() |
|||
]).subscribe( |
|||
([templates, oauth2Settings]) => { |
|||
this.initTemplates(templates); |
|||
this.oauth2Settings = oauth2Settings; |
|||
this.initOAuth2Settings(this.oauth2Settings); |
|||
} |
|||
); |
|||
} |
|||
|
|||
ngOnDestroy() { |
|||
super.ngOnDestroy(); |
|||
this.subscriptions.forEach((subscription) => { |
|||
subscription.unsubscribe(); |
|||
}); |
|||
} |
|||
|
|||
private initTemplates(templates: ClientProviderTemplated[]): void { |
|||
templates.map(provider => this.templates.set(provider.name, provider)); |
|||
this.templateProvider.push(...Array.from(this.templates.keys())); |
|||
this.templateProvider.sort(); |
|||
} |
|||
|
|||
get domainsParams(): FormArray { |
|||
return this.oauth2SettingsForm.get('domainsParams') as FormArray; |
|||
} |
|||
|
|||
private formBasicGroup(mapperConfigBasic?: MapperConfigBasic): FormGroup { |
|||
let tenantNamePattern; |
|||
if (mapperConfigBasic?.tenantNamePattern) { |
|||
tenantNamePattern = mapperConfigBasic.tenantNamePattern; |
|||
} else { |
|||
tenantNamePattern = {value: null, disabled: true}; |
|||
} |
|||
const basicGroup = this.fb.group({ |
|||
emailAttributeKey: [mapperConfigBasic?.emailAttributeKey ? mapperConfigBasic.emailAttributeKey : 'email', Validators.required], |
|||
firstNameAttributeKey: [mapperConfigBasic?.firstNameAttributeKey ? mapperConfigBasic.firstNameAttributeKey : ''], |
|||
lastNameAttributeKey: [mapperConfigBasic?.lastNameAttributeKey ? mapperConfigBasic.lastNameAttributeKey : ''], |
|||
tenantNameStrategy: [mapperConfigBasic?.tenantNameStrategy ? mapperConfigBasic.tenantNameStrategy : TenantNameStrategy.DOMAIN], |
|||
tenantNamePattern: [tenantNamePattern, Validators.required], |
|||
customerNamePattern: [mapperConfigBasic?.customerNamePattern ? mapperConfigBasic.customerNamePattern : null], |
|||
defaultDashboardName: [mapperConfigBasic?.defaultDashboardName ? mapperConfigBasic.defaultDashboardName : null], |
|||
alwaysFullScreen: [mapperConfigBasic?.alwaysFullScreen ? mapperConfigBasic.alwaysFullScreen : false] |
|||
}); |
|||
|
|||
this.subscriptions.push(basicGroup.get('tenantNameStrategy').valueChanges.subscribe((domain) => { |
|||
if (domain === 'CUSTOM') { |
|||
basicGroup.get('tenantNamePattern').enable(); |
|||
} else { |
|||
basicGroup.get('tenantNamePattern').disable(); |
|||
} |
|||
})); |
|||
|
|||
return basicGroup; |
|||
} |
|||
|
|||
private formCustomGroup(mapperConfigCustom?: MapperConfigCustom): FormGroup { |
|||
return this.fb.group({ |
|||
url: [mapperConfigCustom?.url ? mapperConfigCustom.url : null, [Validators.required, Validators.pattern(this.URL_REGEXP)]], |
|||
username: [mapperConfigCustom?.username ? mapperConfigCustom.username : null], |
|||
password: [mapperConfigCustom?.password ? mapperConfigCustom.password : null] |
|||
}); |
|||
} |
|||
|
|||
private buildOAuth2SettingsForm(): void { |
|||
this.oauth2SettingsForm = this.fb.group({ |
|||
domainsParams: this.fb.array([]), |
|||
enabled: [false] |
|||
}); |
|||
} |
|||
|
|||
private initOAuth2Settings(oauth2Settings: OAuth2Settings): void { |
|||
if (oauth2Settings) { |
|||
this.oauth2SettingsForm.patchValue({enabled: oauth2Settings.enabled}, {emitEvent: false}); |
|||
oauth2Settings.domainsParams.forEach((domain) => { |
|||
this.domainsParams.push(this.buildDomainsForm(domain)); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
private uniqueDomainValidator(control: FormGroup): { [key: string]: boolean } | null { |
|||
if (control.parent?.value) { |
|||
const domain = control.value.name; |
|||
const listProtocols = control.parent.getRawValue() |
|||
.filter((domainInfo) => domainInfo.name === domain) |
|||
.map((domainInfo) => domainInfo.scheme); |
|||
if (listProtocols.length > 1 && listProtocols.indexOf(DomainSchema.MIXED) > -1 || |
|||
new Set(listProtocols).size !== listProtocols.length) { |
|||
return {unique: true}; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
public domainListTittle(control: AbstractControl): string { |
|||
const domainInfos = control.get('domainInfos').value as DomainInfo[]; |
|||
if (domainInfos.length) { |
|||
const domainList = new Set<string>(); |
|||
domainInfos.forEach((domain) => { |
|||
domainList.add(domain.name); |
|||
}); |
|||
return Array.from(domainList).join(', '); |
|||
} |
|||
return this.translate.instant('admin.oauth2.new-domain'); |
|||
} |
|||
|
|||
private buildDomainsForm(domainParams?: DomainsParam): FormGroup { |
|||
const formDomain = this.fb.group({ |
|||
domainInfos: this.fb.array([], Validators.required), |
|||
clientRegistrations: this.fb.array([], Validators.required) |
|||
}); |
|||
|
|||
if (domainParams) { |
|||
domainParams.domainInfos.forEach((domain) => { |
|||
this.clientDomainInfos(formDomain).push(this.buildDomainForm(domain)); |
|||
}); |
|||
domainParams.clientRegistrations.forEach((registration) => { |
|||
this.clientDomainProviders(formDomain).push(this.buildProviderForm(registration)); |
|||
}); |
|||
} else { |
|||
this.clientDomainProviders(formDomain).push(this.buildProviderForm()); |
|||
this.clientDomainInfos(formDomain).push(this.buildDomainForm()); |
|||
} |
|||
|
|||
return formDomain; |
|||
} |
|||
|
|||
private buildDomainForm(domainInfo?: DomainInfo): FormGroup { |
|||
const domain = this.fb.group({ |
|||
name: [domainInfo ? domainInfo.name : this.window.location.hostname, [ |
|||
Validators.required, |
|||
Validators.pattern('((?![:/]).)*$')]], |
|||
scheme: [domainInfo?.scheme ? domainInfo.scheme : DomainSchema.HTTPS, Validators.required] |
|||
}, {validators: this.uniqueDomainValidator}); |
|||
return domain; |
|||
} |
|||
|
|||
private buildProviderForm(registrationData?: ClientRegistration): FormGroup { |
|||
let additionalInfo = null; |
|||
if (registrationData?.additionalInfo) { |
|||
additionalInfo = JSON.parse(registrationData.additionalInfo); |
|||
if (this.templateProvider.indexOf(additionalInfo.providerName) === -1) { |
|||
additionalInfo.providerName = 'Custom'; |
|||
} |
|||
} |
|||
let defaultProviderName = 'Custom'; |
|||
if (this.templateProvider.indexOf('Google')) { |
|||
defaultProviderName = 'Google'; |
|||
} |
|||
|
|||
const clientRegistration = this.fb.group({ |
|||
id: this.fb.group({ |
|||
id: [registrationData?.id?.id ? registrationData.id.id : null], |
|||
entityType: [registrationData?.id?.entityType ? registrationData.id.entityType : null] |
|||
}), |
|||
additionalInfo: this.fb.group({ |
|||
providerName: [additionalInfo?.providerName ? additionalInfo?.providerName : defaultProviderName, Validators.required] |
|||
}), |
|||
loginButtonLabel: [registrationData?.loginButtonLabel ? registrationData.loginButtonLabel : null, Validators.required], |
|||
loginButtonIcon: [registrationData?.loginButtonIcon ? registrationData.loginButtonIcon : null], |
|||
clientId: [registrationData?.clientId ? registrationData.clientId : '', Validators.required], |
|||
clientSecret: [registrationData?.clientSecret ? registrationData.clientSecret : '', Validators.required], |
|||
accessTokenUri: [registrationData?.accessTokenUri ? registrationData.accessTokenUri : '', |
|||
[Validators.required, |
|||
Validators.pattern(this.URL_REGEXP)]], |
|||
authorizationUri: [registrationData?.authorizationUri ? registrationData.authorizationUri : '', |
|||
[Validators.required, |
|||
Validators.pattern(this.URL_REGEXP)]], |
|||
scope: this.fb.array(registrationData?.scope ? registrationData.scope : [], Validators.required), |
|||
jwkSetUri: [registrationData?.jwkSetUri ? registrationData.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], |
|||
userInfoUri: [registrationData?.userInfoUri ? registrationData.userInfoUri : '', |
|||
[Validators.required, |
|||
Validators.pattern(this.URL_REGEXP)]], |
|||
clientAuthenticationMethod: [ |
|||
registrationData?.clientAuthenticationMethod ? registrationData.clientAuthenticationMethod : ClientAuthenticationMethod.POST, |
|||
Validators.required], |
|||
userNameAttributeName: [ |
|||
registrationData?.userNameAttributeName ? registrationData.userNameAttributeName : 'email', Validators.required], |
|||
mapperConfig: this.fb.group({ |
|||
allowUserCreation: [registrationData?.mapperConfig?.allowUserCreation ? registrationData.mapperConfig.allowUserCreation : true], |
|||
activateUser: [registrationData?.mapperConfig?.activateUser ? registrationData.mapperConfig.activateUser : false], |
|||
type: [registrationData?.mapperConfig?.type ? registrationData.mapperConfig.type : MapperConfigType.BASIC, Validators.required] |
|||
} |
|||
) |
|||
}); |
|||
|
|||
if (registrationData) { |
|||
this.changeMapperConfigType(clientRegistration, registrationData.mapperConfig.type, registrationData.mapperConfig); |
|||
} else { |
|||
this.changeMapperConfigType(clientRegistration, MapperConfigType.BASIC); |
|||
this.setProviderDefaultValue(defaultProviderName, clientRegistration); |
|||
} |
|||
|
|||
this.subscriptions.push(clientRegistration.get('mapperConfig.type').valueChanges.subscribe((value) => { |
|||
this.changeMapperConfigType(clientRegistration, value); |
|||
})); |
|||
|
|||
this.subscriptions.push(clientRegistration.get('additionalInfo.providerName').valueChanges.subscribe((provider) => { |
|||
(clientRegistration.get('scope') as FormArray).clear(); |
|||
this.setProviderDefaultValue(provider, clientRegistration); |
|||
})); |
|||
|
|||
return clientRegistration; |
|||
} |
|||
|
|||
private setProviderDefaultValue(provider: string, clientRegistration: FormGroup) { |
|||
if (provider === 'Custom') { |
|||
const defaultSettings = {...this.defaultProvider, ...{id: clientRegistration.get('id').value}}; |
|||
clientRegistration.reset(defaultSettings, {emitEvent: false}); |
|||
clientRegistration.get('accessTokenUri').enable(); |
|||
clientRegistration.get('authorizationUri').enable(); |
|||
clientRegistration.get('jwkSetUri').enable(); |
|||
clientRegistration.get('userInfoUri').enable(); |
|||
} else { |
|||
const template = this.templates.get(provider); |
|||
delete template.id; |
|||
delete template.additionalInfo; |
|||
template.clientId = ''; |
|||
template.clientSecret = ''; |
|||
template.scope.forEach(() => { |
|||
(clientRegistration.get('scope') as FormArray).push(this.fb.control('')); |
|||
}); |
|||
clientRegistration.get('accessTokenUri').disable(); |
|||
clientRegistration.get('authorizationUri').disable(); |
|||
clientRegistration.get('jwkSetUri').disable(); |
|||
clientRegistration.get('userInfoUri').disable(); |
|||
clientRegistration.patchValue(template, {emitEvent: false}); |
|||
} |
|||
} |
|||
|
|||
private changeMapperConfigType(control: AbstractControl, type: MapperConfigType, predefinedValue?: MapperConfig) { |
|||
const mapperConfig = control.get('mapperConfig') as FormGroup; |
|||
if (type === MapperConfigType.BASIC) { |
|||
mapperConfig.removeControl('custom'); |
|||
mapperConfig.addControl('basic', this.formBasicGroup(predefinedValue?.basic)); |
|||
} else { |
|||
mapperConfig.removeControl('basic'); |
|||
mapperConfig.addControl('custom', this.formCustomGroup(predefinedValue?.custom)); |
|||
} |
|||
} |
|||
|
|||
save(): void { |
|||
const setting = this.prepareFormValue(this.oauth2SettingsForm.getRawValue()); |
|||
this.adminService.saveOAuth2Settings(setting).subscribe( |
|||
(oauth2Settings) => { |
|||
this.oauth2Settings = oauth2Settings; |
|||
this.oauth2SettingsForm.markAsPristine(); |
|||
this.oauth2SettingsForm.markAsUntouched(); |
|||
} |
|||
); |
|||
} |
|||
|
|||
private prepareFormValue(formValue: OAuth2Settings): OAuth2Settings{ |
|||
formValue.domainsParams.forEach((setting, index) => { |
|||
setting.clientRegistrations.forEach((registration) => { |
|||
registration.additionalInfo = JSON.stringify(registration.additionalInfo); |
|||
if (registration.id.id === null) { |
|||
delete registration.id; |
|||
} |
|||
}); |
|||
}); |
|||
return formValue; |
|||
} |
|||
|
|||
confirmForm(): FormGroup { |
|||
return this.oauth2SettingsForm; |
|||
} |
|||
|
|||
addScope(event: MatChipInputEvent, control: AbstractControl): void { |
|||
const input = event.input; |
|||
const value = event.value; |
|||
const controller = control.get('scope') as FormArray; |
|||
if ((value.trim() !== '')) { |
|||
controller.push(this.fb.control(value.trim())); |
|||
controller.markAsDirty(); |
|||
} |
|||
|
|||
if (input) { |
|||
input.value = ''; |
|||
} |
|||
} |
|||
|
|||
removeScope(i: number, control: AbstractControl): void { |
|||
const controller = control.get('scope') as FormArray; |
|||
controller.removeAt(i); |
|||
controller.markAsTouched(); |
|||
controller.markAsDirty(); |
|||
} |
|||
|
|||
addDomain(): void { |
|||
this.domainsParams.push(this.buildDomainsForm()); |
|||
} |
|||
|
|||
deleteDomain($event: Event, index: number): void { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
$event.preventDefault(); |
|||
} |
|||
|
|||
const domainName = this.domainListTittle(this.domainsParams.at(index)); |
|||
this.dialogService.confirm( |
|||
this.translate.instant('admin.oauth2.delete-domain-title', {domainName: domainName || ''}), |
|||
this.translate.instant('admin.oauth2.delete-domain-text'), null, |
|||
this.translate.instant('action.delete') |
|||
).subscribe((data) => { |
|||
if (data) { |
|||
this.domainsParams.removeAt(index); |
|||
this.domainsParams.markAsTouched(); |
|||
this.domainsParams.markAsDirty(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
clientDomainProviders(control: AbstractControl): FormArray { |
|||
return control.get('clientRegistrations') as FormArray; |
|||
} |
|||
|
|||
clientDomainInfos(control: AbstractControl): FormArray { |
|||
return control.get('domainInfos') as FormArray; |
|||
} |
|||
|
|||
addProvider(control: AbstractControl): void { |
|||
this.clientDomainProviders(control).push(this.buildProviderForm()); |
|||
} |
|||
|
|||
deleteProvider($event: Event, control: AbstractControl, index: number): void { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
$event.preventDefault(); |
|||
} |
|||
|
|||
const providerName = this.clientDomainProviders(control).at(index).get('additionalInfo.providerName').value; |
|||
this.dialogService.confirm( |
|||
this.translate.instant('admin.oauth2.delete-registration-title', {name: providerName || ''}), |
|||
this.translate.instant('admin.oauth2.delete-registration-text'), null, |
|||
this.translate.instant('action.delete') |
|||
).subscribe((data) => { |
|||
if (data) { |
|||
this.clientDomainProviders(control).removeAt(index); |
|||
this.clientDomainProviders(control).markAsTouched(); |
|||
this.clientDomainProviders(control).markAsDirty(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
toggleEditMode(control: AbstractControl, path: string) { |
|||
control.get(path).disabled ? control.get(path).enable() : control.get(path).disable(); |
|||
} |
|||
|
|||
getProviderName(controller: AbstractControl): string { |
|||
return controller.get('additionalInfo.providerName').value; |
|||
} |
|||
|
|||
getHelpLink(controller: AbstractControl): string { |
|||
const provider = controller.get('additionalInfo.providerName').value; |
|||
if (provider === null || provider === 'Custom') { |
|||
return ''; |
|||
} |
|||
return this.templates.get(provider).helpLink; |
|||
} |
|||
|
|||
addDomainInfo(control: AbstractControl): void { |
|||
this.clientDomainInfos(control).push(this.buildDomainForm({ |
|||
name: '', |
|||
scheme: DomainSchema.HTTPS |
|||
})); |
|||
} |
|||
|
|||
removeDomain($event: Event, control: AbstractControl, index: number): void { |
|||
if ($event) { |
|||
$event.stopPropagation(); |
|||
$event.preventDefault(); |
|||
} |
|||
this.clientDomainInfos(control).removeAt(index); |
|||
this.clientDomainInfos(control).markAsTouched(); |
|||
this.clientDomainInfos(control).markAsDirty(); |
|||
} |
|||
|
|||
redirectURI(control: AbstractControl, schema?: DomainSchema): string { |
|||
const domainInfo = control.value as DomainInfo; |
|||
if (domainInfo.name !== '') { |
|||
let protocol; |
|||
if (isDefined(schema)) { |
|||
protocol = schema.toLowerCase(); |
|||
} else { |
|||
protocol = domainInfo.scheme === DomainSchema.MIXED ? DomainSchema.HTTPS.toLowerCase() : domainInfo.scheme.toLowerCase(); |
|||
} |
|||
return `${protocol}://${domainInfo.name}/login/oauth2/code/`; |
|||
} |
|||
return ''; |
|||
} |
|||
|
|||
redirectURIMixed(control: AbstractControl): string { |
|||
return this.redirectURI(control, DomainSchema.HTTP); |
|||
} |
|||
|
|||
trackByParams(index: number): number { |
|||
return index; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue