diff --git a/.github/dependabot.yml b/.github/dependabot.yml index fd4483ad..2383c5f2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,11 +1,22 @@ version: 2 updates: +# branch - master +- package-ecosystem: "maven" + directory: "/" + labels: + - "dependencies" + - "v3.x" + schedule: + interval: "weekly" + +# branch - v2.x - package-ecosystem: "maven" directory: "/" labels: - "dependencies" - "v2.x" - schedule: + target-branch: "v2.x" + schedule: interval: "weekly" # branch - v1.x diff --git a/amazon-kinesis-client-multilang/pom.xml b/amazon-kinesis-client-multilang/pom.xml index 7ee65595..f944fd5d 100644 --- a/amazon-kinesis-client-multilang/pom.xml +++ b/amazon-kinesis-client-multilang/pom.xml @@ -27,10 +27,6 @@ amazon-kinesis-client-multilang - - 1.12.668 - - software.amazon.kinesis @@ -43,32 +39,6 @@ ${awssdk.version} - - com.amazonaws - aws-java-sdk-core - ${aws-java-sdk.version} - - - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.dataformat - jackson-dataformat-cbor - - - org.apache.httpcomponents - httpclient - - - - - - com.amazonaws - aws-java-sdk-sts - ${aws-java-sdk.version} - - org.projectlombok lombok diff --git a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/MultiLangDaemon.java b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/MultiLangDaemon.java index 4588b246..d70fb1af 100644 --- a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/MultiLangDaemon.java +++ b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/MultiLangDaemon.java @@ -61,10 +61,10 @@ import software.amazon.kinesis.coordinator.Scheduler; * applicationName = PythonKCLSample * * # Users can change the credentials provider the KCL will use to retrieve credentials. - * # The DefaultAWSCredentialsProviderChain checks several other providers, which is + * # The DefaultCredentialsProvider checks several other providers, which is * # described here: - * # http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html - * AWSCredentialsProvider = DefaultAWSCredentialsProviderChain + * # https://sdk.amazonaws.com/java/api/2.0.0-preview-11/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.html + * AwsCredentialsProvider = DefaultCredentialsProvider * */ @Slf4j diff --git a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/NestedPropertyKey.java b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/NestedPropertyKey.java index 19211822..13acfeb1 100644 --- a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/NestedPropertyKey.java +++ b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/NestedPropertyKey.java @@ -15,13 +15,14 @@ package software.amazon.kinesis.multilang; import java.util.HashMap; +import java.util.List; import java.util.Map; -import com.amazonaws.regions.Regions; import com.google.common.base.CaseFormat; import lombok.AccessLevel; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import software.amazon.awssdk.regions.Region; /** * Key-Value pairs which may be nested in, and extracted from, a property value @@ -73,8 +74,13 @@ public enum NestedPropertyKey { * @see Available Regions */ ENDPOINT_REGION { - void visit(final NestedPropertyProcessor processor, final String region) { - processor.acceptEndpointRegion(Regions.fromName(region)); + void visit(final NestedPropertyProcessor processor, final String regionName) { + List validRegions = Region.regions(); + Region region = Region.of(regionName); + if (!validRegions.contains(region)) { + throw new IllegalArgumentException("Invalid region name: " + regionName); + } + processor.acceptEndpointRegion(region); } }, diff --git a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/NestedPropertyProcessor.java b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/NestedPropertyProcessor.java index f7587297..fee33514 100644 --- a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/NestedPropertyProcessor.java +++ b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/NestedPropertyProcessor.java @@ -14,7 +14,7 @@ */ package software.amazon.kinesis.multilang; -import com.amazonaws.regions.Regions; +import software.amazon.awssdk.regions.Region; /** * Defines methods to process {@link NestedPropertyKey}s. @@ -26,11 +26,11 @@ public interface NestedPropertyProcessor { * * @param serviceEndpoint the service endpoint either with or without the protocol * (e.g., https://sns.us-west-1.amazonaws.com, sns.us-west-1.amazonaws.com) - * @param signingRegion the region to use for SigV4 signing of requests (e.g. us-west-1) + * @param signingRegion the region to use for the client (e.g. us-west-1) * - * @see #acceptEndpointRegion(Regions) - * @see - * AwsClientBuilder.EndpointConfiguration + * @see #acceptEndpointRegion(Region) + * @see + * AwsClientBuilder.endpointOverride */ void acceptEndpoint(String serviceEndpoint, String signingRegion); @@ -42,7 +42,7 @@ public interface NestedPropertyProcessor { * * @see #acceptEndpoint(String, String) */ - void acceptEndpointRegion(Regions region); + void acceptEndpointRegion(Region region); /** * Set the external id, an optional field to designate who can assume an IAM role. diff --git a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/auth/KclSTSAssumeRoleSessionCredentialsProvider.java b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/auth/KclSTSAssumeRoleSessionCredentialsProvider.java deleted file mode 100644 index b5b9f924..00000000 --- a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/auth/KclSTSAssumeRoleSessionCredentialsProvider.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2023 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package software.amazon.kinesis.multilang.auth; - -import java.util.Arrays; - -import com.amazonaws.auth.AWSSessionCredentials; -import com.amazonaws.auth.AWSSessionCredentialsProvider; -import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider; -import com.amazonaws.auth.STSAssumeRoleSessionCredentialsProvider.Builder; -import com.amazonaws.client.builder.AwsClientBuilder.EndpointConfiguration; -import com.amazonaws.regions.Regions; -import com.amazonaws.services.securitytoken.AWSSecurityTokenService; -import com.amazonaws.services.securitytoken.AWSSecurityTokenServiceClient; -import software.amazon.kinesis.multilang.NestedPropertyKey; -import software.amazon.kinesis.multilang.NestedPropertyProcessor; - -/** - * An {@link AWSSessionCredentialsProvider} that is backed by STSAssumeRole. - */ -public class KclSTSAssumeRoleSessionCredentialsProvider - implements AWSSessionCredentialsProvider, NestedPropertyProcessor { - - private final Builder builder; - - private final STSAssumeRoleSessionCredentialsProvider provider; - - /** - * - * @param params vararg parameters which must include roleArn at index=0, - * and roleSessionName at index=1 - */ - public KclSTSAssumeRoleSessionCredentialsProvider(final String[] params) { - this(params[0], params[1], Arrays.copyOfRange(params, 2, params.length)); - } - - public KclSTSAssumeRoleSessionCredentialsProvider( - final String roleArn, final String roleSessionName, final String... params) { - builder = new Builder(roleArn, roleSessionName); - NestedPropertyKey.parse(this, params); - provider = builder.build(); - } - - @Override - public AWSSessionCredentials getCredentials() { - return provider.getCredentials(); - } - - @Override - public void refresh() { - // do nothing - } - - @Override - public void acceptEndpoint(final String serviceEndpoint, final String signingRegion) { - final EndpointConfiguration endpoint = new EndpointConfiguration(serviceEndpoint, signingRegion); - final AWSSecurityTokenService stsClient = AWSSecurityTokenServiceClient.builder() - .withEndpointConfiguration(endpoint) - .build(); - builder.withStsClient(stsClient); - } - - @Override - public void acceptEndpointRegion(final Regions region) { - final AWSSecurityTokenService stsClient = - AWSSecurityTokenServiceClient.builder().withRegion(region).build(); - builder.withStsClient(stsClient); - } - - @Override - public void acceptExternalId(final String externalId) { - builder.withExternalId(externalId); - } -} diff --git a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/auth/KclStsAssumeRoleCredentialsProvider.java b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/auth/KclStsAssumeRoleCredentialsProvider.java new file mode 100644 index 00000000..96ecfdd6 --- /dev/null +++ b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/auth/KclStsAssumeRoleCredentialsProvider.java @@ -0,0 +1,75 @@ +/* + * Copyright 2024 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.kinesis.multilang.auth; + +import java.net.URI; +import java.util.Arrays; + +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.sts.StsClient; +import software.amazon.awssdk.services.sts.StsClientBuilder; +import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider; +import software.amazon.awssdk.services.sts.model.AssumeRoleRequest; +import software.amazon.awssdk.services.sts.model.AssumeRoleRequest.Builder; +import software.amazon.kinesis.multilang.NestedPropertyKey; +import software.amazon.kinesis.multilang.NestedPropertyProcessor; + +public class KclStsAssumeRoleCredentialsProvider implements AwsCredentialsProvider, NestedPropertyProcessor { + private final Builder assumeRoleRequestBuilder; + private final StsClientBuilder stsClientBuilder; + private final StsAssumeRoleCredentialsProvider stsAssumeRoleCredentialsProvider; + + public KclStsAssumeRoleCredentialsProvider(String[] params) { + this(params[0], params[1], Arrays.copyOfRange(params, 2, params.length)); + } + + public KclStsAssumeRoleCredentialsProvider(String roleArn, String roleSessionName, String... params) { + this.assumeRoleRequestBuilder = + AssumeRoleRequest.builder().roleArn(roleArn).roleSessionName(roleSessionName); + this.stsClientBuilder = StsClient.builder(); + NestedPropertyKey.parse(this, params); + this.stsAssumeRoleCredentialsProvider = StsAssumeRoleCredentialsProvider.builder() + .refreshRequest(assumeRoleRequestBuilder.build()) + .asyncCredentialUpdateEnabled(true) + .stsClient(stsClientBuilder.build()) + .build(); + } + + @Override + public AwsCredentials resolveCredentials() { + return stsAssumeRoleCredentialsProvider.resolveCredentials(); + } + + @Override + public void acceptEndpoint(String serviceEndpoint, String signingRegion) { + if (!serviceEndpoint.startsWith("http://") && !serviceEndpoint.startsWith("https://")) { + serviceEndpoint = "https://" + serviceEndpoint; + } + stsClientBuilder.endpointOverride(URI.create(serviceEndpoint)); + stsClientBuilder.region(Region.of(signingRegion)); + } + + @Override + public void acceptEndpointRegion(Region region) { + stsClientBuilder.region(region); + } + + @Override + public void acceptExternalId(String externalId) { + assumeRoleRequestBuilder.externalId(externalId); + } +} diff --git a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/AWSCredentialsProviderPropertyValueDecoder.java b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/AWSCredentialsProviderPropertyValueDecoder.java deleted file mode 100644 index 8110d4f7..00000000 --- a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/AWSCredentialsProviderPropertyValueDecoder.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2019 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package software.amazon.kinesis.multilang.config; - -import java.lang.reflect.InvocationTargetException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSCredentialsProviderChain; -import lombok.extern.slf4j.Slf4j; - -/** - * Get AWSCredentialsProvider property. - */ -@Slf4j -class AWSCredentialsProviderPropertyValueDecoder implements IPropertyValueDecoder { - private static final String LIST_DELIMITER = ","; - private static final String ARG_DELIMITER = "|"; - - /** - * Constructor. - */ - AWSCredentialsProviderPropertyValueDecoder() {} - - /** - * Get AWSCredentialsProvider property. - * - * @param value - * property value as String - * @return corresponding variable in correct type - */ - @Override - public AWSCredentialsProvider decodeValue(String value) { - if (value != null) { - List providerNames = getProviderNames(value); - List providers = getValidCredentialsProviders(providerNames); - AWSCredentialsProvider[] ps = new AWSCredentialsProvider[providers.size()]; - providers.toArray(ps); - return new AWSCredentialsProviderChain(providers); - } else { - throw new IllegalArgumentException("Property AWSCredentialsProvider is missing."); - } - } - - /** - * @return list of supported types - */ - @Override - public List> getSupportedTypes() { - return Collections.singletonList(AWSCredentialsProvider.class); - } - - /** - * Convert string list to a list of valid credentials providers. - */ - private static List getValidCredentialsProviders(List providerNames) { - List credentialsProviders = new ArrayList<>(); - - for (String providerName : providerNames) { - final String[] nameAndArgs = providerName.split("\\" + ARG_DELIMITER); - final Class clazz; - try { - final Class c = Class.forName(nameAndArgs[0]); - if (!AWSCredentialsProvider.class.isAssignableFrom(c)) { - continue; - } - clazz = (Class) c; - } catch (ClassNotFoundException cnfe) { - // Providers are a product of prefixed Strings to cover multiple - // namespaces (e.g., "Foo" -> { "some.auth.Foo", "kcl.auth.Foo" }). - // It's expected that many class names will not resolve. - continue; - } - log.info("Attempting to construct {}", clazz); - - AWSCredentialsProvider provider = null; - if (nameAndArgs.length > 1) { - final String[] varargs = Arrays.copyOfRange(nameAndArgs, 1, nameAndArgs.length); - - // attempt to invoke an explicit N-arg constructor of FooClass(String, String, ...) - provider = constructProvider(providerName, () -> { - Class[] argTypes = new Class[nameAndArgs.length - 1]; - Arrays.fill(argTypes, String.class); - return clazz.getConstructor(argTypes).newInstance(varargs); - }); - - if (provider == null) { - // attempt to invoke a public varargs/array constructor of FooClass(String[]) - provider = constructProvider(providerName, () -> clazz.getConstructor(String[].class) - .newInstance((Object) varargs)); - } - } - - if (provider == null) { - // regardless of parameters, fallback to invoke a public no-arg constructor - provider = constructProvider(providerName, clazz::newInstance); - } - - if (provider != null) { - credentialsProviders.add(provider); - } - } - return credentialsProviders; - } - - private static List getProviderNames(String property) { - // assume list delimiter is "," - String[] elements = property.split(LIST_DELIMITER); - List result = new ArrayList<>(); - for (int i = 0; i < elements.length; i++) { - String string = elements[i].trim(); - if (!string.isEmpty()) { - // find all possible names and add them to name list - result.addAll(getPossibleFullClassNames(string)); - } - } - return result; - } - - private static List getPossibleFullClassNames(final String provider) { - return Stream.of( - // Customer provides a short name of common providers in com.amazonaws.auth package - // (e.g., any classes implementing the AWSCredentialsProvider interface) - // @see - // http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/AWSCredentialsProvider.html - "com.amazonaws.auth.", - - // Customer provides a short name of a provider offered by this multi-lang package - "software.amazon.kinesis.multilang.auth.", - - // Customer provides a fully-qualified provider name, or a custom credentials provider - // (e.g., com.amazonaws.auth.ClasspathFileCredentialsProvider, org.mycompany.FooProvider) - "") - .map(prefix -> prefix + provider) - .collect(Collectors.toList()); - } - - @FunctionalInterface - private interface CredentialsProviderConstructor { - T construct() - throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException; - } - - /** - * Attempts to construct an {@link AWSCredentialsProvider}. - * - * @param providerName Raw, unmodified provider name. Should there be an - * Exeception during construction, this parameter will be logged. - * @param constructor supplier-like function that will perform the construction - * @return the constructed provider, if successful; otherwise, null - * - * @param type of the CredentialsProvider to construct - */ - private static T constructProvider( - final String providerName, final CredentialsProviderConstructor constructor) { - try { - return constructor.construct(); - } catch (NoSuchMethodException ignored) { - // ignore - } catch (IllegalAccessException | InstantiationException | InvocationTargetException | RuntimeException e) { - log.warn("Failed to construct {}", providerName, e); - } - return null; - } -} diff --git a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/AwsCredentialsProviderPropertyValueDecoder.java b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/AwsCredentialsProviderPropertyValueDecoder.java new file mode 100644 index 00000000..fd29a3db --- /dev/null +++ b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/AwsCredentialsProviderPropertyValueDecoder.java @@ -0,0 +1,261 @@ +/* + * Copyright 2019 Amazon.com, Inc. or its affiliates. + * Licensed under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package software.amazon.kinesis.multilang.config; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import lombok.extern.slf4j.Slf4j; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain; +import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider; +import software.amazon.kinesis.multilang.auth.KclStsAssumeRoleCredentialsProvider; + +/** + * Get AwsCredentialsProvider property. + */ +@Slf4j +class AwsCredentialsProviderPropertyValueDecoder implements IPropertyValueDecoder { + private static final String LIST_DELIMITER = ","; + private static final String ARG_DELIMITER = "|"; + + /** + * Constructor. + */ + AwsCredentialsProviderPropertyValueDecoder() {} + + /** + * Get AwsCredentialsProvider property. + * + * @param value + * property value as String + * @return corresponding variable in correct type + */ + @Override + public AwsCredentialsProvider decodeValue(String value) { + if (value != null) { + List providerNames = getProviderNames(value); + List providers = getValidCredentialsProviders(providerNames); + AwsCredentialsProvider[] ps = new AwsCredentialsProvider[providers.size()]; + providers.toArray(ps); + if (providers.isEmpty()) { + log.warn("Unable to construct any provider with name {}", value); + log.warn("Please verify that all AwsCredentialsProvider properties are passed correctly"); + } + return AwsCredentialsProviderChain.builder() + .credentialsProviders(providers) + .build(); + } else { + throw new IllegalArgumentException("Property AwsCredentialsProvider is missing."); + } + } + + /** + * @return list of supported types + */ + @Override + public List> getSupportedTypes() { + return Collections.singletonList(AwsCredentialsProvider.class); + } + + /** + * Convert string list to a list of valid credentials providers. + */ + private static List getValidCredentialsProviders(List providerNames) { + List credentialsProviders = new ArrayList<>(); + + for (String providerName : providerNames) { + final String[] nameAndArgs = providerName.split("\\" + ARG_DELIMITER); + final Class clazz = getClass(nameAndArgs[0]); + if (clazz == null) { + continue; + } + log.info("Attempting to construct {}", clazz); + final String[] varargs = + nameAndArgs.length > 1 ? Arrays.copyOfRange(nameAndArgs, 1, nameAndArgs.length) : new String[0]; + AwsCredentialsProvider provider = tryConstructor(providerName, clazz, varargs); + if (provider == null) { + provider = tryCreate(providerName, clazz, varargs); + } + if (provider != null) { + log.info("Provider constructed successfully: {}", provider); + credentialsProviders.add(provider); + } + } + return credentialsProviders; + } + + private static AwsCredentialsProvider tryConstructor( + String providerName, Class clazz, String[] varargs) { + AwsCredentialsProvider provider = + constructProvider(providerName, () -> getConstructorWithVarArgs(clazz, varargs)); + if (provider == null) { + provider = constructProvider(providerName, () -> getConstructorWithArgs(clazz, varargs)); + } + if (provider == null) { + provider = constructProvider(providerName, clazz::newInstance); + } + return provider; + } + + private static AwsCredentialsProvider tryCreate( + String providerName, Class clazz, String[] varargs) { + AwsCredentialsProvider provider = + constructProvider(providerName, () -> getCreateMethod(clazz, (Object) varargs)); + if (provider == null) { + provider = constructProvider(providerName, () -> getCreateMethod(clazz, varargs)); + } + if (provider == null) { + provider = constructProvider(providerName, () -> getCreateMethod(clazz)); + } + return provider; + } + + private static AwsCredentialsProvider getConstructorWithVarArgs( + Class clazz, String[] varargs) { + try { + return clazz.getConstructor(String[].class).newInstance((Object) varargs); + } catch (Exception e) { + return null; + } + } + + private static AwsCredentialsProvider getConstructorWithArgs( + Class clazz, String[] varargs) { + try { + Class[] argTypes = new Class[varargs.length]; + Arrays.fill(argTypes, String.class); + return clazz.getConstructor(argTypes).newInstance((Object[]) varargs); + } catch (Exception e) { + return null; + } + } + + private static AwsCredentialsProvider getCreateMethod( + Class clazz, Object... args) { + try { + Class[] argTypes = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + argTypes[i] = args[i].getClass(); + } + Method createMethod = clazz.getDeclaredMethod("create", argTypes); + if (Modifier.isStatic(createMethod.getModifiers())) { + return clazz.cast(createMethod.invoke(null, args)); + } else { + log.warn("Found non-static create() method in {}", clazz.getName()); + } + } catch (NoSuchMethodException e) { + // No matching create method found for class + } catch (Exception e) { + log.warn("Failed to invoke create() method in {}", clazz.getName(), e); + } + return null; + } + + /** + * Resolves the class for the given provider name. + * + * @param providerName A string containing the provider name. + * + * @return The Class object representing the resolved AwsCredentialsProvider implementation, + * or null if the class cannot be resolved or does not extend AwsCredentialsProvider. + */ + private static Class getClass(String providerName) { + // Convert any form of StsAssumeRoleCredentialsProvider string to KclStsAssumeRoleCredentialsProvider + if (providerName.equals(StsAssumeRoleCredentialsProvider.class.getSimpleName()) + || providerName.equals(StsAssumeRoleCredentialsProvider.class.getName())) { + providerName = KclStsAssumeRoleCredentialsProvider.class.getName(); + } + try { + final Class c = Class.forName(providerName); + if (!AwsCredentialsProvider.class.isAssignableFrom(c)) { + return null; + } + return (Class) c; + } catch (ClassNotFoundException cnfe) { + // Providers are a product of prefixed Strings to cover multiple + // namespaces (e.g., "Foo" -> { "some.auth.Foo", "kcl.auth.Foo" }). + // It's expected that many class names will not resolve. + return null; + } + } + + private static List getProviderNames(String property) { + // assume list delimiter is "," + String[] elements = property.split(LIST_DELIMITER); + List result = new ArrayList<>(); + for (int i = 0; i < elements.length; i++) { + String string = elements[i].trim(); + if (!string.isEmpty()) { + // find all possible names and add them to name list + result.addAll(getPossibleFullClassNames(string)); + } + } + return result; + } + + private static List getPossibleFullClassNames(final String provider) { + return Stream.of( + // Customer provides a short name of a provider offered by this multi-lang package + "software.amazon.kinesis.multilang.auth.", + // Customer provides a short name of common providers in software.amazon.awssdk.auth.credentials + // package (e.g., any classes implementing the AwsCredentialsProvider interface) + // @see + // https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/AwsCredentialsProvider.html + "software.amazon.awssdk.auth.credentials.", + // Customer provides a fully-qualified provider name, or a custom credentials provider + // (e.g., org.mycompany.FooProvider) + "") + .map(prefix -> prefix + provider) + .collect(Collectors.toList()); + } + + @FunctionalInterface + private interface CredentialsProviderConstructor { + T construct() + throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException; + } + + /** + * Attempts to construct an {@link AwsCredentialsProvider}. + * + * @param providerName Raw, unmodified provider name. Should there be an + * Exception during construction, this parameter will be logged. + * @param constructor supplier-like function that will perform the construction + * @return the constructed provider, if successful; otherwise, null + * + * @param type of the CredentialsProvider to construct + */ + private static T constructProvider( + final String providerName, final CredentialsProviderConstructor constructor) { + try { + return constructor.construct(); + } catch (NoSuchMethodException + | IllegalAccessException + | InstantiationException + | InvocationTargetException + | RuntimeException ignored) { + // ignore + } + return null; + } +} diff --git a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/KinesisClientLibConfigurator.java b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/KinesisClientLibConfigurator.java index 42b617a0..0d897efa 100644 --- a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/KinesisClientLibConfigurator.java +++ b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/KinesisClientLibConfigurator.java @@ -28,7 +28,7 @@ import software.amazon.kinesis.common.StreamIdentifier; /** * KinesisClientLibConfigurator constructs a KinesisClientLibConfiguration from java properties file. The following - * three properties must be provided. 1) "applicationName" 2) "streamName" 3) "AWSCredentialsProvider" + * three properties must be provided. 1) "applicationName" 2) "streamName" 3) "AwsCredentialsProvider" * KinesisClientLibConfigurator will help to automatically assign the value of "workerId" if this property is not * provided. In the specified properties file, any properties, which matches the variable name in * KinesisClientLibConfiguration and has a corresponding "with{variableName}" setter method, will be read in, and its @@ -62,7 +62,7 @@ public class KinesisClientLibConfigurator { properties.entrySet().forEach(e -> { try { log.info("Processing (key={}, value={})", e.getKey(), e.getValue()); - utilsBean.setProperty(configuration, (String) e.getKey(), e.getValue()); + utilsBean.setProperty(configuration, processKey((String) e.getKey()), e.getValue()); } catch (IllegalAccessException | InvocationTargetException ex) { throw new RuntimeException(ex); } @@ -110,4 +110,17 @@ public class KinesisClientLibConfigurator { } return getConfiguration(properties); } + + /** + * Processes a configuration key to normalize AWS credentials provider naming. Necessary to conform to + * autogenerated setters. + * @param key the config param key + * @return case-configured param key name + */ + String processKey(String key) { + if (key.toLowerCase().startsWith("awscredentialsprovider")) { + key = key.replaceAll("(?i)awscredentialsprovider", "awsCredentialsProvider"); + } + return key; + } } diff --git a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/MultiLangDaemonConfiguration.java b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/MultiLangDaemonConfiguration.java index 3336be88..5a4c3092 100644 --- a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/MultiLangDaemonConfiguration.java +++ b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/MultiLangDaemonConfiguration.java @@ -55,7 +55,6 @@ import software.amazon.kinesis.leases.ShardPrioritization; import software.amazon.kinesis.lifecycle.LifecycleConfig; import software.amazon.kinesis.metrics.MetricsConfig; import software.amazon.kinesis.metrics.MetricsLevel; -import software.amazon.kinesis.multilang.config.credentials.V2CredentialWrapper; import software.amazon.kinesis.processor.ProcessorConfig; import software.amazon.kinesis.processor.ShardRecordProcessorFactory; import software.amazon.kinesis.retrieval.RetrievalConfig; @@ -196,19 +195,19 @@ public class MultiLangDaemonConfiguration { private final BuilderDynaBean kinesisCredentialsProvider; - public void setAWSCredentialsProvider(String providerString) { + public void setAwsCredentialsProvider(String providerString) { kinesisCredentialsProvider.set("", providerString); } private final BuilderDynaBean dynamoDBCredentialsProvider; - public void setAWSCredentialsProviderDynamoDB(String providerString) { + public void setAwsCredentialsProviderDynamoDB(String providerString) { dynamoDBCredentialsProvider.set("", providerString); } private final BuilderDynaBean cloudWatchCredentialsProvider; - public void setAWSCredentialsProviderCloudWatch(String providerString) { + public void setAwsCredentialsProviderCloudWatch(String providerString) { cloudWatchCredentialsProvider.set("", providerString); } @@ -282,9 +281,9 @@ public class MultiLangDaemonConfiguration { ArrayConverter arrayConverter = new ArrayConverter(String[].class, new StringConverter()); arrayConverter.setDelimiter(','); convertUtilsBean.register(arrayConverter, String[].class); - AWSCredentialsProviderPropertyValueDecoder oldCredentialsDecoder = - new AWSCredentialsProviderPropertyValueDecoder(); - Function converter = s -> new V2CredentialWrapper(oldCredentialsDecoder.decodeValue(s)); + AwsCredentialsProviderPropertyValueDecoder credentialsDecoder = + new AwsCredentialsProviderPropertyValueDecoder(); + Function converter = credentialsDecoder::decodeValue; this.kinesisCredentialsProvider = new BuilderDynaBean( AwsCredentialsProvider.class, convertUtilsBean, converter, CREDENTIALS_DEFAULT_SEARCH_PATH); diff --git a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/credentials/V2CredentialWrapper.java b/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/credentials/V2CredentialWrapper.java deleted file mode 100644 index e1b6072a..00000000 --- a/amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/config/credentials/V2CredentialWrapper.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2019 Amazon.com, Inc. or its affiliates. - * Licensed under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package software.amazon.kinesis.multilang.config.credentials; - -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSSessionCredentials; -import lombok.RequiredArgsConstructor; -import software.amazon.awssdk.auth.credentials.AwsCredentials; -import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; -import software.amazon.awssdk.auth.credentials.AwsSessionCredentials; - -@RequiredArgsConstructor -public class V2CredentialWrapper implements AwsCredentialsProvider { - - private final AWSCredentialsProvider oldCredentialsProvider; - - @Override - public AwsCredentials resolveCredentials() { - AWSCredentials current = oldCredentialsProvider.getCredentials(); - if (current instanceof AWSSessionCredentials) { - return AwsSessionCredentials.create( - current.getAWSAccessKeyId(), - current.getAWSSecretKey(), - ((AWSSessionCredentials) current).getSessionToken()); - } - return new AwsCredentials() { - @Override - public String accessKeyId() { - return current.getAWSAccessKeyId(); - } - - @Override - public String secretAccessKey() { - return current.getAWSSecretKey(); - } - }; - } -} diff --git a/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/MultiLangDaemonConfigTest.java b/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/MultiLangDaemonConfigTest.java index de5a1405..53b7f2d8 100644 --- a/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/MultiLangDaemonConfigTest.java +++ b/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/MultiLangDaemonConfigTest.java @@ -65,7 +65,7 @@ public class MultiLangDaemonConfigTest { String properties = String.format( "executableName = %s\n" + "applicationName = %s\n" - + "AWSCredentialsProvider = DefaultAWSCredentialsProviderChain\n" + + "AwsCredentialsProvider = DefaultCredentialsProvider\n" + "processingLanguage = malbolge\n" + "regionName = %s\n", EXE, APPLICATION_NAME, "us-east-1"); @@ -182,7 +182,7 @@ public class MultiLangDaemonConfigTest { @Test public void testPropertyValidation() { String propertiesNoExecutableName = "applicationName = testApp \n" + "streamName = fakeStream \n" - + "AWSCredentialsProvider = DefaultAWSCredentialsProviderChain\n" + "processingLanguage = malbolge"; + + "AwsCredentialsProvider = DefaultCredentialsProvider\n" + "processingLanguage = malbolge"; ClassLoader classLoader = Mockito.mock(ClassLoader.class); Mockito.doReturn(new ByteArrayInputStream(propertiesNoExecutableName.getBytes())) diff --git a/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/NestedPropertyKeyTest.java b/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/NestedPropertyKeyTest.java index fbffee81..3c2de9c9 100644 --- a/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/NestedPropertyKeyTest.java +++ b/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/NestedPropertyKeyTest.java @@ -14,11 +14,11 @@ */ package software.amazon.kinesis.multilang; -import com.amazonaws.regions.Regions; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; +import software.amazon.awssdk.regions.Region; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.verify; @@ -64,9 +64,9 @@ public class NestedPropertyKeyTest { @Test public void testEndpointRegion() { - final Regions expectedRegion = Regions.GovCloud; + final Region expectedRegion = Region.US_GOV_WEST_1; - parse(mockProcessor, createKey(ENDPOINT_REGION, expectedRegion.getName())); + parse(mockProcessor, createKey(ENDPOINT_REGION, expectedRegion.id())); verify(mockProcessor).acceptEndpointRegion(expectedRegion); } diff --git a/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/auth/KclSTSAssumeRoleSessionCredentialsProviderTest.java b/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/auth/KclStsAssumeRoleCredentialsProviderTest.java similarity index 80% rename from amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/auth/KclSTSAssumeRoleSessionCredentialsProviderTest.java rename to amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/auth/KclStsAssumeRoleCredentialsProviderTest.java index c27a425d..9a4d5b0c 100644 --- a/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/auth/KclSTSAssumeRoleSessionCredentialsProviderTest.java +++ b/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/auth/KclStsAssumeRoleCredentialsProviderTest.java @@ -20,7 +20,7 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; -public class KclSTSAssumeRoleSessionCredentialsProviderTest { +public class KclStsAssumeRoleCredentialsProviderTest { private static final String ARN = "arn"; private static final String SESSION_NAME = "sessionName"; @@ -31,29 +31,29 @@ public class KclSTSAssumeRoleSessionCredentialsProviderTest { */ @Test public void testConstructorWithoutOptionalParams() { - new KclSTSAssumeRoleSessionCredentialsProvider(new String[] {ARN, SESSION_NAME}); + new KclStsAssumeRoleCredentialsProvider(new String[] {ARN, SESSION_NAME, "endpointRegion=us-east-1"}); } @Test public void testAcceptEndpoint() { // discovered exception during e2e testing; therefore, this test is // to simply verify the constructed STS client doesn't go *boom* - final KclSTSAssumeRoleSessionCredentialsProvider provider = - new KclSTSAssumeRoleSessionCredentialsProvider(ARN, SESSION_NAME); + final KclStsAssumeRoleCredentialsProvider provider = + new KclStsAssumeRoleCredentialsProvider(ARN, SESSION_NAME, "endpointRegion=us-east-1"); provider.acceptEndpoint("endpoint", "us-east-1"); } @Test public void testVarArgs() { for (final String[] varargs : Arrays.asList( - new String[] {ARN, SESSION_NAME, "externalId=eid", "foo"}, - new String[] {ARN, SESSION_NAME, "foo", "externalId=eid"})) { + new String[] {ARN, SESSION_NAME, "externalId=eid", "foo", "endpointRegion=us-east-1"}, + new String[] {ARN, SESSION_NAME, "foo", "externalId=eid", "endpointRegion=us-east-1"})) { final VarArgsSpy provider = new VarArgsSpy(varargs); assertEquals("eid", provider.externalId); } } - private static class VarArgsSpy extends KclSTSAssumeRoleSessionCredentialsProvider { + private static class VarArgsSpy extends KclStsAssumeRoleCredentialsProvider { private String externalId; diff --git a/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/config/AWSCredentialsProviderPropertyValueDecoderTest.java b/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/config/AwsCredentialsProviderPropertyValueDecoderTest.java similarity index 52% rename from amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/config/AWSCredentialsProviderPropertyValueDecoderTest.java rename to amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/config/AwsCredentialsProviderPropertyValueDecoderTest.java index ba5a0925..3e9380e6 100644 --- a/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/config/AWSCredentialsProviderPropertyValueDecoderTest.java +++ b/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/config/AwsCredentialsProviderPropertyValueDecoderTest.java @@ -16,16 +16,17 @@ package software.amazon.kinesis.multilang.config; import java.util.Arrays; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.AWSCredentialsProviderChain; -import com.amazonaws.auth.BasicAWSCredentials; import lombok.ToString; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeDiagnosingMatcher; import org.junit.Test; -import software.amazon.kinesis.multilang.auth.KclSTSAssumeRoleSessionCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProviderChain; +import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider; +import software.amazon.kinesis.multilang.auth.KclStsAssumeRoleCredentialsProvider; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.instanceOf; @@ -33,31 +34,32 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; -public class AWSCredentialsProviderPropertyValueDecoderTest { +public class AwsCredentialsProviderPropertyValueDecoderTest { private static final String TEST_ACCESS_KEY_ID = "123"; private static final String TEST_SECRET_KEY = "456"; private final String credentialName1 = AlwaysSucceedCredentialsProvider.class.getName(); private final String credentialName2 = ConstructorCredentialsProvider.class.getName(); - private final AWSCredentialsProviderPropertyValueDecoder decoder = new AWSCredentialsProviderPropertyValueDecoder(); + private final String createCredentialClass = CreateProvider.class.getName(); + private final AwsCredentialsProviderPropertyValueDecoder decoder = new AwsCredentialsProviderPropertyValueDecoder(); @ToString - private static class AWSCredentialsMatcher extends TypeSafeDiagnosingMatcher { + private static class AwsCredentialsMatcher extends TypeSafeDiagnosingMatcher { private final Matcher akidMatcher; private final Matcher secretMatcher; private final Matcher> classMatcher; - public AWSCredentialsMatcher(String akid, String secret) { + public AwsCredentialsMatcher(String akid, String secret) { this.akidMatcher = equalTo(akid); this.secretMatcher = equalTo(secret); - this.classMatcher = instanceOf(AWSCredentialsProviderChain.class); + this.classMatcher = instanceOf(AwsCredentialsProviderChain.class); } @Override - protected boolean matchesSafely(AWSCredentialsProvider item, Description mismatchDescription) { - AWSCredentials actual = item.getCredentials(); + protected boolean matchesSafely(AwsCredentialsProvider item, Description mismatchDescription) { + AwsCredentials actual = item.resolveCredentials(); boolean matched = true; if (!classMatcher.matches(item)) { @@ -65,12 +67,12 @@ public class AWSCredentialsProviderPropertyValueDecoderTest { matched = false; } - if (!akidMatcher.matches(actual.getAWSAccessKeyId())) { - akidMatcher.describeMismatch(actual.getAWSAccessKeyId(), mismatchDescription); + if (!akidMatcher.matches(actual.accessKeyId())) { + akidMatcher.describeMismatch(actual.accessKeyId(), mismatchDescription); matched = false; } - if (!secretMatcher.matches(actual.getAWSSecretKey())) { - secretMatcher.describeMismatch(actual.getAWSSecretKey(), mismatchDescription); + if (!secretMatcher.matches(actual.secretAccessKey())) { + secretMatcher.describeMismatch(actual.secretAccessKey(), mismatchDescription); matched = false; } return matched; @@ -84,31 +86,31 @@ public class AWSCredentialsProviderPropertyValueDecoderTest { } } - private static AWSCredentialsMatcher hasCredentials(String akid, String secret) { - return new AWSCredentialsMatcher(akid, secret); + private static AwsCredentialsMatcher hasCredentials(String akid, String secret) { + return new AwsCredentialsMatcher(akid, secret); } @Test public void testSingleProvider() { - AWSCredentialsProvider provider = decoder.decodeValue(credentialName1); + AwsCredentialsProvider provider = decoder.decodeValue(credentialName1); assertThat(provider, hasCredentials(TEST_ACCESS_KEY_ID, TEST_SECRET_KEY)); } @Test public void testTwoProviders() { - AWSCredentialsProvider provider = decoder.decodeValue(credentialName1 + "," + credentialName1); + AwsCredentialsProvider provider = decoder.decodeValue(credentialName1 + "," + credentialName1); assertThat(provider, hasCredentials(TEST_ACCESS_KEY_ID, TEST_SECRET_KEY)); } @Test public void testProfileProviderWithOneArg() { - AWSCredentialsProvider provider = decoder.decodeValue(credentialName2 + "|arg"); + AwsCredentialsProvider provider = decoder.decodeValue(credentialName2 + "|arg"); assertThat(provider, hasCredentials("arg", "blank")); } @Test public void testProfileProviderWithTwoArgs() { - AWSCredentialsProvider provider = decoder.decodeValue(credentialName2 + "|arg1|arg2"); + AwsCredentialsProvider provider = decoder.decodeValue(credentialName2 + "|arg1|arg2"); assertThat(provider, hasCredentials("arg1", "arg2")); } @@ -118,14 +120,34 @@ public class AWSCredentialsProviderPropertyValueDecoderTest { @Test public void testKclAuthProvider() { for (final String className : Arrays.asList( - KclSTSAssumeRoleSessionCredentialsProvider.class.getName(), // fully-qualified name - KclSTSAssumeRoleSessionCredentialsProvider.class.getSimpleName() // name-only; needs prefix - )) { - final AWSCredentialsProvider provider = decoder.decodeValue(className + "|arn|sessionName"); + KclStsAssumeRoleCredentialsProvider.class.getName(), // fully-qualified name + KclStsAssumeRoleCredentialsProvider.class.getSimpleName(), // name-only; needs prefix + StsAssumeRoleCredentialsProvider.class.getName(), // user passes full sts package path + StsAssumeRoleCredentialsProvider.class.getSimpleName())) { + final AwsCredentialsProvider provider = + decoder.decodeValue(className + "|arn|sessionName|endpointRegion=us-east-1"); assertNotNull(className, provider); } } + /** + * Test that OneArgCreateProvider in the SDK v2 can process a create() method + */ + @Test + public void testEmptyCreateProvider() { + AwsCredentialsProvider provider = decoder.decodeValue(createCredentialClass); + assertThat(provider, hasCredentials(TEST_ACCESS_KEY_ID, TEST_SECRET_KEY)); + } + + /** + * Test that OneArgCreateProvider in the SDK v2 can process a create(arg1) method + */ + @Test + public void testOneArgCreateProvider() { + AwsCredentialsProvider provider = decoder.decodeValue(createCredentialClass + "|testCreateProperty"); + assertThat(provider, hasCredentials("testCreateProperty", TEST_SECRET_KEY)); + } + /** * Test that a provider can be instantiated by its varargs constructor. */ @@ -135,28 +157,25 @@ public class AWSCredentialsProviderPropertyValueDecoderTest { final String className = VarArgCredentialsProvider.class.getName(); final String encodedValue = className + "|" + String.join("|", args); - final AWSCredentialsProvider provider = decoder.decodeValue(encodedValue); - assertEquals(Arrays.toString(args), provider.getCredentials().getAWSAccessKeyId()); + final AwsCredentialsProvider provider = decoder.decodeValue(encodedValue); + assertEquals(Arrays.toString(args), provider.resolveCredentials().accessKeyId()); } /** * This credentials provider will always succeed */ - public static class AlwaysSucceedCredentialsProvider implements AWSCredentialsProvider { + public static class AlwaysSucceedCredentialsProvider implements AwsCredentialsProvider { @Override - public AWSCredentials getCredentials() { - return new BasicAWSCredentials(TEST_ACCESS_KEY_ID, TEST_SECRET_KEY); + public AwsCredentials resolveCredentials() { + return AwsBasicCredentials.create(TEST_ACCESS_KEY_ID, TEST_SECRET_KEY); } - - @Override - public void refresh() {} } /** * This credentials provider needs a constructor call to instantiate it */ - public static class ConstructorCredentialsProvider implements AWSCredentialsProvider { + public static class ConstructorCredentialsProvider implements AwsCredentialsProvider { private String arg1; private String arg2; @@ -172,15 +191,12 @@ public class AWSCredentialsProviderPropertyValueDecoderTest { } @Override - public AWSCredentials getCredentials() { - return new BasicAWSCredentials(arg1, arg2); + public AwsCredentials resolveCredentials() { + return AwsBasicCredentials.create(arg1, arg2); } - - @Override - public void refresh() {} } - private static class VarArgCredentialsProvider implements AWSCredentialsProvider { + private static class VarArgCredentialsProvider implements AwsCredentialsProvider { private final String[] args; @@ -189,13 +205,33 @@ public class AWSCredentialsProviderPropertyValueDecoderTest { } @Override - public AWSCredentials getCredentials() { + public AwsCredentials resolveCredentials() { // KISS solution to surface the constructor args final String flattenedArgs = Arrays.toString(args); - return new BasicAWSCredentials(flattenedArgs, flattenedArgs); + return AwsBasicCredentials.create(flattenedArgs, flattenedArgs); + } + } + /** + * Credentials provider to test AWS SDK v2 create() methods for providers like ProfileCredentialsProvider + */ + public static class CreateProvider implements AwsCredentialsProvider { + private String accessKeyId; + + private CreateProvider(String accessKeyId) { + this.accessKeyId = accessKeyId; + } + + public static CreateProvider create() { + return new CreateProvider(TEST_ACCESS_KEY_ID); + } + + public static CreateProvider create(String accessKeyId) { + return new CreateProvider(accessKeyId); } @Override - public void refresh() {} + public AwsCredentials resolveCredentials() { + return AwsBasicCredentials.create(accessKeyId, TEST_SECRET_KEY); + } } } diff --git a/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/config/KinesisClientLibConfiguratorTest.java b/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/config/KinesisClientLibConfiguratorTest.java index b0e3b870..4a19dfdc 100644 --- a/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/config/KinesisClientLibConfiguratorTest.java +++ b/amazon-kinesis-client-multilang/src/test/java/software/amazon/kinesis/multilang/config/KinesisClientLibConfiguratorTest.java @@ -22,15 +22,14 @@ import java.util.Date; import java.util.HashSet; import java.util.Set; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.AWSCredentialsProvider; -import com.amazonaws.auth.BasicAWSCredentials; import com.google.common.collect.ImmutableSet; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.exception.ExceptionUtils; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.runners.MockitoJUnitRunner; +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; +import software.amazon.awssdk.auth.credentials.AwsCredentials; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.kinesis.common.InitialPositionInStream; import software.amazon.kinesis.metrics.MetricsLevel; @@ -60,7 +59,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = " + credentialName1, + "AwsCredentialsProvider = " + credentialName1, "workerId = 123" }, '\n')); @@ -77,7 +76,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "applicationName = app", "streamName = 123", - "AWSCredentialsProvider = " + credentialName1 + ", " + credentialName2, + "AwsCredentialsProvider = " + credentialName1 + ", " + credentialName2, "workerId = 123", "failoverTimeMillis = 100", "shardSyncIntervalMillis = 500" @@ -98,7 +97,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "applicationName = app", "streamName = 123", - "AWSCredentialsProvider = " + credentialName1 + ", " + credentialName2, + "AwsCredentialsProvider = " + credentialName1 + ", " + credentialName2, "initialPositionInStreamExtended = " + epochTimeInSeconds }, '\n')); @@ -116,7 +115,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "applicationName = app", "streamName = 123", - "AWSCredentialsProvider = " + credentialName1 + ", " + credentialName2, + "AwsCredentialsProvider = " + credentialName1 + ", " + credentialName2, "initialPositionInStream = AT_TIMESTAMP" }, '\n')); @@ -136,7 +135,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "applicationName = app", "streamName = 123", - "AWSCredentialsProvider = " + credentialName1 + ", " + credentialName2, + "AwsCredentialsProvider = " + credentialName1 + ", " + credentialName2, "initialPositionInStreamExtended = null" }, '\n')); @@ -151,7 +150,7 @@ public class KinesisClientLibConfiguratorTest { public void testWithUnsupportedClientConfigurationVariables() { MultiLangDaemonConfiguration config = getConfiguration(StringUtils.join( new String[] { - "AWSCredentialsProvider = " + credentialName1 + ", " + credentialName2, + "AwsCredentialsProvider = " + credentialName1 + ", " + credentialName2, "workerId = id", "kinesisClientConfig = {}", "streamName = stream", @@ -170,7 +169,7 @@ public class KinesisClientLibConfiguratorTest { MultiLangDaemonConfiguration config = getConfiguration(StringUtils.join( new String[] { "streamName = kinesis", - "AWSCredentialsProvider = " + credentialName2 + ", " + credentialName1, + "AwsCredentialsProvider = " + credentialName2 + ", " + credentialName1, "workerId = w123", "maxRecords = 10", "metricsMaxQueueSize = 20", @@ -195,7 +194,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = ABCD, " + credentialName1, + "AwsCredentialsProvider = ABCD, " + credentialName1, "workerId = 0", "cleanupLeasesUponShardCompletion = false", "validateSequenceNumberBeforeCheckpointing = true" @@ -233,7 +232,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = ABCD," + credentialName1, + "AwsCredentialsProvider = ABCD," + credentialName1, "workerId = 1", "metricsEnabledDimensions = ShardId, WorkerIdentifier" }, @@ -253,7 +252,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = ABCD," + credentialName1, + "AwsCredentialsProvider = ABCD," + credentialName1, "workerId = 123", "initialPositionInStream = TriM_Horizon" }, @@ -268,7 +267,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = ABCD," + credentialName1, + "AwsCredentialsProvider = ABCD," + credentialName1, "workerId = 123", "initialPositionInStream = LateSt" }, @@ -283,7 +282,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = ABCD," + credentialName1, + "AwsCredentialsProvider = ABCD," + credentialName1, "workerId = 123", "initialPositionInStream = TriM_Horizon", "abc = 1" @@ -302,7 +301,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = ABCD," + credentialName1, + "AwsCredentialsProvider = ABCD," + credentialName1, "workerId = 123", "initialPositionInStream = TriM_Horizon", "maxGetRecordsThreadPool = 1" @@ -318,7 +317,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = ABCD," + credentialName1, + "AwsCredentialsProvider = ABCD," + credentialName1, "workerId = 123", "initialPositionInStream = TriM_Horizon", "maxGetRecordsThreadPool = 0", @@ -334,7 +333,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = " + credentialName1, + "AwsCredentialsProvider = " + credentialName1, "workerId = 123", "failoverTimeMillis = 100nf" }, @@ -348,7 +347,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = " + credentialName1, + "AwsCredentialsProvider = " + credentialName1, "workerId = 123", "failoverTimeMillis = -12" }, @@ -380,7 +379,7 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = " + credentialName1, + "AwsCredentialsProvider = " + credentialName1, "failoverTimeMillis = 100", "shardSyncIntervalMillis = 500" }, @@ -397,7 +396,7 @@ public class KinesisClientLibConfiguratorTest { String test = StringUtils.join( new String[] { "applicationName = b", - "AWSCredentialsProvider = " + credentialName1, + "AwsCredentialsProvider = " + credentialName1, "workerId = 123", "failoverTimeMillis = 100" }, @@ -410,7 +409,7 @@ public class KinesisClientLibConfiguratorTest { String test = StringUtils.join( new String[] { "applicationName = b", - "AWSCredentialsProvider = " + credentialName1, + "AwsCredentialsProvider = " + credentialName1, "workerId = 123", "failoverTimeMillis = 100", "streamName = ", @@ -425,7 +424,7 @@ public class KinesisClientLibConfiguratorTest { String test = StringUtils.join( new String[] { "streamName = a", - "AWSCredentialsProvider = " + credentialName1, + "AwsCredentialsProvider = " + credentialName1, "workerId = 123", "failoverTimeMillis = 100" }, @@ -434,12 +433,12 @@ public class KinesisClientLibConfiguratorTest { } @Test - public void testWithAWSCredentialsFailed() { + public void testWithAwsCredentialsFailed() { String test = StringUtils.join( new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = " + credentialName2, + "AwsCredentialsProvider = " + credentialName2, "failoverTimeMillis = 100", "shardSyncIntervalMillis = 500" }, @@ -457,6 +456,34 @@ public class KinesisClientLibConfiguratorTest { } } + @Test + public void testProcessKeyWithExpectedCasing() { + String key = "AwsCredentialsProvider"; + String result = configurator.processKey(key); + assertEquals("awsCredentialsProvider", result); + } + + @Test + public void testProcessKeyWithOldCasing() { + String key = "AWSCredentialsProvider"; + String result = configurator.processKey(key); + assertEquals("awsCredentialsProvider", result); + } + + @Test + public void testProcessKeyWithMixedCasing() { + String key = "AwScReDeNtIaLsPrOvIdEr"; + String result = configurator.processKey(key); + assertEquals("awsCredentialsProvider", result); + } + + @Test + public void testProcessKeyWithSuffix() { + String key = "awscredentialsproviderDynamoDB"; + String result = configurator.processKey(key); + assertEquals("awsCredentialsProviderDynamoDB", result); + } + // TODO: fix this test @Test public void testWithDifferentAWSCredentialsForDynamoDBAndCloudWatch() { @@ -464,9 +491,9 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = " + credentialNameKinesis, - "AWSCredentialsProviderDynamoDB = " + credentialNameDynamoDB, - "AWSCredentialsProviderCloudWatch = " + credentialNameCloudWatch, + "AwsCredentialsProvider = " + credentialNameKinesis, + "AwsCredentialsProviderDynamoDB = " + credentialNameDynamoDB, + "AwsCredentialsProviderCloudWatch = " + credentialNameCloudWatch, "failoverTimeMillis = 100", "shardSyncIntervalMillis = 500" }, @@ -492,9 +519,9 @@ public class KinesisClientLibConfiguratorTest { new String[] { "streamName = a", "applicationName = b", - "AWSCredentialsProvider = " + credentialNameKinesis, - "AWSCredentialsProviderDynamoDB = " + credentialName2, - "AWSCredentialsProviderCloudWatch = " + credentialName2, + "AwsCredentialsProvider = " + credentialNameKinesis, + "AwsCredentialsProviderDynamoDB = " + credentialName2, + "AwsCredentialsProviderCloudWatch = " + credentialName2, "failoverTimeMillis = 100", "shardSyncIntervalMillis = 500" }, @@ -526,71 +553,56 @@ public class KinesisClientLibConfiguratorTest { /** * This credentials provider will always succeed */ - public static class AlwaysSucceedCredentialsProvider implements AWSCredentialsProvider { + public static class AlwaysSucceedCredentialsProvider implements AwsCredentialsProvider { @Override - public AWSCredentials getCredentials() { - return new BasicAWSCredentials("a", "b"); + public AwsCredentials resolveCredentials() { + return AwsBasicCredentials.create("a", "b"); } - - @Override - public void refresh() {} } /** * This credentials provider will always succeed */ - public static class AlwaysSucceedCredentialsProviderKinesis implements AWSCredentialsProvider { + public static class AlwaysSucceedCredentialsProviderKinesis implements AwsCredentialsProvider { @Override - public AWSCredentials getCredentials() { - return new BasicAWSCredentials("", ""); + public AwsCredentials resolveCredentials() { + return AwsBasicCredentials.create("DUMMY_ACCESS_KEY_ID", "DUMMY_SECRET_ACCESS_KEY"); } - - @Override - public void refresh() {} } /** * This credentials provider will always succeed */ - public static class AlwaysSucceedCredentialsProviderDynamoDB implements AWSCredentialsProvider { + public static class AlwaysSucceedCredentialsProviderDynamoDB implements AwsCredentialsProvider { @Override - public AWSCredentials getCredentials() { - return new BasicAWSCredentials("", ""); + public AwsCredentials resolveCredentials() { + return AwsBasicCredentials.create("DUMMY_ACCESS_KEY_ID", "DUMMY_SECRET_ACCESS_KEY"); } - - @Override - public void refresh() {} } /** * This credentials provider will always succeed */ - public static class AlwaysSucceedCredentialsProviderCloudWatch implements AWSCredentialsProvider { + public static class AlwaysSucceedCredentialsProviderCloudWatch implements AwsCredentialsProvider { @Override - public AWSCredentials getCredentials() { - return new BasicAWSCredentials("", ""); + public AwsCredentials resolveCredentials() { + return AwsBasicCredentials.create("DUMMY_ACCESS_KEY_ID", "DUMMY_SECRET_ACCESS_KEY"); } - - @Override - public void refresh() {} } /** * This credentials provider will always fail */ - public static class AlwaysFailCredentialsProvider implements AWSCredentialsProvider { + public static class AlwaysFailCredentialsProvider implements AwsCredentialsProvider { @Override - public AWSCredentials getCredentials() { + public AwsCredentials resolveCredentials() { throw new IllegalArgumentException(); } - - @Override - public void refresh() {} } private MultiLangDaemonConfiguration getConfiguration(String configString) { diff --git a/amazon-kinesis-client-multilang/src/test/resources/multilang.properties b/amazon-kinesis-client-multilang/src/test/resources/multilang.properties index 34cb0c1a..8ded9bc9 100644 --- a/amazon-kinesis-client-multilang/src/test/resources/multilang.properties +++ b/amazon-kinesis-client-multilang/src/test/resources/multilang.properties @@ -17,10 +17,12 @@ streamName = kclpysample applicationName = MultiLangTest # Users can change the credentials provider the KCL will use to retrieve credentials. -# The DefaultAWSCredentialsProviderChain checks several other providers, which is +# Expected key name (case-sensitive): +# AwsCredentialsProvider / AwsCredentialsProviderDynamoDB / AwsCredentialsProviderCloudWatch +# The DefaultCredentialsProvider checks several other providers, which is # described here: -# http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html -AWSCredentialsProvider = DefaultAWSCredentialsProviderChain +# https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.html +AwsCredentialsProvider = DefaultCredentialsProvider # Appended to the user agent of the KCL. Does not impact the functionality of the # KCL in any other way. diff --git a/amazon-kinesis-client/pom.xml b/amazon-kinesis-client/pom.xml index a185756b..4106a09f 100644 --- a/amazon-kinesis-client/pom.xml +++ b/amazon-kinesis-client/pom.xml @@ -82,6 +82,12 @@ software.amazon.glue schema-registry-serde ${gsr.version} + + + com.amazonaws + aws-java-sdk-sts + + software.amazon.glue diff --git a/docs/multilang/configuring-credential-providers.md b/docs/multilang/configuring-credential-providers.md index 9b85baaa..56bde627 100644 --- a/docs/multilang/configuring-credential-providers.md +++ b/docs/multilang/configuring-credential-providers.md @@ -8,24 +8,35 @@ This document should help multilang customers configure a suitable `CredentialPr ## Sample Provider Configuration -In a Properties file, an `AWSCredentialsProperty` configuration might look like: +In a Properties file, an `AwsCredentialsProperty` configuration might look like: ``` -AWSCredentialsProvider = STSAssumeRoleSessionCredentialsProvider|| +AwsCredentialsProvider = StsAssumeRoleCredentialsProvider|||endpointRegion=us-east-1 ``` -This basic configuration creates an [STSAssumeRoleSessionCredentialsProvider][sts-assume-provider] with an ARN and session name. +This basic configuration creates an [StsAssumeRoleCredentialsProvider][sts-assume-provider] with an ARN and session name. +If `endpointRegion` is not specified, the provider will use the region defined in the AWS config file. +If no region is found in the config file, the credentials provider will fail. + +The providers generated by this config property will be [AWS SDK v2 AwsCredentialsProviders][aws-credentials-provider]. +These differ from the SDK v1 AWSCredentialsProviders in a number of ways. See [Credentials Provider Changes][credentials-provider-changes]. + While functional, this configuration is limited. For example, this configuration cannot set a regional endpoint (e.g., VPC use case). -Leveraging nested properties, an `AWSCredentialsProperty` value might change to: +Leveraging nested properties, an `AwsCredentialsProperty` value might change to: ``` -AWSCredentialsProvider = KclSTSAssumeRoleSessionCredentialsProvider||\ +AwsCredentialsProvider = KclSTSAssumeRoleSessionCredentialsProvider||\ |endpointRegion=us-east-1|externalId=spartacus ``` N.B. Backslash (`\`) is for multi-line legibility and is not required. +You can create a default [DefaultCredentialsProvider][default-credentials-provider] by passing it in the config like: +``` +AwsCredentialsProvider = DefaultCredentialsProvider +``` + ## Nested Properties -KCL multilang supports "nested properties" on the `AWSCredentialsProvider` key in the properties file. +KCL multilang supports "nested properties" on the `AwsCredentialsProvider` key in the properties file. The [Backus-Naur form][bnf] of the value: ``` ::= ["|" ]* ["|" ]* @@ -36,8 +47,9 @@ The [Backus-Naur form][bnf] of the value: # this depends on the nested key ``` -In general, required parameters are passed directly to the class' constructor -(e.g., [STSAssumeRoleSessionCredentialsProvider(String, String)][sts-assume-provider-constructor]). +In general, required parameters are passed directly to the class' constructor or .create() method +(e.g., [ProfileCredentialsProvider(String)][profile-credentials-provider-create]). However, most of these providers +require builders and will require a custom implementation similar to `KclStsAssumeRoleCredentialsProvider` for customization Nested properties are a custom mapping provided by KCL multilang, and do not exist in the AWS SDK. See [NestedPropertyKey][nested-property-key] for the supported keys, and details on their expected values. @@ -54,18 +66,27 @@ A backwards-compatible addition might look like: } ``` -### KclSTSAssumeRoleSessionCredentialsProvider - -KCL multilang includes a [custom nested property processor for `STSAssumeRole`][kcl-sts-provider]. -Multilang configurations that use `STSAssumeRoleSessionCredentialsProvider` need only prefix `Kcl` to exercise this new provider: +Leveraging nested properties, an `AwsCredentialsProperty` value might look like: ``` -AWSCredentialsProvider = KclSTSAssumeRoleSessionCredentialsProvider|| +AwsCredentialsProvider = KclSTSAssumeRoleSessionCredentialsProvider||\ + |endpointRegion=us-east-1|externalId=spartacus +``` + +N.B. Backslash (`\`) is for multi-line legibility and is not required. +### KclStsAssumeRoleCredentialsProvider + +KCL multilang includes a [custom nested property processor for `StsAssumeRole`][kcl-sts-provider]. +Multilang configurations that use `StsAssumeRoleSessionCredentialsProvider` need only prefix `Kcl` to exercise this new provider: +``` +AwsCredentialsProvider = KclStsAssumeRoleCredentialsProvider|||endpointRegion=us-east-1 ``` [aws-credentials-provider]: https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/AwsCredentialsProvider.html [bnf]: https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form -[kcl-sts-provider]: /amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/auth/KclSTSAssumeRoleSessionCredentialsProvider.java +[kcl-sts-provider]: /amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/auth/KclStsAssumeRoleCredentialsProvider.java [nested-property-key]: /amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/NestedPropertyKey.java [nested-property-processor]: /amazon-kinesis-client-multilang/src/main/java/software/amazon/kinesis/multilang/NestedPropertyProcessor.java -[sts-assume-provider]: https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/STSAssumeRoleSessionCredentialsProvider.html -[sts-assume-provider-constructor]: https://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/STSAssumeRoleSessionCredentialsProvider.html#STSAssumeRoleSessionCredentialsProvider-java.lang.String-java.lang.String- +[sts-assume-provider]: https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/sts/auth/StsAssumeRoleCredentialsProvider.html +[profile-credentials-provider-create]: https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/ProfileCredentialsProvider.html#create(java.lang.String) +[default-credentials-provider]: https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/auth/credentials/DefaultCredentialsProvider.html +[credentials-provider-changes]: https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/migration-client-credentials.html \ No newline at end of file