From c8b2c0c1adfef2d6a26218e0fb50ca5aef1cb328 Mon Sep 17 00:00:00 2001 From: melanahammel Date: Tue, 11 Dec 2018 11:47:42 -0800 Subject: [PATCH 01/14] created functionality for sts auth --- gradle/dependencies.gradle | 2 + .../auth/aws/BaseAwsCredentialsProvider.java | 12 +- .../aws/StsCerberusCredentialsProvider.java | 190 ++++++++++++++++++ .../StsCerberusCredentialsProviderTest.java | 91 +++++++++ 4 files changed, 291 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java create mode 100644 src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 622ea02..2664344 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -49,6 +49,8 @@ dependencies { compile "com.amazonaws:aws-java-sdk-core:${AWS_SDK_VERSION}" compile "com.amazonaws:aws-java-sdk-kms:${AWS_SDK_VERSION}" compile "com.amazonaws:aws-java-sdk-lambda:${AWS_SDK_VERSION}" + compile "com.amazonaws:aws-java-sdk-sts:${AWS_SDK_VERSION}" + testRuntime 'org.slf4j:slf4j-simple:1.7.25' testCompile "junit:junit:4.12" diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProvider.java b/src/main/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProvider.java index 8d48e36..be27511 100644 --- a/src/main/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProvider.java +++ b/src/main/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProvider.java @@ -117,7 +117,7 @@ public BaseAwsCredentialsProvider(UrlResolver urlResolver) { super(); this.urlResolver = urlResolver; this.cerberusJavaClientHeaderValue = ClientVersion.getClientHeaderValue(); - LOGGER.info("Cerberus URL={}", urlResolver.resolve()); +// LOGGER.info("Cerberus URL={}", urlResolver.resolve()); this.httpClient = createHttpClient(); @@ -351,7 +351,7 @@ private void sleep(long milliseconds) { } } - private RequestBody buildCredentialsRequestBody(final String iamPrincipalArn, Region region) { + protected RequestBody buildCredentialsRequestBody(final String iamPrincipalArn, Region region) { final String regionName = region == null ? Regions.getCurrentRegion().getName() : region.getName(); final Map credentials = new HashMap<>(); @@ -361,7 +361,7 @@ private RequestBody buildCredentialsRequestBody(final String iamPrincipalArn, Re return RequestBody.create(DEFAULT_MEDIA_TYPE, gson.toJson(credentials)); } - private void parseAndThrowErrorResponse(final int responseCode, final String responseBody) { + protected void parseAndThrowErrorResponse(final int responseCode, final String responseBody) { final String message = String.format("Failed to authenticate. Response: %s", responseBody); LOGGER.warn(message); List errors = new ArrayList<>(1); @@ -369,7 +369,11 @@ private void parseAndThrowErrorResponse(final int responseCode, final String res throw new CerberusServerException(responseCode, errors); } - private OkHttpClient createHttpClient() { + public UrlResolver getUrlResolver(){ + return urlResolver; + } + + public OkHttpClient createHttpClient() { List connectionSpecs = new ArrayList<>(); connectionSpecs.add(TLS_1_2_OR_NEWER); diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java b/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java new file mode 100644 index 0000000..7e36596 --- /dev/null +++ b/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2018 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.client.auth.aws; + +import com.amazonaws.DefaultRequest; +import com.amazonaws.auth.AWS4Signer; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; +import com.amazonaws.http.HttpMethodName; +import com.amazonaws.regions.Region; +import com.amazonaws.regions.Regions; +import com.amazonaws.services.securitytoken.AWSSecurityTokenService; +import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder; +import com.amazonaws.services.securitytoken.model.GetCallerIdentityRequest; +import com.amazonaws.services.securitytoken.model.GetCallerIdentityResult; +import com.google.gson.FieldNamingPolicy; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.nike.cerberus.client.CerberusClientException; +import com.nike.cerberus.client.ClientVersion; +import com.nike.cerberus.client.UrlResolver; +import com.nike.cerberus.client.http.HttpMethod; +import com.nike.cerberus.client.http.HttpStatus; +import okhttp3.*; + +import java.io.IOException; +import java.lang.reflect.Type; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Provider for allowing users to authenticate with Cerberus with the STS auth endpoint + */ + +public class StsCerberusCredentialsProvider extends BaseAwsCredentialsProvider { + + private static final String REGION_STRING = "us-east-1"; + protected static final int DEFAULT_AUTH_RETRIES = 3; + + protected static final int DEFAULT_RETRY_INTERVAL_IN_MILLIS = 200; + private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) + .create(); + + /** + * Constructor to setup credentials provider using the specified + * implementation of {@link UrlResolver} + * + * @param urlResolver Resolver for resolving the Cerberus URL + */ + public StsCerberusCredentialsProvider(UrlResolver urlResolver) { + super(urlResolver); + + } + + + public GetCallerIdentityResult getCallerIdentity() { + final AWSSecurityTokenService sts = AWSSecurityTokenServiceClientBuilder.defaultClient(); + + GetCallerIdentityRequest getCallerIdentityRequest = new GetCallerIdentityRequest(); + + GetCallerIdentityResult callerIdentity = sts.getCallerIdentity(getCallerIdentityRequest); + return callerIdentity; + } + + public AWSCredentials getAWSCredentials(){ + return DefaultAWSCredentialsProviderChain.getInstance().getCredentials(); + } + + private void signRequest(com.amazonaws.Request request, AWSCredentials credentials){ + AWS4Signer signer = new AWS4Signer(); + signer.setRegionName(REGION_STRING); + signer.setServiceName("sts"); + signer.sign(request, credentials); + + } + + public Map getSignedHeaders(){ + +// final String url = this.getUrlResolver().resolve(); +// final String url = "https://sts.amazonaws.com"; + final String url = "https://sts.us-east-1.amazonaws.com"; + + URI endpoint = null; + + try { + endpoint = new URI(url); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + + Map> parameters = new HashMap<>(); + parameters.put("Action", Arrays.asList("GetCallerIdentity")); + parameters.put("Version", Arrays.asList("2011-06-15")); + + DefaultRequest requestToSign = new DefaultRequest<>("sts"); + requestToSign.setParameters(parameters); + requestToSign.setHttpMethod(HttpMethodName.POST); + requestToSign.setEndpoint(endpoint); + + signRequest(requestToSign, getAWSCredentials()); + + return requestToSign.getHeaders(); + } + + public String buildRequest(final String iamPrincipalArn, Region region){ + + + Map signedHeaders = getSignedHeaders(); + + final String url = this.getUrlResolver().resolve(); + +// Map> parameters = new HashMap<>(); +// parameters.put("Action", Arrays.asList("GetCallerIdentity")); +// parameters.put("Version", Arrays.asList("2011-06-15")); + +// if (StringUtils.isBlank(url)) { +// throw new CerberusClientException("Unable to find the Cerberus URL."); +// } + +// LOGGER.info(String.format("Attempting to authenticate with AWS IAM principal ARN [%s] against [%s]", +// iamPrincipalArn, url)); + + try { + + Request request = new Request.Builder() + .url(url + "/v2/auth/sts-identity") + .headers(Headers.of(signedHeaders)) + .method(HttpMethod.POST, buildCredentialsRequestBody(iamPrincipalArn, region)) +// .post(body) + .build(); +// +//// okhttp3.Request.Builder requestBuilder = new Request.Builder().url(url + "/v2/auth/sts-identity"); +//// .addHeader(HttpHeader.ACCEPT, DEFAULT_MEDIA_TYPE.toString()) +//// .addHeader(HttpHeader.CONTENT_TYPE, DEFAULT_MEDIA_TYPE.toString()) +//// .addHeader(ClientVersion.CERBERUS_CLIENT_HEADER, cerberusJavaClientHeaderValue) +//// .method(HttpMethod.POST, buildCredentialsRequestBody(iamPrincipalArn, region)); +//// .method(HttpMethod.POST); +// Request builtRequest = new Request(requestBuilder); + +// Response response = executeRequestWithRetry(requestBuilder.build(), DEFAULT_AUTH_RETRIES, DEFAULT_RETRY_INTERVAL_IN_MILLIS); + Response response = executeRequestWithRetry(request, DEFAULT_AUTH_RETRIES, DEFAULT_RETRY_INTERVAL_IN_MILLIS); + + if (response.code() != HttpStatus.OK) { + parseAndThrowErrorResponse(response.code(), response.body().string()); + } + + final Type mapType = new TypeToken>() { + }.getType(); + final Map authData = gson.fromJson(response.body().string(), mapType); + final String key = "auth_data"; + + if (authData.containsKey(key)) { +// LOGGER.info(String.format("Authentication successful with AWS IAM principal ARN [%s] against [%s]", +// iamPrincipalArn, url)); + return authData.get(key); + } else { + throw new CerberusClientException("Success response from IAM role authenticate endpoint missing auth data!"); + } + + } catch (IOException e) { + throw new CerberusClientException("I/O error while communicating with Cerberus", e); + } + + } + + @Override + protected void authenticate() { + + } + +} diff --git a/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java b/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java new file mode 100644 index 0000000..823b6f3 --- /dev/null +++ b/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2018 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.client.auth.aws; + +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.regions.Region; +import com.amazonaws.regions.RegionUtils; +import com.amazonaws.services.securitytoken.model.GetCallerIdentityResult; +import com.nike.cerberus.client.UrlResolver; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.when; + +public class StsCerberusCredentialsProviderTest { + + private static final String ACCOUNT_ID = "1234"; + private static final String ROLE_NAME = "foo/base/bar"; + private static final String ROLE_ARN = String.format("arn:aws:iam::%s:role/%s", ACCOUNT_ID, ROLE_NAME); + private static final String REGION_STRING = "us-east-1"; +// private static final Region REGION = Region.getRegion(Regions.US_EAST_1); + + private static final String cerberusUrl = "https://dev.cerberus.nikecloud.com"; + + private static final String amazonUrl = "https://sts.us-east-1.amazonaws.com"; + + public static final Region REGION = RegionUtils.getRegion("us-east-1"); + public static final String CERBERUS_TEST_ARN = "arn:aws:iam::123456789012:role/cerberus-test-role"; + + + private StsCerberusCredentialsProvider credentialsProvider; + private UrlResolver urlResolver; + + @Before + public void setUp() throws Exception { + urlResolver = mock(UrlResolver.class); + +// urlResolver = () -> "https://dev.cerberus.nikecloud.com"; + + credentialsProvider = new StsCerberusCredentialsProvider(urlResolver); + } + + + @Test + public void test_get_caller_identity(){ + GetCallerIdentityResult callerIdentityResult = credentialsProvider.getCallerIdentity(); + assertThat(callerIdentityResult).isNotNull(); + } + + @Test + public void get_aws_credentials(){ + AWSCredentials credentials = credentialsProvider.getAWSCredentials(); + assertThat(credentials).isNotNull(); + } + + + @Test + public void get_signed_headers(){ + when(urlResolver.resolve()).thenReturn(cerberusUrl); + Map headers = credentialsProvider.getSignedHeaders(); + assertThat(headers).isNotNull(); + } + + + @Test + public void get_token(){ + when(urlResolver.resolve()).thenReturn(cerberusUrl); + String token = credentialsProvider.buildRequest(CERBERUS_TEST_ARN, REGION); + assertThat(token).isNotNull(); + } + +} + From c659475cb0bc9165573c9dab76d91d97dc0014b7 Mon Sep 17 00:00:00 2001 From: melanahammel Date: Fri, 14 Dec 2018 11:07:26 -0800 Subject: [PATCH 02/14] wired up authentication with the STS auth endpoint --- .../auth/aws/StsAuthCerberusClientTest.java | 47 +++++ .../aws/StsCerberusCredentialsProvider.java | 171 ++++++++++-------- .../StsCerberusCredentialsProviderTest.java | 130 +++++++++---- 3 files changed, 236 insertions(+), 112 deletions(-) create mode 100644 src/integration/java/com/nike/cerberus/client/auth/aws/StsAuthCerberusClientTest.java diff --git a/src/integration/java/com/nike/cerberus/client/auth/aws/StsAuthCerberusClientTest.java b/src/integration/java/com/nike/cerberus/client/auth/aws/StsAuthCerberusClientTest.java new file mode 100644 index 0000000..609dc0e --- /dev/null +++ b/src/integration/java/com/nike/cerberus/client/auth/aws/StsAuthCerberusClientTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Nike, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.nike.cerberus.client.auth.aws; + +import com.fieldju.commons.EnvUtils; +import com.nike.cerberus.client.auth.CerberusCredentials; +import org.apache.commons.lang3.StringUtils; +import org.junit.Before; +import org.junit.Test; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests StsCerberusCredentialsProvider class + */ +public class StsAuthCerberusClientTest { + + private static String region; + private static String cerberusUrl; + + @Before + public void setUp() { + region = EnvUtils.getRequiredEnv("TEST_REGION"); + cerberusUrl = EnvUtils.getRequiredEnv("CERBERUS_ADDR"); + } + + @Test + public void test_get_token(){ + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, region); + CerberusCredentials creds = credentialsProvider.getCredentials(); + assertThat(creds).isNotNull(); + assertThat(StringUtils.isNotEmpty(creds.getToken())); + } +} diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java b/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java index 7e36596..56677ee 100644 --- a/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java +++ b/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java @@ -21,25 +21,23 @@ import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; import com.amazonaws.http.HttpMethodName; -import com.amazonaws.regions.Region; import com.amazonaws.regions.Regions; -import com.amazonaws.services.securitytoken.AWSSecurityTokenService; -import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder; -import com.amazonaws.services.securitytoken.model.GetCallerIdentityRequest; -import com.amazonaws.services.securitytoken.model.GetCallerIdentityResult; import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; import com.nike.cerberus.client.CerberusClientException; -import com.nike.cerberus.client.ClientVersion; -import com.nike.cerberus.client.UrlResolver; +import com.nike.cerberus.client.auth.TokenCerberusCredentials; import com.nike.cerberus.client.http.HttpMethod; import com.nike.cerberus.client.http.HttpStatus; +import com.nike.cerberus.client.model.CerberusAuthResponse; import okhttp3.*; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; -import java.lang.reflect.Type; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; @@ -48,56 +46,97 @@ import java.util.Map; /** - * Provider for allowing users to authenticate with Cerberus with the STS auth endpoint + * Provider for allowing users to authenticate with Cerberus with the STS auth endpoint. */ public class StsCerberusCredentialsProvider extends BaseAwsCredentialsProvider { - private static final String REGION_STRING = "us-east-1"; + protected static String regionName; + protected static final int DEFAULT_AUTH_RETRIES = 3; protected static final int DEFAULT_RETRY_INTERVAL_IN_MILLIS = 200; + + private static final Logger LOGGER = LoggerFactory.getLogger(BaseAwsCredentialsProvider.class); + private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .create(); /** - * Constructor to setup credentials provider using the specified - * implementation of {@link UrlResolver} + * Constructor to setup credentials provider * - * @param urlResolver Resolver for resolving the Cerberus URL + * @param cerberusUrl Cerberus URL + * @param region AWS Region used in auth with Cerberus */ - public StsCerberusCredentialsProvider(UrlResolver urlResolver) { - super(urlResolver); + public StsCerberusCredentialsProvider(String cerberusUrl, String region) { + super(cerberusUrl); + if (region != null ) { + regionName = Regions.fromName(region).getName(); + } else { + throw new CerberusClientException("Region is null. Please provide valid AWS region."); + } } + /** + * Constructor to setup credentials provider + * + * @param cerberusUrl Cerberus URL + * @param region AWS Region used in auth with Cerberus + * @param xCerberusClientOverride Overrides the default header value for the 'X-Cerberus-Client' header + */ + public StsCerberusCredentialsProvider(String cerberusUrl, String region, String xCerberusClientOverride) { + super(cerberusUrl, xCerberusClientOverride); + if (region != null ) { + regionName = Regions.fromName(region).getName(); + } else { + throw new CerberusClientException("Region is null. Please provide valid AWS region."); + } + } - public GetCallerIdentityResult getCallerIdentity() { - final AWSSecurityTokenService sts = AWSSecurityTokenServiceClientBuilder.defaultClient(); - - GetCallerIdentityRequest getCallerIdentityRequest = new GetCallerIdentityRequest(); - - GetCallerIdentityResult callerIdentity = sts.getCallerIdentity(getCallerIdentityRequest); - return callerIdentity; + /** + * Constructor to setup credentials provider using the specified + * implementation of {@link OkHttpClient} + * + * @param cerberusUrl Cerberus URL + * @param region AWS Region used in auth with Cerberus + * @param httpClient the client for interacting with Cerberus + */ + public StsCerberusCredentialsProvider(String cerberusUrl, String region, OkHttpClient httpClient) { + super(cerberusUrl, httpClient); + if (region != null ) { + regionName = Regions.fromName(region).getName(); + } else { + throw new CerberusClientException("Region is null. Please provide valid AWS region."); + } } - public AWSCredentials getAWSCredentials(){ + /** + * Obtains AWS Credentials. + */ + private AWSCredentials getAWSCredentials(){ return DefaultAWSCredentialsProviderChain.getInstance().getCredentials(); } + /** + * Signs request using AWS V4 signing. + * @param request AWS STS request to sign + * @param credentials AWS credentials + */ private void signRequest(com.amazonaws.Request request, AWSCredentials credentials){ + AWS4Signer signer = new AWS4Signer(); - signer.setRegionName(REGION_STRING); + signer.setRegionName(regionName); signer.setServiceName("sts"); signer.sign(request, credentials); - } - public Map getSignedHeaders(){ + /** + * Generates and returns signed headers. + */ + protected Map getSignedHeaders(){ -// final String url = this.getUrlResolver().resolve(); -// final String url = "https://sts.amazonaws.com"; - final String url = "https://sts.us-east-1.amazonaws.com"; + final String url = "https://sts." + regionName + ".amazonaws.com"; URI endpoint = null; @@ -116,75 +155,63 @@ public Map getSignedHeaders(){ requestToSign.setHttpMethod(HttpMethodName.POST); requestToSign.setEndpoint(endpoint); + LOGGER.info(String.format("Signing request with [%s] as host", url)); + signRequest(requestToSign, getAWSCredentials()); return requestToSign.getHeaders(); } - public String buildRequest(final String iamPrincipalArn, Region region){ - - - Map signedHeaders = getSignedHeaders(); - - final String url = this.getUrlResolver().resolve(); + /** + * Sends request with signed headers to Cerberus to obtain token using STS Auth. + */ + protected CerberusAuthResponse getToken(){ -// Map> parameters = new HashMap<>(); -// parameters.put("Action", Arrays.asList("GetCallerIdentity")); -// parameters.put("Version", Arrays.asList("2011-06-15")); + if (StringUtils.isBlank(cerberusUrl)) { + throw new CerberusClientException("Unable to find the Cerberus URL."); + } -// if (StringUtils.isBlank(url)) { -// throw new CerberusClientException("Unable to find the Cerberus URL."); -// } + LOGGER.info(String.format("Attempting to authenticate against [%s]", cerberusUrl)); -// LOGGER.info(String.format("Attempting to authenticate with AWS IAM principal ARN [%s] against [%s]", -// iamPrincipalArn, url)); + Map signedHeaders = getSignedHeaders(); try { - Request request = new Request.Builder() - .url(url + "/v2/auth/sts-identity") + .url(cerberusUrl + "/v2/auth/sts-identity") .headers(Headers.of(signedHeaders)) - .method(HttpMethod.POST, buildCredentialsRequestBody(iamPrincipalArn, region)) -// .post(body) + .method(HttpMethod.POST, RequestBody.create(DEFAULT_MEDIA_TYPE, "")) .build(); -// -//// okhttp3.Request.Builder requestBuilder = new Request.Builder().url(url + "/v2/auth/sts-identity"); -//// .addHeader(HttpHeader.ACCEPT, DEFAULT_MEDIA_TYPE.toString()) -//// .addHeader(HttpHeader.CONTENT_TYPE, DEFAULT_MEDIA_TYPE.toString()) -//// .addHeader(ClientVersion.CERBERUS_CLIENT_HEADER, cerberusJavaClientHeaderValue) -//// .method(HttpMethod.POST, buildCredentialsRequestBody(iamPrincipalArn, region)); -//// .method(HttpMethod.POST); -// Request builtRequest = new Request(requestBuilder); - -// Response response = executeRequestWithRetry(requestBuilder.build(), DEFAULT_AUTH_RETRIES, DEFAULT_RETRY_INTERVAL_IN_MILLIS); + Response response = executeRequestWithRetry(request, DEFAULT_AUTH_RETRIES, DEFAULT_RETRY_INTERVAL_IN_MILLIS); if (response.code() != HttpStatus.OK) { parseAndThrowErrorResponse(response.code(), response.body().string()); } - final Type mapType = new TypeToken>() { - }.getType(); - final Map authData = gson.fromJson(response.body().string(), mapType); - final String key = "auth_data"; - - if (authData.containsKey(key)) { -// LOGGER.info(String.format("Authentication successful with AWS IAM principal ARN [%s] against [%s]", -// iamPrincipalArn, url)); - return authData.get(key); - } else { - throw new CerberusClientException("Success response from IAM role authenticate endpoint missing auth data!"); - } + return gson.fromJson(response.body().string(), CerberusAuthResponse.class); } catch (IOException e) { throw new CerberusClientException("I/O error while communicating with Cerberus", e); } - } + /** + * Requests a token from Cerberus using STS Auth and sets the token and expiration details. + */ @Override protected void authenticate() { - } + CerberusAuthResponse token = getToken(); + if (token.getClientToken() != null) { + LOGGER.info(String.format("Authentication successful")); + } else { + throw new CerberusClientException("Success response from Cerberus missing token"); + } + + final DateTime expires = DateTime.now(DateTimeZone.UTC) + .plusSeconds(token.getLeaseDuration() - paddingTimeInSeconds); + credentials = new TokenCerberusCredentials(token.getClientToken()); + expireDateTime = expires; + } } diff --git a/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java b/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java index 823b6f3..34ad579 100644 --- a/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java +++ b/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java @@ -16,76 +16,126 @@ package com.nike.cerberus.client.auth.aws; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.regions.Region; -import com.amazonaws.regions.RegionUtils; -import com.amazonaws.services.securitytoken.model.GetCallerIdentityResult; -import com.nike.cerberus.client.UrlResolver; +import com.nike.cerberus.client.CerberusClientException; +import com.nike.cerberus.client.model.CerberusAuthResponse; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.apache.commons.lang3.StringUtils; import org.junit.Before; import org.junit.Test; +import java.io.IOException; import java.util.Map; import static org.assertj.core.api.Assertions.assertThat; import static org.powermock.api.mockito.PowerMockito.mock; import static org.powermock.api.mockito.PowerMockito.when; -public class StsCerberusCredentialsProviderTest { - - private static final String ACCOUNT_ID = "1234"; - private static final String ROLE_NAME = "foo/base/bar"; - private static final String ROLE_ARN = String.format("arn:aws:iam::%s:role/%s", ACCOUNT_ID, ROLE_NAME); - private static final String REGION_STRING = "us-east-1"; -// private static final Region REGION = Region.getRegion(Regions.US_EAST_1); +/** + * Tests the StsCerberusCredentialsProvider class + */ +public class StsCerberusCredentialsProviderTest extends BaseCredentialsProviderTest { - private static final String cerberusUrl = "https://dev.cerberus.nikecloud.com"; + private static final String REGION_STRING_EAST = "us-east-1"; + private static final String REGION_STRING_WEST = "us-west-2"; - private static final String amazonUrl = "https://sts.us-east-1.amazonaws.com"; + private String cerberusUrl; - public static final Region REGION = RegionUtils.getRegion("us-east-1"); - public static final String CERBERUS_TEST_ARN = "arn:aws:iam::123456789012:role/cerberus-test-role"; + @Before + public void setUp() { + cerberusUrl = mock(String.class); + } + @Test + public void test_sts_creds_provider_constructor() { + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); + assertThat(credentialsProvider.getCerberusUrl()).isEqualTo(cerberusUrl); + assertThat(credentialsProvider.regionName).isEqualTo(REGION_STRING_EAST); + } - private StsCerberusCredentialsProvider credentialsProvider; - private UrlResolver urlResolver; + @Test + public void test_get_signed_headers_for_east_region() throws IOException { + MockWebServer mockWebServer = new MockWebServer(); + mockWebServer.start(); + final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); - @Before - public void setUp() throws Exception { - urlResolver = mock(UrlResolver.class); + Map headers = credentialsProvider.getSignedHeaders(); + assertThat(headers).isNotNull(); + assertThat(headers.get("Authorization")).isNotEmpty(); + assertThat(headers.get("X-Amz-Date")).isNotEmpty(); + assertThat(headers.get("X-Amz-Security-Token")).isNotEmpty(); + assertThat(headers.get("Host")).isNotEmpty(); + } -// urlResolver = () -> "https://dev.cerberus.nikecloud.com"; + @Test + public void test_get_signed_headers_for_west_region() throws IOException { + MockWebServer mockWebServer = new MockWebServer(); + mockWebServer.start(); + final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_WEST); - credentialsProvider = new StsCerberusCredentialsProvider(urlResolver); + Map headers = credentialsProvider.getSignedHeaders(); + assertThat(headers).isNotNull(); + assertThat(headers.get("Authorization")).isNotEmpty(); + assertThat(headers.get("X-Amz-Date")).isNotEmpty(); + assertThat(headers.get("X-Amz-Security-Token")).isNotEmpty(); + assertThat(headers.get("Host")).isNotEmpty(); } - @Test - public void test_get_caller_identity(){ - GetCallerIdentityResult callerIdentityResult = credentialsProvider.getCallerIdentity(); - assertThat(callerIdentityResult).isNotNull(); + public void get_token_returns_token() throws IOException { + MockWebServer mockWebServer = new MockWebServer(); + mockWebServer.start(); + final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); + + mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(DECODED_AUTH_DATA)); + CerberusAuthResponse token = credentialsProvider.getToken(); + assertThat(token).isNotNull(); + assertThat(StringUtils.isNotEmpty(token.getClientToken())); } - @Test - public void get_aws_credentials(){ - AWSCredentials credentials = credentialsProvider.getAWSCredentials(); - assertThat(credentials).isNotNull(); + @Test(expected = CerberusClientException.class) + public void get_token_throws_exception_timeout() throws IOException { + MockWebServer mockWebServer = new MockWebServer(); + mockWebServer.start(); + final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); + + CerberusAuthResponse token = credentialsProvider.getToken(); + assertThat(token).isNotNull(); + assertThat(StringUtils.isNotEmpty(token.getClientToken())); } + @Test(expected = CerberusClientException.class) - @Test - public void get_signed_headers(){ - when(urlResolver.resolve()).thenReturn(cerberusUrl); - Map headers = credentialsProvider.getSignedHeaders(); - assertThat(headers).isNotNull(); + public void get_token_throws_exception_when_url_is_blank(){ + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); + CerberusAuthResponse token = credentialsProvider.getToken(); + assertThat(token).isNotNull(); + assertThat(StringUtils.isNotEmpty(token.getClientToken())); } + @Test(expected = CerberusClientException.class) + public void get_token_throws_exception_when_response_is_bad() throws IOException { + MockWebServer mockWebServer = new MockWebServer(); + mockWebServer.start(); + final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); + mockWebServer.enqueue(new MockResponse().setResponseCode(400).setBody(AUTH_RESPONSE)); - @Test - public void get_token(){ - when(urlResolver.resolve()).thenReturn(cerberusUrl); - String token = credentialsProvider.buildRequest(CERBERUS_TEST_ARN, REGION); + CerberusAuthResponse token = credentialsProvider.getToken(); assertThat(token).isNotNull(); + assertThat(StringUtils.isNotEmpty(token.getClientToken())); } + @Test(expected = CerberusClientException.class) + public void authenticate_throws_exception_when_token_is_null() { + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); + CerberusAuthResponse token = mock(CerberusAuthResponse.class); + when(token.getClientToken()).thenReturn(null); + credentialsProvider.authenticate(); + } } From e4d4f9b4e81f984580de7c00187a2ab31c7f82d8 Mon Sep 17 00:00:00 2001 From: melanahammel Date: Fri, 14 Dec 2018 11:12:23 -0800 Subject: [PATCH 03/14] deleted resolver files --- .../client/DefaultCerberusUrlResolver.java | 59 ----------------- .../client/StaticCerberusUrlResolver.java | 50 -------------- .../com/nike/cerberus/client/UrlResolver.java | 26 -------- .../DefaultCerberusUrlResolverTest.java | 65 ------------------- .../client/StaticCerberusUrlResolverTest.java | 41 ------------ 5 files changed, 241 deletions(-) delete mode 100644 src/main/java/com/nike/cerberus/client/DefaultCerberusUrlResolver.java delete mode 100644 src/main/java/com/nike/cerberus/client/StaticCerberusUrlResolver.java delete mode 100644 src/main/java/com/nike/cerberus/client/UrlResolver.java delete mode 100644 src/test/java/com/nike/cerberus/client/DefaultCerberusUrlResolverTest.java delete mode 100644 src/test/java/com/nike/cerberus/client/StaticCerberusUrlResolverTest.java diff --git a/src/main/java/com/nike/cerberus/client/DefaultCerberusUrlResolver.java b/src/main/java/com/nike/cerberus/client/DefaultCerberusUrlResolver.java deleted file mode 100644 index 01b62bc..0000000 --- a/src/main/java/com/nike/cerberus/client/DefaultCerberusUrlResolver.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client; - -import okhttp3.HttpUrl; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.annotation.Nullable; - -/** - * Class for resolving the Cerberus URL via Archaius. - */ -public class DefaultCerberusUrlResolver implements UrlResolver { - - public static final String CERBERUS_ADDR_ENV_PROPERTY = "CERBERUS_ADDR"; - - public static final String CERBERUS_ADDR_SYS_PROPERTY = "cerberus.addr"; - - private final Logger logger = LoggerFactory.getLogger(getClass()); - - /** - * Attempts to acquire the Cerberus URL from Archaius. - * - * @return Cerberus URL - */ - @Nullable - @Override - public String resolve() { - - final String envUrl = System.getenv(CERBERUS_ADDR_ENV_PROPERTY); - final String sysUrl = System.getProperty(CERBERUS_ADDR_SYS_PROPERTY); - - if (StringUtils.isNotBlank(envUrl) && HttpUrl.parse(envUrl) != null) { - return envUrl; - } else if (StringUtils.isNotBlank(sysUrl) && HttpUrl.parse(sysUrl) != null) { - return sysUrl; - } - - logger.warn("Unable to resolve the Cerberus URL."); - - return null; - } -} diff --git a/src/main/java/com/nike/cerberus/client/StaticCerberusUrlResolver.java b/src/main/java/com/nike/cerberus/client/StaticCerberusUrlResolver.java deleted file mode 100644 index 0e751de..0000000 --- a/src/main/java/com/nike/cerberus/client/StaticCerberusUrlResolver.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client; - -import org.apache.commons.lang3.StringUtils; - -/** - * Wrapper for the URL resolver interface for a static URL. - */ -public class StaticCerberusUrlResolver implements UrlResolver { - - private final String cerberusUrl; - - /** - * Explicit constructor for holding a static Cerberus URL. - * - * @param cerberusUrl Cerberus URL - */ - public StaticCerberusUrlResolver(final String cerberusUrl) { - if (StringUtils.isBlank(cerberusUrl)) { - throw new IllegalArgumentException("Cerberus URL can not be blank."); - } - - this.cerberusUrl = cerberusUrl; - } - - /** - * Returns a static Cerberus URL. - * - * @return Cerberus URL - */ - @Override - public String resolve() { - return cerberusUrl; - } -} diff --git a/src/main/java/com/nike/cerberus/client/UrlResolver.java b/src/main/java/com/nike/cerberus/client/UrlResolver.java deleted file mode 100644 index dae0971..0000000 --- a/src/main/java/com/nike/cerberus/client/UrlResolver.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client; - -public interface UrlResolver { - /** - * Resolves the URL for the Cerberus instance. - * - * @return Cerberus URL - */ - String resolve(); -} diff --git a/src/test/java/com/nike/cerberus/client/DefaultCerberusUrlResolverTest.java b/src/test/java/com/nike/cerberus/client/DefaultCerberusUrlResolverTest.java deleted file mode 100644 index 8194a9f..0000000 --- a/src/test/java/com/nike/cerberus/client/DefaultCerberusUrlResolverTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; - -/** - * Tests the resolve methods on DefaultCerberusUrlResolver - */ -@RunWith(PowerMockRunner.class) -@PrepareForTest({DefaultCerberusUrlResolver.class, System.class}) -public class DefaultCerberusUrlResolverTest { - - private final String url = "http://localhost:8080"; - - private DefaultCerberusUrlResolver subject; - - @Before - public void setup() { - mockStatic(System.class); - subject = new DefaultCerberusUrlResolver(); - } - - @Test - public void lookupCerberusUrl_returns_url_if_env_variable_is_set() { - when(System.getenv(DefaultCerberusUrlResolver.CERBERUS_ADDR_ENV_PROPERTY)).thenReturn(url); - - assertThat(subject.resolve()).isEqualTo(url); - } - - @Test - public void lookupCerberusUrl_returns_url_if_sys_property_is_set() { - when(System.getProperty(DefaultCerberusUrlResolver.CERBERUS_ADDR_SYS_PROPERTY)).thenReturn(url); - - assertThat(subject.resolve()).isEqualTo(url); - } - - @Test - public void lookupCerberusUrl_returns_null_if_env_and_sys_not_set() { - assertThat(subject.resolve()).isNull(); - } -} diff --git a/src/test/java/com/nike/cerberus/client/StaticCerberusUrlResolverTest.java b/src/test/java/com/nike/cerberus/client/StaticCerberusUrlResolverTest.java deleted file mode 100644 index 6d48689..0000000 --- a/src/test/java/com/nike/cerberus/client/StaticCerberusUrlResolverTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * Tests the StaticCerberusUrlResolverTest class - */ -public class StaticCerberusUrlResolverTest { - - private final String testUrl = "https://localhost"; - - @Test(expected = IllegalArgumentException.class) - public void test_constructor_throws_error_if_cerberus_url_is_blank() { - new StaticCerberusUrlResolver(" "); - } - - @Test - public void test_resolve_returns_url_that_was_set() { - final UrlResolver urlResolver = new StaticCerberusUrlResolver(testUrl); - - assertThat(urlResolver.resolve()).isEqualTo(testUrl); - } -} \ No newline at end of file From 05bb9f1f0bf502308769c02b7c1edb6fb977c80d Mon Sep 17 00:00:00 2001 From: melanahammel Date: Fri, 14 Dec 2018 11:16:23 -0800 Subject: [PATCH 04/14] deleted old credentials providers --- ...csTaskRoleCerberusCredentialsProvider.java | 198 ------------- ...stanceRoleCerberusCredentialsProvider.java | 259 ------------------ ...LambdaRoleCerberusCredentialsProvider.java | 121 -------- ...ticIamRoleCerberusCredentialsProvider.java | 96 ------- ...onRoleCerberusCredentialsProviderTest.java | 112 -------- ...ceRoleCerberusCredentialsProviderTest.java | 229 ---------------- ...daRoleCerberusCredentialsProviderTest.java | 157 ----------- ...amRoleCerberusCredentialsProviderTest.java | 134 --------- 8 files changed, 1306 deletions(-) delete mode 100644 src/main/java/com/nike/cerberus/client/auth/aws/EcsTaskRoleCerberusCredentialsProvider.java delete mode 100644 src/main/java/com/nike/cerberus/client/auth/aws/InstanceRoleCerberusCredentialsProvider.java delete mode 100644 src/main/java/com/nike/cerberus/client/auth/aws/LambdaRoleCerberusCredentialsProvider.java delete mode 100644 src/main/java/com/nike/cerberus/client/auth/aws/StaticIamRoleCerberusCredentialsProvider.java delete mode 100644 src/test/java/com/nike/cerberus/client/auth/aws/EcsTaskExecutionRoleCerberusCredentialsProviderTest.java delete mode 100644 src/test/java/com/nike/cerberus/client/auth/aws/InstanceRoleCerberusCredentialsProviderTest.java delete mode 100644 src/test/java/com/nike/cerberus/client/auth/aws/LambdaRoleCerberusCredentialsProviderTest.java delete mode 100644 src/test/java/com/nike/cerberus/client/auth/aws/StaticIamRoleCerberusCredentialsProviderTest.java diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/EcsTaskRoleCerberusCredentialsProvider.java b/src/main/java/com/nike/cerberus/client/auth/aws/EcsTaskRoleCerberusCredentialsProvider.java deleted file mode 100644 index c3217fc..0000000 --- a/src/main/java/com/nike/cerberus/client/auth/aws/EcsTaskRoleCerberusCredentialsProvider.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client.auth.aws; - -import com.amazonaws.AmazonClientException; -import com.amazonaws.internal.EC2CredentialsUtils; -import com.amazonaws.regions.Region; -import com.amazonaws.regions.Regions; -import com.amazonaws.util.json.Jackson; -import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.google.gson.JsonSyntaxException; -import com.nike.cerberus.client.CerberusClientException; -import com.nike.cerberus.client.UrlResolver; -import com.nike.cerberus.client.auth.CerberusCredentialsProvider; -import com.nike.cerberus.client.util.EnvironmentUtils; -import okhttp3.OkHttpClient; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - - -/** - * {@link CerberusCredentialsProvider} implementation that uses the assigned role - * to an ECS task to authenticate with Cerberus and decrypt the auth - * response using KMS. If the assigned role has been granted the appropriate - * provisioned for usage of Cerberus, it will succeed and have a token that can be - * used to interact with Cerberus. - *

- * This class uses the AWS Task Metadata endpoint to look-up information automatically. - * - * @see Amazon ECS Task Metadata Endpoint - */ -public class EcsTaskRoleCerberusCredentialsProvider extends BaseAwsCredentialsProvider { - - private static final Logger LOGGER = LoggerFactory.getLogger(EcsTaskRoleCerberusCredentialsProvider.class); - - private static final Pattern TASK_ARN_PATTERN = Pattern.compile("arn:aws:ecs:(?.*):(.*):task/(.*)"); - - /** The name of the Json Object that contains the role ARN.*/ - final String ROLE_ARN = "RoleArn"; - - /** Environment variable to get the Amazon ECS credentials resource path. */ - private static final String ECS_CONTAINER_CREDENTIALS_PATH = "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"; - - private static final String ECS_TASK_METADATA_RELATIVE_URI = "/v2/metadata"; - - /** Default endpoint to retrieve the Amazon ECS Credentials and metadata. */ - private static final String ECS_CREDENTIALS_ENDPOINT = "http://169.254.170.2"; - - /** - * Constructor to setup credentials provider using the specified - * implementation of {@link UrlResolver} - * - * @param urlResolver Resolver for resolving the Cerberus URL - */ - public EcsTaskRoleCerberusCredentialsProvider(UrlResolver urlResolver) { - super(urlResolver); - } - - /** - * Constructor to setup credentials provider using the specified - * implementation of {@link UrlResolver} and {@link OkHttpClient} - * - * @param urlResolver Resolver for resolving the Cerberus URL - * @param httpClient the client for interacting with Cerberus - */ - public EcsTaskRoleCerberusCredentialsProvider(UrlResolver urlResolver, OkHttpClient httpClient) { - super(urlResolver, httpClient); - } - - /** - * Constructor to setup credentials provider using the specified - * implementation of {@link UrlResolver} - * - * @param urlResolver Resolver for resolving the Cerberus URL - * @param xCerberusClientOverride Overrides the default header value for the 'X-Cerberus-Client' header - */ - public EcsTaskRoleCerberusCredentialsProvider(UrlResolver urlResolver, String xCerberusClientOverride) { - super(urlResolver, xCerberusClientOverride); - } - - public boolean shouldRun() { - return EnvironmentUtils.isRunningInEcs(); - } - - /** - * Looks up the IAM roles assigned to the task via the ECS task metadata - * service. An attempt is made to authenticate and decrypt the Cerberus - * auth response with KMS using the task execution role. If successful, - * the token retrieved is cached locally for future calls to - * {@link BaseAwsCredentialsProvider#getCredentials()}. - */ - @Override - protected void authenticate() { - String roleArn = getRoleArn(); - Region region = getRegion(); - - try { - getAndSetToken(roleArn, region); - return; - } catch (AmazonClientException ace) { - LOGGER.warn("Unexpected error communicating with AWS services.", ace); - } catch (JsonSyntaxException jse) { - LOGGER.error("The decrypted auth response was not in the expected format!", jse); - } catch (CerberusClientException sce) { - LOGGER.warn("Unable to acquire Cerberus token for IAM role: " + roleArn, sce); - } - - throw new CerberusClientException("Unable to acquire token with ECS task execution role."); - } - - private String getRoleArn(){ - JsonNode node; - JsonNode roleArn; - try { - String credentialsResponse = EC2CredentialsUtils.getInstance().readResource( - getCredentialsEndpoint()); - - node = Jackson.jsonNodeOf(credentialsResponse); - roleArn = node.get(ROLE_ARN); - if (roleArn == null){ - throw new CerberusClientException("Task execution role ARN not found in task credentials."); - } - return roleArn.asText(); - } catch (JsonMappingException e) { - LOGGER.error("Unable to parse response returned from service endpoint", e); - } catch (IOException e) { - LOGGER.error("Unable to load credentials from service endpoint", e); - } catch (AmazonClientException ace) { - LOGGER.warn("Unexpected error communicating with AWS services.", ace); - } - throw new CerberusClientException("Unable to find task execution role ARN."); - } - - private Region getRegion(){ - try { - String credentialsResponse = EC2CredentialsUtils.getInstance().readResource(getMetadataEndpoint()); - JsonNode node = Jackson.jsonNodeOf(credentialsResponse); - JsonNode taskArn = node.get("TaskARN"); - final Matcher matcher = TASK_ARN_PATTERN.matcher(taskArn.asText()); - - if (matcher.matches()) { - final String region = matcher.group("region"); - if (StringUtils.isNotBlank(region)) { - return Region.getRegion(Regions.fromName(region)); - } else { - LOGGER.warn("Cannot parse region from task ARN {}", taskArn.asText()); - } - } - } catch (IOException e) { - LOGGER.warn("Unable to read resource from the task metadata endpoint.", e); - } catch (URISyntaxException e) { - LOGGER.warn(ECS_CREDENTIALS_ENDPOINT + ECS_TASK_METADATA_RELATIVE_URI + " could not be parsed as a URI reference."); - } catch (RuntimeException e) { - LOGGER.warn("Region lookup failed", e); - } - LOGGER.info("Using default region as fallback."); - return Region.getRegion(Regions.DEFAULT_REGION); - } - - private URI getMetadataEndpoint() throws URISyntaxException { - return new URI(ECS_CREDENTIALS_ENDPOINT + ECS_TASK_METADATA_RELATIVE_URI); - } - - - private URI getCredentialsEndpoint(){ - String path = System.getenv(ECS_CONTAINER_CREDENTIALS_PATH); - if (path == null) { - throw new CerberusClientException("The environment variable " + ECS_CONTAINER_CREDENTIALS_PATH + " is empty"); - } - try { - return new URI(ECS_CREDENTIALS_ENDPOINT + path); - } catch (URISyntaxException e) { - throw new CerberusClientException(ECS_CREDENTIALS_ENDPOINT + path + " could not be parsed as a URI reference."); - } - } -} diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/InstanceRoleCerberusCredentialsProvider.java b/src/main/java/com/nike/cerberus/client/auth/aws/InstanceRoleCerberusCredentialsProvider.java deleted file mode 100644 index 953d1a1..0000000 --- a/src/main/java/com/nike/cerberus/client/auth/aws/InstanceRoleCerberusCredentialsProvider.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client.auth.aws; - -import com.amazonaws.AmazonClientException; -import com.amazonaws.regions.Region; -import com.amazonaws.regions.Regions; -import com.amazonaws.util.EC2MetadataUtils; -import com.google.gson.JsonSyntaxException; -import com.nike.cerberus.client.CerberusClientException; -import com.nike.cerberus.client.UrlResolver; -import com.nike.cerberus.client.auth.CerberusCredentialsProvider; -import com.nike.cerberus.client.util.EnvironmentUtils; -import okhttp3.OkHttpClient; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashSet; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static com.nike.cerberus.client.auth.aws.StaticIamRoleCerberusCredentialsProvider.IAM_ROLE_ARN_FORMAT; - - -/** - * {@link CerberusCredentialsProvider} implementation that uses the assigned role - * to an EC2 instance to authenticate with Cerberus and decrypt the auth - * response using KMS. If the assigned role has been granted the appropriate - * provisioned for usage of Cerberus, it will succeed and have a token that can be - * used to interact with Cerberus. - *

- * This class uses the AWS Instance Metadata endpoint to look-up information automatically. - * - * @see AWS Instance Metadata - */ -public class InstanceRoleCerberusCredentialsProvider extends BaseAwsCredentialsProvider { - - private static final Logger LOGGER = LoggerFactory.getLogger(InstanceRoleCerberusCredentialsProvider.class); - - public static final Pattern IAM_ARN_PATTERN = Pattern.compile("(arn\\:aws\\:iam\\:\\:)(?[0-9].*)(\\:.*)"); - - private static final Pattern INSTANCE_PROFILE_ARN_PATTERN = Pattern.compile("arn:aws:iam::(.*?):instance-profile/(.*)"); - - /** - * Constructor to setup credentials provider using the specified - * implementation of {@link UrlResolver} - * - * @param urlResolver Resolver for resolving the Cerberus URL - */ - public InstanceRoleCerberusCredentialsProvider(UrlResolver urlResolver) { - super(urlResolver); - } - - /** - * Constructor to setup credentials provider using the specified - * implementation of {@link UrlResolver} and {@link OkHttpClient} - * - * @param urlResolver Resolver for resolving the Cerberus URL - * @param httpClient the client for interacting with Cerberus - */ - public InstanceRoleCerberusCredentialsProvider(UrlResolver urlResolver, OkHttpClient httpClient) { - super(urlResolver, httpClient); - } - - /** - * Constructor to setup credentials provider using the specified - * implementation of {@link UrlResolver} - * - * @param urlResolver Resolver for resolving the Cerberus URL - * @param xCerberusClientOverride Overrides the default header value for the 'X-Cerberus-Client' header - */ - public InstanceRoleCerberusCredentialsProvider(UrlResolver urlResolver, String xCerberusClientOverride) { - super(urlResolver, xCerberusClientOverride); - } - - public boolean shouldRun() { - return EnvironmentUtils.isRunningInEc2(); - } - - /** - * Looks up the IAM roles assigned to the instance via the EC2 metadata - * service. For each role assigned, an attempt is made to authenticate and - * decrypt the Cerberus auth response with KMS. If successful, the token - * retrieved is cached locally for future calls to - * {@link BaseAwsCredentialsProvider#getCredentials()}. - */ - @Override - protected void authenticate() { - try { - final String instanceProfileArn = getInstanceProfileArn(); - final Set iamRoleSet = EC2MetadataUtils.getIAMSecurityCredentials().keySet(); - Region region = Regions.getCurrentRegion(); - - for (String iamRole : buildIamRoleArns(instanceProfileArn, iamRoleSet)) { - try { - getAndSetToken(iamRole, region); - return; - } catch (CerberusClientException sce) { - LOGGER.warn("Unable to acquire Cerberus token for IAM role: " + iamRole + ", instance profile was " + instanceProfileArn, sce); - } - } - } catch (AmazonClientException ace) { - LOGGER.warn("Unexpected error communicating with AWS services.", ace); - } catch (JsonSyntaxException jse) { - LOGGER.error("The decrypted auth response was not in the expected format!", jse); - } - - throw new CerberusClientException("Unable to acquire token with EC2 instance role."); - } - - /** - * Parses and returns the AWS account ID from the instance profile ARN. - * - * @return AWS account ID - * @deprecated no longer used, will be removed - */ - protected String lookupAccountId() { - final Matcher matcher = IAM_ARN_PATTERN.matcher(getInstanceProfileArn()); - - if (matcher.matches()) { - final String accountId = matcher.group("accountId"); - if (StringUtils.isNotBlank(accountId)) { - return accountId; - } - } - - throw new CerberusClientException("Unable to obtain AWS account ID from instance profile ARN."); - } - - protected String getInstanceProfileArn() { - EC2MetadataUtils.IAMInfo iamInfo = EC2MetadataUtils.getIAMInstanceProfileInfo(); - - if (iamInfo == null) { - final String errorMessage = "No IAM Instance Profile assigned to running instance."; - LOGGER.error(errorMessage); - throw new CerberusClientException(errorMessage); - } - return iamInfo.instanceProfileArn; - } - - /** - * Build a set of IAM Role ARNs from the information collected from the meta-data endpoint - * - * @param instanceProfileArn the instance-profile ARN - * @param securityCredentialsKeySet a set of role names - * @return - */ - protected static Set buildIamRoleArns(String instanceProfileArn, Set securityCredentialsKeySet) { - - final Set result = new HashSet<>(); - - final InstanceProfileInfo instanceProfileInfo = parseInstanceProfileArn(instanceProfileArn); - final String accountId = instanceProfileInfo.accountId; - final String path = parsePathFromInstanceProfileName(instanceProfileInfo.profileName); - - // There isn't a 100% reliable method for constructing the role ARN from the meta-data endpoint. - // So here we try both with and without the path that was used in the instanceProfileArn. - // The only reason we don't try and auth with the instanceProfileArn is it isn't a valid ARN type - // that can be included in a KMS key policy. - for (String roleName : securityCredentialsKeySet) { - if (path != null) { - // if path was supplied in instanceProfileArn, we'll try using it first - // there is no guarantee that the path used in the instanceProfileArn was also used in the roleArn but it is a common pattern - result.add(buildRoleArn(accountId, path, roleName)); - } - result.add(buildRoleArn(accountId, null, roleName)); - } - - return result; - } - - /** - * Parse an instance-profile ARN into parts - */ - protected static InstanceProfileInfo parseInstanceProfileArn(String instanceProfileArn) { - if (instanceProfileArn == null) { - throw new CerberusClientException("instanceProfileArn provided was null rather than valid arn"); - } - - InstanceProfileInfo info = new InstanceProfileInfo(); - Matcher matcher = INSTANCE_PROFILE_ARN_PATTERN.matcher(instanceProfileArn); - boolean found = matcher.find(); - if (!found) { - throw new CerberusClientException(String.format( - "Failed to find account id and role / instance profile name from ARN: %s using pattern %s", - instanceProfileArn, INSTANCE_PROFILE_ARN_PATTERN.pattern())); - } - - info.accountId = matcher.group(1); - info.profileName = matcher.group(2); - - return info; - } - - /** - * Parse the path out of a instanceProfileName or return null for no path - *

- * e.g. parse "foo/bar" out of "foo/bar/name" - */ - protected static String parsePathFromInstanceProfileName(String instanceProfileName) { - if (StringUtils.contains(instanceProfileName, "/")) { - return StringUtils.substringBeforeLast(instanceProfileName, "/"); - } else { - return null; - } - } - - /** - * Build a role arn from the supplied arguments - */ - protected static String buildRoleArn(String accountId, String path, String roleName) { - return String.format(IAM_ROLE_ARN_FORMAT, accountId, roleWithPath(path, roleName)); - } - - /** - * If a path is supplied, prepend it to the role name. - *

- * e.g. roleWithPath(null, "foo") returns "foo". - * e.g. roleWithPath("bar", "foo") returns "bar/foo". - * e.g. roleWithPath("bar/more", "foo") returns "bar/more/foo". - */ - protected static String roleWithPath(String path, String role) { - if (StringUtils.isBlank(path)) { - return role; - } else { - return StringUtils.appendIfMissing(path, "/") + role; - } - } - - /** - * Bean for holding Instance Profile parse results - */ - protected static class InstanceProfileInfo { - /** - * AWS Account ID - */ - String accountId; - /** - * Name found after "instance-profile/" in the instance profile ARN, includes paths e.g. "foo/bar/name" - */ - String profileName; - } -} diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/LambdaRoleCerberusCredentialsProvider.java b/src/main/java/com/nike/cerberus/client/auth/aws/LambdaRoleCerberusCredentialsProvider.java deleted file mode 100644 index 74ea550..0000000 --- a/src/main/java/com/nike/cerberus/client/auth/aws/LambdaRoleCerberusCredentialsProvider.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client.auth.aws; - -import com.amazonaws.AmazonClientException; -import com.amazonaws.regions.Region; -import com.amazonaws.regions.RegionUtils; -import com.amazonaws.services.lambda.AWSLambda; -import com.amazonaws.services.lambda.AWSLambdaClient; -import com.amazonaws.services.lambda.model.GetFunctionConfigurationRequest; -import com.amazonaws.services.lambda.model.GetFunctionConfigurationResult; -import com.google.gson.JsonSyntaxException; -import com.nike.cerberus.client.CerberusClientException; -import com.nike.cerberus.client.UrlResolver; -import com.nike.cerberus.client.auth.CerberusCredentialsProvider; -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * {@link CerberusCredentialsProvider} implementation that uses the assigned role - * to lambda function to authenticate with Cerberus and decrypt the auth - * response using KMS. If the assigned role has been granted the appropriate - * provisioned for usage of Cerberus, it will succeed and have a token that can be - * used to interact with Cerberus. - */ -public class LambdaRoleCerberusCredentialsProvider extends BaseAwsCredentialsProvider { - - public static final Logger LOGGER = LoggerFactory.getLogger(LambdaRoleCerberusCredentialsProvider.class); - - public static final Pattern LAMBDA_FUNCTION_ARN_PATTERN = - Pattern.compile("arn:aws:lambda:(?[a-zA-Z0-9-]+):(?[0-9]{12}):function:(?[a-zA-Z0-9-_]+)(:(?.*))?"); - - public static final Pattern IAM_ROLE_ARN_PATTERN = - Pattern.compile("arn:aws:iam::(?\\d{12}):role/?(?[a-zA-Z_0-9+=,.@\\-_/]+)"); - - private final String functionName; - private final String qualifier; - private final String region; - - /** - * Constructor to setup credentials provider using the specified - * implementation of {@link UrlResolver} - * - * @param urlResolver Resolver for resolving the Cerberus URL - * @param invokedFunctionArn The invoked lambda function's ARN - */ - public LambdaRoleCerberusCredentialsProvider(final UrlResolver urlResolver, final String invokedFunctionArn) { - super(urlResolver); - final Matcher matcher = LAMBDA_FUNCTION_ARN_PATTERN.matcher(invokedFunctionArn); - - if (!matcher.matches()) { - throw new IllegalArgumentException("invokedFunctionArn not a properly formatted lambda function ARN."); - } - - this.functionName = matcher.group("functionName"); - this.qualifier = matcher.group("qualifier"); - this.region = matcher.group("awsRegion"); - } - - /** - * Looks up the assigned role for the running Lambda via the GetFunctionConfiguration API. Requests a token from - * Cerberus and attempts to decrypt it as that role. - */ - @Override - protected void authenticate() { - final Region currentRegion = RegionUtils.getRegion(this.region); - - final AWSLambda lambdaClient = new AWSLambdaClient(); - lambdaClient.setRegion(currentRegion); - - final GetFunctionConfigurationResult functionConfiguration = lambdaClient.getFunctionConfiguration( - new GetFunctionConfigurationRequest() - .withFunctionName(functionName) - .withQualifier(qualifier)); - - final String roleArn = functionConfiguration.getRole(); - - if (StringUtils.isBlank(roleArn)) { - throw new IllegalStateException("Lambda function has no assigned role, aborting Cerberus authentication."); - } - - final Matcher roleArnMatcher = IAM_ROLE_ARN_PATTERN.matcher(roleArn); - - if (!roleArnMatcher.matches()) { - throw new IllegalStateException("Lambda function assigned role is not a valid IAM role ARN."); - } - - try { - getAndSetToken(roleArn, currentRegion); - return; - } catch (AmazonClientException ace) { - LOGGER.warn("Unexpected error communicating with AWS services.", ace); - } catch (JsonSyntaxException jse) { - LOGGER.error("The decrypted auth response was not in the expected format!", jse); - } catch (CerberusClientException sce) { - LOGGER.warn("Unable to acquire Cerberus token for IAM role: " + roleArn, sce); - } - - throw new CerberusClientException("Unable to acquire token with Lambda instance role."); - } - - -} diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/StaticIamRoleCerberusCredentialsProvider.java b/src/main/java/com/nike/cerberus/client/auth/aws/StaticIamRoleCerberusCredentialsProvider.java deleted file mode 100644 index fb0130f..0000000 --- a/src/main/java/com/nike/cerberus/client/auth/aws/StaticIamRoleCerberusCredentialsProvider.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client.auth.aws; - -import com.amazonaws.regions.Region; -import com.amazonaws.regions.Regions; -import com.nike.cerberus.client.StaticCerberusUrlResolver; -import com.nike.cerberus.client.UrlResolver; - -/** - * Provider for allowing users to explicitly set the account id, role name and region that they want to authenticate as. - */ -public class StaticIamRoleCerberusCredentialsProvider extends BaseAwsCredentialsProvider { - - public static final String IAM_ROLE_ARN_FORMAT = "arn:aws:iam::%s:role/%s"; - protected String iamPrincipalArn; - protected Region region; - - public StaticIamRoleCerberusCredentialsProvider(UrlResolver urlResolver, String accountId, String roleName, String region) { - this(urlResolver); - this.iamPrincipalArn = generateIamRoleArn(accountId, roleName); - this.region = Region.getRegion(Regions.fromName(region)); - } - - public StaticIamRoleCerberusCredentialsProvider(String cerberusUrl, String accountId, String roleName, String region) { - this(new StaticCerberusUrlResolver(cerberusUrl)); - this.iamPrincipalArn = generateIamRoleArn(accountId, roleName); - this.region = Region.getRegion(Regions.fromName(region)); - } - - public StaticIamRoleCerberusCredentialsProvider(UrlResolver urlResolver, String accountId, String roleName, Region region) { - this(urlResolver); - this.iamPrincipalArn = generateIamRoleArn(accountId, roleName); - this.region = region; - } - - public StaticIamRoleCerberusCredentialsProvider(String cerberusUrl, String accountId, String roleName, Region region) { - this(new StaticCerberusUrlResolver(cerberusUrl)); - this.iamPrincipalArn = generateIamRoleArn(accountId, roleName); - this.region = region; - } - - public StaticIamRoleCerberusCredentialsProvider(UrlResolver urlResolver, String iamRoleArn, String region) { - this(urlResolver); - this.iamPrincipalArn = iamRoleArn; - this.region = Region.getRegion(Regions.fromName(region)); - } - - - public StaticIamRoleCerberusCredentialsProvider(String cerberusUrl, String iamRoleArn, String region) { - this(new StaticCerberusUrlResolver(cerberusUrl)); - this.iamPrincipalArn = iamRoleArn; - this.region = Region.getRegion(Regions.fromName(region)); - } - - - public StaticIamRoleCerberusCredentialsProvider(UrlResolver urlResolver, String iamRoleArn, Region region) { - this(urlResolver); - this.iamPrincipalArn = iamRoleArn; - this.region = region; - } - - public StaticIamRoleCerberusCredentialsProvider(String cerberusUrl, String iamRoleArn, Region region) { - this(new StaticCerberusUrlResolver(cerberusUrl)); - this.iamPrincipalArn = iamRoleArn; - this.region = region; - } - - private StaticIamRoleCerberusCredentialsProvider(UrlResolver urlResolver) { - super(urlResolver); - } - - private String generateIamRoleArn(String accountId, String roleName) { - - return String.format(IAM_ROLE_ARN_FORMAT, accountId, roleName); - } - - @Override - protected void authenticate() { - getAndSetToken(iamPrincipalArn, region); - } -} diff --git a/src/test/java/com/nike/cerberus/client/auth/aws/EcsTaskExecutionRoleCerberusCredentialsProviderTest.java b/src/test/java/com/nike/cerberus/client/auth/aws/EcsTaskExecutionRoleCerberusCredentialsProviderTest.java deleted file mode 100644 index 2e24bad..0000000 --- a/src/test/java/com/nike/cerberus/client/auth/aws/EcsTaskExecutionRoleCerberusCredentialsProviderTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client.auth.aws; - -import com.amazonaws.AmazonClientException; -import com.amazonaws.internal.EC2CredentialsUtils; -import com.amazonaws.services.kms.AWSKMSClient; -import com.nike.cerberus.client.CerberusClientException; -import com.nike.cerberus.client.DefaultCerberusUrlResolver; -import com.nike.cerberus.client.UrlResolver; -import com.nike.cerberus.client.auth.CerberusCredentials; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mockito; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import java.io.IOException; -import java.net.URI; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.powermock.api.mockito.PowerMockito.*; - -/** - * Tests the EcsTaskRoleCerberusCredentialsProvider class - */ -@RunWith(PowerMockRunner.class) -@PrepareForTest({AWSKMSClient.class, - EcsTaskRoleCerberusCredentialsProvider.class, EC2CredentialsUtils.class}) -@PowerMockIgnore({"javax.management.*","javax.net.*"}) -public class EcsTaskExecutionRoleCerberusCredentialsProviderTest extends BaseCredentialsProviderTest { - - private UrlResolver urlResolver; - - private AWSKMSClient kmsClient; - - private EcsTaskRoleCerberusCredentialsProvider provider; - - private EC2CredentialsUtils ec2CredentialsUtils; - - @Before - public void setup() throws Exception { - kmsClient = mock(AWSKMSClient.class); - urlResolver = mock(UrlResolver.class); - provider = new EcsTaskRoleCerberusCredentialsProvider(urlResolver); - - whenNew(AWSKMSClient.class).withAnyArguments().thenReturn(kmsClient); - mockStatic(System.class); - mockGetCredentialsRelativeUri(); - mockStatic(EC2CredentialsUtils.class); - ec2CredentialsUtils = mock(EC2CredentialsUtils.class); - when(EC2CredentialsUtils.getInstance()).thenReturn(ec2CredentialsUtils); - } - - @Test - public void getCredentials_returns_valid_credentials() throws IOException { - - MockWebServer mockWebServer = new MockWebServer(); - mockWebServer.start(); - final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); - - mockDecrypt(kmsClient, DECODED_AUTH_DATA); - when(urlResolver.resolve()).thenReturn(cerberusUrl); - - System.setProperty(DefaultCerberusUrlResolver.CERBERUS_ADDR_SYS_PROPERTY, cerberusUrl); - mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(AUTH_RESPONSE)); - - when(ec2CredentialsUtils.readResource(Mockito.any(URI.class))) - .thenReturn("{\"RoleArn\":\"arn:aws:iam::123456789:role/ecsTaskExecutionRole\"}") - .thenReturn("{\"TaskARN\":\"arn:aws:ecs:us-west-1:123456789:task/task-id\"}"); - - CerberusCredentials credentials = provider.getCredentials(); - assertThat(credentials.getToken()).isEqualTo(AUTH_TOKEN); - - } - - @Test(expected = CerberusClientException.class) - public void getCredentials_throws_client_exception_when_task_arn_is_missing() throws IOException { - when(ec2CredentialsUtils.readResource(Mockito.any(URI.class))) - .thenReturn("{}") - .thenReturn("{\"TaskARN\":\"arn:aws:ecs:us-west-1:123456789:task/task-id\"}"); - provider.getCredentials(); - } - - @Test(expected = CerberusClientException.class) - public void getCredentials_throws_client_exception_when_not_running_in_ecs_task() throws IOException{ - when(ec2CredentialsUtils.readResource(Mockito.any(URI.class))).thenThrow(new AmazonClientException("BAD")); - provider.getCredentials(); - } - - private void mockGetCredentialsRelativeUri() { - when(System.getenv("AWS_CONTAINER_CREDENTIALS_RELATIVE_URI")).thenReturn("/mockuri"); - } -} \ No newline at end of file diff --git a/src/test/java/com/nike/cerberus/client/auth/aws/InstanceRoleCerberusCredentialsProviderTest.java b/src/test/java/com/nike/cerberus/client/auth/aws/InstanceRoleCerberusCredentialsProviderTest.java deleted file mode 100644 index ab99926..0000000 --- a/src/test/java/com/nike/cerberus/client/auth/aws/InstanceRoleCerberusCredentialsProviderTest.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client.auth.aws; - -import com.amazonaws.AmazonClientException; -import com.amazonaws.regions.Regions; -import com.amazonaws.services.kms.AWSKMSClient; -import com.amazonaws.util.EC2MetadataUtils; -import com.nike.cerberus.client.CerberusClientException; -import com.nike.cerberus.client.DefaultCerberusUrlResolver; -import com.nike.cerberus.client.UrlResolver; -import com.nike.cerberus.client.auth.CerberusCredentials; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.internal.util.collections.Sets; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import java.io.IOException; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import static com.nike.cerberus.client.auth.aws.InstanceRoleCerberusCredentialsProvider.buildIamRoleArns; -import static com.nike.cerberus.client.auth.aws.InstanceRoleCerberusCredentialsProvider.buildRoleArn; -import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertEquals; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -/** - * Tests the InstanceRoleCerberusCredentialsProvider class - */ -@RunWith(PowerMockRunner.class) -@PrepareForTest({AWSKMSClient.class, - EC2MetadataUtils.class, InstanceRoleCerberusCredentialsProvider.class}) -@PowerMockIgnore({"javax.management.*","javax.net.*"}) -public class InstanceRoleCerberusCredentialsProviderTest extends BaseCredentialsProviderTest { - - private static final String GOOD_INSTANCE_PROFILE_ARN = "arn:aws:iam::107274433934:instance-profile/rawr"; - - private static final String DEFAULT_ROLE = "role"; - - private UrlResolver urlResolver; - - private AWSKMSClient kmsClient; - - private InstanceRoleCerberusCredentialsProvider provider; - - @Before - public void setup() throws Exception { - kmsClient = mock(AWSKMSClient.class); - urlResolver = mock(UrlResolver.class); - provider = new InstanceRoleCerberusCredentialsProvider(urlResolver); - - whenNew(AWSKMSClient.class).withAnyArguments().thenReturn(kmsClient); - mockStatic(EC2MetadataUtils.class); - mockGetCurrentRegion(); - } - - @Test - public void getCredentials_returns_valid_credentials() throws IOException { - - MockWebServer mockWebServer = new MockWebServer(); - mockWebServer.start(); - final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); - - mockGetIamSecurityCredentials(DEFAULT_ROLE); - mockGetIamInstanceProfileInfo(GOOD_INSTANCE_PROFILE_ARN); - mockDecrypt(kmsClient, DECODED_AUTH_DATA); - when(urlResolver.resolve()).thenReturn(cerberusUrl); - - System.setProperty(DefaultCerberusUrlResolver.CERBERUS_ADDR_SYS_PROPERTY, cerberusUrl); - mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(AUTH_RESPONSE)); - - CerberusCredentials credentials = provider.getCredentials(); - assertThat(credentials.getToken()).isEqualTo(AUTH_TOKEN); - - } - - @Test(expected = CerberusClientException.class) - public void getCredentials_throws_client_exception_when_accountId_missing() { - mockGetIamSecurityCredentials(DEFAULT_ROLE); - mockGetIamInstanceProfileInfo("arn:aws:iam:instance-profile/rawr"); - - provider.getCredentials(); - } - - @Test(expected = CerberusClientException.class) - public void getCredentials_throws_client_exception_when_no_roles_are_set() { - when(EC2MetadataUtils.getIAMSecurityCredentials()) - .thenReturn(Collections.emptyMap()); - mockGetIamInstanceProfileInfo(GOOD_INSTANCE_PROFILE_ARN); - - provider.getCredentials(); - } - - @Test(expected = CerberusClientException.class) - public void getCredentials_throws_client_exception_when_not_running_on_ec2_instance() { - when(EC2MetadataUtils.getIAMSecurityCredentials()).thenThrow(new AmazonClientException("BAD")); - - provider.getCredentials(); - } - - @Test(expected = CerberusClientException.class) - public void getCredentials_thorws_client_exception_when_no_instance_profile_assigned() { - when(EC2MetadataUtils.getIAMInstanceProfileInfo()).thenReturn(null); - - provider.getCredentials(); - } - - private void mockGetCurrentRegion() { - when(EC2MetadataUtils.getEC2InstanceRegion()).thenReturn(Regions.US_WEST_2.getName()); - } - - private void mockGetIamSecurityCredentials(final String role) { - Map map = new HashMap<>(); - map.put(role, null); - when(EC2MetadataUtils.getIAMSecurityCredentials()).thenReturn(map); - } - - private void mockGetIamInstanceProfileInfo(final String instanceProfileArn) { - EC2MetadataUtils.IAMInfo iamInfo = new EC2MetadataUtils.IAMInfo(); - iamInfo.instanceProfileArn = instanceProfileArn; - when(EC2MetadataUtils.getIAMInstanceProfileInfo()).thenReturn(iamInfo); - } - - @Test - public void test_buildIamRoleArns_with_path() { - String instanceProfileArn = "arn:aws:iam::1234567890123:instance-profile/brewmaster/foo/brewmaster-foo-cerberus"; - Set roles = Sets.newSet("brewmaster-foo-cerberus"); - - Set results = buildIamRoleArns(instanceProfileArn, roles); - assertEquals(2, results.size()); - Iterator iterator = results.iterator(); - assertEquals("arn:aws:iam::1234567890123:role/brewmaster/foo/brewmaster-foo-cerberus", iterator.next()); - assertEquals("arn:aws:iam::1234567890123:role/brewmaster-foo-cerberus", iterator.next()); - } - - @Test - public void test_buildIamRoleArns_with_CloudFormation_style_names() { - String instanceProfileArn = "arn:aws:iam::1234567890123:instance-profile/foo-cerberus-LKSJDFIWERWER"; - Set roles = Sets.newSet("foo-cerberus-SDFLKJRWE234"); - - Set results = buildIamRoleArns(instanceProfileArn, roles); - assertEquals(1, results.size()); - String result = results.iterator().next(); - - assertEquals("arn:aws:iam::1234567890123:role/foo-cerberus-SDFLKJRWE234", result); - } - - @Test - public void test_buildIamRoleArns_CloudFormation_style_names_with_paths() { - String instanceProfileArn = "arn:aws:iam::1234567890123:instance-profile/brewmaster/foo/foo-cerberus-LKSJDFIWERWER"; - Set roles = Sets.newSet("foo-cerberus-SDFLKJRWE234"); - - Set results = buildIamRoleArns(instanceProfileArn, roles); - assertEquals(2, results.size()); - } - - @Test - public void test_parseInstanceProfileArn_with_path() { - String instanceProfileArn = "arn:aws:iam::1234567890123:instance-profile/brewmaster/foo/brewmaster-foo-cerberus"; - InstanceRoleCerberusCredentialsProvider.InstanceProfileInfo info = InstanceRoleCerberusCredentialsProvider.parseInstanceProfileArn(instanceProfileArn); - assertEquals("1234567890123", info.accountId); - assertEquals("brewmaster/foo/brewmaster-foo-cerberus", info.profileName); - } - - @Test - public void test_parseInstanceProfileArn_without_path() { - String instanceProfileArn = "arn:aws:iam::1234567890123:instance-profile/foo-cerberus"; - InstanceRoleCerberusCredentialsProvider.InstanceProfileInfo info = InstanceRoleCerberusCredentialsProvider.parseInstanceProfileArn(instanceProfileArn); - assertEquals("1234567890123", info.accountId); - assertEquals("foo-cerberus", info.profileName); - } - - @Test - public void test_parsePathFromInstanceProfileName() { - String instanceProfileName = "brewmaster/foo/brewmaster-foo-cerberus"; - String path = InstanceRoleCerberusCredentialsProvider.parsePathFromInstanceProfileName(instanceProfileName); - assertEquals("brewmaster/foo", path); - } - - @Test - public void test_buildRoleArn_without_path() { - String accountId = "1234567890123"; - assertEquals("arn:aws:iam::1234567890123:role/foo", buildRoleArn(accountId, "", "foo")); - } - - @Test - public void test_buildRoleArn_with_null_path() { - String accountId = "1234567890123"; - assertEquals("arn:aws:iam::1234567890123:role/foo", buildRoleArn(accountId, null, "foo")); - } - - @Test - public void test_buildRoleArn_with_path() { - String accountId = "1234567890123"; - assertEquals("arn:aws:iam::1234567890123:role/bar/foo", buildRoleArn(accountId, "bar", "foo")); - } - - @Test - public void test_buildRoleArn_with_longer_path() { - String accountId = "1234567890123"; - assertEquals("arn:aws:iam::1234567890123:role/bar/more/foo", buildRoleArn(accountId, "bar/more", "foo")); - } -} \ No newline at end of file diff --git a/src/test/java/com/nike/cerberus/client/auth/aws/LambdaRoleCerberusCredentialsProviderTest.java b/src/test/java/com/nike/cerberus/client/auth/aws/LambdaRoleCerberusCredentialsProviderTest.java deleted file mode 100644 index 86534da..0000000 --- a/src/test/java/com/nike/cerberus/client/auth/aws/LambdaRoleCerberusCredentialsProviderTest.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client.auth.aws; - -import com.amazonaws.regions.RegionUtils; -import com.amazonaws.regions.Regions; -import com.amazonaws.services.kms.AWSKMSClient; -import com.amazonaws.services.lambda.AWSLambdaClient; -import com.amazonaws.services.lambda.model.GetFunctionConfigurationRequest; -import com.amazonaws.services.lambda.model.GetFunctionConfigurationResult; -import com.nike.cerberus.client.CerberusClientException; -import com.nike.cerberus.client.DefaultCerberusUrlResolver; -import com.nike.cerberus.client.UrlResolver; -import com.nike.cerberus.client.auth.CerberusCredentials; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.powermock.api.mockito.PowerMockito; -import org.powermock.core.classloader.annotations.PowerMockIgnore; -import org.powermock.core.classloader.annotations.PrepareForTest; -import org.powermock.modules.junit4.PowerMockRunner; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.mockStatic; -import static org.powermock.api.mockito.PowerMockito.when; -import static org.powermock.api.mockito.PowerMockito.whenNew; - -@RunWith(PowerMockRunner.class) -@PrepareForTest({AWSKMSClient.class, Regions.class, AWSLambdaClient.class, LambdaRoleCerberusCredentialsProvider.class}) -@PowerMockIgnore({"javax.management.*", "javax.net.*"}) -public class LambdaRoleCerberusCredentialsProviderTest extends BaseCredentialsProviderTest { - private static final String VALID_LAMBDA_ARN = "arn:aws:lambda:us-west-2:123456789012:function:lambda-test:1.1.0"; - private static final String VALID_LAMBDA_ARN_NO_QUALIFIER = "arn:aws:lambda:us-west-2:012345678912:function:lambda-test"; - private static final String VALID_IAM_ARN = "arn:aws:iam::123456789012:role/cerberus-role"; - private static final String INVALID_ARN = "invalid-arn"; - - private AWSKMSClient kmsClient; - private UrlResolver urlResolver; - private AWSLambdaClient lambdaClient; - private MockWebServer mockWebServer; - private String cerberusUrl; - - @Before - public void setup() throws Exception { - kmsClient = mock(AWSKMSClient.class); - urlResolver = mock(UrlResolver.class); - lambdaClient = mock(AWSLambdaClient.class); - - mockWebServer = new MockWebServer(); - mockWebServer.start(); - cerberusUrl = "http://localhost:" + mockWebServer.getPort(); - - when(urlResolver.resolve()).thenReturn(cerberusUrl); - - - mockStatic(Regions.class); - - when(Regions.getCurrentRegion()).thenReturn(RegionUtils.getRegion("us-west-2")); - whenNew(AWSLambdaClient.class).withNoArguments().thenReturn(lambdaClient); - whenNew(AWSKMSClient.class).withAnyArguments().thenReturn(kmsClient); - } - - @Test(expected = IllegalArgumentException.class) - public void provider_creation_fails_on_invalid_arn() { - LambdaRoleCerberusCredentialsProvider provider = new LambdaRoleCerberusCredentialsProvider(urlResolver, "invalid-lambda-arn"); - } - - @Test - public void valid_arn_and_no_qualifier_matched_properly_on_provider_creation() { - LambdaRoleCerberusCredentialsProvider provider = new LambdaRoleCerberusCredentialsProvider(urlResolver, VALID_LAMBDA_ARN_NO_QUALIFIER); - } - - @Test - public void getCredentials_returns_valid_creds() throws Exception { - final LambdaRoleCerberusCredentialsProvider provider = PowerMockito.spy(new LambdaRoleCerberusCredentialsProvider(urlResolver, VALID_LAMBDA_ARN)); - final GetFunctionConfigurationRequest request = new GetFunctionConfigurationRequest().withFunctionName("lambda-test").withQualifier("1.1.0"); - - when(urlResolver.resolve()).thenReturn(cerberusUrl); - - System.setProperty(DefaultCerberusUrlResolver.CERBERUS_ADDR_SYS_PROPERTY, cerberusUrl); - mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(AUTH_RESPONSE)); - - mockDecrypt(kmsClient, DECODED_AUTH_DATA); - - when(lambdaClient.getFunctionConfiguration(request)).thenReturn(new GetFunctionConfigurationResult().withRole(VALID_IAM_ARN)); - - final CerberusCredentials credentials = provider.getCredentials(); - - assertThat(credentials.getToken()).isEqualTo(AUTH_TOKEN); - verify(lambdaClient, times(1)).getFunctionConfiguration(request); - } - - @Test(expected = CerberusClientException.class) - public void CerberusClientException_thrown_when_bad_json_returned() throws Exception { - final LambdaRoleCerberusCredentialsProvider provider = PowerMockito.spy(new LambdaRoleCerberusCredentialsProvider(urlResolver, VALID_LAMBDA_ARN)); - final GetFunctionConfigurationRequest request = new GetFunctionConfigurationRequest().withFunctionName("lambda-test").withQualifier("1.1.0"); - - - System.setProperty(DefaultCerberusUrlResolver.CERBERUS_ADDR_SYS_PROPERTY, cerberusUrl); - mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(BAD_AUTH_RESPONSE_JSON)); - - mockDecrypt(kmsClient, DECODED_AUTH_DATA); - - when(lambdaClient.getFunctionConfiguration(request)).thenReturn(new GetFunctionConfigurationResult().withRole(VALID_IAM_ARN)); - - provider.getCredentials(); - } - - @Test(expected = IllegalStateException.class) - public void authenticate_fails_when_lambda_has_invalid_assigned_role() throws Exception { - final LambdaRoleCerberusCredentialsProvider provider = new LambdaRoleCerberusCredentialsProvider(urlResolver, VALID_LAMBDA_ARN); - final GetFunctionConfigurationRequest request = new GetFunctionConfigurationRequest().withFunctionName("lambda-test").withQualifier("1.1.0"); - - when(lambdaClient.getFunctionConfiguration(request)).thenReturn(new GetFunctionConfigurationResult().withRole(INVALID_ARN)); - - provider.authenticate(); - } - - - @Test(expected = IllegalStateException.class) - public void authenticate_fails_when_lambda_has_no_assigned_role() throws Exception { - final LambdaRoleCerberusCredentialsProvider provider = new LambdaRoleCerberusCredentialsProvider(urlResolver, VALID_LAMBDA_ARN); - final GetFunctionConfigurationRequest request = new GetFunctionConfigurationRequest().withFunctionName("lambda-test").withQualifier("1.1.0"); - - when(lambdaClient.getFunctionConfiguration(request)).thenReturn(new GetFunctionConfigurationResult().withRole("")); - - provider.authenticate(); - } - - @After - public void resetMocks() { - reset(lambdaClient, kmsClient); - } - - -} \ No newline at end of file diff --git a/src/test/java/com/nike/cerberus/client/auth/aws/StaticIamRoleCerberusCredentialsProviderTest.java b/src/test/java/com/nike/cerberus/client/auth/aws/StaticIamRoleCerberusCredentialsProviderTest.java deleted file mode 100644 index b728173..0000000 --- a/src/test/java/com/nike/cerberus/client/auth/aws/StaticIamRoleCerberusCredentialsProviderTest.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client.auth.aws; - -import com.amazonaws.regions.Region; -import com.amazonaws.regions.Regions; -import com.nike.cerberus.client.StaticCerberusUrlResolver; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class StaticIamRoleCerberusCredentialsProviderTest { - - private static final String ACCOUNT_ID = "1234"; - private static final String ROLE_NAME = "foo/base/bar"; - private static final String ROLE_ARN = String.format("arn:aws:iam::%s:role/%s", ACCOUNT_ID, ROLE_NAME); - private static final String REGION_STRING = "us-west-2"; - private static final Region REGION = Region.getRegion(Regions.US_WEST_2); - - @Test - public void test_constructor_1() { - StaticIamRoleCerberusCredentialsProvider provider = new StaticIamRoleCerberusCredentialsProvider( - new StaticCerberusUrlResolver("foo"), - ACCOUNT_ID, - ROLE_NAME, - REGION_STRING - ); - - assertEquals(ROLE_ARN, provider.iamPrincipalArn); - assertEquals(REGION, provider.region); - } - - @Test - public void test_constructor_2() { - StaticIamRoleCerberusCredentialsProvider provider = new StaticIamRoleCerberusCredentialsProvider( - "foo", - ACCOUNT_ID, - ROLE_NAME, - REGION_STRING - ); - - assertEquals(ROLE_ARN, provider.iamPrincipalArn); - assertEquals(REGION, provider.region); - } - - @Test - public void test_constructor_3() { - StaticIamRoleCerberusCredentialsProvider provider = new StaticIamRoleCerberusCredentialsProvider( - new StaticCerberusUrlResolver("foo"), - ACCOUNT_ID, - ROLE_NAME, - REGION - ); - - assertEquals(ROLE_ARN, provider.iamPrincipalArn); - assertEquals(REGION, provider.region); - } - - @Test - public void test_constructor_4() { - StaticIamRoleCerberusCredentialsProvider provider = new StaticIamRoleCerberusCredentialsProvider( - "foo", - ACCOUNT_ID, - ROLE_NAME, - REGION - ); - - assertEquals(ROLE_ARN, provider.iamPrincipalArn); - assertEquals(REGION, provider.region); - } - - @Test - public void test_constructor_5() { - StaticIamRoleCerberusCredentialsProvider provider = new StaticIamRoleCerberusCredentialsProvider( - new StaticCerberusUrlResolver("foo"), - ROLE_ARN, - REGION_STRING - ); - - assertEquals(ROLE_ARN, provider.iamPrincipalArn); - assertEquals(REGION, provider.region); - } - - @Test - public void test_constructor_6() { - StaticIamRoleCerberusCredentialsProvider provider = new StaticIamRoleCerberusCredentialsProvider( - "foo", - ROLE_ARN, - REGION_STRING - ); - - assertEquals(ROLE_ARN, provider.iamPrincipalArn); - assertEquals(REGION, provider.region); - } - - @Test - public void test_constructor_7() { - StaticIamRoleCerberusCredentialsProvider provider = new StaticIamRoleCerberusCredentialsProvider( - new StaticCerberusUrlResolver("foo"), - ROLE_ARN, - REGION - ); - - assertEquals(ROLE_ARN, provider.iamPrincipalArn); - assertEquals(REGION, provider.region); - } - - @Test - public void test_constructor_8() { - StaticIamRoleCerberusCredentialsProvider provider = new StaticIamRoleCerberusCredentialsProvider( - "foo", - ROLE_ARN, - REGION - ); - - assertEquals(ROLE_ARN, provider.iamPrincipalArn); - assertEquals(REGION, provider.region); - } - -} From 09b1e14a17535179c263a8bd7f73e49a9f15c16d Mon Sep 17 00:00:00 2001 From: melanahammel Date: Fri, 14 Dec 2018 11:28:56 -0800 Subject: [PATCH 05/14] refactored Cerberus Client and tests for STS auth --- .../client/auth/aws/CerberusClientTest.java | 83 +++---------- .../nike/cerberus/client/CerberusClient.java | 110 ++++++++---------- .../cerberus/client/CerberusClientTest.java | 47 ++++---- 3 files changed, 90 insertions(+), 150 deletions(-) diff --git a/src/integration/java/com/nike/cerberus/client/auth/aws/CerberusClientTest.java b/src/integration/java/com/nike/cerberus/client/auth/aws/CerberusClientTest.java index 5b6d066..7607ec4 100644 --- a/src/integration/java/com/nike/cerberus/client/auth/aws/CerberusClientTest.java +++ b/src/integration/java/com/nike/cerberus/client/auth/aws/CerberusClientTest.java @@ -20,7 +20,6 @@ import com.nike.cerberus.client.CerberusClient; import com.nike.cerberus.client.CerberusServerApiException; import com.nike.cerberus.client.CerberusServerException; -import com.nike.cerberus.client.DefaultCerberusUrlResolver; import com.nike.cerberus.client.model.CerberusListFilesResponse; import com.nike.cerberus.client.model.CerberusListResponse; import com.nike.cerberus.client.model.CerberusResponse; @@ -34,21 +33,19 @@ import java.util.HashMap; import java.util.Map; import java.util.UUID; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** - * Tests StaticIamRoleCerberusCredentialsProvider class + * Tests StsCerberusCredentialsProvider class */ public class CerberusClientTest { private static final String ROOT_SDB_PATH = "app/cerberus-integration-tests-sdb/"; - private static String iam_principal_arn; private static String region; + private static String cerberusUrl; private static String secretPath; private static String sdbFullSecretPath; @@ -56,14 +53,14 @@ public class CerberusClientTest { private static CerberusClient cerberusClient; - private static StaticIamRoleCerberusCredentialsProvider staticIamRoleCerberusCredentialsProvider; + private static StsCerberusCredentialsProvider stsCerberusCredentialsProvider; @BeforeClass public static void setUp() { - iam_principal_arn = EnvUtils.getRequiredEnv("TEST_IAM_PRINCIPAL_ARN", "The role to be assume by the integration test"); + region = EnvUtils.getRequiredEnv("TEST_REGION"); - EnvUtils.getRequiredEnv("CERBERUS_ADDR"); + cerberusUrl = EnvUtils.getRequiredEnv("CERBERUS_ADDR"); secretPath = UUID.randomUUID().toString(); sdbFullSecretPath = ROOT_SDB_PATH + secretPath; @@ -84,65 +81,14 @@ private Map generateNewSecretData() { } @Test - public void test_cerberus_client_crud_after_auth_with_account_id_and_role_name() { - Pattern arn_pattern = Pattern.compile("arn:aws:iam::(?[0-9].*):role\\/(?.*)"); - Matcher matcher = arn_pattern.matcher(iam_principal_arn); - if (! matcher.matches()) { - throw new AssertionError("IAM Principal ARN does not match expected format"); - } - String account_id = matcher.group("accountId"); - String role_name = matcher.group("roleName"); - - staticIamRoleCerberusCredentialsProvider = new StaticIamRoleCerberusCredentialsProvider( - new DefaultCerberusUrlResolver(), - account_id, - role_name, - region); - - cerberusClient = new CerberusClient(new DefaultCerberusUrlResolver(), - staticIamRoleCerberusCredentialsProvider, new OkHttpClient()); - - // create secret - cerberusClient.write(sdbFullSecretPath, secretData); - - // read secret - CerberusResponse cerberusReadResponse = cerberusClient.read(sdbFullSecretPath); - assertEquals(secretData, cerberusReadResponse.getData()); - - // list secrets - CerberusListResponse cerberusListResponse = cerberusClient.list(ROOT_SDB_PATH); - assertTrue(cerberusListResponse.getKeys().contains(secretPath)); - - // update secret - Map newSecretData = generateNewSecretData(); - cerberusClient.write(sdbFullSecretPath, newSecretData); - secretData = newSecretData; - - // confirm updated secret data - CerberusResponse cerberusReadResponseUpdated = cerberusClient.read(sdbFullSecretPath); - assertEquals(newSecretData, cerberusReadResponseUpdated.getData()); - - // delete secret - cerberusClient.delete(sdbFullSecretPath); - - // confirm secret is deleted - try { - cerberusClient.read(sdbFullSecretPath); - } catch (CerberusServerException cse) { - assertEquals(404, cse.getCode()); - } - } - - @Test - public void test_secret_is_deleted_after_auth_with_iam_principal_name() { + public void test_secret_is_deleted_after_auth() { - staticIamRoleCerberusCredentialsProvider = new StaticIamRoleCerberusCredentialsProvider( - new DefaultCerberusUrlResolver(), - iam_principal_arn, + stsCerberusCredentialsProvider = new StsCerberusCredentialsProvider( + cerberusUrl, region); - cerberusClient = new CerberusClient(new DefaultCerberusUrlResolver(), - staticIamRoleCerberusCredentialsProvider, new OkHttpClient()); + cerberusClient = new CerberusClient(cerberusUrl, + stsCerberusCredentialsProvider, new OkHttpClient()); // create secret cerberusClient.write(sdbFullSecretPath, secretData); @@ -178,13 +124,12 @@ public void test_secret_is_deleted_after_auth_with_iam_principal_name() { @Test public void test_crud_for_files() { - staticIamRoleCerberusCredentialsProvider = new StaticIamRoleCerberusCredentialsProvider( - new DefaultCerberusUrlResolver(), - iam_principal_arn, + stsCerberusCredentialsProvider = new StsCerberusCredentialsProvider( + cerberusUrl, region); - cerberusClient = new CerberusClient(new DefaultCerberusUrlResolver(), - staticIamRoleCerberusCredentialsProvider, new OkHttpClient()); + cerberusClient = new CerberusClient(cerberusUrl, + stsCerberusCredentialsProvider, new OkHttpClient()); String fileContentStr = "file content string!"; byte[] fileContentArr = fileContentStr.getBytes(StandardCharsets.UTF_8); diff --git a/src/main/java/com/nike/cerberus/client/CerberusClient.java b/src/main/java/com/nike/cerberus/client/CerberusClient.java index 850ff32..4230b82 100644 --- a/src/main/java/com/nike/cerberus/client/CerberusClient.java +++ b/src/main/java/com/nike/cerberus/client/CerberusClient.java @@ -19,10 +19,7 @@ import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; -import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; import com.google.gson.JsonSyntaxException; import com.google.gson.reflect.TypeToken; import com.nike.cerberus.client.auth.CerberusCredentialsProvider; @@ -72,30 +69,26 @@ public class CerberusClient { private final OkHttpClient httpClient; - private final UrlResolver urlResolver; + private final String url; private final Headers defaultHeaders; private final Gson gson = new GsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .disableHtmlEscaping() - .registerTypeAdapter(DateTime.class, new JsonDeserializer() { - @Override - public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - return new DateTime(json.getAsString()); - } - }) + .registerTypeAdapter(DateTime.class, + (JsonDeserializer) (json, typeOfT, context) -> new DateTime(json.getAsString())) .create(); private final Logger logger = LoggerFactory.getLogger(getClass()); - public CerberusClient(final UrlResolver cerberusUrlResolver, + public CerberusClient(final String cerberusUrl, final CerberusCredentialsProvider credentialsProvider, final OkHttpClient httpClient, final Headers defaultHeaders) { - if (cerberusUrlResolver == null) { - throw new IllegalArgumentException("Cerberus URL resolver cannot be null."); + + if (cerberusUrl == null) { + throw new IllegalArgumentException("Cerberus URL cannot be null."); } if (credentialsProvider == null) { @@ -110,7 +103,7 @@ public CerberusClient(final UrlResolver cerberusUrlResolver, throw new IllegalArgumentException("Default headers cannot be null."); } - this.urlResolver = cerberusUrlResolver; + this.url = cerberusUrl; this.credentialsProvider = credentialsProvider; this.httpClient = httpClient; this.defaultHeaders = defaultHeaders; @@ -119,14 +112,15 @@ public CerberusClient(final UrlResolver cerberusUrlResolver, /** * Explicit constructor that allows for full control over construction of the Cerberus client. * - * @param cerberusUrlResolver URL resolver for Cerberus + * @param cerberusUrl URL for Cerberus * @param credentialsProvider Credential provider for acquiring a token for interacting with Cerberus * @param httpClient HTTP client for calling Cerberus */ - public CerberusClient(final UrlResolver cerberusUrlResolver, + public CerberusClient(final String cerberusUrl, final CerberusCredentialsProvider credentialsProvider, final OkHttpClient httpClient) { - if (cerberusUrlResolver == null) { + + if (cerberusUrl == null) { throw new IllegalArgumentException("Cerberus URL resolver can not be null."); } @@ -138,7 +132,7 @@ public CerberusClient(final UrlResolver cerberusUrlResolver, throw new IllegalArgumentException("Http client can not be null."); } - this.urlResolver = cerberusUrlResolver; + this.url = cerberusUrl; this.credentialsProvider = credentialsProvider; this.httpClient = httpClient; this.defaultHeaders = new Headers.Builder().build(); @@ -157,10 +151,10 @@ public CerberusClient(final UrlResolver cerberusUrlResolver, * @return Map containing the keys at that path */ public CerberusListResponse list(final String path) { - final HttpUrl url = buildUrl(SECRET_PATH_PREFIX, path + "?list=true"); - logger.debug("list: requestUrl={}", url); + final HttpUrl httpUrl = buildUrl(SECRET_PATH_PREFIX, path + "?list=true"); + logger.debug("list: requestUrl={}", httpUrl); - final Response response = execute(url, HttpMethod.GET, null); + final Response response = execute(httpUrl, HttpMethod.GET, null); if (response.code() == HttpStatus.NOT_FOUND) { response.close(); @@ -208,10 +202,10 @@ public CerberusListFilesResponse listFiles(final String path) { * @return List of metadata for secure files at the specified path */ public CerberusListFilesResponse listFiles(final String path, Integer limit, Integer offset) { - final HttpUrl url = buildUrl("v1/secure-files/", path, limit, offset); + final HttpUrl httpUrl = buildUrl("v1/secure-files/", path, limit, offset); - logger.debug("list: requestUrl={}, limit={}, offset={}", url, limit, offset); - final Response response = execute(url, HttpMethod.GET, null); + logger.debug("list: requestUrl={}, limit={}, offset={}", httpUrl, limit, offset); + final Response response = execute(httpUrl, HttpMethod.GET, null); if (response.code() != HttpStatus.OK) { parseAndThrowApiErrorResponse(response); @@ -230,10 +224,10 @@ public CerberusListFilesResponse listFiles(final String path, Integer limit, Int * @return Map of the data */ public CerberusResponse read(final String path) { - final HttpUrl url = buildUrl(SECRET_PATH_PREFIX, path); - logger.debug("read: requestUrl={}", url); + final HttpUrl httpUrl = buildUrl(SECRET_PATH_PREFIX, path); + logger.debug("read: requestUrl={}", httpUrl); - final Response response = executeWithRetry(url, HttpMethod.GET, null, DEFAULT_NUM_RETRIES, DEFAULT_RETRY_INTERVAL_IN_MILLIS); + final Response response = executeWithRetry(httpUrl, HttpMethod.GET, null, DEFAULT_NUM_RETRIES, DEFAULT_RETRY_INTERVAL_IN_MILLIS); if (response.code() != HttpStatus.OK) { parseAndThrowErrorResponse(response); @@ -252,10 +246,10 @@ public CerberusResponse read(final String path) { * @return File contents */ public byte[] readFileAsBytes(final String path) { - final HttpUrl url = buildUrl(SECURE_FILE_PATH_PREFIX, path); - logger.debug("read: requestUrl={}", url); + final HttpUrl httpUrl = buildUrl(SECURE_FILE_PATH_PREFIX, path); + logger.debug("read: requestUrl={}", httpUrl); - final Response response = execute(url, HttpMethod.GET, null); + final Response response = execute(httpUrl, HttpMethod.GET, null); if (response.code() != HttpStatus.OK) { parseAndThrowApiErrorResponse(response); @@ -273,10 +267,10 @@ public byte[] readFileAsBytes(final String path) { * @param data Data to be stored */ public void write(final String path, final Map data) { - final HttpUrl url = buildUrl(SECRET_PATH_PREFIX, path); - logger.debug("write: requestUrl={}", url); + final HttpUrl httpUrl = buildUrl(SECRET_PATH_PREFIX, path); + logger.debug("write: requestUrl={}", httpUrl); - final Response response = execute(url, HttpMethod.POST, data); + final Response response = execute(httpUrl, HttpMethod.POST, data); if (response.code() != HttpStatus.NO_CONTENT) { parseAndThrowErrorResponse(response); @@ -293,8 +287,8 @@ public void write(final String path, final Map data) { */ public void writeFile(final String path, final byte[] contents) { final String fileName = StringUtils.substringAfterLast(path, "/"); - final HttpUrl url = buildUrl(SECURE_FILE_PATH_PREFIX, path); - logger.debug("write: requestUrl={}", url); + final HttpUrl httpUrl = buildUrl(SECURE_FILE_PATH_PREFIX, path); + logger.debug("write: requestUrl={}", httpUrl); RequestBody requestBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) @@ -303,7 +297,7 @@ public void writeFile(final String path, final byte[] contents) { .build(); Request request = new Request.Builder() - .url(url) + .url(httpUrl) .headers(defaultHeaders) .addHeader(HttpHeader.CERBERUS_TOKEN, credentialsProvider.getCredentials().getToken()) .addHeader(HttpHeader.ACCEPT, DEFAULT_MEDIA_TYPE.toString()) @@ -325,10 +319,10 @@ public void writeFile(final String path, final byte[] contents) { * @param path Path to file to be deleted */ public void deleteFile(final String path) { - final HttpUrl url = buildUrl(SECURE_FILE_PATH_PREFIX, path); - logger.debug("delete: requestUrl={}", url); + final HttpUrl httpUrl = buildUrl(SECURE_FILE_PATH_PREFIX, path); + logger.debug("delete: requestUrl={}", httpUrl); - final Response response = execute(url, HttpMethod.DELETE, null); + final Response response = execute(httpUrl, HttpMethod.DELETE, null); if (response.code() != HttpStatus.NO_CONTENT) { parseAndThrowApiErrorResponse(response); @@ -343,10 +337,10 @@ public void deleteFile(final String path) { * @param path Path to data to be deleted */ public void delete(final String path) { - final HttpUrl url = buildUrl(SECRET_PATH_PREFIX, path); - logger.debug("delete: requestUrl={}", url); + final HttpUrl httpUrl = buildUrl(SECRET_PATH_PREFIX, path); + logger.debug("delete: requestUrl={}", httpUrl); - final Response response = execute(url, HttpMethod.DELETE, null); + final Response response = execute(httpUrl, HttpMethod.DELETE, null); if (response.code() != HttpStatus.NO_CONTENT) { parseAndThrowErrorResponse(response); @@ -355,16 +349,14 @@ public void delete(final String path) { /** * Returns a copy of the URL being used for communicating with Cerberus - * * @return Copy of the HttpUrl object */ public HttpUrl getCerberusUrl() { - return HttpUrl.parse(urlResolver.resolve()); + return HttpUrl.parse(url); } /** * Returns the configured credentials provider. - * * @return The configured credentials provider */ public CerberusCredentialsProvider getCredentialsProvider() { @@ -373,7 +365,6 @@ public CerberusCredentialsProvider getCredentialsProvider() { /** * Gets the Gson object used for serializing and de-serializing requests. - * * @return Gson object */ public Gson getGson() { @@ -382,7 +373,6 @@ public Gson getGson() { /** * Returns the configured default HTTP headers. - * * @return The configured default HTTP headers */ public Headers getDefaultHeaders() { @@ -402,7 +392,7 @@ protected HttpUrl buildUrl(final String prefix, final String path, final Integer limit, final Integer offset) { - String baseUrl = urlResolver.resolve(); + String baseUrl = url; baseUrl = StringUtils.appendIfMissing(baseUrl, "/"); final StringBuilder fullUrl = new StringBuilder() @@ -429,7 +419,7 @@ protected HttpUrl buildUrl(final String prefix, * @return Full URL to execute a request against */ protected HttpUrl buildUrl(final String prefix, final String path) { - String baseUrl = urlResolver.resolve(); + String baseUrl = url; if (!StringUtils.endsWith(baseUrl, "/")) { baseUrl += "/"; @@ -440,14 +430,14 @@ protected HttpUrl buildUrl(final String prefix, final String path) { /** * Executes an HTTP request and retries if a 500 level error is returned - * @param url Full URL to which to make the HTTP request + * @param httpUrl Full URL to which to make the HTTP request * @param method HTTP Method (e.g. GET, PUT, POST) * @param requestBody Body to add to the request. Nullable * @param numRetries Maximum number of times to retry on 500 failures * @param sleepIntervalInMillis Time in milliseconds to sleep between retries. Zero for no sleep * @return Any HTTP response with status code below 500, or the last error response if only 500's are returned */ - protected Response executeWithRetry(final HttpUrl url, + protected Response executeWithRetry(final HttpUrl httpUrl, final String method, final Object requestBody, final int numRetries, @@ -456,12 +446,12 @@ protected Response executeWithRetry(final HttpUrl url, Response response = null; for(int retryNumber = 0; retryNumber < numRetries; retryNumber++) { try { - response = execute(url, method, requestBody); + response = execute(httpUrl, method, requestBody); if (response.code() < 500) { return response; } } catch (CerberusClientException cce) { - logger.debug(String.format("Failed to call %s %s. Retrying...", method, url), cce); + logger.debug(String.format("Failed to call %s %s. Retrying...", method, httpUrl), cce); exception = cce; } sleep(sleepIntervalInMillis * (long) Math.pow(2, retryNumber)); @@ -477,14 +467,14 @@ protected Response executeWithRetry(final HttpUrl url, /** * Executes the HTTP request based on the input parameters. * - * @param url The URL to execute the request against + * @param httpUrl The URL to execute the request against * @param method The HTTP method for the request * @param requestBody The request body of the HTTP request * @return Response from the server */ - protected Response execute(final HttpUrl url, final String method, final Object requestBody) { + protected Response execute(final HttpUrl httpUrl, final String method, final Object requestBody) { try { - Request request = buildRequest(url, method, requestBody); + Request request = buildRequest(httpUrl, method, requestBody); return httpClient.newCall(request).execute(); } catch (IOException e) { @@ -520,14 +510,14 @@ protected Response execute(final Request request) { /** * Build the HTTP request to execute for the Cerberus Client - * @param url The URL to execute the request against + * @param httpUrl The URL to execute the request against * @param method The HTTP method for the request * @param requestBody The request body of the HTTP request * @return - The HTTP request */ - protected Request buildRequest(final HttpUrl url, final String method, final Object requestBody) { + protected Request buildRequest(final HttpUrl httpUrl, final String method, final Object requestBody) { Request.Builder requestBuilder = new Request.Builder() - .url(url) + .url(httpUrl) .headers(defaultHeaders) // call headers method first because it overwrites all existing headers .addHeader(HttpHeader.CERBERUS_TOKEN, credentialsProvider.getCredentials().getToken()) .addHeader(HttpHeader.ACCEPT, DEFAULT_MEDIA_TYPE.toString()); diff --git a/src/test/java/com/nike/cerberus/client/CerberusClientTest.java b/src/test/java/com/nike/cerberus/client/CerberusClientTest.java index 303f9fb..c735839 100644 --- a/src/test/java/com/nike/cerberus/client/CerberusClientTest.java +++ b/src/test/java/com/nike/cerberus/client/CerberusClientTest.java @@ -16,9 +16,9 @@ package com.nike.cerberus.client; -import com.nike.cerberus.client.auth.DefaultCerberusCredentialsProviderChain; import com.nike.cerberus.client.auth.CerberusCredentials; import com.nike.cerberus.client.auth.CerberusCredentialsProvider; +import com.nike.cerberus.client.auth.DefaultCerberusCredentialsProviderChain; import com.nike.cerberus.client.model.CerberusListResponse; import com.nike.cerberus.client.model.CerberusResponse; import okhttp3.Call; @@ -59,14 +59,19 @@ public class CerberusClientTest { private MockWebServer mockWebServer; + private String cerberusUrl; + + private String region; + @Before public void setup() throws IOException { mockWebServer = new MockWebServer(); mockWebServer.start(); - final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); + cerberusUrl = "http://localhost:" + mockWebServer.getPort(); + region = "us-west-2"; final CerberusCredentialsProvider cerberusCredentialsProvider = mock(CerberusCredentialsProvider.class); cerberusClient = CerberusClientFactory.getClient( - new StaticCerberusUrlResolver(cerberusUrl), + cerberusUrl, cerberusCredentialsProvider); when(cerberusCredentialsProvider.getCredentials()).thenReturn(new TestCerberusCredentials()); @@ -78,28 +83,28 @@ public void teardown() throws IOException { } @Test(expected = IllegalArgumentException.class) - public void constructor_throws_error_if_no_resolver_set() { + public void constructor_throws_error_if_no_url_set() { new CerberusClient(null, - new DefaultCerberusCredentialsProviderChain(), + new DefaultCerberusCredentialsProviderChain(cerberusUrl, region), new OkHttpClient.Builder().build()); } @Test(expected = IllegalArgumentException.class) public void constructor_throws_error_if_no_creds_provider() { - new CerberusClient(new DefaultCerberusUrlResolver(), + new CerberusClient(cerberusUrl, null, new OkHttpClient.Builder().build()); } @Test(expected = IllegalArgumentException.class) public void constructor_throws_error_if_no_http_client() { - new CerberusClient(new DefaultCerberusUrlResolver(), - new DefaultCerberusCredentialsProviderChain(), + new CerberusClient(cerberusUrl, + new DefaultCerberusCredentialsProviderChain(cerberusUrl, region), null); } @Test - public void list_returns_map_of_keys_for_specified_path_if_exists() throws IOException { + public void list_returns_map_of_keys_for_specified_path_if_exists() { final MockResponse response = new MockResponse(); response.setResponseCode(200); response.setBody(getResponseJson("list")); @@ -113,7 +118,7 @@ public void list_returns_map_of_keys_for_specified_path_if_exists() throws IOExc } @Test - public void list_returns_an_empty_response_if_cerberus_returns_a_404() throws IOException { + public void list_returns_an_empty_response_if_cerberus_returns_a_404() { final MockResponse response = new MockResponse(); response.setResponseCode(404); mockWebServer.enqueue(response); @@ -125,7 +130,7 @@ public void list_returns_an_empty_response_if_cerberus_returns_a_404() throws IO } @Test - public void read_returns_map_of_data_for_specified_path_if_exists() throws IOException { + public void read_returns_map_of_data_for_specified_path_if_exists() { final MockResponse response = new MockResponse(); response.setResponseCode(200); response.setBody(getResponseJson("secret")); @@ -139,7 +144,7 @@ public void read_returns_map_of_data_for_specified_path_if_exists() throws IOExc } @Test - public void read_does_not_retry_on_200() throws IOException { + public void read_does_not_retry_on_200() { mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(getResponseJson("secret"))); mockWebServer.enqueue(new MockResponse().setResponseCode(500).setBody(getResponseJson("error"))); @@ -151,7 +156,7 @@ public void read_does_not_retry_on_200() throws IOException { } @Test - public void read_retries_on_500_errors() throws IOException { + public void read_retries_on_500_errors() { for (int i = 0; i < DEFAULT_NUM_RETRIES - 1; i++) { mockWebServer.enqueue(new MockResponse().setResponseCode(500).setBody(getResponseJson("error"))); } @@ -166,8 +171,8 @@ public void read_retries_on_500_errors() throws IOException { @Test public void read_retries_on_IOException() throws IOException { - UrlResolver urlResolver = mock(UrlResolver.class); - when(urlResolver.resolve()).thenReturn("http://localhost:" + mockWebServer.getPort()); + + String url = "http://localhost:" + mockWebServer.getPort(); OkHttpClient httpClient = mock(OkHttpClient.class); Call call = mock(Call.class); @@ -176,7 +181,7 @@ public void read_retries_on_IOException() throws IOException { final CerberusCredentialsProvider cerberusCredentialsProvider = mock(CerberusCredentialsProvider.class); when(cerberusCredentialsProvider.getCredentials()).thenReturn(new TestCerberusCredentials()); - CerberusClient cerberusClient = new CerberusClient(urlResolver, cerberusCredentialsProvider, httpClient); + CerberusClient cerberusClient = new CerberusClient(url, cerberusCredentialsProvider, httpClient); try { cerberusClient.read("app/api-key"); @@ -213,7 +218,7 @@ public void read_throws_runtime_exception_if_unexpected_error_encountered() thro final String cerberusUrl = "http://localhost:" + serverSocket.getLocalPort(); final CerberusCredentialsProvider cerberusCredentialsProvider = mock(CerberusCredentialsProvider.class); final OkHttpClient httpClient = buildHttpClient(1, TimeUnit.SECONDS); - CerberusClient cerberusClient = new CerberusClient(new StaticCerberusUrlResolver(cerberusUrl), cerberusCredentialsProvider, httpClient); + CerberusClient cerberusClient = new CerberusClient(cerberusUrl, cerberusCredentialsProvider, httpClient); when(cerberusCredentialsProvider.getCredentials()).thenReturn(new TestCerberusCredentials()); @@ -254,7 +259,7 @@ public void write_throws_runtime_exception_if_unexpected_error_encountered() thr final String cerberusUrl = "http://localhost:" + serverSocket.getLocalPort(); final CerberusCredentialsProvider cerberusCredentialsProvider = mock(CerberusCredentialsProvider.class); final OkHttpClient httpClient = buildHttpClient(1, TimeUnit.SECONDS); - cerberusClient = new CerberusClient(new StaticCerberusUrlResolver(cerberusUrl), cerberusCredentialsProvider, httpClient); + cerberusClient = new CerberusClient(cerberusUrl, cerberusCredentialsProvider, httpClient); when(cerberusCredentialsProvider.getCredentials()).thenReturn(new TestCerberusCredentials()); @@ -293,7 +298,7 @@ public void delete_throws_runtime_exception_if_unexpected_error_encountered() th final String cerberusUrl = "http://localhost:" + serverSocket.getLocalPort(); final CerberusCredentialsProvider cerberusCredentialsProvider = mock(CerberusCredentialsProvider.class); final OkHttpClient httpClient = buildHttpClient(1, TimeUnit.SECONDS); - cerberusClient = new CerberusClient(new StaticCerberusUrlResolver(cerberusUrl), cerberusCredentialsProvider, httpClient); + cerberusClient = new CerberusClient(cerberusUrl, cerberusCredentialsProvider, httpClient); when(cerberusCredentialsProvider.getCredentials()).thenReturn(new TestCerberusCredentials()); @@ -301,7 +306,7 @@ public void delete_throws_runtime_exception_if_unexpected_error_encountered() th } @Test - public void build_request_includes_default_headers() throws IOException { + public void build_request_includes_default_headers() { final String headerKey = "headerKey"; final String headerValue = "headerValue"; final Headers headers = new Headers.Builder().add(headerKey, headerValue).build(); @@ -310,7 +315,7 @@ public void build_request_includes_default_headers() throws IOException { final CerberusCredentialsProvider cerberusCredentialsProvider = mock(CerberusCredentialsProvider.class); when(cerberusCredentialsProvider.getCredentials()).thenReturn(new TestCerberusCredentials()); final OkHttpClient httpClient = buildHttpClient(1, TimeUnit.SECONDS); - cerberusClient = new CerberusClient(new StaticCerberusUrlResolver(cerberusUrl), cerberusCredentialsProvider, httpClient, headers); + cerberusClient = new CerberusClient(cerberusUrl, cerberusCredentialsProvider, httpClient, headers); Request result = cerberusClient.buildRequest(HttpUrl.parse(cerberusUrl), "get", null); From d7e03c4538d28ca1f59d0a78b96382d21291acba Mon Sep 17 00:00:00 2001 From: melanahammel Date: Fri, 14 Dec 2018 11:36:19 -0800 Subject: [PATCH 06/14] refactored base aws credentials provider --- .../auth/aws/BaseAwsCredentialsProvider.java | 52 +++++++++---------- .../aws/BaseAwsCredentialsProviderTest.java | 44 +++++----------- 2 files changed, 38 insertions(+), 58 deletions(-) diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProvider.java b/src/main/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProvider.java index be27511..9406a90 100644 --- a/src/main/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProvider.java +++ b/src/main/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProvider.java @@ -31,7 +31,6 @@ import com.nike.cerberus.client.CerberusClientException; import com.nike.cerberus.client.CerberusServerException; import com.nike.cerberus.client.ClientVersion; -import com.nike.cerberus.client.UrlResolver; import com.nike.cerberus.client.auth.CerberusCredentials; import com.nike.cerberus.client.auth.CerberusCredentialsProvider; import com.nike.cerberus.client.auth.TokenCerberusCredentials; @@ -66,7 +65,6 @@ import static com.nike.cerberus.client.CerberusClientFactory.DEFAULT_TIMEOUT; import static com.nike.cerberus.client.CerberusClientFactory.DEFAULT_TIMEOUT_UNIT; import static com.nike.cerberus.client.CerberusClientFactory.TLS_1_2_OR_NEWER; -import static com.nike.cerberus.client.auth.aws.StaticIamRoleCerberusCredentialsProvider.IAM_ROLE_ARN_FORMAT; import static okhttp3.ConnectionSpec.CLEARTEXT; /** @@ -80,6 +78,8 @@ public abstract class BaseAwsCredentialsProvider implements CerberusCredentialsP public static final MediaType DEFAULT_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8"); + public static final String IAM_ROLE_ARN_FORMAT = "arn:aws:iam::%s:role/%s"; + private static final Logger LOGGER = LoggerFactory.getLogger(BaseAwsCredentialsProvider.class); protected static final int DEFAULT_AUTH_RETRIES = 3; @@ -101,56 +101,54 @@ public abstract class BaseAwsCredentialsProvider implements CerberusCredentialsP protected volatile DateTime expireDateTime = DateTime.now().minus(paddingTimeInSeconds); - private final UrlResolver urlResolver; + protected final String cerberusUrl; private final String cerberusJavaClientHeaderValue; private final OkHttpClient httpClient; /** - * Constructor to setup credentials provider using the specified - * implementation of {@link UrlResolver} + * Constructor to setup credentials provider * - * @param urlResolver Resolver for resolving the Cerberus URL + * @param cerberusUrl Cerberus URL */ - public BaseAwsCredentialsProvider(UrlResolver urlResolver) { + public BaseAwsCredentialsProvider(String cerberusUrl) { super(); - this.urlResolver = urlResolver; + this.cerberusUrl = cerberusUrl; this.cerberusJavaClientHeaderValue = ClientVersion.getClientHeaderValue(); -// LOGGER.info("Cerberus URL={}", urlResolver.resolve()); + LOGGER.info("Cerberus URL={}", this.cerberusUrl); this.httpClient = createHttpClient(); } /** - * Constructor to setup credentials provider using the specified - * implementation of {@link UrlResolver} + * Constructor to setup credentials provider * - * @param urlResolver Resolver for resolving the Cerberus URL + * @param cerberusUrl Cerberus URL * @param xCerberusClientOverride Overrides the default header value for the 'X-Cerberus-Client' header */ - public BaseAwsCredentialsProvider(UrlResolver urlResolver, String xCerberusClientOverride) { + public BaseAwsCredentialsProvider(String cerberusUrl, String xCerberusClientOverride) { super(); - this.urlResolver = urlResolver; + this.cerberusUrl = cerberusUrl; this.cerberusJavaClientHeaderValue = xCerberusClientOverride; - LOGGER.info("Cerberus URL={}", urlResolver.resolve()); + LOGGER.info("Cerberus URL={}", this.cerberusUrl); this.httpClient = createHttpClient(); } /** * Constructor to setup credentials provider using the specified - * implementation of {@link UrlResolver} and {@link OkHttpClient} + * implementation of {@link OkHttpClient} * - * @param urlResolver Resolver for resolving the Cerberus URL + * @param cerberusUrl Cerberus URL * @param httpClient the client to use for auth */ - public BaseAwsCredentialsProvider(UrlResolver urlResolver, OkHttpClient httpClient) { + public BaseAwsCredentialsProvider(String cerberusUrl, OkHttpClient httpClient) { super(); - this.urlResolver = urlResolver; + this.cerberusUrl = cerberusUrl; this.cerberusJavaClientHeaderValue = ClientVersion.getClientHeaderValue(); - LOGGER.info("Cerberus URL={}", urlResolver.resolve()); + LOGGER.info("Cerberus URL={}", this.cerberusUrl); this.httpClient = httpClient; } @@ -220,6 +218,7 @@ protected void getAndSetToken(final String accountId, final String iamRoleName) * @param region AWS Region used in auth with cerberus */ protected void getAndSetToken(final String iamPrincipalArn, final Region region) { + final AWSKMSClient kmsClient = new AWSKMSClient(); kmsClient.setRegion(region); @@ -240,17 +239,16 @@ protected void getAndSetToken(final String iamPrincipalArn, final Region region) * @return Base64 and encrypted token */ protected String getEncryptedAuthData(final String iamPrincipalArn, Region region) { - final String url = urlResolver.resolve(); - if (StringUtils.isBlank(url)) { + if (StringUtils.isBlank(cerberusUrl)) { throw new CerberusClientException("Unable to find the Cerberus URL."); } LOGGER.info(String.format("Attempting to authenticate with AWS IAM principal ARN [%s] against [%s]", - iamPrincipalArn, url)); + iamPrincipalArn, cerberusUrl)); try { - Request.Builder requestBuilder = new Request.Builder().url(url + "/v2/auth/iam-principal") + Request.Builder requestBuilder = new Request.Builder().url(cerberusUrl + "/v2/auth/iam-principal") .addHeader(HttpHeader.ACCEPT, DEFAULT_MEDIA_TYPE.toString()) .addHeader(HttpHeader.CONTENT_TYPE, DEFAULT_MEDIA_TYPE.toString()) .addHeader(ClientVersion.CERBERUS_CLIENT_HEADER, cerberusJavaClientHeaderValue) @@ -269,7 +267,7 @@ protected String getEncryptedAuthData(final String iamPrincipalArn, Region regio if (authData.containsKey(key)) { LOGGER.info(String.format("Authentication successful with AWS IAM principal ARN [%s] against [%s]", - iamPrincipalArn, url)); + iamPrincipalArn, cerberusUrl)); return authData.get(key); } else { throw new CerberusClientException("Success response from IAM role authenticate endpoint missing auth data!"); @@ -369,8 +367,8 @@ protected void parseAndThrowErrorResponse(final int responseCode, final String r throw new CerberusServerException(responseCode, errors); } - public UrlResolver getUrlResolver(){ - return urlResolver; + public String getCerberusUrl(){ + return cerberusUrl; } public OkHttpClient createHttpClient() { diff --git a/src/test/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProviderTest.java b/src/test/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProviderTest.java index e36780f..32a595d 100644 --- a/src/test/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProviderTest.java +++ b/src/test/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProviderTest.java @@ -21,14 +21,11 @@ import com.amazonaws.services.kms.AWSKMSClient; import com.nike.cerberus.client.CerberusClientException; import com.nike.cerberus.client.CerberusServerException; -import com.nike.cerberus.client.DefaultCerberusUrlResolver; -import com.nike.cerberus.client.UrlResolver; import okhttp3.Call; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; -import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -36,7 +33,6 @@ import static com.nike.cerberus.client.auth.aws.BaseAwsCredentialsProvider.DEFAULT_AUTH_RETRIES; import static org.mockito.Matchers.any; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.powermock.api.mockito.PowerMockito.mock; @@ -52,30 +48,23 @@ public class BaseAwsCredentialsProviderTest extends BaseCredentialsProviderTest{ private BaseAwsCredentialsProvider provider; - private UrlResolver urlResolver; private String cerberusUrl; private MockWebServer mockWebServer; @Before public void setUp() throws Exception { - urlResolver = mock(UrlResolver.class); - - provider = new TestAwsCredentialsProvider(urlResolver); mockWebServer = new MockWebServer(); mockWebServer.start(); cerberusUrl = "http://localhost:" + mockWebServer.getPort(); - } - @After - public void tearDown() throws Exception { - reset(urlResolver); + provider = new TestAwsCredentialsProvider(cerberusUrl); } @Test(expected = CerberusClientException.class) - public void getEncryptedAuthData_blank_url_throws_exception() throws Exception { - when(urlResolver.resolve()).thenReturn(""); + public void getEncryptedAuthData_blank_url_throws_exception() { + cerberusUrl = ""; provider.getEncryptedAuthData(CERBERUS_TEST_ARN, REGION); } @@ -86,20 +75,17 @@ public void decryptToken_throws_exception_when_non_encrypted_data_provided() { } @Test(expected = CerberusServerException.class) - public void getEncryptedAuthData_throws_exception_on_bad_response_code() throws IOException { - when(urlResolver.resolve()).thenReturn(cerberusUrl); + public void getEncryptedAuthData_throws_exception_on_bad_response_code() { + - System.setProperty(DefaultCerberusUrlResolver.CERBERUS_ADDR_SYS_PROPERTY, cerberusUrl); mockWebServer.enqueue(new MockResponse().setResponseCode(400).setBody(ERROR_RESPONSE)); provider.getEncryptedAuthData(CERBERUS_TEST_ARN, REGION); } @Test(expected = CerberusClientException.class) - public void getEncryptedAuthData_throws_exception_on_missing_auth_data() throws IOException { - when(urlResolver.resolve()).thenReturn(cerberusUrl); + public void getEncryptedAuthData_throws_exception_on_missing_auth_data() { - System.setProperty(DefaultCerberusUrlResolver.CERBERUS_ADDR_SYS_PROPERTY, cerberusUrl); mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(MISSING_AUTH_DATA)); provider.getEncryptedAuthData(CERBERUS_TEST_ARN, REGION); @@ -107,7 +93,6 @@ public void getEncryptedAuthData_throws_exception_on_missing_auth_data() throws @Test public void test_that_getEncryptedAuthData_retries_on_500_errors() { - when(urlResolver.resolve()).thenReturn(cerberusUrl); for (int i = 0; i < DEFAULT_AUTH_RETRIES - 1; i++) { mockWebServer.enqueue(new MockResponse().setResponseCode(500).setBody(ERROR_RESPONSE)); @@ -119,13 +104,12 @@ public void test_that_getEncryptedAuthData_retries_on_500_errors() { @Test public void test_that_getEncryptedAuthData_retries_on_IOException_errors() throws IOException { - when(urlResolver.resolve()).thenReturn(cerberusUrl); OkHttpClient httpClient = mock(OkHttpClient.class); Call call = mock(Call.class); when(call.execute()).thenThrow(new IOException()); when(httpClient.newCall(any(Request.class))).thenReturn(call); - TestAwsCredentialsProvider provider = new TestAwsCredentialsProvider(urlResolver, httpClient); + TestAwsCredentialsProvider provider = new TestAwsCredentialsProvider(cerberusUrl, httpClient); try { provider.getEncryptedAuthData(CERBERUS_TEST_ARN, REGION); @@ -144,7 +128,6 @@ public void test_that_getEncryptedAuthData_retries_on_IOException_errors() throw @Test public void test_that_getEncryptedAuthData_does_not_retry_on_200() { - when(urlResolver.resolve()).thenReturn(cerberusUrl); mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(SUCCESS_RESPONSE)); mockWebServer.enqueue(new MockResponse().setResponseCode(500).setBody(ERROR_RESPONSE)); @@ -154,17 +137,16 @@ public void test_that_getEncryptedAuthData_does_not_retry_on_200() { class TestAwsCredentialsProvider extends BaseAwsCredentialsProvider { /** - * Constructor to setup credentials provider using the specified - * implementation of {@link UrlResolver} + * Constructor to setup credentials provider * - * @param urlResolver Resolver for resolving the Cerberus URL + * @param cerberusUrl Cerberus URL */ - public TestAwsCredentialsProvider(UrlResolver urlResolver) { - super(urlResolver); + public TestAwsCredentialsProvider(String cerberusUrl) { + super(cerberusUrl); } - public TestAwsCredentialsProvider(UrlResolver urlResolver, OkHttpClient client) { - super(urlResolver, client); + public TestAwsCredentialsProvider(String cerberusUrl, OkHttpClient client) { + super(cerberusUrl, client); } @Override From f03d9eae8630b6f1635daa15a7f459c6efd5cf98 Mon Sep 17 00:00:00 2001 From: melanahammel Date: Fri, 14 Dec 2018 11:46:42 -0800 Subject: [PATCH 07/14] refactored client factory and provider chain --- .../client/CerberusClientFactory.java | 76 +++++---------- .../client/DefaultCerberusClientFactory.java | 95 ++----------------- ...faultCerberusCredentialsProviderChain.java | 63 +++++------- .../client/CerberusClientFactoryTest.java | 59 +----------- .../DefaultCerberusClientFactoryTest.java | 12 +-- ...tCerberusCredentialsProviderChainTest.java | 6 +- 6 files changed, 72 insertions(+), 239 deletions(-) diff --git a/src/main/java/com/nike/cerberus/client/CerberusClientFactory.java b/src/main/java/com/nike/cerberus/client/CerberusClientFactory.java index 52b8acf..e43666f 100644 --- a/src/main/java/com/nike/cerberus/client/CerberusClientFactory.java +++ b/src/main/java/com/nike/cerberus/client/CerberusClientFactory.java @@ -17,7 +17,6 @@ package com.nike.cerberus.client; import com.nike.cerberus.client.auth.CerberusCredentialsProvider; -import com.nike.cerberus.client.auth.DefaultCerberusCredentialsProviderChain; import okhttp3.ConnectionSpec; import okhttp3.Dispatcher; import okhttp3.Headers; @@ -56,56 +55,29 @@ public class CerberusClientFactory { private static final int DEFAULT_MAX_REQUESTS = 200; private static final Map DEFAULT_HEADERS = new HashMap<>(); - /** - * Basic factory method that will build a Cerberus client that - * looks up the Cerberus URL from one of the following places: - *

    - *
  • Environment Variable - CERBERUS_ADDR
  • - *
  • Java System Property - cerberus.addr
  • - *
- * Default recommended credential provider and http client are used. - * - * @return Cerberus client - */ - public static CerberusClient getClient() { - return getClient(new DefaultCerberusUrlResolver(), - new DefaultCerberusCredentialsProviderChain(), - new HashMap()); - } - - /** - * Factory method allows setting of the Cerberus URL resolver, but will use - * the default recommended credentials provider chain and http client. - * - * @param cerberusUrlResolver URL resolver for Cerberus - * @return Cerberus client - */ - public static CerberusClient getClient(final UrlResolver cerberusUrlResolver) { - return getClient(cerberusUrlResolver, new DefaultCerberusCredentialsProviderChain(), DEFAULT_HEADERS); - } - /** * Factory method that allows for a user defined Cerberus URL resolver and credentials provider. * - * @param cerberusUrlResolver URL resolver for Cerberus + * @param cerberusUrl URL for Cerberus * @param cerberusCredentialsProvider Credential provider for acquiring a token for interacting with Cerberus * @return Cerberus client */ - public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, + public static CerberusClient getClient(final String cerberusUrl, final CerberusCredentialsProvider cerberusCredentialsProvider) { - return getClient(cerberusUrlResolver, cerberusCredentialsProvider, DEFAULT_HEADERS); + + return getClient(cerberusUrl, cerberusCredentialsProvider, DEFAULT_HEADERS); } /** * Factory method that allows a user to define default HTTP defaultHeaders to be added to every HTTP request made from the * CerberusClient. The user can also define their Cerberus URL resolver and credentials provider. * - * @param cerberusUrlResolver URL resolver for Cerberus + * @param cerberusUrl URL for Cerberus * @param cerberusCredentialsProvider Credential provider for acquiring a token for interacting with Cerberus * @param defaultHeaders Map of default header names and values to add to every HTTP request * @return Cerberus client */ - public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, + public static CerberusClient getClient(final String cerberusUrl, final CerberusCredentialsProvider cerberusCredentialsProvider, final Map defaultHeaders) { @@ -115,7 +87,7 @@ public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, connectionSpecs.add(CLEARTEXT); return getClient( - cerberusUrlResolver, + cerberusUrl, cerberusCredentialsProvider, defaultHeaders, new OkHttpClient.Builder() @@ -130,15 +102,16 @@ public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, /** * Factory method that allows for a user defined Cerberus URL resolver and credentials provider. * - * @param cerberusUrlResolver URL resolver for Cerberus + * @param cerberusUrl URL for Cerberus * @param cerberusCredentialsProvider Credential provider for acquiring a token for interacting with Cerberus - * @param maxRequestsPerHost Max Requests per Host used by the dispatcher + * @param maxRequestsPerHost Max Requests per Host used by the dispatcher * @return Cerberus admin client */ - public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, + public static CerberusClient getClient(final String cerberusUrl, final CerberusCredentialsProvider cerberusCredentialsProvider, final int maxRequestsPerHost) { - return getClient(cerberusUrlResolver, + + return getClient(cerberusUrl, cerberusCredentialsProvider, DEFAULT_MAX_REQUESTS, maxRequestsPerHost, @@ -151,16 +124,17 @@ public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, /** * Factory method that allows a user to define the OkHttpClient to be used. * - * @param cerberusUrlResolver URL resolver for Cerberus + * @param cerberusUrl URL for Cerberus * @param cerberusCredentialsProvider Credential provider for acquiring a token for interacting with Cerberus * @param defaultHeaders Map of default header names and values to add to every HTTP request * @param httpClient * @return Cerberus client */ - public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, + public static CerberusClient getClient(final String cerberusUrl, final CerberusCredentialsProvider cerberusCredentialsProvider, final Map defaultHeaders, final OkHttpClient httpClient) { + if (defaultHeaders == null) { throw new IllegalArgumentException("Default headers cannot be null."); } @@ -170,7 +144,7 @@ public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, headers.add(header.getKey(), header.getValue()); } - return new CerberusClient(cerberusUrlResolver, + return new CerberusClient(cerberusUrl, cerberusCredentialsProvider, httpClient, headers.build()); @@ -179,17 +153,18 @@ public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, /** * Factory method that allows the user to completely configure the CerberusClient. * - * @param cerberusUrlResolver URL resolver for Cerberus + * @param cerberusUrl URL for Cerberus * @param cerberusCredentialsProvider Credential provider for acquiring a token for interacting with Cerberus - * @param maxRequestsPerHost Max Requests per Host used by the dispatcher - * @param defaultHeaders Map of default header names and values to add to every HTTP request + * @param maxRequestsPerHost Max Requests per Host used by the dispatcher + * @param defaultHeaders Map of default header names and values to add to every HTTP request * @return Cerberus admin client */ - public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, + public static CerberusClient getClient(final String cerberusUrl, final CerberusCredentialsProvider cerberusCredentialsProvider, final int maxRequestsPerHost, final Map defaultHeaders) { - return getClient(cerberusUrlResolver, + + return getClient(cerberusUrl, cerberusCredentialsProvider, DEFAULT_MAX_REQUESTS, maxRequestsPerHost, @@ -202,7 +177,7 @@ public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, /** * Factory method that allows the user to completely configure the CerberusClient. * - * @param cerberusUrlResolver URL resolver for Cerberus + * @param cerberusUrl URL for Cerberus * @param cerberusCredentialsProvider Credential provider for acquiring a token for interacting with Cerberus * @param maxRequests Max HTTP Requests allowed in-flight * @param maxRequestsPerHost Max HTTP Requests per Host @@ -212,7 +187,7 @@ public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, * @param defaultHeaders Map of default header names and values to add to every HTTP request * @return Cerberus admin client */ - public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, + public static CerberusClient getClient(final String cerberusUrl, final CerberusCredentialsProvider cerberusCredentialsProvider, final int maxRequests, final int maxRequestsPerHost, @@ -220,6 +195,7 @@ public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, final int readTimeoutMillis, final int writeTimeoutMillis, final Map defaultHeaders) { + if (defaultHeaders == null) { throw new IllegalArgumentException("Default headers cannot be null."); } @@ -239,7 +215,7 @@ public static CerberusClient getClient(final UrlResolver cerberusUrlResolver, headers.add(header.getKey(), header.getValue()); } - return new CerberusClient(cerberusUrlResolver, + return new CerberusClient(cerberusUrl, cerberusCredentialsProvider, new OkHttpClient.Builder() .connectTimeout(connectTimeoutMillis, DEFAULT_TIMEOUT_UNIT) diff --git a/src/main/java/com/nike/cerberus/client/DefaultCerberusClientFactory.java b/src/main/java/com/nike/cerberus/client/DefaultCerberusClientFactory.java index 93a4533..2114efa 100644 --- a/src/main/java/com/nike/cerberus/client/DefaultCerberusClientFactory.java +++ b/src/main/java/com/nike/cerberus/client/DefaultCerberusClientFactory.java @@ -16,12 +16,7 @@ package com.nike.cerberus.client; -import com.nike.cerberus.client.auth.CerberusCredentialsProviderChain; import com.nike.cerberus.client.auth.DefaultCerberusCredentialsProviderChain; -import com.nike.cerberus.client.auth.EnvironmentCerberusCredentialsProvider; -import com.nike.cerberus.client.auth.SystemPropertyCerberusCredentialsProvider; -import com.nike.cerberus.client.auth.aws.LambdaRoleCerberusCredentialsProvider; -import com.nike.cerberus.client.auth.aws.StaticIamRoleCerberusCredentialsProvider; import okhttp3.ConnectionSpec; import okhttp3.OkHttpClient; @@ -37,41 +32,24 @@ */ public final class DefaultCerberusClientFactory { - /** - * Creates a new {@link CerberusClient} with the {@link DefaultCerberusUrlResolver} for URL resolving - * and {@link DefaultCerberusCredentialsProviderChain} for obtaining credentials. - * - * @return Cerberus client - */ - public static CerberusClient getClient() { - final Map defaultHeaders = new HashMap<>(); - defaultHeaders.put(ClientVersion.CERBERUS_CLIENT_HEADER, ClientVersion.getClientHeaderValue()); - - return CerberusClientFactory.getClient( - new DefaultCerberusUrlResolver(), - new DefaultCerberusCredentialsProviderChain(), - defaultHeaders); - } /** * Creates a new {@link CerberusClient} for the supplied Cerberus URL * and {@link DefaultCerberusCredentialsProviderChain} for obtaining credentials. * - * @param cerberusUrl e.g. https://dev.cerberus.example.com + * @param cerberusUrl URL for Cerberus + * @param region AWS region * @return Cerberus client */ - public static CerberusClient getClient(String cerberusUrl) { + public static CerberusClient getClient(String cerberusUrl, String region) { final Map defaultHeaders = new HashMap<>(); defaultHeaders.put(ClientVersion.CERBERUS_CLIENT_HEADER, ClientVersion.getClientHeaderValue()); - UrlResolver urlResolver = new StaticCerberusUrlResolver(cerberusUrl); - return CerberusClientFactory.getClient( - urlResolver, - new DefaultCerberusCredentialsProviderChain(urlResolver), + cerberusUrl, + new DefaultCerberusCredentialsProviderChain(cerberusUrl, region), defaultHeaders); - } /** @@ -80,18 +58,17 @@ public static CerberusClient getClient(String cerberusUrl) { * This factory method is generally not recommended unless you have a specific need * to configure your TLS for your httpClient differently than the default, e.g. Java 7. * - * @param cerberusUrl e.g. https://dev.cerberus.example.com + * @param cerberusUrl URL for Cerberus + * @param region AWS region * @param sslSocketFactory the factory to use for TLS * @param trustManager the trust manager to use for TLS * @return Cerberus client */ - public static CerberusClient getClient(String cerberusUrl, SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) { + public static CerberusClient getClient(String cerberusUrl, String region, SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) { final Map defaultHeaders = new HashMap<>(); defaultHeaders.put(ClientVersion.CERBERUS_CLIENT_HEADER, ClientVersion.getClientHeaderValue()); - UrlResolver urlResolver = new StaticCerberusUrlResolver(cerberusUrl); - List connectionSpecs = new ArrayList<>(); connectionSpecs.add(CerberusClientFactory.TLS_1_2_OR_NEWER); @@ -104,61 +81,9 @@ public static CerberusClient getClient(String cerberusUrl, SSLSocketFactory sslS .build(); return CerberusClientFactory.getClient( - urlResolver, - new DefaultCerberusCredentialsProviderChain(urlResolver, httpClient), + cerberusUrl, + new DefaultCerberusCredentialsProviderChain(cerberusUrl, region, httpClient), defaultHeaders, httpClient); } - - /** - * Creates a new {@link CerberusClient} for the supplied Cerberus URL and a credentials provider chain - * that includes the {@link StaticIamRoleCerberusCredentialsProvider} for obtaining credentials. - *

- * This method is used when you want to use a particular iamPrincipalArn during authentication rather - * than auto-determining the ARN to use. Generally, it is simpler to use the {@code getClient()} or the - * {@code getClient(cerberusUrl)} factory methods. This method is ONLY needed when those methods aren't - * producing the desired behavior. - * - * @param cerberusUrl e.g. https://dev.cerberus.example.com - * @param iamPrincipalArn the IAM principal to use in authentication, e.g. "arn:aws:iam::123456789012:role/some-role" - * @param region the Region for the KMS key used in auth. Usually, this is your current region. - * @return Cerberus client - */ - public static CerberusClient getClient(String cerberusUrl, String iamPrincipalArn, String region) { - - final Map defaultHeaders = new HashMap<>(); - defaultHeaders.put(ClientVersion.CERBERUS_CLIENT_HEADER, ClientVersion.getClientHeaderValue()); - - UrlResolver urlResolver = new StaticCerberusUrlResolver(cerberusUrl); - - return CerberusClientFactory.getClient( - urlResolver, - new CerberusCredentialsProviderChain( - new EnvironmentCerberusCredentialsProvider(), - new SystemPropertyCerberusCredentialsProvider(), - new StaticIamRoleCerberusCredentialsProvider(urlResolver, iamPrincipalArn, region)), - defaultHeaders); - } - - /** - * Creates a new {@link CerberusClient} with the {@link DefaultCerberusUrlResolver} for URL resolving - * and a credentials provider chain that includes the {@link LambdaRoleCerberusCredentialsProvider} for obtaining - * credentials. - * - * @param invokedFunctionArn The ARN for the AWS Lambda function being invoked. - * @return Cerberus client - */ - public static CerberusClient getClientForLambda(final String invokedFunctionArn) { - final Map defaultHeaders = new HashMap<>(); - defaultHeaders.put(ClientVersion.CERBERUS_CLIENT_HEADER, ClientVersion.getClientHeaderValue()); - - final DefaultCerberusUrlResolver urlResolver = new DefaultCerberusUrlResolver(); - return CerberusClientFactory.getClient( - urlResolver, - new CerberusCredentialsProviderChain( - new EnvironmentCerberusCredentialsProvider(), - new SystemPropertyCerberusCredentialsProvider(), - new LambdaRoleCerberusCredentialsProvider(urlResolver, invokedFunctionArn)), - defaultHeaders); - } } diff --git a/src/main/java/com/nike/cerberus/client/auth/DefaultCerberusCredentialsProviderChain.java b/src/main/java/com/nike/cerberus/client/auth/DefaultCerberusCredentialsProviderChain.java index f229026..207efbb 100644 --- a/src/main/java/com/nike/cerberus/client/auth/DefaultCerberusCredentialsProviderChain.java +++ b/src/main/java/com/nike/cerberus/client/auth/DefaultCerberusCredentialsProviderChain.java @@ -15,11 +15,7 @@ */ package com.nike.cerberus.client.auth; - -import com.nike.cerberus.client.DefaultCerberusUrlResolver; -import com.nike.cerberus.client.UrlResolver; -import com.nike.cerberus.client.auth.aws.EcsTaskRoleCerberusCredentialsProvider; -import com.nike.cerberus.client.auth.aws.InstanceRoleCerberusCredentialsProvider; +import com.nike.cerberus.client.auth.aws.StsCerberusCredentialsProvider; import okhttp3.OkHttpClient; /** @@ -28,65 +24,54 @@ *

    *
  • Environment Variable - CERBERUS_TOKEN
  • *
  • Java System Property - cerberus.token
  • - *
  • Custom lookup using instance profile from EC2 metadata service
  • + *
  • STS authentication using current IAM role
  • *
* * @see EnvironmentCerberusCredentialsProvider * @see SystemPropertyCerberusCredentialsProvider - * @see InstanceRoleCerberusCredentialsProvider + * @see StsCerberusCredentialsProvider */ public class DefaultCerberusCredentialsProviderChain extends CerberusCredentialsProviderChain { - /** - * Default constructor that sets up a default provider chain. - */ - public DefaultCerberusCredentialsProviderChain() { - this(new DefaultCerberusUrlResolver()); - } - /** * Constructor that sets up a provider chain using the specified implementation of UrlResolver. * - * @param urlResolver Resolves the Cerberus URL - */ - public DefaultCerberusCredentialsProviderChain(UrlResolver urlResolver) { - super(new EnvironmentCerberusCredentialsProvider(), - new SystemPropertyCerberusCredentialsProvider(), - new InstanceRoleCerberusCredentialsProvider(urlResolver), - new EcsTaskRoleCerberusCredentialsProvider(urlResolver)); - } - - /** - * Constructor that sets up a provider chain using the specified implementation of UrlResolver - * and OkHttpClient - * - * @param urlResolver Resolves the Cerberus URL - * @param httpClient the client to use + * @param url Cerberus URL + * @param region AWS region */ - public DefaultCerberusCredentialsProviderChain(UrlResolver urlResolver, OkHttpClient httpClient) { - super(new EnvironmentCerberusCredentialsProvider(), + public DefaultCerberusCredentialsProviderChain(String url, String region) { + super( + new EnvironmentCerberusCredentialsProvider(), new SystemPropertyCerberusCredentialsProvider(), - new InstanceRoleCerberusCredentialsProvider(urlResolver, httpClient)); + new StsCerberusCredentialsProvider(url, region) + ); } /** * Constructor that sets up a provider chain using the specified implementation of UrlResolver. * + * @param url Cerberus URL + * @param region AWS region * @param xCerberusClientOverride - Overrides the default header value for the 'X-Cerberus-Client' header */ - public DefaultCerberusCredentialsProviderChain(String xCerberusClientOverride) { - this(new DefaultCerberusUrlResolver(), xCerberusClientOverride); + public DefaultCerberusCredentialsProviderChain(String url, String region, String xCerberusClientOverride) { + super( + new EnvironmentCerberusCredentialsProvider(), + new SystemPropertyCerberusCredentialsProvider(), + new StsCerberusCredentialsProvider(url, region, xCerberusClientOverride) + ); } /** - * Constructor that sets up a provider chain using the specified implementation of UrlResolver. + * Constructor that sets up a provider chain using the specified implementation of UrlResolver + * and OkHttpClient * - * @param urlResolver Resolves the Cerberus URL - * @param xCerberusClientOverride - Overrides the default header value for the 'X-Cerberus-Client' header + * @param url Cerberus URL + * @param httpClient the client to use */ - public DefaultCerberusCredentialsProviderChain(UrlResolver urlResolver, String xCerberusClientOverride) { + public DefaultCerberusCredentialsProviderChain(String url, String region, OkHttpClient httpClient) { super(new EnvironmentCerberusCredentialsProvider(), new SystemPropertyCerberusCredentialsProvider(), - new InstanceRoleCerberusCredentialsProvider(urlResolver, xCerberusClientOverride)); + new StsCerberusCredentialsProvider(url, region, httpClient)); } } diff --git a/src/test/java/com/nike/cerberus/client/CerberusClientFactoryTest.java b/src/test/java/com/nike/cerberus/client/CerberusClientFactoryTest.java index 7a33dc0..021cd0d 100644 --- a/src/test/java/com/nike/cerberus/client/CerberusClientFactoryTest.java +++ b/src/test/java/com/nike/cerberus/client/CerberusClientFactoryTest.java @@ -33,8 +33,6 @@ public class CerberusClientFactoryTest { private final String url = "https://localhost/"; - private final StaticCerberusUrlResolver urlResolver = new StaticCerberusUrlResolver(url); - private final String TOKEN = "TOKEN"; private final CerberusCredentialsProvider credentialsProvider = new CerberusCredentialsProvider() { @@ -46,21 +44,8 @@ public CerberusCredentials getCredentials() { }; @Test - public void test_get_client_returns_configured_client() { - final CerberusClient client = CerberusClientFactory.getClient(); - assertThat(client).isNotNull(); - } - - @Test - public void test_get_client_uses_custom_url_resolver() { - final CerberusClient client = CerberusClientFactory.getClient(urlResolver); - assertThat(client).isNotNull(); - assertThat(client.getCerberusUrl().url().toString()).isEqualTo(url); - } - - @Test - public void test_get_client_uses_custom_url_resolver_and_creds_provider() { - final CerberusClient client = CerberusClientFactory.getClient(urlResolver, credentialsProvider); + public void test_get_client_uses_url_and_creds_provider() { + final CerberusClient client = CerberusClientFactory.getClient(url, credentialsProvider); assertThat(client).isNotNull(); assertThat(client.getCerberusUrl().url().toString()).isEqualTo(url); assertThat(client.getCredentialsProvider()).isNotNull(); @@ -73,7 +58,7 @@ public void test_get_client_uses_default_headers() { final String headerValue = "header value"; final Map defaultHeaders = new HashMap<>(); defaultHeaders.put(headerKey, headerValue); - final CerberusClient client = CerberusClientFactory.getClient(urlResolver, credentialsProvider, defaultHeaders); + final CerberusClient client = CerberusClientFactory.getClient(url, credentialsProvider, defaultHeaders); assertThat(client).isNotNull(); assertThat(client.getCerberusUrl().url().toString()).isEqualTo(url); assertThat(client.getCredentialsProvider()).isNotNull(); @@ -82,35 +67,13 @@ public void test_get_client_uses_default_headers() { assertThat(client.getDefaultHeaders().get(headerKey)).isEqualTo(headerValue); } - @Test - public void test_get_admin_client_returns_configured_client() { - final CerberusClient client = CerberusClientFactory.getClient(); - assertThat(client).isNotNull(); - } - - @Test - public void test_get_admin_client_uses_custom_url_resolver() { - final CerberusClient client = CerberusClientFactory.getClient(urlResolver); - assertThat(client).isNotNull(); - assertThat(client.getCerberusUrl().url().toString()).isEqualTo(url); - } - - @Test - public void test_get_admin_client_uses_custom_url_resolver_and_creds_provider() { - final CerberusClient client = CerberusClientFactory.getClient(urlResolver, credentialsProvider); - assertThat(client).isNotNull(); - assertThat(client.getCerberusUrl().url().toString()).isEqualTo(url); - assertThat(client.getCredentialsProvider()).isNotNull(); - assertThat(client.getCredentialsProvider().getCredentials().getToken()).isEqualTo(TOKEN); - } - @Test public void test_get_admin_client_uses_all_parameters() { final String headerKey = "HeaderKey"; final String headerValue = "header value"; final Map defaultHeaders = new HashMap<>(); defaultHeaders.put(headerKey, headerValue); - final CerberusClient client = CerberusClientFactory.getClient(urlResolver, credentialsProvider, 100, defaultHeaders); + final CerberusClient client = CerberusClientFactory.getClient(url, credentialsProvider, 100, defaultHeaders); assertThat(client).isNotNull(); assertThat(client.getCerberusUrl().url().toString()).isEqualTo(url); assertThat(client.getCredentialsProvider()).isNotNull(); @@ -119,18 +82,4 @@ public void test_get_admin_client_uses_all_parameters() { assertThat(client.getDefaultHeaders().get(headerKey)).isEqualTo(headerValue); } - @Test - public void test_get_admin_client_uses_default_headers() { - final String headerKey = "HeaderKey"; - final String headerValue = "header value"; - final Map defaultHeaders = new HashMap<>(); - defaultHeaders.put(headerKey, headerValue); - final CerberusClient client = CerberusClientFactory.getClient(urlResolver, credentialsProvider, defaultHeaders); - assertThat(client).isNotNull(); - assertThat(client.getCerberusUrl().url().toString()).isEqualTo(url); - assertThat(client.getCredentialsProvider()).isNotNull(); - assertThat(client.getCredentialsProvider().getCredentials().getToken()).isEqualTo(TOKEN); - assertThat(client.getDefaultHeaders().size()).isEqualTo(1); - assertThat(client.getDefaultHeaders().get(headerKey)).isEqualTo(headerValue); - } } \ No newline at end of file diff --git a/src/test/java/com/nike/cerberus/client/DefaultCerberusClientFactoryTest.java b/src/test/java/com/nike/cerberus/client/DefaultCerberusClientFactoryTest.java index 12aa93e..65b6c57 100644 --- a/src/test/java/com/nike/cerberus/client/DefaultCerberusClientFactoryTest.java +++ b/src/test/java/com/nike/cerberus/client/DefaultCerberusClientFactoryTest.java @@ -27,15 +27,9 @@ public class DefaultCerberusClientFactoryTest { @Test public void test_that_getClient_adds_client_version_as_a_default_header() { - CerberusClient result = DefaultCerberusClientFactory.getClient(); - assertEquals( - ClientVersion.getClientHeaderValue(), - result.getDefaultHeaders().get(ClientVersion.CERBERUS_CLIENT_HEADER)); - } - - @Test - public void test_that_getClientForLambda_adds_client_version_as_a_default_header() { - CerberusClient result = DefaultCerberusClientFactory.getClientForLambda("arn:aws:lambda:us-west-2:000000000000:function:name:qualifier"); + String region = "us-west-2"; + String url = "url"; + CerberusClient result = DefaultCerberusClientFactory.getClient(url, region); assertEquals( ClientVersion.getClientHeaderValue(), result.getDefaultHeaders().get(ClientVersion.CERBERUS_CLIENT_HEADER)); diff --git a/src/test/java/com/nike/cerberus/client/auth/DefaultCerberusCredentialsProviderChainTest.java b/src/test/java/com/nike/cerberus/client/auth/DefaultCerberusCredentialsProviderChainTest.java index 13e8cd7..dbac529 100644 --- a/src/test/java/com/nike/cerberus/client/auth/DefaultCerberusCredentialsProviderChainTest.java +++ b/src/test/java/com/nike/cerberus/client/auth/DefaultCerberusCredentialsProviderChainTest.java @@ -39,11 +39,15 @@ public class DefaultCerberusCredentialsProviderChainTest { private static final String SYS_VALUE = "SYS"; + private static final String region = "us-west-2"; + + private static final String url = "url"; + private DefaultCerberusCredentialsProviderChain credentialsProviderChain; @Before public void setup() { - credentialsProviderChain = new DefaultCerberusCredentialsProviderChain(); + credentialsProviderChain = new DefaultCerberusCredentialsProviderChain(url, region); } @Test From 2c3d3f210bfcdf1f581aa243a638c38db31b4019 Mon Sep 17 00:00:00 2001 From: melanahammel Date: Mon, 17 Dec 2018 09:25:02 -0800 Subject: [PATCH 08/14] deleted EnvironmentUtils and test --- .../client/util/EnvironmentUtils.java | 80 ------------------- .../client/util/EnvironmentUtilsTest.java | 23 ------ 2 files changed, 103 deletions(-) delete mode 100644 src/main/java/com/nike/cerberus/client/util/EnvironmentUtils.java delete mode 100644 src/test/java/com/nike/cerberus/client/util/EnvironmentUtilsTest.java diff --git a/src/main/java/com/nike/cerberus/client/util/EnvironmentUtils.java b/src/main/java/com/nike/cerberus/client/util/EnvironmentUtils.java deleted file mode 100644 index 30545f4..0000000 --- a/src/main/java/com/nike/cerberus/client/util/EnvironmentUtils.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.nike.cerberus.client.util; - -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.HashMap; -import java.util.Map; - -import static java.util.concurrent.TimeUnit.MILLISECONDS; - -/** - * Simple Util for determining environment metadata - */ -public class EnvironmentUtils { - - private static final Map canGetSuccessfullyResults = new HashMap<>(); - - private static final Logger LOGGER = LoggerFactory.getLogger(EnvironmentUtils.class); - - /** - * This endpoint is available on EC2 instances - */ - private static final String INSTANCE_IDENTITY_DOCUMENT = "http://169.254.169.254/latest/dynamic/instance-identity/document"; - - /** - * This endpoint is available on ECS Containers - */ - private static final String ECS_METADATA_ENDPOINT = "http://localhost:51678/v1/metadata"; - - private EnvironmentUtils() { - } - - /** - * True if the current system is running in a pure EC2 environment, and not ECS. - */ - public static boolean isRunningInEc2() { - return hasInstanceIdentity() && ! isRunningInEcs(); - } - - /** - * True if the http://169.254.169.254/latest/dynamic/instance-identity/document endpoint - * is available and returns a 2xx status code. True means we are currently running on - * an Ec2 instance. This check should work both on Linux and Windows. - */ - private static boolean hasInstanceIdentity() { - return canGetSuccessfully(INSTANCE_IDENTITY_DOCUMENT); - } - - /** - * - * @return true if this is a container in ECS - */ - public static boolean isRunningInEcs() { - return canGetSuccessfully(ECS_METADATA_ENDPOINT); - } - - static boolean canGetSuccessfully(String url) { - return canGetSuccessfullyResults.computeIfAbsent(url, theUrl -> { - OkHttpClient httpClient = new OkHttpClient.Builder() - .connectTimeout(500, MILLISECONDS) - .readTimeout(500, MILLISECONDS) - .writeTimeout(500, MILLISECONDS) - .build(); - - Request request = new Request.Builder().get().url(theUrl).build(); - - try (Response response = httpClient.newCall(request).execute()) { - if (response.isSuccessful()) { - return true; - } - } catch (Exception e) { - LOGGER.debug("Error when trying to GET {}", theUrl, e); - } - return false; - }); - } -} diff --git a/src/test/java/com/nike/cerberus/client/util/EnvironmentUtilsTest.java b/src/test/java/com/nike/cerberus/client/util/EnvironmentUtilsTest.java deleted file mode 100644 index 40ab555..0000000 --- a/src/test/java/com/nike/cerberus/client/util/EnvironmentUtilsTest.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.nike.cerberus.client.util; - -import org.junit.Test; - -import java.util.UUID; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -public class EnvironmentUtilsTest { - - @Test - public void test_that_canGetSuccessfully_can_get_google_index() { - assertTrue(EnvironmentUtils.canGetSuccessfully("http://www.google.com")); - assertTrue(EnvironmentUtils.canGetSuccessfully("http://www.google.com")); - } - - @Test - public void test_that_canGetSuccessfully_can_not_get_random_http_address() { - assertFalse(EnvironmentUtils.canGetSuccessfully("http://" + UUID.randomUUID().toString() + ".com/" + UUID.randomUUID().toString())); - } - -} From aee803d62ae6c21a98290c26467c063654d27166 Mon Sep 17 00:00:00 2001 From: melanahammel Date: Mon, 17 Dec 2018 10:42:54 -0800 Subject: [PATCH 09/14] deleted outdated base credentials tests --- .../aws/BaseAwsCredentialsProviderTest.java | 158 ------------------ .../auth/aws/BaseCredentialsProviderTest.java | 39 ----- 2 files changed, 197 deletions(-) delete mode 100644 src/test/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProviderTest.java delete mode 100644 src/test/java/com/nike/cerberus/client/auth/aws/BaseCredentialsProviderTest.java diff --git a/src/test/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProviderTest.java b/src/test/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProviderTest.java deleted file mode 100644 index 32a595d..0000000 --- a/src/test/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProviderTest.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client.auth.aws; - -import com.amazonaws.regions.Region; -import com.amazonaws.regions.RegionUtils; -import com.amazonaws.services.kms.AWSKMSClient; -import com.nike.cerberus.client.CerberusClientException; -import com.nike.cerberus.client.CerberusServerException; -import okhttp3.Call; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.mockwebserver.MockResponse; -import okhttp3.mockwebserver.MockWebServer; -import org.junit.Before; -import org.junit.Test; - -import java.io.IOException; - -import static com.nike.cerberus.client.auth.aws.BaseAwsCredentialsProvider.DEFAULT_AUTH_RETRIES; -import static org.mockito.Matchers.any; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.powermock.api.mockito.PowerMockito.mock; -import static org.powermock.api.mockito.PowerMockito.when; - -public class BaseAwsCredentialsProviderTest extends BaseCredentialsProviderTest{ - public static final Region REGION = RegionUtils.getRegion("us-west-2"); - public static final String CERBERUS_TEST_ARN = "arn:aws:iam::123456789012:role/cerberus-test-role"; - public static final String ERROR_RESPONSE = "Error calling cerberus"; - public static final String SUCCESS_RESPONSE = "{\"auth_data\": \"\"}"; - - protected static final String MISSING_AUTH_DATA = "{}"; - - - private BaseAwsCredentialsProvider provider; - private String cerberusUrl; - private MockWebServer mockWebServer; - - @Before - public void setUp() throws Exception { - - mockWebServer = new MockWebServer(); - mockWebServer.start(); - - cerberusUrl = "http://localhost:" + mockWebServer.getPort(); - - provider = new TestAwsCredentialsProvider(cerberusUrl); - } - - @Test(expected = CerberusClientException.class) - public void getEncryptedAuthData_blank_url_throws_exception() { - cerberusUrl = ""; - - provider.getEncryptedAuthData(CERBERUS_TEST_ARN, REGION); - } - - @Test(expected = CerberusClientException.class) - public void decryptToken_throws_exception_when_non_encrypted_data_provided() { - provider.decryptToken(mock(AWSKMSClient.class), "non-encrypted-token"); - } - - @Test(expected = CerberusServerException.class) - public void getEncryptedAuthData_throws_exception_on_bad_response_code() { - - - mockWebServer.enqueue(new MockResponse().setResponseCode(400).setBody(ERROR_RESPONSE)); - - provider.getEncryptedAuthData(CERBERUS_TEST_ARN, REGION); - } - - @Test(expected = CerberusClientException.class) - public void getEncryptedAuthData_throws_exception_on_missing_auth_data() { - - mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(MISSING_AUTH_DATA)); - - provider.getEncryptedAuthData(CERBERUS_TEST_ARN, REGION); - } - - @Test - public void test_that_getEncryptedAuthData_retries_on_500_errors() { - - for (int i = 0; i < DEFAULT_AUTH_RETRIES - 1; i++) { - mockWebServer.enqueue(new MockResponse().setResponseCode(500).setBody(ERROR_RESPONSE)); - } - mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(SUCCESS_RESPONSE)); - - provider.getEncryptedAuthData(CERBERUS_TEST_ARN, REGION); - } - - @Test - public void test_that_getEncryptedAuthData_retries_on_IOException_errors() throws IOException { - - OkHttpClient httpClient = mock(OkHttpClient.class); - Call call = mock(Call.class); - when(call.execute()).thenThrow(new IOException()); - when(httpClient.newCall(any(Request.class))).thenReturn(call); - TestAwsCredentialsProvider provider = new TestAwsCredentialsProvider(cerberusUrl, httpClient); - - try { - provider.getEncryptedAuthData(CERBERUS_TEST_ARN, REGION); - - // code should not reach this point, throw an error if it does - throw new AssertionError("Expected CerberusClientException, but was not thrown"); - } catch(CerberusClientException cce) { // catch this error so that the remaining tests will run - // ensure that error is thrown because of mocked IOException - if ( !(cce.getCause() instanceof IOException) ) { - throw new AssertionError("Expected error cause to be IOException, but was " + cce.getCause().getClass()); - } - } - - verify(httpClient, times(DEFAULT_AUTH_RETRIES)).newCall(any(Request.class)); - } - - @Test - public void test_that_getEncryptedAuthData_does_not_retry_on_200() { - - mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(SUCCESS_RESPONSE)); - mockWebServer.enqueue(new MockResponse().setResponseCode(500).setBody(ERROR_RESPONSE)); - - provider.getEncryptedAuthData(CERBERUS_TEST_ARN, REGION); - } - - class TestAwsCredentialsProvider extends BaseAwsCredentialsProvider { - /** - * Constructor to setup credentials provider - * - * @param cerberusUrl Cerberus URL - */ - public TestAwsCredentialsProvider(String cerberusUrl) { - super(cerberusUrl); - } - - public TestAwsCredentialsProvider(String cerberusUrl, OkHttpClient client) { - super(cerberusUrl, client); - } - - @Override - protected void authenticate() { - - } - } - -} \ No newline at end of file diff --git a/src/test/java/com/nike/cerberus/client/auth/aws/BaseCredentialsProviderTest.java b/src/test/java/com/nike/cerberus/client/auth/aws/BaseCredentialsProviderTest.java deleted file mode 100644 index 87d1ed9..0000000 --- a/src/test/java/com/nike/cerberus/client/auth/aws/BaseCredentialsProviderTest.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2018 Nike, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.nike.cerberus.client.auth.aws; - -import com.amazonaws.services.kms.AWSKMSClient; -import com.amazonaws.services.kms.model.DecryptRequest; -import com.amazonaws.services.kms.model.DecryptResult; - -import java.nio.ByteBuffer; - -import static org.mockito.Matchers.any; -import static org.powermock.api.mockito.PowerMockito.when; - -public class BaseCredentialsProviderTest { - protected static final String AUTH_RESPONSE = "{\"auth_data\":\"eyJjbGllbnRfdG9rZW4iOiI2NjMyY2I1Zi1mMTBjLTQ1NzItOTU0NS1lNTJmNDdmNmEzZmQiLCAibGVhc2VfZHVyYXRpb24iOiIzNjAwIn0=\"}"; - protected static final String BAD_AUTH_RESPONSE_JSON = "{,\"auth_data\":\"eyJjbGllbnRfdG9rZW4iOiI2NjMyY2I1Zi1mMTBjLTQ1NzItOTU0NS1lNTJmNDdmNmEzZmQiLCAibGVhc2VfZHVyYXRpb24iOiIzNjAwIn0=\"}"; - protected static final String DECODED_AUTH_DATA = "{\"client_token\":\"6632cb5f-f10c-4572-9545-e52f47f6a3fd\", \"lease_duration\":\"3600\"}"; - protected static final String AUTH_TOKEN = "6632cb5f-f10c-4572-9545-e52f47f6a3fd"; - - protected void mockDecrypt(AWSKMSClient kmsClient, final String toDecrypt) { - DecryptResult decryptResult = new DecryptResult(); - decryptResult.setPlaintext(ByteBuffer.wrap(toDecrypt.getBytes())); - when(kmsClient.decrypt(any(DecryptRequest.class))).thenReturn(decryptResult); - } -} From 95035185842421190457b514c7a3fc8a80864c7c Mon Sep 17 00:00:00 2001 From: melanahammel Date: Mon, 17 Dec 2018 11:00:30 -0800 Subject: [PATCH 10/14] cleaned up Sts Auth creds provider and tests --- .../client/auth/aws/StsAuthCerberusClientTest.java | 4 ++-- .../auth/aws/StsCerberusCredentialsProvider.java | 12 +++++------- .../auth/aws/StsCerberusCredentialsProviderTest.java | 6 ++++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/integration/java/com/nike/cerberus/client/auth/aws/StsAuthCerberusClientTest.java b/src/integration/java/com/nike/cerberus/client/auth/aws/StsAuthCerberusClientTest.java index 609dc0e..ce973bd 100644 --- a/src/integration/java/com/nike/cerberus/client/auth/aws/StsAuthCerberusClientTest.java +++ b/src/integration/java/com/nike/cerberus/client/auth/aws/StsAuthCerberusClientTest.java @@ -28,8 +28,8 @@ */ public class StsAuthCerberusClientTest { - private static String region; - private static String cerberusUrl; + private String region; + private String cerberusUrl; @Before public void setUp() { diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java b/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java index 56677ee..73597a9 100644 --- a/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java +++ b/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java @@ -48,14 +48,9 @@ /** * Provider for allowing users to authenticate with Cerberus with the STS auth endpoint. */ - public class StsCerberusCredentialsProvider extends BaseAwsCredentialsProvider { - protected static String regionName; - - protected static final int DEFAULT_AUTH_RETRIES = 3; - - protected static final int DEFAULT_RETRY_INTERVAL_IN_MILLIS = 200; + protected String regionName; private static final Logger LOGGER = LoggerFactory.getLogger(BaseAwsCredentialsProvider.class); @@ -133,6 +128,7 @@ private void signRequest(com.amazonaws.Request request, AWSCredentials credentia /** * Generates and returns signed headers. + * @return Signed headers */ protected Map getSignedHeaders(){ @@ -143,7 +139,8 @@ protected Map getSignedHeaders(){ try { endpoint = new URI(url); } catch (URISyntaxException e) { - e.printStackTrace(); + LOGGER.info(String.format("URL is not formatted correctly"), e); + } Map> parameters = new HashMap<>(); @@ -164,6 +161,7 @@ protected Map getSignedHeaders(){ /** * Sends request with signed headers to Cerberus to obtain token using STS Auth. + * @return Cerberus Auth Response with token */ protected CerberusAuthResponse getToken(){ diff --git a/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java b/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java index 34ad579..43197d7 100644 --- a/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java +++ b/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java @@ -34,11 +34,14 @@ /** * Tests the StsCerberusCredentialsProvider class */ -public class StsCerberusCredentialsProviderTest extends BaseCredentialsProviderTest { +public class StsCerberusCredentialsProviderTest { private static final String REGION_STRING_EAST = "us-east-1"; private static final String REGION_STRING_WEST = "us-west-2"; + protected static final String AUTH_RESPONSE = "{\"auth_data\":\"eyJjbGllbnRfdG9rZW4iOiI2NjMyY2I1Zi1mMTBjLTQ1NzItOTU0NS1lNTJmNDdmNmEzZmQiLCAibGVhc2VfZHVyYXRpb24iOiIzNjAwIn0=\"}"; + protected static final String DECODED_AUTH_DATA = "{\"client_token\":\"6632cb5f-f10c-4572-9545-e52f47f6a3fd\", \"lease_duration\":\"3600\"}"; + private String cerberusUrl; @Before @@ -109,7 +112,6 @@ public void get_token_throws_exception_timeout() throws IOException { } @Test(expected = CerberusClientException.class) - public void get_token_throws_exception_when_url_is_blank(){ StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); CerberusAuthResponse token = credentialsProvider.getToken(); From 408fb757c5e58f2c8655f68852962b9655abe008 Mon Sep 17 00:00:00 2001 From: melanahammel Date: Mon, 17 Dec 2018 11:03:18 -0800 Subject: [PATCH 11/14] removed KMS dependencies and cleaned up warnings --- gradle/dependencies.gradle | 3 - .../client/CerberusClientFactory.java | 2 +- ...faultCerberusCredentialsProviderChain.java | 2 +- .../auth/aws/BaseAwsCredentialsProvider.java | 166 +----------------- ...ltAWSCredentialsProviderChainDebugger.java | 7 +- 5 files changed, 10 insertions(+), 170 deletions(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index 2664344..cea5d1b 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -47,11 +47,8 @@ dependencies { compileOnly 'com.google.code.findbugs:annotations:3.0.1' compile "com.amazonaws:aws-java-sdk-core:${AWS_SDK_VERSION}" - compile "com.amazonaws:aws-java-sdk-kms:${AWS_SDK_VERSION}" - compile "com.amazonaws:aws-java-sdk-lambda:${AWS_SDK_VERSION}" compile "com.amazonaws:aws-java-sdk-sts:${AWS_SDK_VERSION}" - testRuntime 'org.slf4j:slf4j-simple:1.7.25' testCompile "junit:junit:4.12" testCompile ("org.mockito:mockito-core:1.10.19") { diff --git a/src/main/java/com/nike/cerberus/client/CerberusClientFactory.java b/src/main/java/com/nike/cerberus/client/CerberusClientFactory.java index e43666f..a33b406 100644 --- a/src/main/java/com/nike/cerberus/client/CerberusClientFactory.java +++ b/src/main/java/com/nike/cerberus/client/CerberusClientFactory.java @@ -127,7 +127,7 @@ public static CerberusClient getClient(final String cerberusUrl, * @param cerberusUrl URL for Cerberus * @param cerberusCredentialsProvider Credential provider for acquiring a token for interacting with Cerberus * @param defaultHeaders Map of default header names and values to add to every HTTP request - * @param httpClient + * @param httpClient the client to use for auth * @return Cerberus client */ public static CerberusClient getClient(final String cerberusUrl, diff --git a/src/main/java/com/nike/cerberus/client/auth/DefaultCerberusCredentialsProviderChain.java b/src/main/java/com/nike/cerberus/client/auth/DefaultCerberusCredentialsProviderChain.java index 207efbb..b65e152 100644 --- a/src/main/java/com/nike/cerberus/client/auth/DefaultCerberusCredentialsProviderChain.java +++ b/src/main/java/com/nike/cerberus/client/auth/DefaultCerberusCredentialsProviderChain.java @@ -20,7 +20,6 @@ /** * Default credentials provider chain that will attempt to retrieve a token in the following order: - *

*

    *
  • Environment Variable - CERBERUS_TOKEN
  • *
  • Java System Property - cerberus.token
  • @@ -67,6 +66,7 @@ public DefaultCerberusCredentialsProviderChain(String url, String region, String * and OkHttpClient * * @param url Cerberus URL + * @param region AWS region * @param httpClient the client to use */ public DefaultCerberusCredentialsProviderChain(String url, String region, OkHttpClient httpClient) { diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProvider.java b/src/main/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProvider.java index 9406a90..96ecc7c 100644 --- a/src/main/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProvider.java +++ b/src/main/java/com/nike/cerberus/client/auth/aws/BaseAwsCredentialsProvider.java @@ -16,61 +16,30 @@ package com.nike.cerberus.client.auth.aws; -import com.amazonaws.regions.Region; -import com.amazonaws.regions.Regions; -import com.amazonaws.services.kms.AWSKMS; -import com.amazonaws.services.kms.AWSKMSClient; -import com.amazonaws.services.kms.model.AWSKMSException; -import com.amazonaws.services.kms.model.DecryptRequest; -import com.amazonaws.services.kms.model.DecryptResult; -import com.amazonaws.util.Base64; -import com.google.gson.FieldNamingPolicy; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.reflect.TypeToken; -import com.nike.cerberus.client.CerberusClientException; import com.nike.cerberus.client.CerberusServerException; import com.nike.cerberus.client.ClientVersion; import com.nike.cerberus.client.auth.CerberusCredentials; import com.nike.cerberus.client.auth.CerberusCredentialsProvider; import com.nike.cerberus.client.auth.TokenCerberusCredentials; -import com.nike.cerberus.client.http.HttpHeader; -import com.nike.cerberus.client.http.HttpMethod; -import com.nike.cerberus.client.http.HttpStatus; -import com.nike.cerberus.client.model.CerberusAuthResponse; -import okhttp3.ConnectionSpec; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import org.apache.commons.lang3.StringUtils; +import okhttp3.*; import org.joda.time.DateTime; -import org.joda.time.DateTimeZone; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; -import java.lang.reflect.Type; -import java.nio.ByteBuffer; -import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import static com.nike.cerberus.client.CerberusClientFactory.DEFAULT_TIMEOUT; -import static com.nike.cerberus.client.CerberusClientFactory.DEFAULT_TIMEOUT_UNIT; -import static com.nike.cerberus.client.CerberusClientFactory.TLS_1_2_OR_NEWER; +import static com.nike.cerberus.client.CerberusClientFactory.*; import static okhttp3.ConnectionSpec.CLEARTEXT; /** * {@link CerberusCredentialsProvider} implementation that uses some AWS * credentials provider to authenticate with Cerberus and decrypt the auth - * response using KMS. If the assigned role has been granted the appropriate + * response using STS Auth. If the assigned role has been granted the appropriate * provisioned for usage of Cerberus, it will succeed and have a token that can be * used to interact with Cerberus. */ @@ -78,8 +47,6 @@ public abstract class BaseAwsCredentialsProvider implements CerberusCredentialsP public static final MediaType DEFAULT_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8"); - public static final String IAM_ROLE_ARN_FORMAT = "arn:aws:iam::%s:role/%s"; - private static final Logger LOGGER = LoggerFactory.getLogger(BaseAwsCredentialsProvider.class); protected static final int DEFAULT_AUTH_RETRIES = 3; @@ -92,9 +59,6 @@ public abstract class BaseAwsCredentialsProvider implements CerberusCredentialsP private final Lock writeLock = readWriteLock.writeLock(); - private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) - .create(); - protected final int paddingTimeInSeconds = 60; protected volatile TokenCerberusCredentials credentials; @@ -119,7 +83,6 @@ public BaseAwsCredentialsProvider(String cerberusUrl) { LOGGER.info("Cerberus URL={}", this.cerberusUrl); this.httpClient = createHttpClient(); - } /** @@ -194,121 +157,10 @@ public CerberusCredentials getCredentials() { /** * Look up the IAM roles associated with the given AWS credentials provider - * and attempt to authenticate and decrypt using KMS. + * and attempt to authenticate and decrypt using STS Auth. */ abstract protected void authenticate(); - /** - * Authenticates with Cerberus and decrypts and sets the token and expiration details. - * - * @param accountId AWS account ID used to auth with cerberus - * @param iamRoleName IAM role name used to auth with cerberus - * @deprecated no longer used, will be removed - */ - protected void getAndSetToken(final String accountId, final String iamRoleName) { - final String iamRoleArn = String.format(IAM_ROLE_ARN_FORMAT, accountId, iamRoleName); - - getAndSetToken(iamRoleArn, Regions.getCurrentRegion()); - } - - /** - * Authenticates with Cerberus and decrypts and sets the token and expiration details. - * - * @param iamPrincipalArn AWS IAM principal ARN used to auth with cerberus - * @param region AWS Region used in auth with cerberus - */ - protected void getAndSetToken(final String iamPrincipalArn, final Region region) { - - final AWSKMSClient kmsClient = new AWSKMSClient(); - kmsClient.setRegion(region); - - final String encryptedAuthData = getEncryptedAuthData(iamPrincipalArn, region); - final CerberusAuthResponse decryptedToken = decryptToken(kmsClient, encryptedAuthData); - final DateTime expires = DateTime.now(DateTimeZone.UTC) - .plusSeconds(decryptedToken.getLeaseDuration() - paddingTimeInSeconds); - - credentials = new TokenCerberusCredentials(decryptedToken.getClientToken()); - expireDateTime = expires; - } - - /** - * Retrieves the encrypted auth response from Cerberus. - * - * @param iamPrincipalArn IAM principal ARN used in the row key - * @param region Current region of the running function or instance - * @return Base64 and encrypted token - */ - protected String getEncryptedAuthData(final String iamPrincipalArn, Region region) { - - if (StringUtils.isBlank(cerberusUrl)) { - throw new CerberusClientException("Unable to find the Cerberus URL."); - } - - LOGGER.info(String.format("Attempting to authenticate with AWS IAM principal ARN [%s] against [%s]", - iamPrincipalArn, cerberusUrl)); - - try { - Request.Builder requestBuilder = new Request.Builder().url(cerberusUrl + "/v2/auth/iam-principal") - .addHeader(HttpHeader.ACCEPT, DEFAULT_MEDIA_TYPE.toString()) - .addHeader(HttpHeader.CONTENT_TYPE, DEFAULT_MEDIA_TYPE.toString()) - .addHeader(ClientVersion.CERBERUS_CLIENT_HEADER, cerberusJavaClientHeaderValue) - .method(HttpMethod.POST, buildCredentialsRequestBody(iamPrincipalArn, region)); - - Response response = executeRequestWithRetry(requestBuilder.build(), DEFAULT_AUTH_RETRIES, DEFAULT_RETRY_INTERVAL_IN_MILLIS); - - if (response.code() != HttpStatus.OK) { - parseAndThrowErrorResponse(response.code(), response.body().string()); - } - - final Type mapType = new TypeToken>() { - }.getType(); - final Map authData = gson.fromJson(response.body().string(), mapType); - final String key = "auth_data"; - - if (authData.containsKey(key)) { - LOGGER.info(String.format("Authentication successful with AWS IAM principal ARN [%s] against [%s]", - iamPrincipalArn, cerberusUrl)); - return authData.get(key); - } else { - throw new CerberusClientException("Success response from IAM role authenticate endpoint missing auth data!"); - } - } catch (IOException e) { - throw new CerberusClientException("I/O error while communicating with Cerberus", e); - } - } - - /** - * Decodes the encrypted token and attempts to decrypt it using AWS KMS. If - * successful, the token is returned. - * - * @param kmsClient KMS client - * @param encryptedToken Token to decode and decrypt - * @return Decrypted token - */ - protected CerberusAuthResponse decryptToken(AWSKMS kmsClient, String encryptedToken) { - byte[] decodedToken; - - try { - decodedToken = Base64.decode(encryptedToken); - } catch (IllegalArgumentException iae) { - throw new CerberusClientException("Encrypted token not Base64 encoded", iae); - } - - try { - final DecryptRequest request = new DecryptRequest().withCiphertextBlob(ByteBuffer.wrap(decodedToken)); - final DecryptResult result = kmsClient.decrypt(request); - - final String decryptedAuthData = new String(result.getPlaintext().array(), Charset.forName("UTF-8")); - - return gson.fromJson(decryptedAuthData, CerberusAuthResponse.class); - - } catch (AWSKMSException e) { - new DefaultAWSCredentialsProviderChainDebugger().logExtraDebuggingIfAppropriate(e); - throw e; - } - } - - /** * Executes an HTTP request and retries if a 500 level error is returned * @@ -349,16 +201,6 @@ private void sleep(long milliseconds) { } } - protected RequestBody buildCredentialsRequestBody(final String iamPrincipalArn, Region region) { - final String regionName = region == null ? Regions.getCurrentRegion().getName() : region.getName(); - - final Map credentials = new HashMap<>(); - credentials.put("iam_principal_arn", iamPrincipalArn); - credentials.put("region", regionName); - - return RequestBody.create(DEFAULT_MEDIA_TYPE, gson.toJson(credentials)); - } - protected void parseAndThrowErrorResponse(final int responseCode, final String responseBody) { final String message = String.format("Failed to authenticate. Response: %s", responseBody); LOGGER.warn(message); diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/DefaultAWSCredentialsProviderChainDebugger.java b/src/main/java/com/nike/cerberus/client/auth/aws/DefaultAWSCredentialsProviderChainDebugger.java index 48bf2eb..8fc9ff3 100644 --- a/src/main/java/com/nike/cerberus/client/auth/aws/DefaultAWSCredentialsProviderChainDebugger.java +++ b/src/main/java/com/nike/cerberus/client/auth/aws/DefaultAWSCredentialsProviderChainDebugger.java @@ -1,12 +1,12 @@ package com.nike.cerberus.client.auth.aws; +import com.amazonaws.AmazonServiceException; import com.amazonaws.auth.AWSCredentials; import com.amazonaws.auth.AWSCredentialsProvider; import com.amazonaws.auth.EC2ContainerCredentialsProviderWrapper; import com.amazonaws.auth.EnvironmentVariableCredentialsProvider; import com.amazonaws.auth.SystemPropertiesCredentialsProvider; import com.amazonaws.auth.profile.ProfileCredentialsProvider; -import com.amazonaws.services.kms.model.AWSKMSException; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,9 +31,10 @@ public class DefaultAWSCredentialsProviderChainDebugger { /** * Log extra debugging information if appropriate + * @param serviceException exception from Amazon */ - public void logExtraDebuggingIfAppropriate(AWSKMSException kmsException) { - if (StringUtils.contains(kmsException.getMessage(), "The security token included in the request is invalid.")) { + public void logExtraDebuggingIfAppropriate(AmazonServiceException serviceException) { + if (StringUtils.contains(serviceException.getMessage(), "The security token included in the request is invalid.")) { LOGGER.warn("Bad credentials may have been picked up from the DefaultAWSCredentialsProviderChain"); boolean firstCredentialsFound = false; for (AWSCredentialsProvider provider : credentialProviderChain) { From a1044fa9e6fb8e3da86d4554057f47b3de5f4989 Mon Sep 17 00:00:00 2001 From: melanahammel Date: Mon, 17 Dec 2018 15:57:58 -0800 Subject: [PATCH 12/14] mocked credentials for tests, used data provider for test parameters, and incorporated provider chain debugger --- gradle/dependencies.gradle | 1 + ...ltAWSCredentialsProviderChainDebugger.java | 8 ++- .../aws/StsCerberusCredentialsProvider.java | 36 ++++++++++- .../StsCerberusCredentialsProviderTest.java | 60 ++++++++++++------- 4 files changed, 78 insertions(+), 27 deletions(-) diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index cea5d1b..74609f0 100644 --- a/gradle/dependencies.gradle +++ b/gradle/dependencies.gradle @@ -63,6 +63,7 @@ dependencies { testCompile "org.assertj:assertj-core:2.3.0" testCompile "com.squareup.okhttp3:mockwebserver:3.7.0" testCompile "commons-io:commons-io:2.4" + testCompile group: 'com.tngtech.java', name: 'junit-dataprovider', version: '1.10.0' } shadowJar { diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/DefaultAWSCredentialsProviderChainDebugger.java b/src/main/java/com/nike/cerberus/client/auth/aws/DefaultAWSCredentialsProviderChainDebugger.java index 8fc9ff3..8482e46 100644 --- a/src/main/java/com/nike/cerberus/client/auth/aws/DefaultAWSCredentialsProviderChainDebugger.java +++ b/src/main/java/com/nike/cerberus/client/auth/aws/DefaultAWSCredentialsProviderChainDebugger.java @@ -18,6 +18,8 @@ public class DefaultAWSCredentialsProviderChainDebugger { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultAWSCredentialsProviderChainDebugger.class); + private static final String TOKEN_IS_EXPIRED = "The security token included in the request is expired."; + private static final String TOKEN_IS_INVALID = "Invalid credentials"; /** * This chain should match that found in DefaultAWSCredentialsProviderChain @@ -31,10 +33,10 @@ public class DefaultAWSCredentialsProviderChainDebugger { /** * Log extra debugging information if appropriate - * @param serviceException exception from Amazon + * @param cerberusErrorMessage error message from Cerberus */ - public void logExtraDebuggingIfAppropriate(AmazonServiceException serviceException) { - if (StringUtils.contains(serviceException.getMessage(), "The security token included in the request is invalid.")) { + public void logExtraDebuggingIfAppropriate(String cerberusErrorMessage) { + if (StringUtils.contains(cerberusErrorMessage, TOKEN_IS_EXPIRED) || StringUtils.contains(cerberusErrorMessage, TOKEN_IS_INVALID)) { LOGGER.warn("Bad credentials may have been picked up from the DefaultAWSCredentialsProviderChain"); boolean firstCredentialsFound = false; for (AWSCredentialsProvider provider : credentialProviderChain) { diff --git a/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java b/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java index 73597a9..65a0e36 100644 --- a/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java +++ b/src/main/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProvider.java @@ -19,6 +19,7 @@ import com.amazonaws.DefaultRequest; import com.amazonaws.auth.AWS4Signer; import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProviderChain; import com.amazonaws.auth.DefaultAWSCredentialsProviderChain; import com.amazonaws.http.HttpMethodName; import com.amazonaws.regions.Regions; @@ -52,6 +53,8 @@ public class StsCerberusCredentialsProvider extends BaseAwsCredentialsProvider { protected String regionName; + protected AWSCredentialsProviderChain providerChain; + private static final Logger LOGGER = LoggerFactory.getLogger(BaseAwsCredentialsProvider.class); private final Gson gson = new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) @@ -106,11 +109,36 @@ public StsCerberusCredentialsProvider(String cerberusUrl, String region, OkHttpC } } + /** + * Constructor to setup credentials provider with specified AWS credentials to sign request + * + * @param cerberusUrl Cerberus URL + * @param region AWS Region used in auth with Cerberus + * @param providerChain AWS Credentials Provider Chain + */ + public StsCerberusCredentialsProvider(String cerberusUrl, String region, AWSCredentialsProviderChain providerChain) { + super(cerberusUrl); + + if (region != null ) { + regionName = Regions.fromName(region).getName(); + } else { + throw new CerberusClientException("Region is null. Please provide valid AWS region."); + } + + this.providerChain = providerChain; + } + /** * Obtains AWS Credentials. */ private AWSCredentials getAWSCredentials(){ - return DefaultAWSCredentialsProviderChain.getInstance().getCredentials(); + + if (providerChain == null) { + return DefaultAWSCredentialsProviderChain.getInstance().getCredentials(); + } + else { + return providerChain.getCredentials(); + } } /** @@ -181,12 +209,14 @@ protected CerberusAuthResponse getToken(){ .build(); Response response = executeRequestWithRetry(request, DEFAULT_AUTH_RETRIES, DEFAULT_RETRY_INTERVAL_IN_MILLIS); + String responseBody = response.body().string(); if (response.code() != HttpStatus.OK) { - parseAndThrowErrorResponse(response.code(), response.body().string()); + new DefaultAWSCredentialsProviderChainDebugger().logExtraDebuggingIfAppropriate(responseBody); + parseAndThrowErrorResponse(response.code(), responseBody); } - return gson.fromJson(response.body().string(), CerberusAuthResponse.class); + return gson.fromJson(responseBody, CerberusAuthResponse.class); } catch (IOException e) { throw new CerberusClientException("I/O error while communicating with Cerberus", e); diff --git a/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java b/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java index 43197d7..42c29ab 100644 --- a/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java +++ b/src/test/java/com/nike/cerberus/client/auth/aws/StsCerberusCredentialsProviderTest.java @@ -16,13 +16,19 @@ package com.nike.cerberus.client.auth.aws; +import com.amazonaws.auth.AWSCredentials; +import com.amazonaws.auth.AWSCredentialsProviderChain; +import com.amazonaws.auth.BasicSessionCredentials; import com.nike.cerberus.client.CerberusClientException; import com.nike.cerberus.client.model.CerberusAuthResponse; +import com.tngtech.java.junit.dataprovider.DataProviderRunner; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.apache.commons.lang3.StringUtils; import org.junit.Before; import org.junit.Test; +import org.junit.runner.RunWith; +import com.tngtech.java.junit.dataprovider.DataProvider; import java.io.IOException; import java.util.Map; @@ -34,49 +40,49 @@ /** * Tests the StsCerberusCredentialsProvider class */ +@RunWith(DataProviderRunner.class) public class StsCerberusCredentialsProviderTest { private static final String REGION_STRING_EAST = "us-east-1"; private static final String REGION_STRING_WEST = "us-west-2"; - protected static final String AUTH_RESPONSE = "{\"auth_data\":\"eyJjbGllbnRfdG9rZW4iOiI2NjMyY2I1Zi1mMTBjLTQ1NzItOTU0NS1lNTJmNDdmNmEzZmQiLCAibGVhc2VfZHVyYXRpb24iOiIzNjAwIn0=\"}"; protected static final String DECODED_AUTH_DATA = "{\"client_token\":\"6632cb5f-f10c-4572-9545-e52f47f6a3fd\", \"lease_duration\":\"3600\"}"; + protected static final String ERROR_RESPONSE = "Invalid credentials"; private String cerberusUrl; + private AWSCredentialsProviderChain chain; + private AWSCredentials credentials; @Before public void setUp() { + cerberusUrl = mock(String.class); + + chain = mock(AWSCredentialsProviderChain.class); + + credentials = new BasicSessionCredentials("foo", "bar", "cat"); } @Test public void test_sts_creds_provider_constructor() { + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); assertThat(credentialsProvider.getCerberusUrl()).isEqualTo(cerberusUrl); assertThat(credentialsProvider.regionName).isEqualTo(REGION_STRING_EAST); } @Test - public void test_get_signed_headers_for_east_region() throws IOException { - MockWebServer mockWebServer = new MockWebServer(); - mockWebServer.start(); - final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); - StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); + @DataProvider(value = { + REGION_STRING_EAST, + REGION_STRING_WEST}) + public void test_get_signed_headers(String testRegion) throws IOException { - Map headers = credentialsProvider.getSignedHeaders(); - assertThat(headers).isNotNull(); - assertThat(headers.get("Authorization")).isNotEmpty(); - assertThat(headers.get("X-Amz-Date")).isNotEmpty(); - assertThat(headers.get("X-Amz-Security-Token")).isNotEmpty(); - assertThat(headers.get("Host")).isNotEmpty(); - } + when(chain.getCredentials()).thenReturn(credentials); - @Test - public void test_get_signed_headers_for_west_region() throws IOException { MockWebServer mockWebServer = new MockWebServer(); mockWebServer.start(); final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); - StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_WEST); + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, testRegion, chain); Map headers = credentialsProvider.getSignedHeaders(); assertThat(headers).isNotNull(); @@ -86,12 +92,16 @@ public void test_get_signed_headers_for_west_region() throws IOException { assertThat(headers.get("Host")).isNotEmpty(); } + @Test public void get_token_returns_token() throws IOException { + + when(chain.getCredentials()).thenReturn(credentials); + MockWebServer mockWebServer = new MockWebServer(); mockWebServer.start(); final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); - StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST, chain); mockWebServer.enqueue(new MockResponse().setResponseCode(200).setBody(DECODED_AUTH_DATA)); CerberusAuthResponse token = credentialsProvider.getToken(); @@ -101,10 +111,13 @@ public void get_token_returns_token() throws IOException { @Test(expected = CerberusClientException.class) public void get_token_throws_exception_timeout() throws IOException { + + when(chain.getCredentials()).thenReturn(credentials); + MockWebServer mockWebServer = new MockWebServer(); mockWebServer.start(); final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); - StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST, chain); CerberusAuthResponse token = credentialsProvider.getToken(); assertThat(token).isNotNull(); @@ -113,7 +126,8 @@ public void get_token_throws_exception_timeout() throws IOException { @Test(expected = CerberusClientException.class) public void get_token_throws_exception_when_url_is_blank(){ - StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); + + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST, chain); CerberusAuthResponse token = credentialsProvider.getToken(); assertThat(token).isNotNull(); assertThat(StringUtils.isNotEmpty(token.getClientToken())); @@ -121,11 +135,14 @@ public void get_token_throws_exception_when_url_is_blank(){ @Test(expected = CerberusClientException.class) public void get_token_throws_exception_when_response_is_bad() throws IOException { + + when(chain.getCredentials()).thenReturn(credentials); + MockWebServer mockWebServer = new MockWebServer(); mockWebServer.start(); final String cerberusUrl = "http://localhost:" + mockWebServer.getPort(); - StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); - mockWebServer.enqueue(new MockResponse().setResponseCode(400).setBody(AUTH_RESPONSE)); + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST, chain); + mockWebServer.enqueue(new MockResponse().setResponseCode(400).setBody(ERROR_RESPONSE)); CerberusAuthResponse token = credentialsProvider.getToken(); assertThat(token).isNotNull(); @@ -134,6 +151,7 @@ public void get_token_throws_exception_when_response_is_bad() throws IOException @Test(expected = CerberusClientException.class) public void authenticate_throws_exception_when_token_is_null() { + StsCerberusCredentialsProvider credentialsProvider = new StsCerberusCredentialsProvider(cerberusUrl, REGION_STRING_EAST); CerberusAuthResponse token = mock(CerberusAuthResponse.class); when(token.getClientToken()).thenReturn(null); From 7f964a4d0bec333d0502c945e9c0ff1ed66fc799 Mon Sep 17 00:00:00 2001 From: melanahammel Date: Tue, 18 Dec 2018 11:24:27 -0800 Subject: [PATCH 13/14] updated README for Sts Auth update --- README.md | 110 +++++++----------------------------------------------- 1 file changed, 13 insertions(+), 97 deletions(-) diff --git a/README.md b/README.md index 0c22a9e..2b233d4 100644 --- a/README.md +++ b/README.md @@ -5,27 +5,24 @@ [![Coverage Status](https://coveralls.io/repos/github/Nike-Inc/cerberus-java-client/badge.svg?branch=master)](https://coveralls.io/github/Nike-Inc/cerberus-java-client) [![][license img]][license] -A java based client library for Cerberus that's built on top of Nike's Cerberus client. +This is a Java based client library for Cerberus that is built on top of Nike's Cerberus client. This library acts as a wrapper around the Nike developed Cerberus client by configuring the client to be Cerberus compatible. To learn more about Cerberus, please see the [Cerberus website](http://engineering.nike.com/cerberus/). -## Quickstart for EC2 +## Quickstart for Cerberus Java Client 1. Start with the [quick start guide](http://engineering.nike.com/cerberus/docs/user-guide/quick-start). -2. Add the [Cerberus client dependency](https://bintray.com/nike/maven/cerberus-client) to your build (e.g. Maven, Gradle) -3. Provide an authentication mechanism. - - For local development it is easiest to export a `CERBERUS_TOKEN` that you copied from the Cerberus dashboard. - When running in AWS, your application will not need this environment variable, instead it will automatically - authenticate using its IAM role. Alternatively, set a `cerberus.token` System property. - - If you would like to test IAM authentication locally, you can do that by [assuming a role](http://docs.aws.amazon.com/cli/latest/userguide/cli-roles.html). -4. Access secrets from Cerberus using Java +2. Add the [Cerberus client dependency](https://bintray.com/nike/maven/cerberus-client) to your build (e.g. Maven, Gradle). +3. Access secrets from Cerberus using Java. ``` java String cerberusUrl = "https://cerberus.example.com"; - CerberusClient cerberusClient = DefaultCerberusClientFactory.getClient(cerberusUrl); + String region = "us-west-2"; + CerberusClient cerberusClient = DefaultCerberusClientFactory.getClient(cerberusUrl, region); Map secrets = cerberusClient.read("/app/my-sdb-name").getData(); ``` +Check out ["Working with AWS Credentials"](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) for more information on how the AWS SDK for Java loads credentials. ## Lambdas @@ -40,91 +37,6 @@ feature provided by AWS. Another option is to store Lambda secrets in Cerberus but only read them at Lambda deploy time, then storing them as encrypted environmental variables, to avoid the extra Cerberus runtime latency. -### Additional permissions - -The IAM role assigned to the Lambda function must contain the following policy statement in addition to the above KMS decrypt policy, this is so the Lambda can look up its metadata to automatically authenticate with the Cerberus IAM auth endpoint: - -``` json - { - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "AllowGetFunctionConfig", - "Effect": "Allow", - "Action": [ - "lambda:GetFunctionConfiguration" - ], - "Resource": [ - "*" - ] - } - ] - } -``` - -### Configure the Client - -Setup the CERBERUS_ADDR environmental variable and access Cerberus using Java: - -``` java - String invokedFunctionArn = context.getInvokedFunctionArn(); - CerberusClient cerberusClient = DefaultCerberusClientFactory.getClientForLambda(invokedFunctionArn); - Map secrets = cerberusClient.read("/app/my-sdb-name").getData(); -``` - -## More Configuration Options - -There are other ways of using this library than the quick start above. - -### Configuring the Cerberus URL - -Provide the URL directly using the factory method `DefaultCerberusClientFactory.getClient(cerberusUrl)` or use the -`DefaultCerberusUrlResolver` by setting the environment variable `CERBERUS_ADDR` or the JVM system property `cerberus.addr` -and then use the factory method that does not require a URL: - -``` java - final CerberusClient cerberusClient = DefaultCerberusClientFactory.getClient(); - Map secrets = cerberusClient.read("/app/my-sdb-name").getData(); -``` - -### Configuring Credentials - -#### Default Credentials Provider Chain - -This client uses a provider chain to resolve the token needed to interact with Cerberus. - -See `DefaultCerberusCredentialsProviderChain.java` for full usage. - -If the client library is running on an EC2 instance, it will attempt to use the instance's assigned IAM role to authenticate -with Cerberus and obtain a token. - -If the client library is running in an ECS task, it will attempt to use the task's execution IAM role to authenticate -with Cerberus and obtain a token. - -The IAM role must be configured for access to Cerberus before this will work. - -The following policy statement must also be assigned to the IAM role, so that the client can automatically decrypt the auth token from the Cerberus IAM auth endpoint: - -``` json - { - "Version": "2012-10-17", - "Statement": [ - { - "Sid": "AllowKmsDecrypt", - "Effect": "Allow", - "Action": [ - "kms:Decrypt" - ], - "Resource": [ - "arn:aws:kms:*:[Cerberus AWS Account ID]:key/*" - ] - } - ] - } -``` - -The account ID in the ARN should be the account ID where Cerberus is deployed. See your company's internal -documentation for the account ID that you should use. ## Development @@ -134,11 +46,15 @@ First, make sure the following environment variables are set before running the ``` bash export CERBERUS_ADDR=https://example.cerberus.com - export TEST_ACCOUNT_ID=12345678910 - export TEST_ROLE_NAME=integration-test-role export TEST_REGION=us-west-2 ``` +Then, make sure AWS credentials have been obtained. One method is by running [gimme-aws-creds](https://github.com/Nike-Inc/gimme-aws-creds): + +```bash + gimme-aws-creds +``` + Next, in the project directory run: ```gradle ./gradlew integration From cb45b7207fa6f568b062d3eb1c963a1df212063b Mon Sep 17 00:00:00 2001 From: melanahammel Date: Tue, 18 Dec 2018 12:06:55 -0800 Subject: [PATCH 14/14] updated readme and version --- README.md | 25 +++---------------------- gradle.properties | 2 +- 2 files changed, 4 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 2b233d4..219a8ac 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,8 @@ To learn more about Cerberus, please see the [Cerberus website](http://engineeri ## Quickstart for Cerberus Java Client -1. Start with the [quick start guide](http://engineering.nike.com/cerberus/docs/user-guide/quick-start). -2. Add the [Cerberus client dependency](https://bintray.com/nike/maven/cerberus-client) to your build (e.g. Maven, Gradle). -3. Access secrets from Cerberus using Java. +1. Add the [Cerberus client dependency](https://bintray.com/nike/maven/cerberus-client) to your build (e.g. Maven, Gradle). +1. Access secrets from Cerberus using Java. ``` java String cerberusUrl = "https://cerberus.example.com"; String region = "us-west-2"; @@ -24,20 +23,6 @@ To learn more about Cerberus, please see the [Cerberus website](http://engineeri ``` Check out ["Working with AWS Credentials"](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html) for more information on how the AWS SDK for Java loads credentials. -## Lambdas - -Generally it does NOT make sense to store Lambda secrets in Cerberus for two reasons: - -1. Cerberus cannot support the scale that lambdas may need, e.g. thousands of requests per second -1. Lambdas will not want the extra latency needed to authenticate and read from Cerberus - -A better solution for Lambda secrets is using the [encrypted environmental variables](http://docs.aws.amazon.com/lambda/latest/dg/env_variables.html) -feature provided by AWS. - -Another option is to store Lambda secrets in Cerberus but only read them at Lambda deploy time, then storing them as encrypted -environmental variables, to avoid the extra Cerberus runtime latency. - - ## Development ### Run Integration Tests @@ -60,14 +45,10 @@ Next, in the project directory run: ./gradlew integration ``` -## Further Details - -Cerberus client is a small project. It only has a few classes and they are all fully documented. For further details please see the source code, including javadocs and unit tests. - ## License -Cerberus client is released under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +Cerberus client is released under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). [travis]:https://travis-ci.org/Nike-Inc/cerberus-java-client [travis img]:https://api.travis-ci.org/Nike-Inc/cerberus-java-client.svg?branch=master diff --git a/gradle.properties b/gradle.properties index 3943d30..cd76b82 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,6 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # -version=6.1.0 +version=7.0.0 groupId=com.nike artifactId=cerberus-client