Browse Source
* Added base impl for OAuth-2 * Added basic and custom OAuth2 user mappers * Removed comment line * Refactoring to review. Added tenantId and customerId. Added email tenant name strategy * Revert debug logger * Fixed compilation * Test fixed * Create UI for OAuthService * Revert package-lock.json * Add translate login es_ES Co-authored-by: Vladyslav_Prykhodko <vprykhodko@thingsboard.io>pull/2729/head
committed by
GitHub
48 changed files with 1117 additions and 54 deletions
@ -0,0 +1,128 @@ |
|||
/** |
|||
* 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.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; |
|||
import org.springframework.security.core.userdetails.UsernameNotFoundException; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.Customer; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.page.PageLink; |
|||
import org.thingsboard.server.common.data.security.Authority; |
|||
import org.thingsboard.server.dao.customer.CustomerService; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2User; |
|||
import org.thingsboard.server.dao.tenant.TenantService; |
|||
import org.thingsboard.server.dao.user.UserService; |
|||
import org.thingsboard.server.service.security.model.SecurityUser; |
|||
import org.thingsboard.server.service.security.model.UserPrincipal; |
|||
|
|||
import java.util.List; |
|||
import java.util.Optional; |
|||
import java.util.concurrent.locks.Lock; |
|||
import java.util.concurrent.locks.ReentrantLock; |
|||
|
|||
@Slf4j |
|||
public abstract class AbstractOAuth2ClientMapper { |
|||
|
|||
@Autowired |
|||
private UserService userService; |
|||
|
|||
@Autowired |
|||
private TenantService tenantService; |
|||
|
|||
@Autowired |
|||
private CustomerService customerService; |
|||
|
|||
private final Lock userCreationLock = new ReentrantLock(); |
|||
|
|||
protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, boolean allowUserCreation) { |
|||
UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail()); |
|||
|
|||
User user = userService.findUserByEmail(TenantId.SYS_TENANT_ID, oauth2User.getEmail()); |
|||
|
|||
if (user == null && !allowUserCreation) { |
|||
throw new UsernameNotFoundException("User not found: " + oauth2User.getEmail()); |
|||
} |
|||
|
|||
if (user == null) { |
|||
userCreationLock.lock(); |
|||
try { |
|||
user = userService.findUserByEmail(TenantId.SYS_TENANT_ID, oauth2User.getEmail()); |
|||
if (user == null) { |
|||
user = new User(); |
|||
if (oauth2User.getCustomerId() == null && StringUtils.isEmpty(oauth2User.getCustomerName())) { |
|||
user.setAuthority(Authority.TENANT_ADMIN); |
|||
} else { |
|||
user.setAuthority(Authority.CUSTOMER_USER); |
|||
} |
|||
TenantId tenantId = oauth2User.getTenantId() != null ? |
|||
oauth2User.getTenantId() : getTenantId(oauth2User.getTenantName()); |
|||
user.setTenantId(tenantId); |
|||
CustomerId customerId = oauth2User.getCustomerId() != null ? |
|||
oauth2User.getCustomerId() : getCustomerId(user.getTenantId(), oauth2User.getCustomerName()); |
|||
user.setCustomerId(customerId); |
|||
user.setEmail(oauth2User.getEmail()); |
|||
user.setFirstName(oauth2User.getFirstName()); |
|||
user.setLastName(oauth2User.getLastName()); |
|||
user = userService.saveUser(user); |
|||
} |
|||
} finally { |
|||
userCreationLock.unlock(); |
|||
} |
|||
} |
|||
|
|||
try { |
|||
SecurityUser securityUser = new SecurityUser(user, true, principal); |
|||
return (SecurityUser) new UsernamePasswordAuthenticationToken(securityUser, null, securityUser.getAuthorities()).getPrincipal(); |
|||
} catch (Exception e) { |
|||
log.error("Can't get or create security user from oauth2 user", e); |
|||
throw new RuntimeException("Can't get or create security user from oauth2 user", e); |
|||
} |
|||
} |
|||
|
|||
private TenantId getTenantId(String tenantName) { |
|||
List<Tenant> tenants = tenantService.findTenants(new PageLink(1, 0, tenantName)).getData(); |
|||
Tenant tenant; |
|||
if (tenants == null || tenants.isEmpty()) { |
|||
tenant = new Tenant(); |
|||
tenant.setTitle(tenantName); |
|||
tenant = tenantService.saveTenant(tenant); |
|||
} else { |
|||
tenant = tenants.get(0); |
|||
} |
|||
return tenant.getTenantId(); |
|||
} |
|||
|
|||
private CustomerId getCustomerId(TenantId tenantId, String customerName) { |
|||
if (StringUtils.isEmpty(customerName)) { |
|||
return null; |
|||
} |
|||
Optional<Customer> customerOpt = customerService.findCustomerByTenantIdAndTitle(tenantId, customerName); |
|||
if (customerOpt.isPresent()) { |
|||
return customerOpt.get().getId(); |
|||
} else { |
|||
Customer customer = new Customer(); |
|||
customer.setTenantId(tenantId); |
|||
customer.setTitle(customerName); |
|||
return customerService.saveCustomer(customer).getId(); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,87 @@ |
|||
/** |
|||
* 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.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2ClientMapperConfig; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2User; |
|||
import org.thingsboard.server.service.security.model.SecurityUser; |
|||
|
|||
import java.util.Map; |
|||
|
|||
@Service(value = "basicOAuth2ClientMapper") |
|||
@Slf4j |
|||
public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { |
|||
|
|||
private static final String START_PLACEHOLDER_PREFIX = "%{"; |
|||
private static final String END_PLACEHOLDER_PREFIX = "}"; |
|||
private static final String EMAIL_TENANT_STRATEGY = "email"; |
|||
private static final String DOMAIN_TENANT_STRATEGY = "domain"; |
|||
private static final String CUSTOM_TENANT_STRATEGY = "custom"; |
|||
|
|||
@Override |
|||
public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig config) { |
|||
OAuth2User oauth2User = new OAuth2User(); |
|||
Map<String, Object> attributes = token.getPrincipal().getAttributes(); |
|||
String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); |
|||
oauth2User.setEmail(email); |
|||
oauth2User.setTenantName(getTenantName(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().getCustomerNameStrategyPattern())) { |
|||
StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); |
|||
String customerName = sub.replace(config.getBasic().getCustomerNameStrategyPattern()); |
|||
oauth2User.setCustomerName(customerName); |
|||
} |
|||
return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.getBasic().isAllowUserCreation()); |
|||
} |
|||
|
|||
private String getTenantName(Map<String, Object> attributes, OAuth2ClientMapperConfig config) { |
|||
switch (config.getBasic().getTenantNameStrategy()) { |
|||
case EMAIL_TENANT_STRATEGY: |
|||
return getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); |
|||
case DOMAIN_TENANT_STRATEGY: |
|||
String email = getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); |
|||
return email.substring(email .indexOf("@") + 1); |
|||
case CUSTOM_TENANT_STRATEGY: |
|||
StrSubstitutor sub = new StrSubstitutor(attributes, START_PLACEHOLDER_PREFIX, END_PLACEHOLDER_PREFIX); |
|||
return sub.replace(config.getBasic().getTenantNameStrategyPattern()); |
|||
default: |
|||
throw new RuntimeException("Tenant Name Strategy with type " + config.getBasic().getTenantNameStrategy() + " is not supported!"); |
|||
} |
|||
} |
|||
|
|||
private 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,63 @@ |
|||
/** |
|||
* 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 com.fasterxml.jackson.core.JsonProcessingException; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.boot.web.client.RestTemplateBuilder; |
|||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
|||
import org.springframework.stereotype.Service; |
|||
import org.springframework.util.StringUtils; |
|||
import org.springframework.web.client.RestTemplate; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2ClientMapperConfig; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2User; |
|||
import org.thingsboard.server.service.security.model.SecurityUser; |
|||
|
|||
@Service(value = "customOAuth2ClientMapper") |
|||
@Slf4j |
|||
public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { |
|||
|
|||
private static final ObjectMapper json = new ObjectMapper(); |
|||
|
|||
private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); |
|||
|
|||
@Override |
|||
public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig config) { |
|||
OAuth2User oauth2User = getOAuth2User(token, config.getCustom()); |
|||
return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.getBasic().isAllowUserCreation()); |
|||
} |
|||
|
|||
public OAuth2User getOAuth2User(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig.CustomOAuth2ClientMapperConfig custom) { |
|||
if (!StringUtils.isEmpty(custom.getUsername()) && !StringUtils.isEmpty(custom.getPassword())) { |
|||
restTemplateBuilder = restTemplateBuilder.basicAuthentication(custom.getUsername(), custom.getPassword()); |
|||
} |
|||
RestTemplate restTemplate = restTemplateBuilder.build(); |
|||
String request; |
|||
try { |
|||
request = json.writeValueAsString(token.getPrincipal()); |
|||
} catch (JsonProcessingException e) { |
|||
log.error("Can't convert principal to JSON string", e); |
|||
throw new RuntimeException("Can't convert principal to JSON string", e); |
|||
} |
|||
try { |
|||
return restTemplate.postForEntity(custom.getUrl(), request, OAuth2User.class).getBody(); |
|||
} catch (Exception e) { |
|||
log.error("Can't connect to custom mapper endpoint", e); |
|||
throw new RuntimeException("Can't connect to custom mapper endpoint", e); |
|||
} |
|||
} |
|||
} |
|||
@ -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.service.security.auth.oauth2; |
|||
|
|||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2ClientMapperConfig; |
|||
import org.thingsboard.server.service.security.model.SecurityUser; |
|||
|
|||
public interface OAuth2ClientMapper { |
|||
SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, OAuth2ClientMapperConfig config); |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
/** |
|||
* 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.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Qualifier; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Component |
|||
@Slf4j |
|||
public class OAuth2ClientMapperProvider { |
|||
|
|||
@Autowired |
|||
@Qualifier("basicOAuth2ClientMapper") |
|||
private OAuth2ClientMapper basicOAuth2ClientMapper; |
|||
|
|||
@Autowired |
|||
@Qualifier("customOAuth2ClientMapper") |
|||
private OAuth2ClientMapper customOAuth2ClientMapper; |
|||
|
|||
public OAuth2ClientMapper getOAuth2ClientMapperByType(String oauth2ClientType) { |
|||
switch (oauth2ClientType) { |
|||
case "custom": |
|||
return customOAuth2ClientMapper; |
|||
case "basic": |
|||
return basicOAuth2ClientMapper; |
|||
default: |
|||
throw new RuntimeException("OAuth2ClientMapper with type " + oauth2ClientType + " is not supported!"); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
/** |
|||
* 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 org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.security.core.Authentication; |
|||
import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
|||
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; |
|||
import org.springframework.stereotype.Component; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2Client; |
|||
import org.thingsboard.server.dao.oauth2.OAuth2Configuration; |
|||
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRepository; |
|||
import org.thingsboard.server.service.security.model.SecurityUser; |
|||
import org.thingsboard.server.service.security.model.token.JwtToken; |
|||
import org.thingsboard.server.service.security.model.token.JwtTokenFactory; |
|||
|
|||
import javax.servlet.http.HttpServletRequest; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
|
|||
@Component(value = "oauth2AuthenticationSuccessHandler") |
|||
@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") |
|||
public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { |
|||
|
|||
private final JwtTokenFactory tokenFactory; |
|||
private final RefreshTokenRepository refreshTokenRepository; |
|||
private final OAuth2ClientMapperProvider oauth2ClientMapperProvider; |
|||
private final OAuth2Configuration oauth2Configuration; |
|||
|
|||
@Autowired |
|||
public Oauth2AuthenticationSuccessHandler(final JwtTokenFactory tokenFactory, |
|||
final RefreshTokenRepository refreshTokenRepository, |
|||
final OAuth2ClientMapperProvider oauth2ClientMapperProvider, |
|||
final OAuth2Configuration oauth2Configuration) { |
|||
this.tokenFactory = tokenFactory; |
|||
this.refreshTokenRepository = refreshTokenRepository; |
|||
this.oauth2ClientMapperProvider = oauth2ClientMapperProvider; |
|||
this.oauth2Configuration = oauth2Configuration; |
|||
} |
|||
|
|||
@Override |
|||
public void onAuthenticationSuccess(HttpServletRequest request, |
|||
HttpServletResponse response, |
|||
Authentication authentication) throws IOException { |
|||
OAuth2AuthenticationToken token = (OAuth2AuthenticationToken) authentication; |
|||
|
|||
OAuth2Client oauth2Client = oauth2Configuration.getClientByRegistrationId(token.getAuthorizedClientRegistrationId()); |
|||
OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(oauth2Client.getMapperConfig().getType()); |
|||
SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oauth2Client.getMapperConfig()); |
|||
|
|||
JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); |
|||
JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); |
|||
|
|||
getRedirectStrategy().sendRedirect(request, response, "/?accessToken=" + accessToken.getToken() + "&refreshToken=" + refreshToken.getToken()); |
|||
} |
|||
} |
|||
@ -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.OAuth2ClientInfo; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface OAuth2Service { |
|||
|
|||
List<OAuth2ClientInfo> getOAuth2Clients(); |
|||
} |
|||
@ -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 lombok.Data; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
|
|||
@Data |
|||
public class OAuth2User { |
|||
private String tenantName; |
|||
private TenantId tenantId; |
|||
private String customerName; |
|||
private CustomerId customerId; |
|||
private String email; |
|||
private String firstName; |
|||
private String lastName; |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
/** |
|||
* 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 OAuth2IntegrationId extends UUIDBased { |
|||
|
|||
private static final long serialVersionUID = 1L; |
|||
|
|||
@JsonCreator |
|||
public OAuth2IntegrationId(@JsonProperty("id") UUID id) { |
|||
super(id); |
|||
} |
|||
|
|||
public static OAuth2IntegrationId fromString(String oauth2IntegrationId) { |
|||
return new OAuth2IntegrationId(UUID.fromString(oauth2IntegrationId)); |
|||
} |
|||
} |
|||
@ -0,0 +1,45 @@ |
|||
/** |
|||
* 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 org.thingsboard.server.common.data.BaseData; |
|||
import org.thingsboard.server.common.data.id.OAuth2IntegrationId; |
|||
|
|||
@EqualsAndHashCode(callSuper = true) |
|||
@Data |
|||
public class OAuth2ClientInfo extends BaseData<OAuth2IntegrationId> { |
|||
|
|||
private String name; |
|||
private String icon; |
|||
private String url; |
|||
|
|||
public OAuth2ClientInfo() { |
|||
super(); |
|||
} |
|||
|
|||
public OAuth2ClientInfo(OAuth2IntegrationId id) { |
|||
super(id); |
|||
} |
|||
|
|||
public OAuth2ClientInfo(OAuth2ClientInfo oauth2ClientInfo) { |
|||
super(oauth2ClientInfo); |
|||
this.name = oauth2ClientInfo.getName(); |
|||
this.icon = oauth2ClientInfo.getIcon(); |
|||
this.url = oauth2ClientInfo.getUrl(); |
|||
} |
|||
} |
|||
@ -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.dao.oauth2; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class OAuth2Client { |
|||
|
|||
private String loginButtonLabel; |
|||
private String loginButtonIcon; |
|||
private String clientName; |
|||
private String clientId; |
|||
private String clientSecret; |
|||
private String accessTokenUri; |
|||
private String authorizationUri; |
|||
private String scope; |
|||
private String redirectUriTemplate; |
|||
private String jwkSetUri; |
|||
private String authorizationGrantType; |
|||
private String clientAuthenticationMethod; |
|||
private String userInfoUri; |
|||
private String userNameAttributeName; |
|||
private OAuth2ClientMapperConfig mapperConfig; |
|||
|
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
/** |
|||
* 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 String type; |
|||
private BasicOAuth2ClientMapperConfig basic; |
|||
private CustomOAuth2ClientMapperConfig custom; |
|||
|
|||
@Data |
|||
public static class BasicOAuth2ClientMapperConfig { |
|||
private boolean allowUserCreation; |
|||
private String emailAttributeKey; |
|||
private String firstNameAttributeKey; |
|||
private String lastNameAttributeKey; |
|||
private String tenantNameStrategy; |
|||
private String tenantNameStrategyPattern; |
|||
private String customerNameStrategyPattern; |
|||
} |
|||
|
|||
@Data |
|||
public static class CustomOAuth2ClientMapperConfig { |
|||
private String url; |
|||
private String username; |
|||
private String password; |
|||
} |
|||
} |
|||
@ -0,0 +1,82 @@ |
|||
/** |
|||
* 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; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
import org.springframework.security.oauth2.client.registration.ClientRegistration; |
|||
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; |
|||
import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; |
|||
import org.springframework.security.oauth2.core.AuthorizationGrantType; |
|||
import org.springframework.security.oauth2.core.ClientAuthenticationMethod; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
@Configuration |
|||
@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true") |
|||
@ConfigurationProperties(prefix = "security.oauth2") |
|||
@Data |
|||
@Slf4j |
|||
public class OAuth2Configuration { |
|||
|
|||
private boolean enabled; |
|||
private String loginProcessingUrl; |
|||
private Map<String, OAuth2Client> clients = new HashMap<>(); |
|||
|
|||
@Bean |
|||
public ClientRegistrationRepository clientRegistrationRepository() { |
|||
List<ClientRegistration> result = new ArrayList<>(); |
|||
for (Map.Entry<String, OAuth2Client> entry : clients.entrySet()) { |
|||
OAuth2Client client = entry.getValue(); |
|||
ClientRegistration registration = ClientRegistration.withRegistrationId(entry.getKey()) |
|||
.clientId(client.getClientId()) |
|||
.authorizationUri(client.getAuthorizationUri()) |
|||
.clientSecret(client.getClientSecret()) |
|||
.tokenUri(client.getAccessTokenUri()) |
|||
.redirectUriTemplate(client.getRedirectUriTemplate()) |
|||
.scope(client.getScope().split(",")) |
|||
.clientName(client.getClientName()) |
|||
.authorizationGrantType(new AuthorizationGrantType(client.getAuthorizationGrantType())) |
|||
.userInfoUri(client.getUserInfoUri()) |
|||
.userNameAttributeName(client.getUserNameAttributeName()) |
|||
.jwkSetUri(client.getJwkSetUri()) |
|||
.clientAuthenticationMethod(new ClientAuthenticationMethod(client.getClientAuthenticationMethod())) |
|||
.build(); |
|||
result.add(registration); |
|||
} |
|||
return new InMemoryClientRegistrationRepository(result); |
|||
} |
|||
|
|||
public OAuth2Client getClientByRegistrationId(String registrationId) { |
|||
OAuth2Client result = null; |
|||
if (clients != null && !clients.isEmpty()) { |
|||
for (String key : clients.keySet()) { |
|||
if (key.equals(registrationId)) { |
|||
result = clients.get(key); |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return result; |
|||
} |
|||
} |
|||
@ -0,0 +1,50 @@ |
|||
/** |
|||
* 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.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
import org.thingsboard.server.common.data.oauth2.OAuth2ClientInfo; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
@Slf4j |
|||
@Service |
|||
public class OAuth2ServiceImpl implements OAuth2Service { |
|||
|
|||
@Autowired(required = false) |
|||
OAuth2Configuration oauth2Configuration; |
|||
|
|||
@Override |
|||
public List<OAuth2ClientInfo> getOAuth2Clients() { |
|||
if (oauth2Configuration == null || !oauth2Configuration.isEnabled()) { |
|||
return Collections.emptyList(); |
|||
} |
|||
List<OAuth2ClientInfo> result = new ArrayList<>(); |
|||
for (Map.Entry<String, OAuth2Client> entry : oauth2Configuration.getClients().entrySet()) { |
|||
OAuth2ClientInfo client = new OAuth2ClientInfo(); |
|||
client.setName(entry.getValue().getLoginButtonLabel()); |
|||
client.setUrl(String.format("/oauth2/authorization/%s", entry.getKey())); |
|||
client.setIcon(entry.getValue().getLoginButtonIcon()); |
|||
result.add(client); |
|||
} |
|||
return result; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue