16 changed files with 216 additions and 55 deletions
@ -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; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue