@@ -1,15 +1,14 @@ /** * Copyright 2015 Brightcove Inc. All rights reserved. * * @author Scott Kidder */ package com.brightcove.castlabs.client; import java.io.IOException; import java.util.ArrayList; import java.util.List; import com.google.common.collect.Lists; import org.apache.commons.io.IOUtils; import org.apache.commons.lang.StringUtils; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.NameValuePair; Expand All @@ -24,14 +23,13 @@ import com.brightcove.castlabs.client.request.IngestKeysRequest; import com.brightcove.castlabs.client.response.IngestAssetsResponse; import com.brightcove.castlabs.client.request.AddSubMerchantAccountRequest; import com.brightcove.castlabs.client.response.AddSubMerchantAccountResponse; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; /** * Client for interacting with the Castlabs key ingestion API. * * @author Scott Kidder * * Client for interacting with the Castlabs API. */ public class CastlabsClient { Expand All @@ -44,12 +42,11 @@ public class CastlabsClient { private int connectionTimeoutSeconds = -1; private ObjectMapper objectMapper; public CastlabsClient(String username, String password) { public CastlabsClient(final String username, final String password) { this(username, password, CASTLABS_AUTH_BASE_URL, CASTLABS_INGESTION_BASE_URL, -1); } public CastlabsClient(String username, String password, String authBaseUrl, String ingestionBaseUrl, int connectionTimeoutSeconds) { public CastlabsClient(final String username, final String password, final String authBaseUrl, final String ingestionBaseUrl, final int connectionTimeoutSeconds) { this.objectMapper = new ObjectMapper() .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); this.username = username; Expand All @@ -71,33 +68,32 @@ public CastlabsClient(String username, String password, String authBaseUrl, /** * Login to the Castlabs API endpoint. * * * @return a ticket URL * @throws CastlabsException error reported by Castlabs * @throws IOException communication error when interacting with Castlabs API * @throws IOException communication error when interacting with Castlabs API */ protected String login() throws CastlabsException, IOException { final HttpPost loginRequest = new HttpPost(this.authBaseUrl + "cas/v1/tickets"); loginRequest.addHeader("Content-Type", "application/x-www-form-urlencoded"); loginRequest.setHeader("Accept", "*/*"); final List<NameValuePair> entityParts =new ArrayList<NameValuePair> (); final List<NameValuePair> entityParts =Lists.newArrayList (); entityParts.add(new BasicNameValuePair("username", this.username)); entityParts.add(new BasicNameValuePair("password", this.password)); if (this.connectionTimeoutSeconds > 0) { final int connectionTimeout = connectionTimeoutSeconds * 1000; final RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionTimeout) .setConnectTimeout(connectionTimeout) .setSocketTimeout(connectionTimeout).build(); loginRequest.setConfig(requestConfig); } loginRequest.setEntity(new UrlEncodedFormEntity(entityParts)); final CloseableHttpClient httpclient = HttpClients.createDefault(); CloseableHttpResponse loginResponse = null; try { if (this.connectionTimeoutSeconds > 0) { int connectionTimeout = connectionTimeoutSeconds * 1000; RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionTimeout) .setConnectTimeout(connectionTimeout) .setSocketTimeout(connectionTimeout).build(); loginRequest.setConfig(requestConfig); } loginRequest.setEntity(new UrlEncodedFormEntity(entityParts)); loginResponse = httpclient.execute(loginRequest); try (final CloseableHttpResponse loginResponse = httpclient.execute(loginRequest)) { if (loginResponse != null) { final int statusCode = loginResponse.getStatusLine().getStatusCode(); final String reason = loginResponse.getStatusLine().getReasonPhrase(); Expand All @@ -115,47 +111,38 @@ protected String login() throws CastlabsException, IOException { } else { throw new CastlabsException("No location header provided in API response"); } } finally { try { if (loginResponse != null) loginResponse.close(); } catch (IOException e) { // ignore } } } /** * Retrieve an authentication ticket with the givenmerchant ID. * * @parammerchantId Castlabs-issued merchant ID * Retrieve an authentication ticket with the givenURL and return the URL with token appended * * @paramurl URL to request the token for * @return ticket that can be used to ingest encryption keys * @throws CastlabsException error reported by Castlabs * @throws IOException communication error when interacting with Castlabs API * @throws IOException communication error when interacting with Castlabs API */ protected StringgetTicket( StringmerchantId ) throws CastlabsException, IOException { protected StringgetUrlWithTicket(final Stringurl ) throws CastlabsException, IOException { final HttpPost ticketRequest = new HttpPost(this.login()); ticketRequest.addHeader("Content-Type", "application/x-www-form-urlencoded"); ticketRequest.setHeader("Accept", "*/*"); final List<NameValuePair> entityParts = new ArrayList<NameValuePair>(); entityParts.add(new BasicNameValuePair("service", this.ingestionBaseUrl + "frontend/api/keys/v2/ingest/" + merchantId)); final List<NameValuePair> entityParts = Lists.newArrayList(); entityParts.add(new BasicNameValuePair("service", url)); if (this.connectionTimeoutSeconds > 0) { final int connectionTimeout = connectionTimeoutSeconds * 1000; final RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionTimeout) .setConnectTimeout(connectionTimeout) .setSocketTimeout(connectionTimeout).build(); ticketRequest.setConfig(requestConfig); } ticketRequest.setEntity(new UrlEncodedFormEntity(entityParts)); final CloseableHttpClient httpclient = HttpClients.createDefault(); CloseableHttpResponse ticketResponse = null; try { if (this.connectionTimeoutSeconds > 0) { int connectionTimeout = connectionTimeoutSeconds * 1000; RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionTimeout) .setConnectTimeout(connectionTimeout) .setSocketTimeout(connectionTimeout).build(); ticketRequest.setConfig(requestConfig); } ticketRequest.setEntity(new UrlEncodedFormEntity(entityParts)); ticketResponse = httpclient.execute(ticketRequest); try (final CloseableHttpResponse ticketResponse = httpclient.execute(ticketRequest)) { if (ticketResponse != null) { final int statusCode = ticketResponse.getStatusLine().getStatusCode(); if (200 != statusCode) { Expand All @@ -168,47 +155,39 @@ protected String getTicket(String merchantId) throws CastlabsException, IOExcept } else { throw new CastlabsException("No response when retrieving Castlabs ticket"); } return IOUtils.toString(ticketResponse.getEntity().getContent()); } finally { try { if (ticketResponse != null) ticketResponse.close(); } catch (IOException e) { // ignore } return url + "?ticket=" + IOUtils.toString(ticketResponse.getEntity().getContent()); } } /** * Ingest one or more keys into the Castlabs keystore. * * @param request * * @param request Request parameters to pass to Castlabs * @param merchantId * @return response from Castlabs * @throws CastlabsException error reported by Castlabs * @throws IOException network error while communicating with Castlabs REST API * @throws IOException network error while communicating with Castlabs REST API */ public IngestAssetsResponse ingestKeys(IngestKeysRequest request, String merchantId) public IngestAssetsResponse ingestKeys(final IngestKeysRequest request, final String merchantId) throws CastlabsException, IOException { final String uri = this.ingestionBaseUrl + "frontend/api/keys/v2/ingest/" + merchantId + "?ticket= " +this.getTicket( merchantId);final String uri = this.getUrlWithTicket(this.ingestionBaseUrl + "frontend/api/keys/v2/ingest/ " + merchantId); final HttpPost ingestRequest = new HttpPost(uri); ingestRequest.addHeader("Content-Type", "application/json"); ingestRequest.setHeader("Accept", "application/json"); if (this.connectionTimeoutSeconds > 0) { final int connectionTimeout = connectionTimeoutSeconds * 1000; final RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionTimeout) .setConnectTimeout(connectionTimeout) .setSocketTimeout(connectionTimeout).build(); ingestRequest.setConfig(requestConfig); } ingestRequest.setEntity(new StringEntity(objectMapper.writeValueAsString(request))); final CloseableHttpClient httpclient = HttpClients.createDefault(); CloseableHttpResponse ingestResponse = null; try { if (this.connectionTimeoutSeconds > 0) { int connectionTimeout = connectionTimeoutSeconds * 1000; RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionTimeout) .setConnectTimeout(connectionTimeout) .setSocketTimeout(connectionTimeout).build(); ingestRequest.setConfig(requestConfig); } ingestRequest.setEntity(new StringEntity(objectMapper.writeValueAsString(request))); ingestResponse = httpclient.execute(ingestRequest); try (final CloseableHttpResponse ingestResponse = httpclient.execute(ingestRequest)) { if (ingestResponse != null) { final int statusCode = ingestResponse.getStatusLine().getStatusCode(); if (200 != statusCode) { Expand All @@ -220,22 +199,61 @@ public IngestAssetsResponse ingestKeys(IngestKeysRequest request, String merchan } final HttpEntity responseEntity = ingestResponse.getEntity(); if (responseEntity != null) { final IngestAssetsResponse getPadResponse = objectMapper .readValue(responseEntity.getContent(), IngestAssetsResponse.class); return getPadResponse; return objectMapper.readValue(responseEntity.getContent(), IngestAssetsResponse.class); } else { throw new CastlabsException("Empty response entity from Castlabs"); } } else { throw new CastlabsException("No response when ingesting keys into Castlabs"); } } finally { try { if (ingestResponse != null) ingestResponse.close(); } catch (IOException e) { // ignore } } /** * Add a sub merchant account to Castlabs. * * @param request Request parameters to pass to Castlabs * @param merchantUuid UUID for the merchant that the sub-merchant is being created off * @return response from Castlabs * @throws CastlabsException error reported by Castlabs * @throws IOException network error while communicating with Castlabs REST API */ public AddSubMerchantAccountResponse addSubMerchantAccount(final AddSubMerchantAccountRequest request, final String merchantUuid) throws IOException, CastlabsException { final String uri = this.getUrlWithTicket(this.ingestionBaseUrl + "frontend/rest/reselling/v1/reseller/" + merchantUuid + "/submerchant/add"); final HttpPost addResellerRequest = new HttpPost(uri); addResellerRequest.addHeader("Content-Type", "application/json"); addResellerRequest.setHeader("Accept", "application/json"); if (this.connectionTimeoutSeconds > 0) { final int connectionTimeout = connectionTimeoutSeconds * 1000; final RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionTimeout) .setConnectTimeout(connectionTimeout) .setSocketTimeout(connectionTimeout).build(); addResellerRequest.setConfig(requestConfig); } addResellerRequest.setEntity(new StringEntity(objectMapper.writeValueAsString(request))); final CloseableHttpClient httpclient = HttpClients.createDefault(); try (final CloseableHttpResponse httpResponse = httpclient.execute(addResellerRequest)){ final HttpEntity responseEntity = httpResponse.getEntity(); if (responseEntity == null) { throw new CastlabsException("Empty response entity from Castlabs. HTTP Status: " + httpResponse.getStatusLine().getStatusCode()); } final String responseBody = IOUtils.toString(responseEntity.getContent()); if(StringUtils.isBlank(responseBody)) { throw new CastlabsException("Empty response entity from Castlabs. HTTP Status: " + httpResponse.getStatusLine().getStatusCode()); } final AddSubMerchantAccountResponse response = objectMapper.readValue(responseBody, AddSubMerchantAccountResponse.class); if(response.getSubMerchantUuid() == null) { throw new CastlabsException("Unexpected response from Castlabs: " + responseBody); } return response; } } }