package io.cattle.platform.iaas.api.auth.integration.external; import io.cattle.platform.api.auth.Identity; import io.cattle.platform.core.constants.ProjectConstants; import io.cattle.platform.core.model.Account; import io.cattle.platform.core.model.AuthToken; import io.cattle.platform.iaas.api.auth.SecurityConstants; import io.cattle.platform.iaas.api.auth.dao.AuthTokenDao; import io.cattle.platform.iaas.api.auth.identity.Token; import io.cattle.platform.json.JsonMapper; import io.cattle.platform.object.util.DataAccessor; import io.cattle.platform.token.TokenException; import io.cattle.platform.token.TokenService; import io.cattle.platform.util.type.CollectionUtils; import io.github.ibuildthecloud.gdapi.context.ApiContext; import io.github.ibuildthecloud.gdapi.exception.ClientVisibleException; import io.github.ibuildthecloud.gdapi.request.ApiRequest; import io.github.ibuildthecloud.gdapi.util.ResponseCodes; import java.io.IOException; import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.inject.Inject; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.http.HttpResponse; import org.apache.http.client.fluent.Request; import org.apache.http.conn.HttpHostConnectException; import org.apache.http.entity.ContentType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ExternalServiceAuthProvider { private static final Logger log = LoggerFactory.getLogger(ExternalServiceAuthProvider.class); @Inject private JsonMapper jsonMapper; @Inject TokenService tokenService; @Inject ExternalServiceTokenUtil tokenUtil; @Inject private AuthTokenDao authTokenDao; public Token getToken(ApiRequest request) { Map<String, Object> requestBody = CollectionUtils.toMap(request.getRequestObject()); String code = ObjectUtils.toString(requestBody.get(SecurityConstants.CODE)); //get the token from the auth service StringBuilder authUrl = new StringBuilder(ServiceAuthConstants.AUTH_SERVICE_URL.get()); authUrl.append("/token"); HttpResponse response; try { Map<String, String> data = new HashMap<String, String>(); data.put("code", code); String jsonString = jsonMapper.writeValueAsString(data); Request temp = Request.Post(authUrl.toString()) .addHeader(ServiceAuthConstants.ACCEPT, ServiceAuthConstants.APPLICATION_JSON) .bodyString(jsonString, ContentType.APPLICATION_JSON); response = temp.execute().returnResponse(); int statusCode = response.getStatusLine().getStatusCode(); if(statusCode >= 300) { log.error("Got error from Auth service. statusCode: {}", statusCode); throw new ClientVisibleException(ResponseCodes.SERVICE_UNAVAILABLE, ServiceAuthConstants.AUTH_ERROR, "Error Response from Auth service", "Status code from Auth Service: " + Integer.toString(statusCode)); } Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent()); String encryptedToken = (String)jsonData.get(ServiceAuthConstants.JWT_KEY); Map<String, Object> decryptedToken = tokenService.getJsonPayload(encryptedToken, false); String accessToken = (String)decryptedToken.get("access_token"); request.setAttribute(ServiceAuthConstants.ACCESS_TOKEN, accessToken); List<?> identityList = CollectionUtils.toList(jsonData.get("identities")); Set<Identity> identities = new HashSet<>(); if (identityList != null && !identityList.isEmpty()) { for(Object identity : identityList) { Map<String, Object> jsonIdentity = CollectionUtils.toMap(identity); identities.add(tokenUtil.jsonToIdentity(jsonIdentity)); } } Token token = tokenUtil.createToken(identities, null); return token; } catch(HttpHostConnectException ex) { log.error("Auth Service not reachable at [{}]", ServiceAuthConstants.AUTH_SERVICE_URL); return null; } catch (IOException e) { log.error("Failed to get token from Auth Service.", e); throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR, ServiceAuthConstants.AUTH_ERROR, "Failed to get Auth token.", null); } catch (TokenException e) { log.error("Failed to decrypt the token from Auth Service.", e); return null; } } public Token refreshToken(String accessToken) { //get the token from the auth service StringBuilder authUrl = new StringBuilder(ServiceAuthConstants.AUTH_SERVICE_URL.get()); authUrl.append("/token"); HttpResponse response; try { Map<String, String> data = new HashMap<String, String>(); data.put("accessToken", accessToken); String jsonString = jsonMapper.writeValueAsString(data); Request temp = Request.Post(authUrl.toString()).addHeader(ServiceAuthConstants.ACCEPT, ServiceAuthConstants.APPLICATION_JSON) .bodyString(jsonString, ContentType.APPLICATION_JSON); response = temp.execute().returnResponse(); int statusCode = response.getStatusLine().getStatusCode(); if(statusCode >= 300) { log.error("Got error from Auth service. statusCode: {}", statusCode); throw new ClientVisibleException(ResponseCodes.SERVICE_UNAVAILABLE, ServiceAuthConstants.AUTH_ERROR, "Error Response from Auth service", "Status code from Auth Service: " + Integer.toString(statusCode)); } Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent()); String encryptedToken = (String)jsonData.get(ServiceAuthConstants.JWT_KEY); Map<String, Object> decryptedToken = tokenService.getJsonPayload(encryptedToken, false); String newAccessToken = (String)decryptedToken.get("access_token"); ApiRequest request = ApiContext.getContext().getApiRequest(); request.setAttribute(ServiceAuthConstants.ACCESS_TOKEN, newAccessToken); List<?> identityList = CollectionUtils.toList(jsonData.get("identities")); Set<Identity> identities = new HashSet<>(); if (identityList != null && !identityList.isEmpty()) { for(Object identity : identityList) { Map<String, Object> jsonIdentity = CollectionUtils.toMap(identity); identities.add(tokenUtil.jsonToIdentity(jsonIdentity)); } } Token token = tokenUtil.createToken(identities, null); return token; } catch(HttpHostConnectException ex) { log.error("Auth Service not reachable at [{}]", ServiceAuthConstants.AUTH_SERVICE_URL); return null; } catch (IOException e) { log.error("Failed to get token from Auth Service.", e); throw new ClientVisibleException(ResponseCodes.INTERNAL_SERVER_ERROR, ServiceAuthConstants.AUTH_ERROR, "Failed to get Auth token.", null); } catch (TokenException e) { log.error("Failed to decrypt the token from Auth Service.", e); return null; } } public List<Identity> searchIdentities(String name, boolean exactMatch) { if (!isConfigured()) { return new ArrayList<Identity>(); } List<Identity> identities = new ArrayList<>(); StringBuilder authUrl = new StringBuilder(ServiceAuthConstants.AUTH_SERVICE_URL.get()); HttpResponse response; try { authUrl.append("/identities?name=").append(URLEncoder.encode(name, "UTF-8")); Request temp = Request.Get(authUrl.toString()).addHeader(ServiceAuthConstants.ACCEPT, ServiceAuthConstants.APPLICATION_JSON); String externalAccessToken = (String) ApiContext.getContext().getApiRequest().getAttribute(ServiceAuthConstants.ACCESS_TOKEN); String bearerToken = " Bearer "+ externalAccessToken; temp.addHeader(ServiceAuthConstants.AUTHORIZATION, bearerToken); response = temp.execute().returnResponse(); int statusCode = response.getStatusLine().getStatusCode(); if(statusCode >= 300) { log.error("searchIdentities: Got error from Auth service. statusCode: {}", statusCode); return identities; } Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent()); List<?> identityList = CollectionUtils.toList(jsonData.get("data")); if (identityList != null && !identityList.isEmpty()) { for(Object identity : identityList) { Map<String, Object> jsonIdentity = CollectionUtils.toMap(identity); identities.add(tokenUtil.jsonToIdentity(jsonIdentity)); } } } catch(HttpHostConnectException ex) { log.error("Auth Service not reachable at [{}]", ServiceAuthConstants.AUTH_SERVICE_URL); } catch (ClientVisibleException e) { log.error("Failed to search identities from Auth Service.", e); } catch (Exception e) { log.error("Failed to search identities from Auth Service.", e); } return identities; } public Identity getIdentity(String id, String scope) { if (!isConfigured()) { return null; } //check if the setting 'support.identity.lookup = false', if yes then lookup the identity from token if(ServiceAuthConstants.NO_IDENTITY_LOOKUP_SUPPORTED.get()) { log.debug("Identity lookup is not supported at the provider"); if (tokenUtil.findAndSetJWT()) { Set<Identity> identitiesInToken = tokenUtil.getIdentities(); log.debug("Found identitiesInToken {}" , identitiesInToken); for (Identity identity : identitiesInToken) { if(identity != null && id.equals(identity.getExternalId()) && scope.equals(identity.getExternalIdType())) { return identity; } } } } StringBuilder authUrl = new StringBuilder(ServiceAuthConstants.AUTH_SERVICE_URL.get()); HttpResponse response; try { authUrl.append("/identities?externalId=").append(URLEncoder.encode(id, "UTF-8")).append("&externalIdType=") .append(URLEncoder.encode(scope, "UTF-8")); Request temp = Request.Get(authUrl.toString()).addHeader(ServiceAuthConstants.ACCEPT, ServiceAuthConstants.APPLICATION_JSON); String externalAccessToken = (String) ApiContext.getContext().getApiRequest().getAttribute(ServiceAuthConstants.ACCESS_TOKEN); String bearerToken = " Bearer "+ externalAccessToken; temp.addHeader(ServiceAuthConstants.AUTHORIZATION, bearerToken); response = temp.execute().returnResponse(); int statusCode = response.getStatusLine().getStatusCode(); if(statusCode >= 300) { log.error("getIdentity: Got error from Auth service. statusCode: {}", statusCode); return null; } Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent()); return tokenUtil.jsonToIdentity(jsonData); } catch(HttpHostConnectException ex) { log.error("Auth Service not reachable at [{}]", ServiceAuthConstants.AUTH_SERVICE_URL); return null; } catch (IOException e) { log.error("Failed to get token from Auth Service.", e); return null; } } public Set<Identity> getIdentities(Account account) { if (!isConfigured()) { return new HashSet<>(); } String accessToken = (String) DataAccessor.fields(account).withKey(ServiceAuthConstants.ACCESS_TOKEN).get(); if (tokenUtil.findAndSetJWT()) { ApiRequest request = ApiContext.getContext().getApiRequest(); request.setAttribute(ServiceAuthConstants.ACCESS_TOKEN, accessToken); return tokenUtil.getIdentities(); } String jwt = null; if (!StringUtils.isBlank(accessToken) && SecurityConstants.SECURITY.get()) { AuthToken authToken = authTokenDao.getTokenByAccountId(account.getId()); if (authToken == null) { try { //refresh token API. Token token = refreshToken(accessToken); if (token != null) { jwt = ProjectConstants.AUTH_TYPE + token.getJwt(); authToken = authTokenDao.createToken(token.getJwt(), token.getAuthProvider(), account.getId()); jwt = authToken.getKey(); accessToken = (String) DataAccessor.fields(account).withKey(ServiceAuthConstants.ACCESS_TOKEN).get(); } } catch (ClientVisibleException e) { log.error("Got error from Auth service.error", e); return Collections.emptySet(); } } else { jwt = authToken.getKey(); } } if (StringUtils.isBlank(jwt)){ return Collections.emptySet(); } ApiRequest request = ApiContext.getContext().getApiRequest(); request.setAttribute(tokenUtil.tokenType(), jwt); request.setAttribute(ServiceAuthConstants.ACCESS_TOKEN, accessToken); return tokenUtil.getIdentities(); } public boolean isConfigured() { if (SecurityConstants.AUTH_PROVIDER.get() != null && !SecurityConstants.NO_PROVIDER.equalsIgnoreCase(SecurityConstants.AUTH_PROVIDER.get()) && !SecurityConstants.INTERNAL_AUTH_PROVIDERS.contains(SecurityConstants.AUTH_PROVIDER.get()) && ServiceAuthConstants.IS_EXTERNAL_AUTH_PROVIDER.get()) { return true; } return false; } public Identity untransform(Identity identity) { return identity; } public Identity transform(Identity identity) { return identity; } public String getRedirectUrl() { StringBuilder authUrl = new StringBuilder(ServiceAuthConstants.AUTH_SERVICE_URL.get()); authUrl.append("/redirectUrl"); HttpResponse response; try { Request temp = Request.Get(authUrl.toString()).addHeader(ServiceAuthConstants.ACCEPT, ServiceAuthConstants.APPLICATION_JSON); response = temp.execute().returnResponse(); int statusCode = response.getStatusLine().getStatusCode(); if(statusCode >= 300) { log.error("Got error from Auth service. statusCode: {}", statusCode); return ""; } Map<String, Object> jsonData = jsonMapper.readValue(response.getEntity().getContent()); if( jsonData != null && !jsonData.isEmpty()) { if (jsonData.containsKey("redirectUrl")) { return (String)jsonData.get("redirectUrl"); } }; } catch(HttpHostConnectException ex) { log.error("Auth Service not reachable at [{}]", ServiceAuthConstants.AUTH_SERVICE_URL); } catch (IOException e) { log.error("Failed to get the redirectUrl from Auth Service.", e); } return ""; } public Token readCurrentToken() { Token token = new Token(); token = tokenUtil.retrieveCurrentToken(); if (token != null) { String redirect = getRedirectUrl(); token.setRedirectUrl(redirect); } return token; } }