package com.sequenceiq.cloudbreak.client; import static javax.ws.rs.core.Response.Status.fromStatusCode; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.net.ssl.SSLHandshakeException; import javax.ws.rs.ProcessingException; import javax.ws.rs.client.Entity; import javax.ws.rs.client.WebTarget; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedHashMap; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.Response; import org.apache.commons.codec.binary.Base64; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class IdentityClient { private static final Logger LOGGER = LoggerFactory.getLogger(IdentityClient.class); private static final Pattern LOCATION_PATTERN = Pattern.compile(".*access_token=(.*)\\&expires_in=(\\d*)\\&scope=.*"); private final String identityServerAddress; private final String clientId; private final WebTarget authorizeWebTarget; private final WebTarget tokenWebTarget; public IdentityClient(String identityServerAddress, String clientId, ConfigKey configKey) { this.identityServerAddress = identityServerAddress; this.clientId = clientId; WebTarget identityWebTarget = RestClientUtil.get(configKey).target(identityServerAddress); authorizeWebTarget = identityWebTarget.path("/oauth/authorize").queryParam("response_type", "token").queryParam("client_id", clientId); tokenWebTarget = identityWebTarget.path("/oauth/token").queryParam("grant_type", "client_credentials"); LOGGER.info("IdentityClient has been created. identity: {}, clientId: {}, configKey: {}", identityServerAddress, clientId, configKey); } public AccessToken getToken(String user, String password) { MultivaluedMap<String, String> formData = new MultivaluedHashMap<>(); formData.add("credentials", String.format("{\"username\":\"%s\",\"password\":\"%s\"}", user, password.replace("\\", "\\\\").replace("\"", "\\\""))); try { Response resp = authorizeWebTarget.request().accept(MediaType.APPLICATION_FORM_URLENCODED_TYPE).post(Entity.form(formData)); String token; int exp; switch (fromStatusCode(resp.getStatus())) { case FOUND: String location = resp.getHeaderString("Location"); Matcher m = LOCATION_PATTERN.matcher(location); if (m.matches()) { token = m.group(1); exp = Integer.parseInt(m.group(2)); } else { throw new TokenUnavailableException(String.format("Failed to parse access token from the identity server, check its configuration! " + "Raw Location response: %s", location)); } break; default: throw new TokenUnavailableException(String.format("Couldn't get an access token from the identity server, check its configuration!" + " Perhaps %s is not autoapproved? Response headers: %s", clientId, resp.getHeaders())); } return new AccessToken(token, "bearer", exp); } catch (ProcessingException e) { if (e.getCause() instanceof SSLHandshakeException) { throw new SSLConnectionException(String.format("Failed to connect (%s) due to SSL handshake error.", identityServerAddress), e); } throw new TokenUnavailableException("Error occurred while getting token from identity server", e); } catch (TokenUnavailableException e) { throw e; } catch (Exception e) { throw new TokenUnavailableException("Error occurred while getting token from identity server", e); } } public AccessToken getToken(String secret) { try { MultivaluedMap<String, Object> headers = new MultivaluedHashMap<>(); headers.add("Authorization", "Basic " + Base64.encodeBase64String((clientId + ":" + secret).getBytes())); return tokenWebTarget.request().accept(MediaType.APPLICATION_JSON_TYPE).headers(headers).post(Entity.json(null), AccessToken.class); } catch (Exception e) { throw new TokenUnavailableException("Error occurred while getting token from identity server", e); } } }