/* * Copyright (c) 2014 EMC Corporation * All Rights Reserved */ package com.emc.storageos.security.helpers; import java.net.URI; import java.util.ArrayList; import java.util.List; import javax.crypto.SecretKey; import com.sun.jersey.api.client.filter.ClientFilter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.emc.storageos.coordinator.client.service.CoordinatorClient; import com.sun.jersey.api.client.Client; import com.sun.jersey.api.client.WebResource; import com.sun.jersey.api.client.WebResource.Builder; import com.emc.storageos.security.authentication.InternalApiSignatureKeyGenerator; import com.emc.storageos.security.authentication.InternalApiSignatureKeyGenerator.SignatureKeyType; import com.emc.storageos.security.exceptions.SecurityException; /** * Abstract class for building ViPR REST clients. This class provides help * in maintaining Client state, methods for constructing requests, and * adding HMAC signatures and tokens. */ public abstract class BaseServiceClient implements AutoCloseable { private static final Logger log = LoggerFactory.getLogger(BaseServiceClient.class); private volatile boolean initialized = false; private ClientRequestHelper clientRequestHelper; private Client client; private int clientMaxRetries = 3; private int clientRetryInterval = 5000; // 5s private int clientReadTimeout = ClientRequestHelper.DEFAULT_CONNECT_TIMEOUT; private int clientConnectTimeout = ClientRequestHelper.DEFAULT_READ_TIMEOUT; private URI serviceURI; private List<ClientFilter> filters = new ArrayList<>(); private SignatureKeyType defaultSignatureType = SignatureKeyType.INTERNAL_API; // the internal clientRequestHelper needs a InternalApiKeyGenerator to function. If one is not available, // the coordinatorClient will be used to create one. Else the coordinator client will be ignored in favor // of an InternalApiKeyGenerator bean already instantiated and maintained by the container private CoordinatorClient coordinatorClient; private InternalApiSignatureKeyGenerator keyGen; public URI getServiceURI() { return serviceURI; } public void setServiceURI(URI serviceURI) { this.serviceURI = serviceURI; } public CoordinatorClient getCoordinatorClient() { return coordinatorClient; } public void setCoordinatorClient(CoordinatorClient coordinatorClient) { this.coordinatorClient = coordinatorClient; } public void setKeyGenerator(InternalApiSignatureKeyGenerator keyGen) { this.keyGen = keyGen; } public void setClientReadTimeout(int clientReadTimeout) { this.clientReadTimeout = clientReadTimeout; } public void setClientConnectTimeout(int clientConnectTimeout) { this.clientConnectTimeout = clientConnectTimeout; } public void setClientMaxRetries(int clientMaxRetries) { this.clientMaxRetries = clientMaxRetries; } public int getClientMaxRetries() { return this.clientMaxRetries; } public void setClientRetryInterval(int clientRetryInterval) { this.clientRetryInterval = clientRetryInterval; } public int getClientRetryInterval() { return this.clientRetryInterval; } public SignatureKeyType getDefaultSignatureType() { return defaultSignatureType; } public void setDefaultSignatureType(SignatureKeyType defaultSignatureType) { this.defaultSignatureType = defaultSignatureType; if (clientRequestHelper != null) { clientRequestHelper.setDefaultSignatureType(defaultSignatureType); } } public void addFilter(ClientFilter filter) { filters.add(filter); } /** * Shut down this client and release associated resources */ public void shutdown() { if (client != null) { client.destroy(); client = null; } } /** * Wrapper for setServiceURI that builds out the full URI from a host or IP * * @param server the host or IP address for the service we want to call */ public abstract void setServer(String server); /** * Create a request object for the specified path, resolved against * the service base URI and using the appropriate client configuration * * @param uriPath the path segment of the request (e.g. /vdc/storage-systems) * @return the request object */ protected WebResource createRequest(String uriPath) { ensureInitialization(); return clientRequestHelper.createRequest(client, serviceURI, URI.create(uriPath)); } /** * Create a request object for the specified path, resolved against * the service base URI and using the appropriate client configuration * * @param uriPath the path segment of the request (e.g. /vdc/storage-systems) * @return the request object */ protected WebResource createRequest(URI uriPath) { ensureInitialization(); return clientRequestHelper.createRequest(client, serviceURI, uriPath); } /** * Add the HMAC authentication headers to a request and return * a builder for additional manipulation * * @param webResource the request to sign */ protected Builder addSignature(WebResource webResource) { ensureInitialization(); return clientRequestHelper.addSignature(webResource); } /** * Add the HMAC authentication headers to a request and return * a builder for additional manipulation. * This version accepts a supplied key to override default behavior * (which uses the internal api key) * * @param webResource the request to sign * @param key secret key to compute the signature with */ protected Builder addSignature(WebResource webResource, SecretKey key) { ensureInitialization(); return clientRequestHelper.addSignature(webResource, key); } /** * Add a user token to the returned request builder * * @param requestBuilder the request builder * @param token the user token * @return the builder with token added */ protected Builder addToken(Builder requestBuilder, String token) { ensureInitialization(); return clientRequestHelper.addTokens(requestBuilder, token, null); } /** * Add user and/or proxy tokens to the returned request builder * * @param requestBuilder the request builder * @param token the user token (or null) * @param proxyToken the proxy token (or null) * @return the builder with token(s) added */ protected Builder addTokens(Builder requestBuilder, String token, String proxyToken) { ensureInitialization(); return clientRequestHelper.addTokens(requestBuilder, token, proxyToken); } private void ensureInitialization() { if (!initialized) { synchronized (this) { if (!initialized) { if ((serviceURI == null) || (coordinatorClient == null && keyGen == null)) { throw SecurityException.fatals.failedToInitializeClientRequestHelper(serviceURI == null ? "serviceURI was null" : serviceURI.toString(), (coordinatorClient == null && keyGen == null) ? "both coordinatorClient and keyGenerator were null" : "coordinatorClient or kenGenerator was provided"); } else { if (keyGen == null) { clientRequestHelper = new ClientRequestHelper(coordinatorClient, this.clientReadTimeout, this.clientConnectTimeout); } else { clientRequestHelper = new ClientRequestHelper(keyGen, this.clientReadTimeout, this.clientConnectTimeout); } clientRequestHelper.setDefaultSignatureType(defaultSignatureType); // create and save a client for re-use, since it's an expensive object // and concurrent request creation is thread-safe client = clientRequestHelper.createClient(); if (filters.isEmpty()) { client.addFilter(new ServiceClientRetryFilter(clientMaxRetries, clientRetryInterval)); } else { for (ClientFilter filter : filters) { client.addFilter(filter); } } initialized = true; } } } } } @Override public void close() { this.shutdown(); } }