Auth library updated to support access token reuse and IAM role
This commit is contained in:
parent
542c2a9b2a
commit
f737a56175
|
@ -108,6 +108,12 @@
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
<artifactId>httpclient</artifactId>
|
<artifactId>httpclient</artifactId>
|
||||||
<version>4.5.9</version>
|
<version>4.5.9</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-sts -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.amazonaws</groupId>
|
||||||
|
<artifactId>aws-java-sdk-sts</artifactId>
|
||||||
|
<version>1.11.236</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -529,7 +529,7 @@ public class ApiClient {
|
||||||
this.awsSigV4Signer = awsSigV4Signer;
|
this.awsSigV4Signer = awsSigV4Signer;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format the given parameter object into string.
|
* Format the given parameter object into string.
|
||||||
*
|
*
|
||||||
|
|
|
@ -43,10 +43,13 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
{{/fullJavaUtil}}
|
{{/fullJavaUtil}}
|
||||||
|
|
||||||
import com.amazon.SellingPartnerAPIAA.AWSSigV4Signer;
|
|
||||||
import com.amazon.SellingPartnerAPIAA.LWAAuthorizationSigner;
|
|
||||||
import com.amazon.SellingPartnerAPIAA.LWAAuthorizationCredentials;
|
|
||||||
import com.amazon.SellingPartnerAPIAA.AWSAuthenticationCredentials;
|
import com.amazon.SellingPartnerAPIAA.AWSAuthenticationCredentials;
|
||||||
|
import com.amazon.SellingPartnerAPIAA.AWSAuthenticationCredentialsProvider;
|
||||||
|
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;
|
||||||
|
|
||||||
{{#operations}}
|
{{#operations}}
|
||||||
public class {{classname}} {
|
public class {{classname}} {
|
||||||
|
@ -275,6 +278,9 @@ public class {{classname}} {
|
||||||
private AWSAuthenticationCredentials awsAuthenticationCredentials;
|
private AWSAuthenticationCredentials awsAuthenticationCredentials;
|
||||||
private LWAAuthorizationCredentials lwaAuthorizationCredentials;
|
private LWAAuthorizationCredentials lwaAuthorizationCredentials;
|
||||||
private String endpoint;
|
private String endpoint;
|
||||||
|
private LWAAccessTokenCache lwaAccessTokenCache;
|
||||||
|
private Boolean disableAccessTokenCache = false;
|
||||||
|
private AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider;
|
||||||
|
|
||||||
public Builder awsAuthenticationCredentials(AWSAuthenticationCredentials awsAuthenticationCredentials) {
|
public Builder awsAuthenticationCredentials(AWSAuthenticationCredentials awsAuthenticationCredentials) {
|
||||||
this.awsAuthenticationCredentials = awsAuthenticationCredentials;
|
this.awsAuthenticationCredentials = awsAuthenticationCredentials;
|
||||||
|
@ -290,6 +296,22 @@ public class {{classname}} {
|
||||||
this.endpoint = endpoint;
|
this.endpoint = endpoint;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder lwaAccessTokenCache(LWAAccessTokenCache lwaAccessTokenCache) {
|
||||||
|
this.lwaAccessTokenCache = lwaAccessTokenCache;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder disableAccessTokenCache() {
|
||||||
|
this.disableAccessTokenCache = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder awsAuthenticationCredentialsProvider(AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider) {
|
||||||
|
this.awsAuthenticationCredentialsProvider = awsAuthenticationCredentialsProvider;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public {{classname}} build() {
|
public {{classname}} build() {
|
||||||
if (awsAuthenticationCredentials == null) {
|
if (awsAuthenticationCredentials == null) {
|
||||||
|
@ -304,8 +326,24 @@ public class {{classname}} {
|
||||||
throw new RuntimeException("Endpoint not set");
|
throw new RuntimeException("Endpoint not set");
|
||||||
}
|
}
|
||||||
|
|
||||||
AWSSigV4Signer awsSigV4Signer = new AWSSigV4Signer(awsAuthenticationCredentials);
|
AWSSigV4Signer awsSigV4Signer;
|
||||||
LWAAuthorizationSigner lwaAuthorizationSigner = new LWAAuthorizationSigner(lwaAuthorizationCredentials);
|
if ( awsAuthenticationCredentialsProvider == null) {
|
||||||
|
awsSigV4Signer = new AWSSigV4Signer(awsAuthenticationCredentials);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
awsSigV4Signer = new AWSSigV4Signer(awsAuthenticationCredentials,awsAuthenticationCredentialsProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
LWAAuthorizationSigner lwaAuthorizationSigner = null;
|
||||||
|
if (disableAccessTokenCache) {
|
||||||
|
lwaAuthorizationSigner = new LWAAuthorizationSigner(lwaAuthorizationCredentials);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (lwaAccessTokenCache == null) {
|
||||||
|
lwaAccessTokenCache = new LWAAccessTokenCacheImpl();
|
||||||
|
}
|
||||||
|
lwaAuthorizationSigner = new LWAAuthorizationSigner(lwaAuthorizationCredentials,lwaAccessTokenCache);
|
||||||
|
}
|
||||||
|
|
||||||
return new {{classname}}(new ApiClient()
|
return new {{classname}}(new ApiClient()
|
||||||
.setAWSSigV4Signer(awsSigV4Signer)
|
.setAWSSigV4Signer(awsSigV4Signer)
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
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;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -3,11 +3,15 @@ package com.amazon.SellingPartnerAPIAA;
|
||||||
import com.amazonaws.SignableRequest;
|
import com.amazonaws.SignableRequest;
|
||||||
import com.amazonaws.auth.AWS4Signer;
|
import com.amazonaws.auth.AWS4Signer;
|
||||||
import com.amazonaws.auth.AWSCredentials;
|
import com.amazonaws.auth.AWSCredentials;
|
||||||
|
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||||
import com.amazonaws.auth.BasicAWSCredentials;
|
import com.amazonaws.auth.BasicAWSCredentials;
|
||||||
import com.squareup.okhttp.Request;
|
import com.squareup.okhttp.Request;
|
||||||
import lombok.AccessLevel;
|
import lombok.AccessLevel;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
|
||||||
|
import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClientBuilder;
|
||||||
|
import com.amazonaws.auth.AWSStaticCredentialsProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AWS Signature Version 4 Signer
|
* AWS Signature Version 4 Signer
|
||||||
|
@ -21,6 +25,10 @@ public class AWSSigV4Signer {
|
||||||
|
|
||||||
private AWSCredentials awsCredentials;
|
private AWSCredentials awsCredentials;
|
||||||
|
|
||||||
|
@Setter(AccessLevel.PACKAGE)
|
||||||
|
@Getter(AccessLevel.PACKAGE)
|
||||||
|
private AWSCredentialsProvider awsCredentialsProvider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param awsAuthenticationCredentials AWS Developer Account Credentials
|
* @param awsAuthenticationCredentials AWS Developer Account Credentials
|
||||||
|
@ -33,6 +41,26 @@ public class AWSSigV4Signer {
|
||||||
awsAuthenticationCredentials.getSecretKey());
|
awsAuthenticationCredentials.getSecretKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param awsAuthenticationCredentials and awsAuthenticationCredentialsProvider AWS Developer Account Credentials
|
||||||
|
*/
|
||||||
|
public AWSSigV4Signer(AWSAuthenticationCredentials awsAuthenticationCredentials,
|
||||||
|
AWSAuthenticationCredentialsProvider awsAuthenticationCredentialsProvider) {
|
||||||
|
aws4Signer = new AWS4Signer();
|
||||||
|
aws4Signer.setServiceName(SERVICE_NAME);
|
||||||
|
aws4Signer.setRegionName(awsAuthenticationCredentials.getRegion());
|
||||||
|
BasicAWSCredentials awsBasicCredentials = new BasicAWSCredentials(awsAuthenticationCredentials.getAccessKeyId(),
|
||||||
|
awsAuthenticationCredentials.getSecretKey());
|
||||||
|
awsCredentialsProvider = new STSAssumeRoleSessionCredentialsProvider.Builder(
|
||||||
|
awsAuthenticationCredentialsProvider.getRoleArn(),
|
||||||
|
awsAuthenticationCredentialsProvider.getRoleSessionName())
|
||||||
|
.withStsClient(AWSSecurityTokenServiceClientBuilder.standard()
|
||||||
|
.withRegion(awsAuthenticationCredentials.getRegion())
|
||||||
|
.withCredentials(new AWSStaticCredentialsProvider(awsBasicCredentials)).build())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs a Request with AWS Signature Version 4
|
* Signs a Request with AWS Signature Version 4
|
||||||
*
|
*
|
||||||
|
@ -41,8 +69,11 @@ public class AWSSigV4Signer {
|
||||||
*/
|
*/
|
||||||
public Request sign(Request originalRequest) {
|
public Request sign(Request originalRequest) {
|
||||||
SignableRequest<Request> signableRequest = new SignableRequestImpl(originalRequest);
|
SignableRequest<Request> signableRequest = new SignableRequestImpl(originalRequest);
|
||||||
aws4Signer.sign(signableRequest, awsCredentials);
|
if (awsCredentialsProvider != null) {
|
||||||
|
aws4Signer.sign(signableRequest, awsCredentialsProvider.getCredentials());
|
||||||
|
} else {
|
||||||
|
aws4Signer.sign(signableRequest, awsCredentials);
|
||||||
|
}
|
||||||
return (Request) signableRequest.getOriginalRequestObject();
|
return (Request) signableRequest.getOriginalRequestObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package com.amazon.SellingPartnerAPIAA;
|
||||||
|
|
||||||
|
public interface LWAAccessTokenCache {
|
||||||
|
String get(Object key);
|
||||||
|
void put(Object key, String accessToken, long tokenTTLInSeconds);
|
||||||
|
}
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.amazon.SellingPartnerAPIAA;
|
||||||
|
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class LWAAccessTokenCacheImpl implements LWAAccessTokenCache {
|
||||||
|
//in milliseconds; to avoid returning a token that would expire before or while a request is made
|
||||||
|
private long expiryAdjustment = 60 * 1000;
|
||||||
|
private static final long SECOND_TO_MILLIS = 1000;
|
||||||
|
private ConcurrentHashMap<Object, Object> accessTokenHashMap =
|
||||||
|
new ConcurrentHashMap<Object, Object>();
|
||||||
|
@Override
|
||||||
|
public void put(Object oLWAAccessTokenRequestMeta, String accessToken, long tokenTTLInSeconds) {
|
||||||
|
LWAAccessTokenCacheItem accessTokenCacheItem = new LWAAccessTokenCacheItem();
|
||||||
|
long insertTime = System.currentTimeMillis();
|
||||||
|
long accessTokenExpiresValueMillis = (tokenTTLInSeconds * SECOND_TO_MILLIS) + insertTime;
|
||||||
|
accessTokenCacheItem.setAccessToken(accessToken);
|
||||||
|
accessTokenCacheItem.setAccessTokenExpiredTime(accessTokenExpiresValueMillis);
|
||||||
|
accessTokenHashMap.put(oLWAAccessTokenRequestMeta, accessTokenCacheItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String get(Object oLWAAccessTokenRequestMeta) {
|
||||||
|
Object accessTokenValue = accessTokenHashMap.get(oLWAAccessTokenRequestMeta);
|
||||||
|
if (accessTokenValue != null) {
|
||||||
|
LWAAccessTokenCacheItem accessTokenData =
|
||||||
|
(LWAAccessTokenCacheItem) accessTokenValue;
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
long accessTokenExpiredTime = accessTokenData.getAccessTokenExpiredTime() - expiryAdjustment;
|
||||||
|
if (currentTime < accessTokenExpiredTime) {
|
||||||
|
return accessTokenData.getAccessToken();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.amazon.SellingPartnerAPIAA;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
class LWAAccessTokenCacheItem {
|
||||||
|
|
||||||
|
private String accessToken;
|
||||||
|
private long accessTokenExpiredTime;
|
||||||
|
|
||||||
|
}
|
|
@ -10,7 +10,6 @@ import lombok.Setter;
|
||||||
*/
|
*/
|
||||||
public class LWAAuthorizationSigner {
|
public class LWAAuthorizationSigner {
|
||||||
private static final String SIGNED_ACCESS_TOKEN_HEADER_NAME = "x-amz-access-token";
|
private static final String SIGNED_ACCESS_TOKEN_HEADER_NAME = "x-amz-access-token";
|
||||||
private final String tokenRequestGrantType;
|
|
||||||
|
|
||||||
@Getter(AccessLevel.PACKAGE)
|
@Getter(AccessLevel.PACKAGE)
|
||||||
@Setter(AccessLevel.PACKAGE)
|
@Setter(AccessLevel.PACKAGE)
|
||||||
|
@ -18,14 +17,8 @@ public class LWAAuthorizationSigner {
|
||||||
|
|
||||||
private LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta;
|
private LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta;
|
||||||
|
|
||||||
/**
|
private void buildLWAAccessTokenRequestMeta(LWAAuthorizationCredentials lwaAuthorizationCredentials) {
|
||||||
*
|
String tokenRequestGrantType;
|
||||||
* @param lwaAuthorizationCredentials LWA Authorization Credentials for token exchange
|
|
||||||
*/
|
|
||||||
public LWAAuthorizationSigner(LWAAuthorizationCredentials lwaAuthorizationCredentials) {
|
|
||||||
|
|
||||||
lwaClient = new LWAClient(lwaAuthorizationCredentials.getEndpoint());
|
|
||||||
|
|
||||||
if (!lwaAuthorizationCredentials.getScopes().isEmpty()) {
|
if (!lwaAuthorizationCredentials.getScopes().isEmpty()) {
|
||||||
tokenRequestGrantType = "client_credentials";
|
tokenRequestGrantType = "client_credentials";
|
||||||
} else {
|
} else {
|
||||||
|
@ -40,6 +33,33 @@ public class LWAAuthorizationSigner {
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param lwaAuthorizationCredentials LWA Authorization Credentials for token exchange
|
||||||
|
*/
|
||||||
|
public LWAAuthorizationSigner(LWAAuthorizationCredentials lwaAuthorizationCredentials) {
|
||||||
|
|
||||||
|
lwaClient = new LWAClient(lwaAuthorizationCredentials.getEndpoint());
|
||||||
|
|
||||||
|
buildLWAAccessTokenRequestMeta(lwaAuthorizationCredentials);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Overloaded Constructor @param lwaAuthorizationCredentials LWA Authorization Credentials for token exchange
|
||||||
|
* and LWAAccessTokenCache
|
||||||
|
*/
|
||||||
|
public LWAAuthorizationSigner(LWAAuthorizationCredentials lwaAuthorizationCredentials,
|
||||||
|
LWAAccessTokenCache lwaAccessTokenCache) {
|
||||||
|
|
||||||
|
lwaClient = new LWAClient(lwaAuthorizationCredentials.getEndpoint());
|
||||||
|
lwaClient.setLWAAccessTokenCache(lwaAccessTokenCache);
|
||||||
|
|
||||||
|
buildLWAAccessTokenRequestMeta(lwaAuthorizationCredentials);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs a Request with an LWA Access Token
|
* Signs a Request with an LWA Access Token
|
||||||
* @param originalRequest Request to sign (treated as immutable)
|
* @param originalRequest Request to sign (treated as immutable)
|
||||||
|
|
|
@ -16,26 +16,45 @@ import java.io.IOException;
|
||||||
|
|
||||||
class LWAClient {
|
class LWAClient {
|
||||||
private static final String ACCESS_TOKEN_KEY = "access_token";
|
private static final String ACCESS_TOKEN_KEY = "access_token";
|
||||||
|
private static final String ACCESS_TOKEN_EXPIRES_IN = "expires_in";
|
||||||
private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
|
private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private String endpoint;
|
private String endpoint;
|
||||||
|
|
||||||
@Setter(AccessLevel.PACKAGE)
|
@Setter(AccessLevel.PACKAGE)
|
||||||
private OkHttpClient okHttpClient;
|
private OkHttpClient okHttpClient;
|
||||||
|
private LWAAccessTokenCache lwaAccessTokenCache;
|
||||||
|
|
||||||
|
/** Sets cache to store access token until token is expired */
|
||||||
|
public void setLWAAccessTokenCache(LWAAccessTokenCache tokenCache) {
|
||||||
|
this.lwaAccessTokenCache = tokenCache;
|
||||||
|
}
|
||||||
|
|
||||||
LWAClient(String endpoint) {
|
LWAClient(String endpoint) {
|
||||||
okHttpClient = new OkHttpClient();
|
okHttpClient = new OkHttpClient();
|
||||||
this.endpoint = endpoint;
|
this.endpoint = endpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getAccessToken(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) {
|
String getAccessToken(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) {
|
||||||
RequestBody requestBody = RequestBody.create(JSON_MEDIA_TYPE,
|
if (lwaAccessTokenCache != null) {
|
||||||
new Gson().toJson(lwaAccessTokenRequestMeta));
|
return getAccessTokenFromCache(lwaAccessTokenRequestMeta);
|
||||||
Request accessTokenRequest = new Request.Builder()
|
} else {
|
||||||
.url(endpoint)
|
return getAccessTokenFromEndpoint(lwaAccessTokenRequestMeta);
|
||||||
.post(requestBody)
|
}
|
||||||
.build();
|
}
|
||||||
|
|
||||||
|
String getAccessTokenFromCache(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) {
|
||||||
|
String accessTokenCacheData = (String) lwaAccessTokenCache.get(lwaAccessTokenRequestMeta);
|
||||||
|
if (accessTokenCacheData != null) {
|
||||||
|
return accessTokenCacheData;
|
||||||
|
} else {
|
||||||
|
return getAccessTokenFromEndpoint(lwaAccessTokenRequestMeta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String getAccessTokenFromEndpoint(LWAAccessTokenRequestMeta lwaAccessTokenRequestMeta) {
|
||||||
|
RequestBody requestBody = RequestBody.create(JSON_MEDIA_TYPE, new Gson().toJson(lwaAccessTokenRequestMeta));
|
||||||
|
Request accessTokenRequest = new Request.Builder().url(endpoint).post(requestBody).build();
|
||||||
|
|
||||||
String accessToken;
|
String accessToken;
|
||||||
try {
|
try {
|
||||||
|
@ -44,15 +63,16 @@ class LWAClient {
|
||||||
throw new IOException("Unsuccessful LWA token exchange");
|
throw new IOException("Unsuccessful LWA token exchange");
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonObject responseJson = new JsonParser()
|
JsonObject responseJson = new JsonParser().parse(response.body().string()).getAsJsonObject();
|
||||||
.parse(response.body().string())
|
|
||||||
.getAsJsonObject();
|
|
||||||
|
|
||||||
accessToken = responseJson.get(ACCESS_TOKEN_KEY).getAsString();
|
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) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException("Error getting LWA Access Token", e);
|
throw new RuntimeException("Error getting LWA Access Token", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
return accessToken;
|
return accessToken;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,12 +3,16 @@ package com.amazon.SellingPartnerAPIAA;
|
||||||
import com.amazonaws.SignableRequest;
|
import com.amazonaws.SignableRequest;
|
||||||
import com.amazonaws.auth.AWS4Signer;
|
import com.amazonaws.auth.AWS4Signer;
|
||||||
import com.amazonaws.auth.AWSCredentials;
|
import com.amazonaws.auth.AWSCredentials;
|
||||||
|
import com.amazonaws.auth.AWSCredentialsProvider;
|
||||||
|
import com.amazonaws.auth.BasicAWSCredentials;
|
||||||
|
import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider;
|
||||||
import com.squareup.okhttp.Request;
|
import com.squareup.okhttp.Request;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
@ -21,11 +25,18 @@ public class AWSSigV4SignerTest {
|
||||||
private static final String TEST_ACCESS_KEY_ID = "aKey";
|
private static final String TEST_ACCESS_KEY_ID = "aKey";
|
||||||
private static final String TEST_SECRET_KEY = "sKey";
|
private static final String TEST_SECRET_KEY = "sKey";
|
||||||
private static final String TEST_REGION = "us-east";
|
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
|
@Mock
|
||||||
private AWS4Signer mockAWS4Signer;
|
private AWS4Signer mockAWS4Signer;
|
||||||
|
@Mock
|
||||||
|
private AWSCredentialsProvider mockAWSCredentialsProvider;
|
||||||
|
@Mock
|
||||||
|
private AWSCredentials mockAWSCredentials;
|
||||||
|
|
||||||
private AWSSigV4Signer underTest;
|
private AWSSigV4Signer underTest;
|
||||||
|
private AWSSigV4Signer underTestCredentialsProvider;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
public void init() {
|
||||||
|
@ -80,4 +91,36 @@ public class AWSSigV4SignerTest {
|
||||||
|
|
||||||
assertEquals(((Request)actualSignableRequest.getOriginalRequestObject()).url(), actualSignedRequest.url());
|
assertEquals(((Request)actualSignableRequest.getOriginalRequestObject()).url(), actualSignedRequest.url());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void returnSignedRequestWithCredentialProvider() {
|
||||||
|
ArgumentCaptor<SignableRequest> 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,38 @@
|
||||||
package com.amazon.SellingPartnerAPIAA;
|
package com.amazon.SellingPartnerAPIAA;
|
||||||
|
|
||||||
|
import com.squareup.okhttp.Call;
|
||||||
|
import com.squareup.okhttp.MediaType;
|
||||||
|
import com.squareup.okhttp.OkHttpClient;
|
||||||
|
import com.squareup.okhttp.Protocol;
|
||||||
import com.squareup.okhttp.Request;
|
import com.squareup.okhttp.Request;
|
||||||
|
import com.squareup.okhttp.Response;
|
||||||
|
import com.squareup.okhttp.ResponseBody;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_MIGRATION_API;
|
|
||||||
import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_NOTIFICATIONS_API;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotSame;
|
import static org.junit.Assert.assertNotSame;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_NOTIFICATIONS_API;
|
||||||
|
import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_MIGRATION_API;
|
||||||
|
|
||||||
public class LWAAuthorizationSignerTest {
|
public class LWAAuthorizationSignerTest {
|
||||||
private static final String TEST_REFRESH_TOKEN = "rToken";
|
private static final String TEST_REFRESH_TOKEN = "rToken";
|
||||||
private static final String TEST_CLIENT_SECRET = "cSecret";
|
private static final String TEST_CLIENT_SECRET = "cSecret";
|
||||||
|
@ -29,11 +42,18 @@ public class LWAAuthorizationSignerTest {
|
||||||
private static final String TEST_SCOPE_2 = SCOPE_MIGRATION_API;
|
private static final String TEST_SCOPE_2 = SCOPE_MIGRATION_API;
|
||||||
private static final String SELLER_TYPE_SELLER = "seller";
|
private static final String SELLER_TYPE_SELLER = "seller";
|
||||||
private static final String SELLER_TYPE_SELLERLESS = "sellerless";
|
private static final String SELLER_TYPE_SELLERLESS = "sellerless";
|
||||||
|
private static final MediaType EXPECTED_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
|
||||||
|
|
||||||
|
|
||||||
private Request request;
|
private Request request;
|
||||||
private static LWAAuthorizationSigner underTestSeller;
|
private static LWAAuthorizationSigner underTestSeller;
|
||||||
private static LWAAuthorizationSigner underTestSellerless;
|
private static LWAAuthorizationSigner underTestSellerless;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private OkHttpClient mockOkHttpClient;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Call mockCall;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
||||||
|
@ -52,11 +72,12 @@ public class LWAAuthorizationSignerTest {
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@BeforeEach
|
@Before @BeforeEach
|
||||||
public void init() {
|
public void init() {
|
||||||
request = new Request.Builder()
|
request = new Request.Builder()
|
||||||
.url("https://www.amazon.com/api")
|
.url("https://www.amazon.com/api")
|
||||||
.build();
|
.build();
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,8 +99,7 @@ public class LWAAuthorizationSignerTest {
|
||||||
@MethodSource("lwaAuthSigner")
|
@MethodSource("lwaAuthSigner")
|
||||||
public void requestLWAAccessTokenFromConfiguration(String sellerType, LWAAuthorizationSigner testAuthSigner) {
|
public void requestLWAAccessTokenFromConfiguration(String sellerType, LWAAuthorizationSigner testAuthSigner) {
|
||||||
LWAClient mockLWAClient = mock(LWAClient.class);
|
LWAClient mockLWAClient = mock(LWAClient.class);
|
||||||
ArgumentCaptor<LWAAccessTokenRequestMeta> lwaAccessTokenRequestMetaArgumentCaptor = ArgumentCaptor.forClass(
|
ArgumentCaptor<LWAAccessTokenRequestMeta> lwaAccessTokenRequestMetaArgumentCaptor = ArgumentCaptor.forClass(LWAAccessTokenRequestMeta.class);
|
||||||
LWAAccessTokenRequestMeta.class);
|
|
||||||
|
|
||||||
when(mockLWAClient.getAccessToken(lwaAccessTokenRequestMetaArgumentCaptor.capture()))
|
when(mockLWAClient.getAccessToken(lwaAccessTokenRequestMetaArgumentCaptor.capture()))
|
||||||
.thenReturn("foo");
|
.thenReturn("foo");
|
||||||
|
@ -128,4 +148,44 @@ public class LWAAuthorizationSignerTest {
|
||||||
testAuthSigner.setLwaClient(mockLWAClient);
|
testAuthSigner.setLwaClient(mockLWAClient);
|
||||||
assertNotSame(request, testAuthSigner.sign(request));
|
assertNotSame(request, testAuthSigner.sign(request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void returnSignedRequestWithAccessTokenFromLWACache() throws IOException {
|
||||||
|
LWAClient testLWAClient = new LWAClient(TEST_ENDPOINT);
|
||||||
|
testLWAClient.setOkHttpClient(mockOkHttpClient);
|
||||||
|
|
||||||
|
when(mockOkHttpClient.newCall(any(Request.class)))
|
||||||
|
.thenReturn(mockCall);
|
||||||
|
when(mockCall.execute())
|
||||||
|
.thenReturn(buildResponse(200, "Azta|foo", "120"))
|
||||||
|
.thenReturn(buildResponse(200, "Azta|foo1", "1"));
|
||||||
|
|
||||||
|
LWAAccessTokenCache testLWACache = new LWAAccessTokenCacheImpl();
|
||||||
|
LWAAuthorizationSigner testlwaSigner = new LWAAuthorizationSigner(LWAAuthorizationCredentials.builder()
|
||||||
|
.clientId(TEST_CLIENT_ID)
|
||||||
|
.clientSecret(TEST_CLIENT_SECRET)
|
||||||
|
.refreshToken(TEST_REFRESH_TOKEN)
|
||||||
|
.endpoint(TEST_ENDPOINT)
|
||||||
|
.build() , testLWACache );
|
||||||
|
|
||||||
|
testlwaSigner.setLwaClient(testLWAClient);
|
||||||
|
testLWAClient.setLWAAccessTokenCache(testLWACache);
|
||||||
|
Request actualSignedRequest = testlwaSigner.sign(request);
|
||||||
|
Request actualSignedSecondRequest = testlwaSigner.sign(request);
|
||||||
|
|
||||||
|
assertEquals("Azta|foo", actualSignedSecondRequest.header("x-amz-access-token"));
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,8 @@ import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_MIGRATION_API;
|
|
||||||
import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_NOTIFICATIONS_API;
|
import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_NOTIFICATIONS_API;
|
||||||
|
import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_MIGRATION_API;
|
||||||
|
|
||||||
public class LWAClientScopesSerializerDeserializerTest {
|
public class LWAClientScopesSerializerDeserializerTest {
|
||||||
private static final String TEST_SCOPE_1 = SCOPE_NOTIFICATIONS_API;
|
private static final String TEST_SCOPE_1 = SCOPE_NOTIFICATIONS_API;
|
||||||
|
|
|
@ -30,13 +30,14 @@ import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_MIGRATION_API;
|
|
||||||
import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_NOTIFICATIONS_API;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_NOTIFICATIONS_API;
|
||||||
|
import static com.amazon.SellingPartnerAPIAA.ScopeConstants.SCOPE_MIGRATION_API;
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
@RunWith(MockitoJUnitRunner.class)
|
||||||
public class LWAClientTest {
|
public class LWAClientTest {
|
||||||
private static final String TEST_ENDPOINT = "https://www.amazon.com/api";
|
private static final String TEST_ENDPOINT = "https://www.amazon.com/api";
|
||||||
|
@ -52,13 +53,13 @@ public class LWAClientTest {
|
||||||
|
|
||||||
private static final String SELLER_TYPE_SELLER = "seller";
|
private static final String SELLER_TYPE_SELLER = "seller";
|
||||||
private static final String SELLER_TYPE_SELLERLESS = "sellerless";
|
private static final String SELLER_TYPE_SELLERLESS = "sellerless";
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private OkHttpClient mockOkHttpClient;
|
private OkHttpClient mockOkHttpClient;
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private Call mockCall;
|
private Call mockCall;
|
||||||
|
|
||||||
private LWAClient underTest;
|
private LWAClient underTest;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -74,17 +75,15 @@ public class LWAClientTest {
|
||||||
.clientSecret("cSecret")
|
.clientSecret("cSecret")
|
||||||
.grantType("cCredentials")
|
.grantType("cCredentials")
|
||||||
.scopes(new LWAClientScopes(scopesTestSellerless))
|
.scopes(new LWAClientScopes(scopesTestSellerless))
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Before
|
@Before @BeforeEach
|
||||||
@BeforeEach
|
|
||||||
public void init() {
|
public void init() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
underTest = new LWAClient(TEST_ENDPOINT);
|
underTest = new LWAClient(TEST_ENDPOINT);
|
||||||
underTest.setOkHttpClient(mockOkHttpClient);
|
underTest.setOkHttpClient(mockOkHttpClient);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Stream<Arguments> lwaClient(){
|
public static Stream<Arguments> lwaClient(){
|
||||||
|
@ -169,10 +168,43 @@ public class LWAClientTest {
|
||||||
underTest.getAccessToken(testLwaAccessTokenRequestMeta);
|
underTest.getAccessToken(testLwaAccessTokenRequestMeta);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Response buildResponse(int code, String accessToken) {
|
//Test for Access Token getting from cache
|
||||||
|
@Test
|
||||||
|
public void returnAccessTokenFromCache() throws IOException, InterruptedException {
|
||||||
|
|
||||||
|
when(mockOkHttpClient.newCall(any(Request.class)))
|
||||||
|
.thenReturn(mockCall);
|
||||||
|
when(mockCall.execute())
|
||||||
|
.thenReturn(buildResponse(200, "Azta|foo", "120"))
|
||||||
|
.thenThrow(IllegalStateException.class);
|
||||||
|
underTest.setLWAAccessTokenCache(new LWAAccessTokenCacheImpl());
|
||||||
|
|
||||||
|
//First call should get from Endpoint
|
||||||
|
assertEquals("Azta|foo", underTest.getAccessToken(lwaAccessTokenRequestMetaSeller));
|
||||||
|
//Second call when the cache is still valid, if it goes to end point it will return IllegalStateException.
|
||||||
|
assertEquals("Azta|foo", underTest.getAccessToken(lwaAccessTokenRequestMetaSeller));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void returnAccessTokenFromCacheWithExpiry() throws IOException, InterruptedException {
|
||||||
|
LWAClient client = new LWAClient(TEST_ENDPOINT);
|
||||||
|
client.setOkHttpClient(mockOkHttpClient);
|
||||||
|
when(mockOkHttpClient.newCall(any(Request.class)))
|
||||||
|
.thenReturn(mockCall);
|
||||||
|
when(mockCall.execute())
|
||||||
|
.thenReturn(buildResponse(200, "Azta|foo", "1"))
|
||||||
|
.thenReturn(buildResponse(200, "Azta|foo1", "1"));
|
||||||
|
|
||||||
|
//First call should get from Endpoint
|
||||||
|
assertEquals("Azta|foo", client.getAccessToken(lwaAccessTokenRequestMetaSeller));
|
||||||
|
//Second call should again go to the endpoint because accesstoken is expired after expiry adjustment.
|
||||||
|
assertEquals("Azta|foo1", client.getAccessToken(lwaAccessTokenRequestMetaSeller));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Response buildResponse(int code, String accessToken, String expiryInSeconds) {
|
||||||
ResponseBody responseBody = ResponseBody.create(EXPECTED_MEDIA_TYPE,
|
ResponseBody responseBody = ResponseBody.create(EXPECTED_MEDIA_TYPE,
|
||||||
String.format("{%s:%s}", "access_token", accessToken));
|
String.format("{%s:%s,%s:%s}", "access_token", accessToken, "expires_in", expiryInSeconds));
|
||||||
|
|
||||||
return new Response.Builder()
|
return new Response.Builder()
|
||||||
.request(new Request.Builder().url(TEST_ENDPOINT).build())
|
.request(new Request.Builder().url(TEST_ENDPOINT).build())
|
||||||
|
@ -182,4 +214,8 @@ public class LWAClientTest {
|
||||||
.message("OK")
|
.message("OK")
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Response buildResponse(int code, String accessToken) {
|
||||||
|
return buildResponse(code, accessToken, "3600");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue