package com.emc.ecs.management.sdk; import com.emc.ecs.management.sdk.model.EcsManagementClientError; import com.emc.ecs.servicebroker.EcsManagementClientException; import com.emc.ecs.servicebroker.EcsManagementResourceNotFoundException; import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature; import org.glassfish.jersey.logging.LoggingFeature; import org.slf4j.LoggerFactory; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.Entity; import javax.ws.rs.client.Invocation.Builder; import javax.ws.rs.core.Response; import javax.ws.rs.core.UriBuilder; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.CertificateFactory; import java.util.logging.Level; import java.util.logging.Logger; import static com.emc.ecs.management.sdk.Constants.*; public class Connection { private static final org.slf4j.Logger logger = LoggerFactory.getLogger(Connection.class); private static final int AUTH_RETRIES_MAX = 3; private final String endpoint; private final String username; private final String password; private String authToken; private String certificate; private int authRetries = 0; public Connection(String endpoint, String username, String password) { super(); this.endpoint = endpoint; this.username = username; this.password = password; } public Connection(String endpoint, String username, String password, String certificate) { super(); this.endpoint = endpoint; this.username = username; this.password = password; this.certificate = certificate; } private static HostnameVerifier getHostnameVerifier() { return (hostname, session) -> true; } public String getAuthToken() { return authToken; } private Client buildJerseyClient() throws EcsManagementClientException { ClientBuilder builder; if (certificate != null) { // Disable host name verification. Should be able to configure the // ECS certificate with the correct host name to avoid this. HostnameVerifier hostnameVerifier = getHostnameVerifier(); HttpsURLConnection.setDefaultHostnameVerifier(hostnameVerifier); builder = ClientBuilder.newBuilder() .register(hostnameVerifier); builder.sslContext(getSSLContext()); } else { builder = ClientBuilder.newBuilder(); } return builder.build(); } private SSLContext getSSLContext() throws EcsManagementClientException { try { CertificateFactory certFactory; //InputStream targetStream = new ByteArrayInputStream(initialString.getBytes()); //InputStream certInputStream = certificate.openStream(); InputStream certInputStream = new ByteArrayInputStream(certificate.getBytes()); SSLContext sslContext = SSLContext.getInstance("TLS"); certFactory = CertificateFactory.getInstance("X.509"); Certificate caCert = certFactory .generateCertificate(certInputStream); TrustManagerFactory trustMgrFactory = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); keyStore.load(null); keyStore.setCertificateEntry("caCert", caCert); trustMgrFactory.init(keyStore); sslContext.init(null, trustMgrFactory.getTrustManagers(), null); return sslContext; } catch (Exception e) { throw new EcsManagementClientException(e); } } public boolean isLoggedIn() { return authToken != null; } public void login() throws EcsManagementClientException { UriBuilder uriBuilder = UriBuilder.fromPath(endpoint).segment("login"); logger.info("Logging into {} as {}", endpoint, username); HttpAuthenticationFeature authFeature = HttpAuthenticationFeature .basicBuilder().credentials(username, password).build(); Client jerseyClient = buildJerseyClient().register(authFeature); Builder request = jerseyClient.target(uriBuilder).request(); Response response = request.get(); try { handleResponse(response); } catch (EcsManagementResourceNotFoundException e) { logger.warn("Login failed to handle response: {}", e.getMessage()); logger.warn("Response: {}", response); throw new EcsManagementClientException(e); } this.authToken = response.getHeaderString("X-SDS-AUTH-TOKEN"); this.authRetries = 0; } public void logout() throws EcsManagementClientException { UriBuilder uri = UriBuilder.fromPath(endpoint).segment("logout") .queryParam("force", true); handleRemoteCall(GET, uri, null); this.authToken = null; } protected Response handleRemoteCall(String method, UriBuilder uri, Object arg) throws EcsManagementClientException { return handleRemoteCall(method, uri, arg, XML); } protected Response handleRemoteCall(String method, UriBuilder uri, Object arg, String contentType) throws EcsManagementClientException { Response response = makeRemoteCall(method, uri, arg, contentType); try { handleResponse(response); } catch (EcsManagementResourceNotFoundException e) { throw new EcsManagementClientException(e); } return response; } protected UriBuilder getUriBuilder() { return UriBuilder.fromPath(endpoint); } protected Response makeRemoteCall(String method, UriBuilder uri, Object arg) throws EcsManagementClientException { return makeRemoteCall(method, uri, arg, XML); } protected Response makeRemoteCall(String method, UriBuilder uri, Object arg, String contentType) throws EcsManagementClientException { try { if (!isLoggedIn()) login(); logger.info("Making {} request to {}", method, uri); Client jerseyClient = buildJerseyClient(); Builder request = jerseyClient.target(uri) .register(LoggingFeature.class).request() .header("X-SDS-AUTH-TOKEN", authToken) .header("Accept", "application/xml"); Response response = null; if (GET.equals(method)) { response = request.get(); } else if (POST.equals(method) || PUT.equals(method)) { Entity<Object> objectEntity; if (XML.equals(contentType)) { objectEntity = Entity.xml(arg); } else if (JSON.equals(contentType)) { objectEntity = Entity.json(arg); } else { throw new EcsManagementClientException("Content type must be \"XML\" or \"JSON\""); } if (POST.equals(method)) { response = request.post(objectEntity); } else if (PUT.equals(method)) { response = request.put(objectEntity); } } else if (DELETE.equals(method)) { response = request.delete(); } else { throw new EcsManagementClientException( "Invalid request method: " + method); } if (response.getStatus() == 401 && authRetries < AUTH_RETRIES_MAX) { // attempt to re-authorize and retry up to _authMaxRetries_ times. authRetries += 1; this.authToken = null; response = makeRemoteCall(method, uri, arg, XML); } return response; } catch (Exception e) { logger.warn("Failed to make a call to {}: {}", uri, e.getMessage()); throw e; } } protected boolean existenceQuery(UriBuilder uri, Object arg) throws EcsManagementClientException { Response response = makeRemoteCall(GET, uri, arg, XML); try { handleResponse(response); } catch (EcsManagementResourceNotFoundException e) { Logger.getAnonymousLogger().log(Level.FINE, "info", e); return false; } return true; } private void handleResponse(Response response) throws EcsManagementClientException, EcsManagementResourceNotFoundException { if (response.getStatus() > 399) { EcsManagementClientError error = response .readEntity(EcsManagementClientError.class); if (response.getStatus() == 404) { throw new EcsManagementResourceNotFoundException( response.getStatusInfo().toString()); } else if (error.getCode() == 1004) { throw new EcsManagementResourceNotFoundException( error.toString()); } else { throw new EcsManagementClientException(error.toString()); } } } public String getCertificate() { return certificate; } public void setCertificate(String certificate) { this.certificate = certificate; } }