From fcf32fdff627a28f6eaf431ffdbba339bbe01eea Mon Sep 17 00:00:00 2001 From: Meena Peri Date: Fri, 29 Sep 2023 17:37:57 -0500 Subject: [PATCH] Java Auth and auth lib changes --- clients/sellingpartner-api-aa-java/README.md | 50 ++-- clients/sellingpartner-api-aa-java/pom.xml | 2 +- .../resources/java/config.json | 3 + .../templates/ApiClient.mustache | 21 +- .../swagger-codegen/templates/api.mustache | 55 +--- .../templates/api_test.mustache | 46 ++++ .../AWSAuthenticationCredentials.java | 30 --- .../AWSAuthenticationCredentialsProvider.java | 23 -- ...thenticationCustomCredentialsProvider.java | 23 -- .../SellingPartnerAPIAA/AWSSigV4Signer.java | 109 -------- .../LWAAuthorizationSigner.java | 3 +- .../amazon/SellingPartnerAPIAA/LWAClient.java | 55 ++-- .../SellingPartnerAPIAA/LWAException.java | 45 ++++ .../LWAExceptionErrorCode.java | 13 + .../SignableRequestImpl.java | 148 ----------- .../AWSSigV4SignerTest.java | 148 ----------- .../LWAAuthorizationSignerTest.java | 8 +- .../SellingPartnerAPIAA/LWAClientTest.java | 74 +++++- .../SignableRequestImplTest.java | 247 ------------------ 19 files changed, 241 insertions(+), 862 deletions(-) create mode 100644 clients/sellingpartner-api-aa-java/resources/java/config.json create mode 100644 clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/api_test.mustache delete mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCredentials.java delete mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCredentialsProvider.java delete mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCustomCredentialsProvider.java delete mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSSigV4Signer.java create mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAException.java create mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAExceptionErrorCode.java delete mode 100644 clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/SignableRequestImpl.java delete mode 100644 clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/AWSSigV4SignerTest.java delete mode 100644 clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/SignableRequestImplTest.java diff --git a/clients/sellingpartner-api-aa-java/README.md b/clients/sellingpartner-api-aa-java/README.md index 1b9acfd..169dd2f 100644 --- a/clients/sellingpartner-api-aa-java/README.md +++ b/clients/sellingpartner-api-aa-java/README.md @@ -38,39 +38,6 @@ com.squareup.okhttp.Request signedRequest = new LWAAuthorizationSigner(lwaAuthor .sign(request); ``` -## AWSSigV4Signer -Signs a request with [AWS Signature Version 4](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html) -using the provided AWS developer account credentials. - -*Example* -``` -com.squareup.okhttp.Request request = new Request.Builder() - .url(...) - ... - .build(); - -AWSAuthenticationCredentials awsAuthenticationCredentials = AWSAuthenticationCredentials.builder() - .accessKeyId("...") - .secretKey("...") - .region("...") - .build(); - -com.squareup.okhttp.Request signedRequest = new AWSSigV4Signer(awsAuthenticationCredentials) - .sign(request); - -/*Signs request using IAM role credentials. - -AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider = AWSAuthenticationCredentialsProvider.builder() - .roleArn("...") - .roleSessionName("...") - .build(); - -com.squareup.okhttp.Request signedRequest = new - AWSSigV4Signer(awsAuthenticationCredentialsProvider.getCredentials()) - .sign(request); - -``` - ## LWAAccessTokenCache Interface to implement cache for access token that is returned in LWAClient and reuse the access token until time to live. @@ -79,7 +46,6 @@ Interface to set and get rateLimit configurations that are used with RateLimiter *Example* ``` - com.squareup.okhttp.Request request = new Request.Builder() .url(...) ... @@ -89,8 +55,22 @@ com.squareup.okhttp.Request request = new Request.Builder() .rateLimitPermit(...) .waitTimeOutInMilliSeconds(...) .build(); - ``` +## Exception +This package returns a custom LWAException when there is an error returned during LWA authorization. LWAException provides additional details like errorCode and errorDescription to help fix the issue. + +*Example* +``` + catch (LWAException e) { + System.err.println("LWA Exception when calling Selling partner API"); + System.err.println(e.getErrorCode()); + System.err.println(e.getErrorMessage()); + e.printStackTrace(); + } +``` + +## Version +Selling Partner API Authentication/Authorization Library version 2.0. ## Resources This package features Mustache templates designed for use with [swagger codegen](https://swagger.io/tools/swagger-codegen/). diff --git a/clients/sellingpartner-api-aa-java/pom.xml b/clients/sellingpartner-api-aa-java/pom.xml index b209b2c..9b51bf4 100644 --- a/clients/sellingpartner-api-aa-java/pom.xml +++ b/clients/sellingpartner-api-aa-java/pom.xml @@ -6,7 +6,7 @@ com.amazon.sellingpartnerapi sellingpartnerapi-aa-java jar - 1.0 + 2.0 diff --git a/clients/sellingpartner-api-aa-java/resources/java/config.json b/clients/sellingpartner-api-aa-java/resources/java/config.json new file mode 100644 index 0000000..bda9b9a --- /dev/null +++ b/clients/sellingpartner-api-aa-java/resources/java/config.json @@ -0,0 +1,3 @@ +{ + "artifactVersion": "2.0" +} \ No newline at end of file diff --git a/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/ApiClient.mustache b/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/ApiClient.mustache index 993d2e2..6d6c6b5 100644 --- a/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/ApiClient.mustache +++ b/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/ApiClient.mustache @@ -51,10 +51,10 @@ import {{invokerPackage}}.auth.HttpBasicAuth; import {{invokerPackage}}.auth.ApiKeyAuth; import {{invokerPackage}}.auth.OAuth; -import com.amazon.SellingPartnerAPIAA.AWSSigV4Signer; import com.amazon.SellingPartnerAPIAA.LWAAuthorizationSigner; import com.google.common.util.concurrent.RateLimiter; import com.amazon.SellingPartnerAPIAA.RateLimitConfiguration; +import com.amazon.SellingPartnerAPIAA.LWAException; public class ApiClient { @@ -79,7 +79,6 @@ public class ApiClient { private HttpLoggingInterceptor loggingInterceptor; private LWAAuthorizationSigner lwaAuthorizationSigner; - private AWSSigV4Signer awsSigV4Signer; private RateLimiter rateLimiter; private RateLimitConfiguration rateLimitConfiguration; @@ -496,17 +495,6 @@ public class ApiClient { this.lwaAuthorizationSigner = lwaAuthorizationSigner; return this; } - - /** - * Sets the AWSSigV4Signer - * - * @param awsSigV4Signer AWSSigV4Signer instance - * @return Api client - */ - public ApiClient setAWSSigV4Signer(AWSSigV4Signer awsSigV4Signer) { - this.awsSigV4Signer = awsSigV4Signer; - return this; - } /** * Sets the RateLimiter @@ -985,8 +973,9 @@ public class ApiClient { * @param progressRequestListener Progress request listener * @return The HTTP call * @throws ApiException If fail to serialize the request body object + * @throws LWAException If calls to fetch LWA access token fails */ - public Call buildCall(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + public Call buildCall(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException, LWAException { Request request = buildRequest(path, method, queryParams, collectionQueryParams, body, headerParams, formParams, authNames, progressRequestListener); return httpClient.newCall(request); @@ -1006,8 +995,9 @@ public class ApiClient { * @param progressRequestListener Progress request listener * @return The HTTP request * @throws ApiException If fail to serialize the request body object + * @throws LWAException If calls to fetch LWA access token fails */ - public Request buildRequest(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + public Request buildRequest(String path, String method, List queryParams, List collectionQueryParams, Object body, Map headerParams, Map formParams, String[] authNames, ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException, LWAException { updateParamsForAuth(authNames, queryParams, headerParams); final String url = buildUrl(path, queryParams, collectionQueryParams); @@ -1049,7 +1039,6 @@ public class ApiClient { } request = lwaAuthorizationSigner.sign(request); - request = awsSigV4Signer.sign(request); return request; } diff --git a/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/api.mustache b/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/api.mustache index 8850f1a..63cab30 100644 --- a/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/api.mustache +++ b/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/api.mustache @@ -43,15 +43,12 @@ import java.util.List; import java.util.Map; {{/fullJavaUtil}} -import com.amazon.SellingPartnerAPIAA.AWSAuthenticationCredentials; -import com.amazon.SellingPartnerAPIAA.AWSAuthenticationCredentialsProvider; -import com.amazon.SellingPartnerAPIAA.AWSAuthenticationCustomCredentialsProvider; -import com.amazon.SellingPartnerAPIAA.AWSSigV4Signer; import com.amazon.SellingPartnerAPIAA.LWAAccessTokenCache; import com.amazon.SellingPartnerAPIAA.LWAAccessTokenCacheImpl; import com.amazon.SellingPartnerAPIAA.LWAAuthorizationCredentials; import com.amazon.SellingPartnerAPIAA.LWAAuthorizationSigner; import com.amazon.SellingPartnerAPIAA.RateLimitConfiguration; +import com.amazon.SellingPartnerAPIAA.LWAException; {{#operations}} public class {{classname}} { @@ -81,6 +78,7 @@ public class {{classname}} { * @param progressRequestListener Progress request listener * @return Call to execute * @throws ApiException If fail to serialize the request body object + * @throws LWAException If calls to fetch LWA access token fails {{#isDeprecated}} * @deprecated {{/isDeprecated}} @@ -92,7 +90,7 @@ public class {{classname}} { {{#isDeprecated}} @Deprecated {{/isDeprecated}} - public com.squareup.okhttp.Call {{operationId}}Call({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + public com.squareup.okhttp.Call {{operationId}}Call({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException, LWAException { Object {{localVariablePrefix}}localVarPostBody = {{#bodyParam}}{{paramName}}{{/bodyParam}}{{^bodyParam}}null{{/bodyParam}}; // create path and map variables @@ -144,7 +142,7 @@ public class {{classname}} { @Deprecated {{/isDeprecated}} @SuppressWarnings("rawtypes") - private com.squareup.okhttp.Call {{operationId}}ValidateBeforeCall({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException { + private com.squareup.okhttp.Call {{operationId}}ValidateBeforeCall({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ProgressResponseBody.ProgressListener progressListener, final ProgressRequestBody.ProgressRequestListener progressRequestListener) throws ApiException, LWAException { {{^performBeanValidation}} {{#allParams}}{{#required}} // verify the required parameter '{{paramName}}' is set @@ -191,6 +189,7 @@ public class {{classname}} { * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}}{{#returnType}} * @return {{returnType}}{{/returnType}} * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @throws LWAException If calls to fetch LWA access token fails {{#isDeprecated}} * @deprecated {{/isDeprecated}} @@ -202,7 +201,7 @@ public class {{classname}} { {{#isDeprecated}} @Deprecated {{/isDeprecated}} - public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { + public {{#returnType}}{{{returnType}}} {{/returnType}}{{^returnType}}void {{/returnType}}{{operationId}}({{#allParams}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException,LWAException { {{#returnType}}ApiResponse<{{{returnType}}}> {{localVariablePrefix}}resp = {{/returnType}}{{operationId}}WithHttpInfo({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}});{{#returnType}} return {{localVariablePrefix}}resp.getData();{{/returnType}} } @@ -213,6 +212,7 @@ public class {{classname}} { * @param {{paramName}} {{description}}{{#required}} (required){{/required}}{{^required}} (optional{{#defaultValue}}, default to {{{.}}}{{/defaultValue}}){{/required}}{{/allParams}} * @return ApiResponse<{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}Void{{/returnType}}> * @throws ApiException If fail to call the API, e.g. server error or cannot deserialize the response body + * @throws LWAException If calls to fetch LWA access token fails {{#isDeprecated}} * @deprecated {{/isDeprecated}} @@ -224,7 +224,7 @@ public class {{classname}} { {{#isDeprecated}} @Deprecated {{/isDeprecated}} - public ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}WithHttpInfo({{#allParams}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException { + public ApiResponse<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{operationId}}WithHttpInfo({{#allParams}}{{#useBeanValidation}}{{>beanValidationQueryParams}}{{/useBeanValidation}}{{{dataType}}} {{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) throws ApiException,LWAException { com.squareup.okhttp.Call {{localVariablePrefix}}call = {{operationId}}ValidateBeforeCall({{#allParams}}{{paramName}}, {{/allParams}}null, null); {{#returnType}}Type {{localVariablePrefix}}localVarReturnType = new TypeToken<{{{returnType}}}>(){}.getType(); return {{localVariablePrefix}}apiClient.execute({{localVariablePrefix}}call, {{localVariablePrefix}}localVarReturnType);{{/returnType}}{{^returnType}}return {{localVariablePrefix}}apiClient.execute({{localVariablePrefix}}call);{{/returnType}} @@ -237,6 +237,7 @@ public class {{classname}} { * @param callback The callback to be executed when the API call finishes * @return The request call * @throws ApiException If fail to process the API call, e.g. serializing the request body object + * @throws LWAException If calls to fetch LWA access token fails {{#isDeprecated}} * @deprecated {{/isDeprecated}} @@ -248,7 +249,7 @@ public class {{classname}} { {{#isDeprecated}} @Deprecated {{/isDeprecated}} - public com.squareup.okhttp.Call {{operationId}}Async({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ApiCallback<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{localVariablePrefix}}callback) throws ApiException { + public com.squareup.okhttp.Call {{operationId}}Async({{#allParams}}{{{dataType}}} {{paramName}}, {{/allParams}}final ApiCallback<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> {{localVariablePrefix}}callback) throws ApiException, LWAException { ProgressResponseBody.ProgressListener progressListener = null; ProgressRequestBody.ProgressRequestListener progressRequestListener = null; @@ -277,19 +278,12 @@ public class {{classname}} { {{/operation}} public static class Builder { - private AWSAuthenticationCredentials awsAuthenticationCredentials; private LWAAuthorizationCredentials lwaAuthorizationCredentials; private String endpoint; private LWAAccessTokenCache lwaAccessTokenCache; private Boolean disableAccessTokenCache = false; - private AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider; private RateLimitConfiguration rateLimitConfiguration; - private AWSAuthenticationCustomCredentialsProvider awsAuthenticationCustomCredentialsProvider; - public Builder awsAuthenticationCredentials(AWSAuthenticationCredentials awsAuthenticationCredentials) { - this.awsAuthenticationCredentials = awsAuthenticationCredentials; - return this; - } public Builder lwaAuthorizationCredentials(LWAAuthorizationCredentials lwaAuthorizationCredentials) { this.lwaAuthorizationCredentials = lwaAuthorizationCredentials; @@ -310,12 +304,7 @@ public class {{classname}} { this.disableAccessTokenCache = true; return this; } - - public Builder awsAuthenticationCredentialsProvider(AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider) { - this.awsAuthenticationCredentialsProvider = awsAuthenticationCredentialsProvider; - return this; - } - + public Builder rateLimitConfigurationOnRequests(RateLimitConfiguration rateLimitConfiguration){ this.rateLimitConfiguration = rateLimitConfiguration; return this; @@ -326,17 +315,7 @@ public class {{classname}} { return this; } - public Builder awsAuthenticationCustomCredentialsProvider(AWSAuthenticationCustomCredentialsProvider awsAuthenticationCustomCredentialsProvider) { - this.awsAuthenticationCustomCredentialsProvider = awsAuthenticationCustomCredentialsProvider; - return this; - } - - public {{classname}} build() { - if (awsAuthenticationCredentials == null && awsAuthenticationCustomCredentialsProvider == null) { - throw new RuntimeException("Neither AWSAuthenticationCredentials or AWSAuthenticationCustomCredentialsProvider are set"); - } - if (lwaAuthorizationCredentials == null) { throw new RuntimeException("LWAAuthorizationCredentials not set"); } @@ -344,17 +323,6 @@ public class {{classname}} { if (StringUtil.isEmpty(endpoint)) { throw new RuntimeException("Endpoint not set"); } - - AWSSigV4Signer awsSigV4Signer; - if (awsAuthenticationCustomCredentialsProvider != null ) { - awsSigV4Signer = new AWSSigV4Signer(awsAuthenticationCustomCredentialsProvider); - } - else if (awsAuthenticationCredentialsProvider == null) { - awsSigV4Signer = new AWSSigV4Signer(awsAuthenticationCredentials); - } - else { - awsSigV4Signer = new AWSSigV4Signer(awsAuthenticationCredentials,awsAuthenticationCredentialsProvider); - } LWAAuthorizationSigner lwaAuthorizationSigner = null; if (disableAccessTokenCache) { @@ -368,7 +336,6 @@ public class {{classname}} { } return new {{classname}}(new ApiClient() - .setAWSSigV4Signer(awsSigV4Signer) .setLWAAuthorizationSigner(lwaAuthorizationSigner) .setBasePath(endpoint) .setRateLimiter(rateLimitConfiguration)); diff --git a/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/api_test.mustache b/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/api_test.mustache new file mode 100644 index 0000000..caddb1a --- /dev/null +++ b/clients/sellingpartner-api-aa-java/resources/swagger-codegen/templates/api_test.mustache @@ -0,0 +1,46 @@ +{{>licenseInfo}} + +package {{package}}; + +import {{invokerPackage}}.ApiException; +{{#imports}}import {{import}}; +{{/imports}} +import org.junit.Test; +import org.junit.Ignore; + +{{^fullJavaUtil}} +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +{{/fullJavaUtil}} +import com.amazon.SellingPartnerAPIAA.LWAException; + +/** +* API tests for {{classname}} +*/ +@Ignore +public class {{classname}}Test { + +private final {{classname}} api = new {{classname}}(); + +{{#operations}}{{#operation}} +/** +* {{summary}} +* +* {{notes}} +* +* @throws ApiException if the Api call fails +* @throws LWAException If calls to fetch LWA access token fails +*/ +@Test +public void {{operationId}}Test() throws ApiException, LWAException { +{{#allParams}} + {{{dataType}}} {{paramName}} = null; +{{/allParams}} +{{#returnType}}{{{returnType}}} response = {{/returnType}}api.{{operationId}}({{#allParams}}{{paramName}}{{#hasMore}}, {{/hasMore}}{{/allParams}}); + +// TODO: test validations +} +{{/operation}}{{/operations}} +} \ No newline at end of file diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCredentials.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCredentials.java deleted file mode 100644 index 6a7026f..0000000 --- a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCredentials.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.amazon.SellingPartnerAPIAA; - -import lombok.Builder; -import lombok.Data; -import lombok.NonNull; - -/** - * AWSAuthenticationCredentials - */ -@Data -@Builder -public class AWSAuthenticationCredentials { - /** - * AWS IAM User Access Key Id - */ - @NonNull - private String accessKeyId; - - /** - * AWS IAM User Secret Key - */ - @NonNull - private String secretKey; - - /** - * AWS Region - */ - @NonNull - private String region; -} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCredentialsProvider.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCredentialsProvider.java deleted file mode 100644 index c3496bd..0000000 --- a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCredentialsProvider.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.amazon.SellingPartnerAPIAA; - -import lombok.Builder; -import lombok.Data; - -/** - * AWSAuthenticationCredentialsProvider - */ -@Data -@Builder -public class AWSAuthenticationCredentialsProvider { - /** - * AWS IAM Role ARN - */ - private String roleArn; - - /** - * AWS IAM Role Session Name - */ - private String roleSessionName; - - private String region; -} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCustomCredentialsProvider.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCustomCredentialsProvider.java deleted file mode 100644 index 76d90b5..0000000 --- a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSAuthenticationCustomCredentialsProvider.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.amazon.SellingPartnerAPIAA; - -import lombok.Builder; -import lombok.Data; - -import com.amazonaws.auth.AWSCredentialsProvider; - -/** - * AWSAuthenticationCustomCredentialsProvider - */ -@Data -@Builder -public class AWSAuthenticationCustomCredentialsProvider { - /** - * AWS Region - */ - private String region; - - /** - * AWS Credentials Provider - */ - private AWSCredentialsProvider awsCredentialsProvider; -} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSSigV4Signer.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSSigV4Signer.java deleted file mode 100644 index 5fccb76..0000000 --- a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/AWSSigV4Signer.java +++ /dev/null @@ -1,109 +0,0 @@ -package com.amazon.SellingPartnerAPIAA; - -import com.amazonaws.SignableRequest; -import com.amazonaws.auth.AWS4Signer; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; -import com.squareup.okhttp.Request; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; -import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider; -import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder; -import com.amazonaws.auth.AWSStaticCredentialsProvider; - -/** - * AWS Signature Version 4 Signer - */ -public class AWSSigV4Signer { - private static final String SERVICE_NAME = "execute-api"; - - @Setter(AccessLevel.PACKAGE) - @Getter(AccessLevel.PACKAGE) - private AWS4Signer aws4Signer; - - private AWSCredentials awsCredentials; - - @Setter(AccessLevel.PACKAGE) - @Getter(AccessLevel.PACKAGE) - private AWSCredentialsProvider awsCredentialsProvider; - - /** - * - * @param awsAuthenticationCredentials AWS Developer Account Credentials - */ - public AWSSigV4Signer(AWSAuthenticationCredentials awsAuthenticationCredentials) { - aws4Signer = new AWS4Signer(); - aws4Signer.setServiceName(SERVICE_NAME); - aws4Signer.setRegionName(awsAuthenticationCredentials.getRegion()); - awsCredentials = new BasicAWSCredentials(awsAuthenticationCredentials.getAccessKeyId(), - awsAuthenticationCredentials.getSecretKey()); - } - - /** - * - * @param awsAuthenticationCredentials and awsAuthenticationCredentialsProvider AWS Developer Account Credentials - */ - public AWSSigV4Signer(AWSAuthenticationCredentials awsAuthenticationCredentials, - AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider) { - aws4Signer = new AWS4Signer(); - aws4Signer.setServiceName(SERVICE_NAME); - - final String region; - AWSSecurityTokenServiceClientBuilder stsClientBuilder = AWSSecurityTokenServiceClientBuilder.standard(); - - if (awsAuthenticationCredentials != null) { - region = awsAuthenticationCredentials.getRegion(); - BasicAWSCredentials awsBasicCredentials = new BasicAWSCredentials( - awsAuthenticationCredentials.getAccessKeyId(), - awsAuthenticationCredentials.getSecretKey() - ); - stsClientBuilder.withCredentials(new AWSStaticCredentialsProvider(awsBasicCredentials)); - } else { - region = awsAuthenticationCredentialsProvider.getRegion(); - } - - aws4Signer.setRegionName(region); - awsCredentialsProvider = new STSAssumeRoleSessionCredentialsProvider.Builder( - awsAuthenticationCredentialsProvider.getRoleArn(), - awsAuthenticationCredentialsProvider.getRoleSessionName()) - .withStsClient(stsClientBuilder.withRegion(region).build()) - .build(); - } - - /** - * - * @param awsAuthenticationCustomCredentialsProvider AWS Credentials Provider - */ - public AWSSigV4Signer(AWSAuthenticationCustomCredentialsProvider awsAuthenticationCustomCredentialsProvider) { - aws4Signer = new AWS4Signer(); - aws4Signer.setServiceName(SERVICE_NAME); - aws4Signer.setRegionName(awsAuthenticationCustomCredentialsProvider.getRegion()); - this.awsCredentialsProvider = awsAuthenticationCustomCredentialsProvider.getAwsCredentialsProvider(); - } - - /** - * - * @param awsAuthenticationCredentialsProvider AWS Credentials Provider containing the role name to be assumed - */ - public AWSSigV4Signer(AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider) { - this(null, awsAuthenticationCredentialsProvider); - } - - /** - * Signs a Request with AWS Signature Version 4 - * - * @param originalRequest Request to sign (treated as immutable) - * @return Copy of originalRequest with AWS Signature - */ - public Request sign(Request originalRequest) { - SignableRequest signableRequest = new SignableRequestImpl(originalRequest); - if (awsCredentialsProvider != null) { - aws4Signer.sign(signableRequest, awsCredentialsProvider.getCredentials()); - } else { - aws4Signer.sign(signableRequest, awsCredentials); - } - return (Request) signableRequest.getOriginalRequestObject(); - } -} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSigner.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSigner.java index 26cc1c4..b3d6409 100644 --- a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSigner.java +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSigner.java @@ -64,8 +64,9 @@ public class LWAAuthorizationSigner { * Signs a Request with an LWA Access Token * @param originalRequest Request to sign (treated as immutable) * @return Copy of originalRequest with LWA signature + * @throws LWAException If calls to fetch LWA access token fails */ - public Request sign(Request originalRequest) { + public Request sign(Request originalRequest) throws LWAException { String accessToken = lwaClient.getAccessToken(lwaAccessTokenRequestMeta); return originalRequest.newBuilder() diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClient.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClient.java index 7ef428d..eb91ee1 100644 --- a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClient.java +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAClient.java @@ -11,8 +11,7 @@ import com.squareup.okhttp.Response; import lombok.AccessLevel; import lombok.Getter; import lombok.Setter; - -import java.io.IOException; +import org.apache.commons.lang3.EnumUtils; class LWAClient { private static final String ACCESS_TOKEN_KEY = "access_token"; @@ -25,7 +24,9 @@ class LWAClient { private OkHttpClient okHttpClient; private LWAAccessTokenCache lwaAccessTokenCache; - /** Sets cache to store access token until token is expired */ + /** + * Sets cache to store access token until token is expired + */ public void setLWAAccessTokenCache(LWAAccessTokenCache tokenCache) { this.lwaAccessTokenCache = tokenCache; } @@ -33,46 +34,54 @@ class LWAClient { LWAClient(String endpoint) { okHttpClient = new OkHttpClient(); this.endpoint = endpoint; - } + } - String getAccessToken(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) { + String getAccessToken(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) throws LWAException { if (lwaAccessTokenCache != null) { - return getAccessTokenFromCache(lwaAccessTokenRequestMeta); + return getAccessTokenFromCache(lwaAccessTokenRequestMeta); } else { return getAccessTokenFromEndpoint(lwaAccessTokenRequestMeta); - } + } } - String getAccessTokenFromCache(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) { + String getAccessTokenFromCache(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) throws LWAException { String accessTokenCacheData = (String) lwaAccessTokenCache.get(lwaAccessTokenRequestMeta); if (accessTokenCacheData != null) { - return accessTokenCacheData; - } else { - return getAccessTokenFromEndpoint(lwaAccessTokenRequestMeta); - } + return accessTokenCacheData; + } else { + return getAccessTokenFromEndpoint(lwaAccessTokenRequestMeta); + } } - String getAccessTokenFromEndpoint(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) { + String getAccessTokenFromEndpoint(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) throws LWAException { RequestBody requestBody = RequestBody.create(JSON_MEDIA_TYPE, new Gson().toJson(lwaAccessTokenRequestMeta)); Request accessTokenRequest = new Request.Builder().url(endpoint).post(requestBody).build(); - + LWAExceptionErrorCode lwaErrorCode = null; String accessToken; try { Response response = okHttpClient.newCall(accessTokenRequest).execute(); - if (!response.isSuccessful()) { - throw new IOException("Unsuccessful LWA token exchange"); - } - JsonObject responseJson = new JsonParser().parse(response.body().string()).getAsJsonObject(); - + if (!response.isSuccessful()) { + // Check if response has element error and is a known LWA error code + if (responseJson.has("error") && + EnumUtils.isValidEnum(LWAExceptionErrorCode.class, responseJson.get("error").getAsString())) { + throw new LWAException(responseJson.get("error").getAsString(), + responseJson.get("error_description").getAsString(), "Error getting LWA Token"); + } else { // else throw other LWA error + throw new LWAException(LWAExceptionErrorCode.other.toString(), "Other LWA Exception", + "Error getting LWA Token"); + } + } accessToken = responseJson.get(ACCESS_TOKEN_KEY).getAsString(); if (lwaAccessTokenCache != null) { long timeToTokenexpiry = responseJson.get(ACCESS_TOKEN_EXPIRES_IN).getAsLong(); lwaAccessTokenCache.put(lwaAccessTokenRequestMeta, accessToken, timeToTokenexpiry); - } - } catch (Exception e) { - throw new RuntimeException("Error getting LWA Access Token", e); } - return accessToken; + } catch (LWAException e) { // throw LWA exception + throw new LWAException(e.getErrorCode(), e.getErrorMessage(), e.getMessage()); + } catch (Exception e) { // throw other runtime exceptions + throw new RuntimeException("Error getting LWA Token"); } + return accessToken; + } } diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAException.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAException.java new file mode 100644 index 0000000..b89f73a --- /dev/null +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAException.java @@ -0,0 +1,45 @@ +package com.amazon.SellingPartnerAPIAA; + +public class LWAException extends Exception { + + private String errorMessage; + + private String errorCode; + + LWAException() { + super(); + } + + LWAException(String errorCode, String errorMessage) { + super(); + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + LWAException(String errorCode, String errorMessage, String message) { + super(message); + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + LWAException(String errorCode, String errorMessage, Throwable cause) { + super(cause); + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + LWAException(String errorCode, String errorMessage, String message, Throwable cause) { + super(message, cause); + this.errorCode = errorCode; + this.errorMessage = errorMessage; + } + + public String getErrorCode() { + return this.errorCode; + } + + public String getErrorMessage() { + return this.errorMessage; + } + +} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAExceptionErrorCode.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAExceptionErrorCode.java new file mode 100644 index 0000000..f73ee22 --- /dev/null +++ b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/LWAExceptionErrorCode.java @@ -0,0 +1,13 @@ +package com.amazon.SellingPartnerAPIAA; + +public enum LWAExceptionErrorCode { + access_denied, + invalid_grant, + invalid_request, + invalid_scope, + server_error, + temporarily_unavailable, + unauthorized_client, + invalid_client, + other; +} diff --git a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/SignableRequestImpl.java b/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/SignableRequestImpl.java deleted file mode 100644 index 636634a..0000000 --- a/clients/sellingpartner-api-aa-java/src/com/amazon/SellingPartnerAPIAA/SignableRequestImpl.java +++ /dev/null @@ -1,148 +0,0 @@ -package com.amazon.SellingPartnerAPIAA; - -import com.amazonaws.ReadLimitInfo; -import com.amazonaws.SignableRequest; -import com.amazonaws.http.HttpMethodName; -import com.squareup.okhttp.HttpUrl; -import com.squareup.okhttp.MediaType; -import com.squareup.okhttp.Request; -import okio.Buffer; -import org.apache.http.NameValuePair; -import org.apache.http.client.utils.URLEncodedUtils; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -class SignableRequestImpl implements SignableRequest { - private static final String CONTENT_TYPE_HEADER_NAME = "Content-Type"; - private Request originalRequest; - private Request.Builder signableRequestBuilder; - - SignableRequestImpl(Request originalRequest) { - this.originalRequest = originalRequest; - signableRequestBuilder = originalRequest.newBuilder(); - } - - @Override - public void addHeader(String name, String value) { - signableRequestBuilder.addHeader(name, value); - } - - @Override - public void addParameter(String name, String value) { - HttpUrl newUrl = signableRequestBuilder.build() - .httpUrl() - .newBuilder() - .addEncodedQueryParameter(name, value) - .build(); - - signableRequestBuilder.url(newUrl); - } - - @Override - public void setContent(InputStream inputStream) { - throw new UnsupportedOperationException(); - } - - @Override - public Map getHeaders() { - Map headers = new HashMap<>(); - - Request requestSnapshot = signableRequestBuilder.build(); - requestSnapshot.headers() - .names() - .forEach(headerName -> headers.put(headerName, requestSnapshot.header(headerName))); - - if (requestSnapshot.body() != null) { - MediaType contentType = requestSnapshot.body().contentType(); - if (contentType != null) { - headers.put(CONTENT_TYPE_HEADER_NAME, contentType.toString()); - } - } - - return headers; - } - - @Override - public String getResourcePath() { - return originalRequest.url() - .getPath(); - } - - @Override - public Map> getParameters() { - Map> parameters = new HashMap<>(); - try { - List nameValuePairs = URLEncodedUtils.parse(originalRequest.url().toURI(), - StandardCharsets.UTF_8); - nameValuePairs.forEach(nameValuePair -> parameters.put(nameValuePair.getName(), - Collections.singletonList(nameValuePair.getValue()))); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - return parameters; - } - - @Override - public URI getEndpoint() { - URI uri = null; - try { - uri = originalRequest.url().toURI(); - } catch (URISyntaxException e) { - throw new RuntimeException(e); - } - - return URI.create(String.format("%s://%s", uri.getScheme(), uri.getHost())); - } - - @Override - public HttpMethodName getHttpMethod() { - return HttpMethodName.fromValue(originalRequest.method().toUpperCase()); - } - - @Override - public int getTimeOffset() { - return 0; - } - - @Override - public InputStream getContent() { - ByteArrayInputStream inputStream = null; - - if (originalRequest.body() != null) { - try { - Buffer buffer = new Buffer(); - originalRequest.body().writeTo(buffer); - inputStream = new ByteArrayInputStream(buffer.readByteArray()); - } catch (IOException e) { - throw new RuntimeException("Unable to buffer request body", e); - } - } - - return inputStream; - } - - @Override - public InputStream getContentUnwrapped() { - return getContent(); - } - - @Override - public ReadLimitInfo getReadLimitInfo() { - return null; - } - - @Override - public Object getOriginalRequestObject() { - return signableRequestBuilder.build(); - } -} diff --git a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/AWSSigV4SignerTest.java b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/AWSSigV4SignerTest.java deleted file mode 100644 index 534d4fa..0000000 --- a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/AWSSigV4SignerTest.java +++ /dev/null @@ -1,148 +0,0 @@ -package com.amazon.SellingPartnerAPIAA; - -import com.amazonaws.SignableRequest; -import com.amazonaws.auth.AWS4Signer; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.squareup.okhttp.Request; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -import static org.junit.Assert.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.verify; - -@RunWith(MockitoJUnitRunner.class) -public class AWSSigV4SignerTest { - private static final String TEST_ACCESS_KEY_ID = "aKey"; - private static final String TEST_SECRET_KEY = "sKey"; - private static final String TEST_REGION = "us-east"; - private static final String TEST_ROLE_ARN = "arnrole"; - private static final String TEST_ROLESESSION_NAME = "test"; - - @Mock - private AWS4Signer mockAWS4Signer; - @Mock - private AWSCredentialsProvider mockAWSCredentialsProvider; - @Mock - private AWSCredentials mockAWSCredentials; - - private AWSSigV4Signer underTest; - private AWSSigV4Signer underTestCredentialsProvider; - - @Before - public void init() { - underTest = new AWSSigV4Signer(AWSAuthenticationCredentials.builder() - .accessKeyId(TEST_ACCESS_KEY_ID) - .secretKey(TEST_SECRET_KEY) - .region(TEST_REGION) - .build() - ); - } - - @Test - public void signRequestUsingProvidedCredentials() { - ArgumentCaptor awsCredentialsArgumentCaptor = ArgumentCaptor.forClass(AWSCredentials.class); - underTest.setAws4Signer(mockAWS4Signer); - - doNothing() - .when(mockAWS4Signer) - .sign(any(SignableRequest.class), awsCredentialsArgumentCaptor.capture()); - - underTest.sign(new Request.Builder().url("https://api.amazon.com").build()); - - AWSCredentials actualAWSCredentials = awsCredentialsArgumentCaptor.getValue(); - - assertEquals(TEST_ACCESS_KEY_ID, actualAWSCredentials.getAWSAccessKeyId()); - assertEquals(TEST_SECRET_KEY, actualAWSCredentials.getAWSSecretKey()); - } - - @Test - public void setSignerRegion() { - assertEquals(TEST_REGION, underTest.getAws4Signer().getRegionName()); - } - - @Test - public void setSignerServiceName() { - assertEquals("execute-api", underTest.getAws4Signer().getServiceName()); - } - - @Test - public void returnSignedRequest() { - ArgumentCaptor signableRequestArgumentCaptor = ArgumentCaptor.forClass(SignableRequest.class); - underTest.setAws4Signer(mockAWS4Signer); - - Request actualSignedRequest = underTest.sign(new Request.Builder() - .url("http://api.amazon.com") - .build()); - - verify(mockAWS4Signer) - .sign(signableRequestArgumentCaptor.capture(), any(AWSCredentials.class)); - - SignableRequest actualSignableRequest = signableRequestArgumentCaptor.getValue(); - - assertEquals(((Request)actualSignableRequest.getOriginalRequestObject()).url(), actualSignedRequest.url()); - } - - @Test - public void returnSignedRequestWithCredentialProvider() { - ArgumentCaptor signableRequestArgumentCaptor = ArgumentCaptor.forClass(SignableRequest.class); - - Mockito.when(mockAWSCredentialsProvider.getCredentials()).thenReturn(mockAWSCredentials); - - underTestCredentialsProvider = new AWSSigV4Signer(AWSAuthenticationCredentials.builder() - .accessKeyId(TEST_ACCESS_KEY_ID) - .secretKey(TEST_SECRET_KEY) - .region(TEST_REGION) - .build(), AWSAuthenticationCredentialsProvider.builder() - .roleArn(TEST_ROLE_ARN) - .roleSessionName(TEST_ROLESESSION_NAME) - .build() - ); - underTestCredentialsProvider.setAws4Signer(mockAWS4Signer); - underTestCredentialsProvider.setAwsCredentialsProvider(mockAWSCredentialsProvider); - - Request actualSignedRequest = underTestCredentialsProvider.sign(new Request.Builder() - .url("http://api.amazon.com") - .build()); - - verify(mockAWS4Signer) - .sign(signableRequestArgumentCaptor.capture(), any(AWSCredentials.class)); - - SignableRequest actualSignableRequest = signableRequestArgumentCaptor.getValue(); - - assertEquals(((Request)actualSignableRequest.getOriginalRequestObject()).url(), actualSignedRequest.url()); - } - - @Test - public void returnSignedRequestWithCustomCredentialsProvider() { - ArgumentCaptor signableRequestArgumentCaptor = ArgumentCaptor.forClass(SignableRequest.class); - - Mockito.when(mockAWSCredentialsProvider.getCredentials()).thenReturn(mockAWSCredentials); - - underTestCredentialsProvider = new AWSSigV4Signer(AWSAuthenticationCustomCredentialsProvider.builder() - .awsCredentialsProvider(mockAWSCredentialsProvider) - .region(TEST_REGION) - .build()); - underTestCredentialsProvider.setAws4Signer(mockAWS4Signer); - - Request actualSignedRequest = underTestCredentialsProvider.sign(new Request.Builder() - .url("http://api.amazon.com") - .build()); - - verify(mockAWS4Signer) - .sign(signableRequestArgumentCaptor.capture(), any(AWSCredentials.class)); - - SignableRequest actualSignableRequest = signableRequestArgumentCaptor.getValue(); - - assertEquals(((Request)actualSignableRequest.getOriginalRequestObject()).url(), actualSignedRequest.url()); - } - - -} diff --git a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSignerTest.java b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSignerTest.java index 85e289d..ee0e6f2 100644 --- a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSignerTest.java +++ b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAAuthorizationSignerTest.java @@ -98,7 +98,7 @@ public class LWAAuthorizationSignerTest { @ParameterizedTest @MethodSource("lwaAuthSigner") - public void requestLWAAccessTokenFromConfiguration(String sellerType, LWAAuthorizationSigner testAuthSigner) { + public void requestLWAAccessTokenFromConfiguration(String sellerType, LWAAuthorizationSigner testAuthSigner) throws LWAException { LWAClient mockLWAClient = mock(LWAClient.class); ArgumentCaptor lwaAccessTokenRequestMetaArgumentCaptor = ArgumentCaptor.forClass(LWAAccessTokenRequestMeta.class); @@ -126,7 +126,7 @@ public class LWAAuthorizationSignerTest { @ParameterizedTest @MethodSource("lwaAuthSigner") - public void returnSignedRequestWithAccessTokenFromLWAClient(String sellerType, LWAAuthorizationSigner testAuthSigner) { + public void returnSignedRequestWithAccessTokenFromLWAClient(String sellerType, LWAAuthorizationSigner testAuthSigner) throws LWAException { LWAClient mockLWAClient = mock(LWAClient.class); when(mockLWAClient.getAccessToken(any(LWAAccessTokenRequestMeta.class))) @@ -140,7 +140,7 @@ public class LWAAuthorizationSignerTest { @ParameterizedTest @MethodSource("lwaAuthSigner") - public void originalRequestIsImmutable(String sellerType, LWAAuthorizationSigner testAuthSigner) { + public void originalRequestIsImmutable(String sellerType, LWAAuthorizationSigner testAuthSigner) throws LWAException { LWAClient mockLWAClient = mock(LWAClient.class); when(mockLWAClient.getAccessToken(any(LWAAccessTokenRequestMeta.class))) @@ -151,7 +151,7 @@ public class LWAAuthorizationSignerTest { } @Test - public void returnSignedRequestWithAccessTokenFromLWACache() throws IOException { + public void returnSignedRequestWithAccessTokenFromLWACache() throws IOException, LWAException { LWAClient testLWAClient = new LWAClient(TEST_ENDPOINT); testLWAClient.setOkHttpClient(mockOkHttpClient); diff --git a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAClientTest.java b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAClientTest.java index 1b19863..94d1a97 100644 --- a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAClientTest.java +++ b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/LWAClientTest.java @@ -53,7 +53,10 @@ public class LWAClientTest { private static final String SELLER_TYPE_SELLER = "seller"; private static final String SELLER_TYPE_SELLERLESS = "sellerless"; - + + private static final String LWA_EXCEPTION_MSG ="Error getting LWA Token"; + private static final String LWA_CLIENT_AUTH_MSG ="Client_authentication_failed"; + private static final String LWA_ACCESS_DENIED_MSG ="Authorization_server_denied_the_request"; @Mock private OkHttpClient mockOkHttpClient; @@ -101,7 +104,7 @@ public class LWAClientTest { @ParameterizedTest @MethodSource("lwaClient") - public void makeRequestFromMeta (String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException { + public void makeRequestFromMeta (String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws LWAException,IOException { ArgumentCaptor requestArgumentCaptor = ArgumentCaptor.forClass(Request.class); when(mockOkHttpClient.newCall(requestArgumentCaptor.capture())) .thenReturn(mockCall); @@ -134,7 +137,7 @@ public class LWAClientTest { @ParameterizedTest @MethodSource("lwaClient") - public void returnAccessTokenFromResponse(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException { + public void returnAccessTokenFromResponse(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException, LWAException { when(mockOkHttpClient.newCall(any(Request.class))) .thenReturn(mockCall); when(mockCall.execute()) @@ -145,20 +148,54 @@ public class LWAClientTest { @ParameterizedTest @MethodSource("lwaClient") - public void unsuccessfulPostThrowsException(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException { + public void unsuccessfulPostThrowsLwaExceptionUnknownErrorCode(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException, LWAException { when(mockOkHttpClient.newCall(any(Request.class))) .thenReturn(mockCall); when(mockCall.execute()) .thenReturn(buildResponse(400, "Azta|foo")); - Assertions.assertThrows(RuntimeException.class, () -> { + Throwable exception = Assertions.assertThrows(LWAException.class, () -> { underTest.getAccessToken(testLwaAccessTokenRequestMeta); }); + assertEquals(LWAExceptionErrorCode.other.toString(),((LWAException)exception).getErrorCode()); + assertEquals(LWA_EXCEPTION_MSG, exception.getMessage()); } @ParameterizedTest @MethodSource("lwaClient") - public void missingAccessTokenInResponseThrowsException(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException { + public void unsuccessfulPostThrowsLwaExceptionKnownErrorCode1(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException, LWAException { + when(mockOkHttpClient.newCall(any(Request.class))) + .thenReturn(mockCall); + when(mockCall.execute()) + .thenReturn(buildResponse(401, "Azta|foo","invalid_client","Client_authentication_failed")); + + Throwable exception = Assertions.assertThrows(LWAException.class, () -> { + underTest.getAccessToken(testLwaAccessTokenRequestMeta); + }); + assertEquals(LWAExceptionErrorCode.invalid_client.toString(),((LWAException)exception).getErrorCode()); + assertEquals(LWA_EXCEPTION_MSG, exception.getMessage()); + assertEquals(LWA_CLIENT_AUTH_MSG,((LWAException)exception).getErrorMessage()); + } + + @ParameterizedTest + @MethodSource("lwaClient") + public void unsuccessfulPostThrowsLwaExceptionKnownErrorCode2(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException, LWAException { + when(mockOkHttpClient.newCall(any(Request.class))) + .thenReturn(mockCall); + when(mockCall.execute()) + .thenReturn(buildResponse(401, "Azta|foo","access_denied","Authorization_server_denied_the_request")); + + Throwable exception = Assertions.assertThrows(LWAException.class, () -> { + underTest.getAccessToken(testLwaAccessTokenRequestMeta); + }); + assertEquals(LWAExceptionErrorCode.access_denied.toString(),((LWAException)exception).getErrorCode()); + assertEquals(LWA_EXCEPTION_MSG, exception.getMessage()); + assertEquals(LWA_ACCESS_DENIED_MSG,((LWAException)exception).getErrorMessage()); + } + + @ParameterizedTest + @MethodSource("lwaClient") + public void missingAccessTokenInResponseThrowsException(String sellerType, LWAAccessTokenRequestMeta testLwaAccessTokenRequestMeta) throws IOException, LWAException { when(mockOkHttpClient.newCall(any(Request.class))) .thenReturn(mockCall); when(mockCall.execute()) @@ -171,7 +208,7 @@ public class LWAClientTest { //Test for Access Token getting from cache @Test - public void returnAccessTokenFromCache() throws IOException, InterruptedException { + public void returnAccessTokenFromCache() throws IOException, InterruptedException, LWAException { when(mockOkHttpClient.newCall(any(Request.class))) .thenReturn(mockCall); @@ -187,7 +224,7 @@ public class LWAClientTest { } @Test - public void returnAccessTokenFromCacheWithExpiry() throws IOException, InterruptedException { + public void returnAccessTokenFromCacheWithExpiry() throws IOException, InterruptedException, LWAException { LWAClient client = new LWAClient(TEST_ENDPOINT); client.setOkHttpClient(mockOkHttpClient); when(mockOkHttpClient.newCall(any(Request.class))) @@ -202,9 +239,9 @@ public class LWAClientTest { assertEquals("Azta|foo1", client.getAccessToken(lwaAccessTokenRequestMetaSeller)); } - private static Response buildResponse(int code, String accessToken, String expiryInSeconds) { + private static Response buildResponse(int code, String accessToken, String expiryInSeconds,String errorCode, String errorMessage) { ResponseBody responseBody = ResponseBody.create(EXPECTED_MEDIA_TYPE, - String.format("{%s:%s,%s:%s}", "access_token", accessToken, "expires_in", expiryInSeconds)); + String.format("{%s:%s,%s:%s,%s:%s,%s:%s}", "access_token", accessToken, "expires_in", expiryInSeconds, "error", errorCode, "error_description", errorMessage)); return new Response.Builder() .request(new Request.Builder().url(TEST_ENDPOINT).build()) @@ -215,7 +252,24 @@ public class LWAClientTest { .build(); } + private static Response buildResponse(int code, String accessToken,String errorCode, String errorMessage) { + return buildResponse(code, accessToken, "3600",errorCode,errorMessage); + } + private static Response buildResponse(int code, String accessToken) { return buildResponse(code, accessToken, "3600"); } + + private static Response buildResponse(int code, String accessToken,String expiryInSeconds) { + ResponseBody responseBody = ResponseBody.create(EXPECTED_MEDIA_TYPE, + String.format("{%s:%s,%s:%s}", "access_token", accessToken, "expires_in", expiryInSeconds)); + + return new Response.Builder() + .request(new Request.Builder().url(TEST_ENDPOINT).build()) + .code(code) + .body(responseBody) + .protocol(Protocol.HTTP_1_1) + .message("OK") + .build(); + } } diff --git a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/SignableRequestImplTest.java b/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/SignableRequestImplTest.java deleted file mode 100644 index c6a87de..0000000 --- a/clients/sellingpartner-api-aa-java/tst/com/amazon/SellingPartnerAPIAA/SignableRequestImplTest.java +++ /dev/null @@ -1,247 +0,0 @@ -package com.amazon.SellingPartnerAPIAA; - -import com.amazonaws.http.HttpMethodName; -import com.squareup.okhttp.HttpUrl; -import com.squareup.okhttp.MediaType; -import com.squareup.okhttp.Request; -import com.squareup.okhttp.RequestBody; -import org.junit.Before; -import org.junit.Test; - -import java.io.ByteArrayInputStream; -import java.net.URI; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Scanner; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -public class SignableRequestImplTest { - private Request testRequest; - private SignableRequestImpl underTest; - - @Before - public void init() { - testRequest = new Request.Builder() - .url("http://www.amazon.com/request/library?test=true&sky=blue&right=右") - .get() - .build(); - - underTest = new SignableRequestImpl(testRequest); - } - - @Test - public void getHttpMethod() { - assertEquals(HttpMethodName.GET, underTest.getHttpMethod()); - - underTest = new SignableRequestImpl(new Request.Builder() - .url("https://www.amazon.com") - .post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), "{\"foo\": \"bar\"}")) - .build()); - - assertEquals(HttpMethodName.POST, underTest.getHttpMethod()); - } - - @Test - public void getOriginalRequestObject() { - Request actualRequest = (Request)underTest.getOriginalRequestObject(); - - assertNotSame(testRequest, actualRequest); - assertEquals(testRequest.method(), actualRequest.method()); - assertEquals(testRequest.url(), actualRequest.url()); - assertEquals(testRequest.headers().toMultimap(), actualRequest.headers().toMultimap()); - assertEquals(testRequest.body(), actualRequest.body()); - } - - @Test - public void getReadLimitInfo() { - assertNull(underTest.getReadLimitInfo()); - } - - @Test - public void getResourcePath() { - assertEquals("/request/library", underTest.getResourcePath()); - } - - @Test - public void getResourcePathWithPoundChar() { - testRequest = new Request.Builder() - .url("http://www.amazon.com/request/%23library") - .get() - .build(); - underTest = new SignableRequestImpl(testRequest); - - assertEquals("/request/%23library", underTest.getResourcePath()); - } - - @Test - public void noTimeOffset() { - assertEquals(0, underTest.getTimeOffset()); - } - - @Test - public void getEndpoint() { - assertEquals(URI.create("http://www.amazon.com"), underTest.getEndpoint()); - } - - @Test - public void headers() { - Map expectedHeaders = new HashMap<>(); - - assertTrue(underTest.getHeaders().isEmpty()); - - underTest.addHeader("foo", "bar"); - expectedHeaders.put("foo", "bar"); - assertEquals(expectedHeaders, underTest.getHeaders()); - - underTest.addHeader("ban", "bop"); - expectedHeaders.put("ban", "bop"); - assertEquals(expectedHeaders, underTest.getHeaders()); - } - - @Test - public void getParameters() { - Map> expectedParamters = new HashMap<>(); - expectedParamters.put("test", Collections.singletonList("true")); - expectedParamters.put("sky", Collections.singletonList("blue")); - expectedParamters.put("right", Collections.singletonList("右")); - - assertEquals(expectedParamters, underTest.getParameters()); - } - - @Test - public void getContent() { - String expectedContent = "{\"foo\":\"bar\"}"; - StringBuilder actualContent = new StringBuilder(); - - underTest = new SignableRequestImpl(new Request.Builder() - .url("https://www.amazon.com") - .post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), expectedContent)) - .build()); - - try(Scanner scanner = new Scanner(underTest.getContent())){ - while(scanner.hasNext()) { - actualContent.append(scanner.next()); - } - } - - assertEquals(expectedContent, actualContent.toString()); - } - - @Test - public void getUnwrappedContent() { - String expectedContent = "{\"ban\":\"bop\"}"; - StringBuilder actualContent = new StringBuilder(); - - underTest = new SignableRequestImpl(new Request.Builder() - .url("https://www.amazon.com") - .post(RequestBody.create(MediaType.parse("application/json; charset=utf-8"), expectedContent)) - .build()); - - try(Scanner scanner = new Scanner(underTest.getContentUnwrapped())){ - while(scanner.hasNext()) { - actualContent.append(scanner.next()); - } - } - - assertEquals(expectedContent, actualContent.toString()); - } - - @Test(expected = UnsupportedOperationException.class) - public void setContentNotSupported() { - underTest.setContent(new ByteArrayInputStream("abc".getBytes())); - } - - @Test - public void addParameter() { - underTest.addParameter("left", "左"); - - HttpUrl actualHttpUrl = ((Request) underTest.getOriginalRequestObject()) - .httpUrl(); - - assertEquals(Collections.singletonList("true"), actualHttpUrl.queryParameterValues("test")); - assertEquals(Collections.singletonList("blue"), actualHttpUrl.queryParameterValues("sky")); - assertEquals(Collections.singletonList("右"), actualHttpUrl.queryParameterValues("right")); - assertEquals(Collections.singletonList("左"), actualHttpUrl.queryParameterValues("left")); - } - - @Test - public void gracefulBlankParametersParse() { - testRequest = new Request.Builder() - .url("http://www.amazon.com/request/library? ") - .get() - .build(); - - underTest = new SignableRequestImpl(testRequest); - - assertTrue(underTest.getParameters().isEmpty()); - } - - @Test - public void gracefulIncompleteParameterPairsParse() { - testRequest = new Request.Builder() - .url("http://www.amazon.com/request/library?isSigned& =false") - .get() - .build(); - - Map> expected = new HashMap<>(); - expected.put("isSigned", Collections.singletonList(null)); - expected.put(" ", Collections.singletonList("false")); - - underTest = new SignableRequestImpl(testRequest); - - assertEquals(expected, underTest.getParameters()); - } - - @Test - public void getHeadersIncludesContentTypeFromRequestBody() { - String expected = "application/json; charset=utf-8"; - RequestBody requestBody = RequestBody.create(MediaType.parse(expected), - "{\"foo\":\"bar\"}"); - - testRequest = new Request.Builder() - .url("http://www.amazon.com") - .post(requestBody) - .header("Content-Type", "THIS SHOULD BE OVERRIDDEN WITH REQUEST BODY CONTENT TYPE") - .build(); - - underTest = new SignableRequestImpl(testRequest); - - assertEquals(expected, underTest.getHeaders().get("Content-Type")); - } - - @Test - public void missingRequestBodyDoesNotOverwriteExistingContentTypeHeader() { - String expected = "testContentType"; - - testRequest = new Request.Builder() - .url("http://www.amazon.com") - .get() - .header("Content-Type", expected) - .build(); - - underTest = new SignableRequestImpl(testRequest); - - assertEquals(expected, underTest.getHeaders().get("Content-Type")); - } - - @Test - public void missingRequestBodyContentTypeDoesNotOverwriteExistingContentTypeHeader() { - String expected = "testContentType"; - - testRequest = new Request.Builder() - .url("http://www.amazon.com") - .post(RequestBody.create(null, "foo")) - .header("Content-Type", expected) - .build(); - - underTest = new SignableRequestImpl(testRequest); - - assertEquals(expected, underTest.getHeaders().get("Content-Type")); - } -}