diff --git a/README.md b/README.md index 9023f6a..21e475e 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,28 @@ sasl.jaas.config = software.amazon.msk.auth.iam.IAMLoginModule required; sasl.client.callback.handler.class = software.amazon.msk.auth.iam.IAMClientCallbackHandler ``` +## Configuring a Kafka client to use AWS IAM with AWS_MSK_IAM mechanism with Custom STS Regional endpoints +You can configure a Kafka client to use AWS IAM for authentication by adding the following properties to the client's +configuration. This is wrapper on the IAM Auth library to support Regional Based STS Endpoint for to retrieve temporary assume role credentials. + +```properties +# Sets up TLS for encryption and SASL for authN. +security.protocol = SASL_SSL + +# Identifies the SASL mechanism to use. +sasl.mechanism = AWS_MSK_IAM + +# Binds SASL client implementation. +sasl.jaas.config = software.amazon.msk.auth.iam.IAMLoginModule required awsRoleArn='awsRoleArn' awsRoleSessionName='awsRoleSessionName' awsStsRegion='awsStsRegion' awsStsRegionalEndpoint='awsStsRegionalEndpoint'; + +# Encapsulates constructing a SigV4 signature based on extracted credentials. +# The SASL client bound by "sasl.jaas.config" invokes this class. +# The SASL client bound by "sasl.jaas.config" invokes this class. +sasl.login.callback.handler.class=software.amazon.msk.auth.iam.STSAssumeRoleIAMClientCallbackHandler +# This is used during client authentication and reauthentication +sasl.client.callback.handler.class=software.amazon.msk.auth.iam.STSAssumeRoleIAMClientCallbackHandler +``` + ## Configuring a Kafka client to use AWS IAM with SASL OAUTHBEARER mechanism You can alternatively use SASL/OAUTHBEARER mechanism using IAM authentication by adding following configuration. For more details on SASL/OAUTHBEARER mechanism, please read - [KIP-255](https://cwiki.apache.org/confluence/pages/viewpage.action?pageId=75968876) @@ -159,6 +181,18 @@ The Default Credential Provider Chain must contain the permissions necessary to For example, if the client is an EC2 instance, its instance profile should have permission to assume the `msk_client_role`. +### Specifying an AWS IAM Role with Custom STS Regional endpoints +The library supports another way to configure a client to assume an IAM role and use the role's temporary credentials. The IAM role's ARN and optionally the session name for the client can be passed in as client configuration property: + +sasl.jaas.config=software.amazon.msk.auth.iam.IAMLoginModule required awsRoleArn="arn:aws:iam::123456789012:role/msk_client_role" awsRoleSessionName="producer" awsStsRegion="us-west-2" awsStsRegionalEndpoint="https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"; +In this case, the awsRoleArn specifies the ARN for the IAM role the client should use and awsRoleSessionName specifies the session name that this particular client should use while assuming the IAM role. If the same IAM Role is used in multiple contexts, the session names can be used to differentiate between the different contexts. The awsRoleSessionName is optional. + +awsStsRegion specifies the regional endpoint of AWS STS to use while assuming the IAM role. If awsStsRegion is omitted the global endpoint for AWS STS is used by default. When the Kafka client is running in a VPC with an STS interface VPC Endpoint (AWS PrivateLink) to a regional endpoint of AWS STS and we want all STS traffic to go over that endpoint, we should set awsStsRegion to the region corresponding to the interface VPC Endpoint. It also be necessary to configure the awsStsRegionalEndpoint which points to custom regional based VPC endpoints. + +The Default Credential Provider Chain must contain the permissions necessary to assume the client role. If the client is an EC2 instance, its instance profile should have permission to assume the msk_client_role and in case of non EC2 instance below environment variables needs to set to work with assume role credentials +AWS_ACCESS_KEY_ID = "This will be permanant access key id should have permission to assume the msk_client_role" ( For security reason we can keep yearly rotation based ) +AWS_SECRET_ACCESS_KEY = "This will be permanant secrete key should have permission to assume the msk_client_role" ( For security reason we can keep yearly rotation based ) + ### Figuring out whether or not to use default credentials When you want the MSK client to connect to MSK using credentials not found in the [AWS Default Credentials Provider Chain][DefaultCreds], you can specify an `awsProfileName` containing the credential profile to use, or an `awsRoleArn` to indicate an IAM Role’s ARN to assume using credentials in the Default Credential Provider Chain. These parameters are optional, and if they are not set the MSK client will use credentials from the Default Credential Provider Chain. There is no need to specify them if you intend to use an IAM role associated with an AWS compute service, such as EC2 or ECS to authenticate to MSK. diff --git a/src/main/java/software/amazon/msk/auth/iam/STSAssumeRoleIAMClientCallbackHandler.java b/src/main/java/software/amazon/msk/auth/iam/STSAssumeRoleIAMClientCallbackHandler.java new file mode 100644 index 0000000..b7cd5be --- /dev/null +++ b/src/main/java/software/amazon/msk/auth/iam/STSAssumeRoleIAMClientCallbackHandler.java @@ -0,0 +1,88 @@ +package software.amazon.msk.auth.iam; + + +import lombok.NonNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.msk.auth.iam.internals.AWSCredentialsCallback; +import software.amazon.msk.auth.iam.internals.STSAssumeRoleMSKCredentialProvider; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.AppConfigurationEntry; +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class STSAssumeRoleIAMClientCallbackHandler extends IAMClientCallbackHandler { + private static final Logger log = LoggerFactory.getLogger(STSAssumeRoleIAMClientCallbackHandler.class); + private AwsCredentialsProvider provider; + + @Override + public void configure(Map configs, + @NonNull String saslMechanism, + @NonNull List jaasConfigEntries) { + if (!IAMLoginModule.MECHANISM.equals(saslMechanism)) { + throw new IllegalArgumentException("Unexpected SASL mechanism: " + saslMechanism); + } + final Optional configEntry = jaasConfigEntries.stream() + .filter(j -> IAMLoginModule.class.getCanonicalName().equals(j.getLoginModuleName())).findFirst(); + provider = configEntry.map(c -> (AwsCredentialsProvider) new STSAssumeRoleMSKCredentialProvider(c.getOptions())) + .orElse(DefaultCredentialsProvider.create()); + log.info("Successfully retrieved Temp Credentials access key, secrete key"); + } + + @Override + public void close() { + try { + if (provider instanceof AutoCloseable) { + ((AutoCloseable) provider).close(); + } + } catch (Exception e) { + log.warn("Error closing provider", e); + } + } + + @Override + public void handle(@NonNull Callback[] callbacks) throws IOException, UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (log.isDebugEnabled()) { + log.debug("Type information for callback: " + debugClassString(callback.getClass()) + " from " + + debugClassString(this.getClass())); + } + if (callback instanceof AWSCredentialsCallback) { + handleCallback((AWSCredentialsCallback) callback); + } else { + String message = "Unsupported callback type: " + debugClassString(callback.getClass()) + " from " + + debugClassString(this.getClass()); + //We are breaking good practice and logging as well as throwing since this is where client side + //integrations might have trouble. Depending on the client framework either logging or throwing might + //surface the error more easily to the user. + log.error(message); + throw new UnsupportedCallbackException(callback, message); + } + } + } + + protected static String debugClassString(Class clazz) { + return "class: " + clazz.getName() + " classloader: " + clazz.getClassLoader().toString(); + } + + protected void handleCallback(AWSCredentialsCallback callback) throws IOException { + if (log.isDebugEnabled()) { + log.debug("Selecting provider {} to load credentials", provider.getClass().getName()); + } + + try { + callback.setAwsCredentials(provider.resolveCredentials()); + log.info("Credentials are set in the callback handler"); + } catch (Exception e) { + callback.setLoadingException(e); + } + + + } +} diff --git a/src/main/java/software/amazon/msk/auth/iam/internals/STSAssumeRoleMSKCredentialProvider.java b/src/main/java/software/amazon/msk/auth/iam/internals/STSAssumeRoleMSKCredentialProvider.java new file mode 100644 index 0000000..835f7b3 --- /dev/null +++ b/src/main/java/software/amazon/msk/auth/iam/internals/STSAssumeRoleMSKCredentialProvider.java @@ -0,0 +1,445 @@ +package software.amazon.msk.auth.iam.internals; + +/** + * This MSK Credential Provider is used to load up AWS Credentials based on options provided on the Jaas config line. + * As an example + * sasl.jaas.config = IAMLoginModule required awsProfileName={profile name}; + * The currently supported options are: + * 1. A particular AWS Credential profile: awsProfileName={profile name} + * 2. A particular AWS IAM Role, with optional access key id, secret key and session token OR optional external id, + * and optionally AWS IAM role session name, AWS region and Regional STS endpoint: + * awsRoleArn={IAM Role ARN}, awsRoleAccessKeyId={access key id}, awsRoleSecretAccessKey={secret access key}, + * awsRoleSessionToken={session token}, awsRoleSessionName={session name}, awsStsRegion={region name}, awsStsRegionalEndpoint={sts endpoint} + * 3. Optional arguments to configure retries when we fail to load credentials: + * awsMaxRetries={Maximum number of retries}, awsMaxBackOffTimeMs={Maximum back off time between retries in ms} + * 4. Optional argument to help debug credentials used to establish connections: + * awsDebugCreds={true|false} + * 5. If no options is provided, the DefaultAWSCredentialsProviderChain is used. + * The DefaultAWSCredentialProviderChain can be pointed to credentials in many different ways: + * Working with AWS Credentials + */ + +import lombok.AccessLevel; +import lombok.Getter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import software.amazon.awssdk.auth.credentials.*; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.core.retry.RetryPolicy; +import software.amazon.awssdk.core.retry.RetryPolicyContext; +import software.amazon.awssdk.core.retry.backoff.BackoffStrategy; +import software.amazon.awssdk.core.retry.backoff.FullJitterBackoffStrategy; +import software.amazon.awssdk.core.retry.conditions.AndRetryCondition; +import software.amazon.awssdk.core.retry.conditions.MaxNumberOfRetriesCondition; +import software.amazon.awssdk.core.retry.conditions.RetryCondition; +import software.amazon.awssdk.core.retry.conditions.RetryOnExceptionsCondition; +import software.amazon.awssdk.profiles.ProfileFileSupplier; +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.endpoints.StsEndpointParams; +import software.amazon.awssdk.services.sts.endpoints.StsEndpointProvider; +import software.amazon.awssdk.services.sts.model.AssumeRoleRequest; +import software.amazon.awssdk.services.sts.model.GetCallerIdentityResponse; + +import java.net.URI; +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +public class STSAssumeRoleMSKCredentialProvider implements AwsCredentialsProvider, AutoCloseable { + private static final Logger log = LoggerFactory.getLogger(STSAssumeRoleMSKCredentialProvider.class); + private static final String AWS_PROFILE_NAME_KEY = "awsProfileName"; + private static final String AWS_ROLE_ARN_KEY = "awsRoleArn"; + private static final String AWS_ROLE_EXTERNAL_ID = "awsRoleExternalId"; + private static final String AWS_ROLE_ACCESS_KEY_ID = "awsRoleAccessKeyId"; + private static final String AWS_ROLE_SECRET_ACCESS_KEY = "awsRoleSecretAccessKey"; + private static final String AWS_ROLE_SESSION_KEY = "awsRoleSessionName"; + private static final String AWS_ROLE_SESSION_TOKEN = "awsRoleSessionToken"; + private static final String AWS_STS_REGION = "awsStsRegion"; + private static final String AWS_STS_REGION_ENDPOINT = "awsStsRegionalEndpoint"; + private static final String AWS_DEBUG_CREDS_KEY = "awsDebugCreds"; + private static final String AWS_MAX_RETRIES = "awsMaxRetries"; + private static final String AWS_MAX_BACK_OFF_TIME_MS = "awsMaxBackOffTimeMs"; + private static final String GLOBAL_REGION = "aws-global"; + private static final int DEFAULT_MAX_RETRIES = 3; + private static final int DEFAULT_MAX_BACK_OFF_TIME_MS = 5000; + private static final Duration BASE_DELAY = Duration.ofMillis(500); + private static final String AWS_ASSUME_USER_ACCESS_KEY_ID = "AWS_ACCESS_KEY_ID"; + private static final String AWS_ASSUME_USER_SECRET_ACCESS_KEY = "AWS_SECRET_ACCESS_KEY"; + + private final List closeableProviders; + private final AwsCredentialsProvider compositeDelegate; + @Getter(AccessLevel.PACKAGE) + private final Boolean shouldDebugCreds; + private final String stsRegion; + private final RetryPolicy retryPolicy; + private final String stsRegionalEndpoint; + + public STSAssumeRoleMSKCredentialProvider(Map options) { + this(new ProviderBuilder(options)); + } + + STSAssumeRoleMSKCredentialProvider(ProviderBuilder builder) { + this(builder.getProviders(), builder.shouldDebugCreds(), builder.getStsRegion(), builder.getMaxRetries(), + builder.getMaxBackOffTimeMs(), builder.getStsRegionalEndPoint()); + } + + STSAssumeRoleMSKCredentialProvider(List providers, + Boolean shouldDebugCreds, + String stsRegion, + int maxRetries, + int maxBackOffTimeMs, + String stsRegionalEndpoint) { + AwsCredentialsProviderChain.Builder chain = AwsCredentialsProviderChain.builder(); + chain.credentialsProviders(providers); + chain.addCredentialsProvider(getDefaultProvider()); + compositeDelegate = chain.build(); + closeableProviders = providers.stream() + .filter(p -> p instanceof AutoCloseable) + .map(p -> (AutoCloseable) p) + .collect(Collectors.toList()); + this.shouldDebugCreds = shouldDebugCreds; + this.stsRegion = stsRegion; + this.stsRegionalEndpoint = stsRegionalEndpoint; + BackoffStrategy backoffStrategy = FullJitterBackoffStrategy.builder() + .baseDelay(BASE_DELAY) + .maxBackoffTime(Duration.ofMillis(maxBackOffTimeMs)) + .build(); + if (maxRetries > 0) { + RetryCondition retryCondition = AndRetryCondition.create( + RetryOnExceptionsCondition.create(SdkClientException.class), + MaxNumberOfRetriesCondition.create(maxRetries) + ); + this.retryPolicy = RetryPolicy.builder() + .retryCondition(retryCondition) + .backoffStrategy(backoffStrategy) + .build(); + + } else { + this.retryPolicy = RetryPolicy.builder() + .retryCondition(RetryCondition.none()) + .backoffStrategy(backoffStrategy) + .build(); + } + } + + //We want to override the ProfileCredentialsProvider with the EnhancedProfileCredentialsProvider + protected AwsCredentialsProvider getDefaultProvider() { + return AwsCredentialsProviderChain.of( + EnvironmentVariableCredentialsProvider.create(), + SystemPropertyCredentialsProvider.create(), + WebIdentityTokenFileCredentialsProvider.builder().asyncCredentialUpdateEnabled(true).build(), + ProfileCredentialsProvider.builder().profileFile(ProfileFileSupplier.defaultSupplier()).build(), + ContainerCredentialsProvider.builder().asyncCredentialUpdateEnabled(true).build(), + InstanceProfileCredentialsProvider.builder().asyncCredentialUpdateEnabled(true).build() + ); + } + + @Override + public AwsCredentials resolveCredentials() { + AwsCredentials credentials = loadCredentialsWithRetry(); + if (credentials != null && shouldDebugCreds && log.isDebugEnabled()) { + logCallerIdentity(credentials); + } + return credentials; + } + + private AwsCredentials loadCredentialsWithRetry() { + RetryPolicyContext retryPolicyContext = RetryPolicyContext.builder().build(); + boolean shouldTry = true; + try { + while (shouldTry) { + try { + AwsCredentials credentials = compositeDelegate.resolveCredentials(); + if (credentials == null) { + throw SdkClientException.create("Composite delegate returned empty credentials."); + } + return credentials; + } catch (SdkException se) { + log.warn("Exception loading credentials. Retry Attempts: {}", + retryPolicyContext.retriesAttempted(), se); + retryPolicyContext = createRetryPolicyContext(se, retryPolicyContext.retriesAttempted()); + shouldTry = retryPolicy.retryCondition().shouldRetry(retryPolicyContext); + if (shouldTry) { + Thread.sleep(retryPolicy.backoffStrategy().computeDelayBeforeNextRetry(retryPolicyContext).toMillis()); + retryPolicyContext = createRetryPolicyContext(retryPolicyContext.exception(), + retryPolicyContext.retriesAttempted() + 1); + } else { + throw se; + } + } + } + throw SdkClientException.create( + "loadCredentialsWithRetry in unexpected location " + retryPolicyContext.totalRequests(), + retryPolicyContext.exception()); + } catch (InterruptedException ie) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Interrupted while waiting for credentials.", ie); + } + } + + private RetryPolicyContext createRetryPolicyContext(SdkException sdkException, int retriesAttempted) { + return RetryPolicyContext.builder() + .exception(sdkException) + .retriesAttempted(retriesAttempted) + .build(); + } + + private void logCallerIdentity(AwsCredentials credentials) { + try { + StsClient stsClient = getStsClientForDebuggingCreds(credentials); + GetCallerIdentityResponse response = stsClient.getCallerIdentity(); + log.debug("The identity of the credentials is {}", response.toString()); + } catch (Exception e) { + //If we run into an exception logging the caller identity, we should log the exception but + //continue running. + log.warn("Error identifying caller identity. If this is not transient, does this application have" + + "access to AWS STS?", e); + } + } + + StsClient getStsClientForDebuggingCreds(AwsCredentials credentials) { + return StsClient.builder() + .credentialsProvider(StaticCredentialsProvider.create(credentials)) + .region(Region.of(stsRegion)) + .build(); + } + + @Override + public void close() { + closeableProviders.stream().forEach(p -> { + try { + p.close(); + } catch (Exception e) { + log.warn("Error closing credential provider", e); + } + }); + } + + public static class ProviderBuilder { + private final Map optionsMap; + + public ProviderBuilder(Map optionsMap) { + this.optionsMap = optionsMap; + if (log.isDebugEnabled()) { + log.debug("Number of options to configure credential provider {}", optionsMap.size()); + } + } + + public List getProviders() { + List providers = new ArrayList<>(); + getProfileProvider().ifPresent(providers::add); + getStsRoleProvider().ifPresent(providers::add); + return providers; + } + + public Boolean shouldDebugCreds() { + return Optional.ofNullable(optionsMap.get(AWS_DEBUG_CREDS_KEY)).map(d -> d.equals("true")).orElse(false); + } + + public String getStsRegion() { + return Optional.ofNullable((String) optionsMap.get(AWS_STS_REGION)) + .orElse(GLOBAL_REGION); + } + + public String getStsRegionalEndPoint() { + return Optional.ofNullable((String) optionsMap.get(AWS_STS_REGION_ENDPOINT)) + .orElse(GLOBAL_REGION); + } + + public int getMaxRetries() { + return Optional.ofNullable(optionsMap.get(AWS_MAX_RETRIES)).map(p -> (String) p).map(Integer::parseInt) + .orElse(DEFAULT_MAX_RETRIES); + } + + public int getMaxBackOffTimeMs() { + return Optional.ofNullable(optionsMap.get(AWS_MAX_BACK_OFF_TIME_MS)).map(p -> (String) p) + .map(Integer::parseInt) + .orElse(DEFAULT_MAX_BACK_OFF_TIME_MS); + } + + public URI buildEndpointConfiguration(Region stsRegion) { + StsEndpointParams params = StsEndpointParams.builder() + .region(stsRegion) + .build(); + + try { + return StsEndpointProvider.defaultProvider() + .resolveEndpoint(params) + .get() + .url(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + public URI buildEndpointConfiguration(Region stsRegion, String stsRegionalEndpoint) { + StsEndpointParams params = StsEndpointParams.builder() + .region(stsRegion) + .endpoint(stsRegionalEndpoint) + .build(); + + log.info("Using STS Client URL in buildEndpointConfiguration :: {}" , stsRegionalEndpoint); + + try { + return StsEndpointProvider.defaultProvider() + .resolveEndpoint(params) + .get() + .url(); + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + } + + private StsClientBuilder getStsClientBuilder(Region stsRegion) { + StsClientBuilder builder = StsClient.builder().region(stsRegion); + if (stsRegion != Region.AWS_GLOBAL) { + builder.endpointOverride(buildEndpointConfiguration(stsRegion)); + } + return builder; + } + + private StsClientBuilder getStsRegionalClientBuilder(Region stsRegion, String stsRegionalEndpoint) { + StsClientBuilder builder = StsClient.builder().region(stsRegion); + if (stsRegionalEndpoint != null) { + builder.endpointOverride(buildEndpointConfiguration(stsRegion, stsRegionalEndpoint)); + } + return builder; + } + + private Optional getProfileProvider() { + return Optional.ofNullable(optionsMap.get(AWS_PROFILE_NAME_KEY)).map(p -> { + if (log.isDebugEnabled()) { + log.debug("Profile name {}", p); + } + return createEnhancedProfileCredentialsProvider((String) p); + }); + } + + ProfileCredentialsProvider createEnhancedProfileCredentialsProvider(String p) { + return ProfileCredentialsProvider.builder() + .profileName(p) + .profileFile(ProfileFileSupplier.defaultSupplier()) + .build(); + } + + private Optional getStsRoleProvider() { + return Optional.ofNullable(optionsMap.get(AWS_ROLE_ARN_KEY)).map(p -> { + if (log.isDebugEnabled()) { + log.debug("Role ARN {}", p); + } + String sessionName = Optional.ofNullable((String) optionsMap.get(AWS_ROLE_SESSION_KEY)) + .orElse("aws-msk-iam-auth"); + String stsRegion = getStsRegion(); + String stsRegionalEndPoint = getStsRegionalEndPoint(); + + + String accessKey = (String) optionsMap.getOrDefault(AWS_ROLE_ACCESS_KEY_ID, null); + String secretKey = (String) optionsMap.getOrDefault(AWS_ROLE_SECRET_ACCESS_KEY, null); + String sessionToken = (String) optionsMap.getOrDefault(AWS_ROLE_SESSION_TOKEN, null); + String externalId = (String) optionsMap.getOrDefault(AWS_ROLE_EXTERNAL_ID, null); + + if (accessKey != null && secretKey != null) { + AwsCredentialsProvider credentials = StaticCredentialsProvider.create( + sessionToken != null + ? AwsSessionCredentials.create(accessKey, secretKey, sessionToken) + : AwsBasicCredentials.create(accessKey, secretKey)); + return createSTSRoleCredentialProvider((String) p, sessionName, stsRegion, credentials); + } + + else if (externalId != null) { + return createSTSRoleCredentialProvider((String) p, externalId, sessionName, stsRegion); + } + + else if(stsRegionalEndPoint != null) { + return createSTSRoleRegionalCredentialProvider((String) p, sessionName, stsRegion, stsRegionalEndPoint); + } + + + return createSTSRoleCredentialProvider((String) p, sessionName, stsRegion); + }); + } + + StsAssumeRoleCredentialsProvider createSTSRoleRegionalCredentialProvider(String roleArn, String sessionName, String stsRegion, String stsRegionalEndPoint) { + AssumeRoleRequest roleRequest = AssumeRoleRequest.builder() + .roleArn(roleArn) + .roleSessionName(sessionName) + .build(); + StsClient stsClient = getStsRegionalClientBuilder(Region.of(stsRegion), stsRegionalEndPoint).credentialsProvider(StaticCredentialsProvider.create(new AwsCredentials() { + @Override + public String accessKeyId() { + return System.getenv(AWS_ASSUME_USER_ACCESS_KEY_ID); + } + @Override + public String secretAccessKey() { + return System.getenv(AWS_ASSUME_USER_SECRET_ACCESS_KEY); + } + })).build(); + + return StsAssumeRoleCredentialsProvider.builder() + .stsClient(stsClient) + .refreshRequest(roleRequest) + .asyncCredentialUpdateEnabled(true) + .build(); + } + + StsAssumeRoleCredentialsProvider createSTSRoleCredentialProvider( + String roleArn, + String sessionName, + String stsRegion) { + AssumeRoleRequest roleRequest = AssumeRoleRequest.builder() + .roleArn(roleArn) + .roleSessionName(sessionName) + .build(); + StsClient stsClient = getStsClientBuilder(Region.of(stsRegion)) + .build(); + return StsAssumeRoleCredentialsProvider.builder() + .stsClient(stsClient) + .refreshRequest(roleRequest) + .asyncCredentialUpdateEnabled(true) + .build(); + } + + StsAssumeRoleCredentialsProvider createSTSRoleCredentialProvider( + String roleArn, + String sessionName, String stsRegion, + AwsCredentialsProvider credentials) { + AssumeRoleRequest roleRequest = AssumeRoleRequest.builder() + .roleArn(roleArn) + .roleSessionName(sessionName) + .build(); + StsClient stsClient = getStsClientBuilder(Region.of(stsRegion)) + .credentialsProvider(credentials) + .build(); + return StsAssumeRoleCredentialsProvider.builder() + .stsClient(stsClient) + .refreshRequest(roleRequest) + .asyncCredentialUpdateEnabled(true) + .build(); + } + + StsAssumeRoleCredentialsProvider createSTSRoleCredentialProvider( + String roleArn, + String externalId, + String sessionName, + String stsRegion) { + AssumeRoleRequest roleRequest = AssumeRoleRequest.builder() + .externalId(externalId) + .roleArn(roleArn) + .roleSessionName(sessionName) + .build(); + return StsAssumeRoleCredentialsProvider.builder() + .stsClient(getStsClientBuilder(Region.of(stsRegion)).build()) + .refreshRequest(roleRequest) + .asyncCredentialUpdateEnabled(true) + .build(); + } + } +} diff --git a/src/test/java/software/amazon/msk/auth/iam/STSAssumeRoleIAMClientCallbackHandlerTest.java b/src/test/java/software/amazon/msk/auth/iam/STSAssumeRoleIAMClientCallbackHandlerTest.java new file mode 100644 index 0000000..0a11fd4 --- /dev/null +++ b/src/test/java/software/amazon/msk/auth/iam/STSAssumeRoleIAMClientCallbackHandlerTest.java @@ -0,0 +1,62 @@ +package software.amazon.msk.auth.iam; + +import org.junit.jupiter.api.Test; +import software.amazon.msk.auth.iam.internals.AWSCredentialsCallback; +import software.amazon.msk.auth.iam.internals.SystemPropertyCredentialsUtils; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; +import java.io.IOException; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.*; + +public class STSAssumeRoleIAMClientCallbackHandlerTest { + + private static final String ACCESS_KEY_VALUE = "ACCESS_KEY_VALUE"; + private static final String SECRET_KEY_VALUE = "SECRET_KEY_VALUE"; + + @Test + public void testDefaultCredentials() throws IOException, UnsupportedCallbackException { + STSAssumeRoleIAMClientCallbackHandler clientCallbackHandler = new STSAssumeRoleIAMClientCallbackHandler(); + clientCallbackHandler.configure(Collections.emptyMap(), "AWS_MSK_IAM", Collections.emptyList()); + SystemPropertyCredentialsUtils.runTestWithSystemPropertyCredentials(() -> { + AWSCredentialsCallback callback = new AWSCredentialsCallback(); + try { + clientCallbackHandler.handle(new Callback[]{callback}); + } catch (Exception e) { + throw new RuntimeException("Test failed", e); + } + + assertTrue(callback.isSuccessful()); + assertEquals(ACCESS_KEY_VALUE, callback.getAwsCredentials().accessKeyId()); + assertEquals(SECRET_KEY_VALUE, callback.getAwsCredentials().secretAccessKey()); + }, ACCESS_KEY_VALUE, SECRET_KEY_VALUE); + } + + @Test + public void testDifferentMechanism() { + STSAssumeRoleIAMClientCallbackHandler clientCallbackHandler = new STSAssumeRoleIAMClientCallbackHandler(); + assertThrows(IllegalArgumentException.class, () -> clientCallbackHandler + .configure(Collections.emptyMap(), "SOME_OTHER_MECHANISM", Collections.emptyList())); + } + + @Test + public void testDifferentCallback() { + STSAssumeRoleIAMClientCallbackHandler clientCallbackHandler = new STSAssumeRoleIAMClientCallbackHandler(); + UnsupportedCallbackException callbackException = assertThrows(UnsupportedCallbackException.class, + () -> clientCallbackHandler.handle(new Callback[]{new Callback() { + }})); + assertTrue(callbackException.getMessage().startsWith("Unsupported")); + } + + @Test + public void testDebugClassString() { + String debug1 = STSAssumeRoleIAMClientCallbackHandler.debugClassString(this.getClass()); + assertTrue(debug1.contains("software.amazon.msk.auth.iam.STSAssumeRoleIAMClientCallbackHandlerTest")); + STSAssumeRoleIAMClientCallbackHandler clientCallbackHandler = new STSAssumeRoleIAMClientCallbackHandler(); + String debug2 = STSAssumeRoleIAMClientCallbackHandler.debugClassString(clientCallbackHandler.getClass()); + assertTrue(debug2.contains("software.amazon.msk.auth.iam.STSAssumeRoleIAMClientCallbackHandler")); + } + +} \ No newline at end of file diff --git a/src/test/java/software/amazon/msk/auth/iam/internals/STSAssumeRoleMSKCredentialProviderTest.java b/src/test/java/software/amazon/msk/auth/iam/internals/STSAssumeRoleMSKCredentialProviderTest.java new file mode 100644 index 0000000..38f8ddb --- /dev/null +++ b/src/test/java/software/amazon/msk/auth/iam/internals/STSAssumeRoleMSKCredentialProviderTest.java @@ -0,0 +1,837 @@ +/* + Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + + 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.msk.auth.iam.internals; + +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import software.amazon.awssdk.auth.credentials.*; +import software.amazon.awssdk.core.exception.SdkClientException; +import software.amazon.awssdk.core.exception.SdkException; +import software.amazon.awssdk.profiles.ProfileFile; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.sts.StsClient; +import software.amazon.awssdk.services.sts.auth.StsAssumeRoleCredentialsProvider; +import software.amazon.awssdk.services.sts.model.GetCallerIdentityResponse; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URL; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.times; +import static software.amazon.msk.auth.iam.internals.SystemPropertyCredentialsUtils.runTestWithSystemPropertyCredentials; +import static software.amazon.msk.auth.iam.internals.SystemPropertyCredentialsUtils.runTestWithSystemPropertyProfile; + +public class STSAssumeRoleMSKCredentialProviderTest { + private static final String ACCESS_KEY_VALUE = "ACCESS_KEY_VALUE"; + private static final String SECRET_KEY_VALUE = "SECRET_KEY_VALUE"; + private static final String ACCESS_KEY_VALUE_TWO = "ACCESS_KEY_VALUE_TWO"; + private static final String SECRET_KEY_VALUE_TWO = "SECRET_KEY_VALUE_TWO"; + private static final String TEST_PROFILE_NAME = "test_profile"; + private static final String PROFILE_ACCESS_KEY_VALUE = "PROFILE_ACCESS_KEY"; + private static final String PROFILE_SECRET_KEY_VALUE = "PROFILE_SECRET_KEY"; + private static final String TEST_ROLE_ARN = "TEST_ROLE_ARN"; + private static final String TEST_ROLE_EXTERNAL_ID = "TEST_EXTERNAL_ID"; + private static final String TEST_ROLE_SESSION_NAME = "TEST_ROLE_SESSION_NAME"; + private static final String SESSION_TOKEN = "SESSION_TOKEN"; + private static final String AWS_ROLE_ARN = "awsRoleArn"; + private static final String AWS_ROLE_EXTERNAL_ID = "awsRoleExternalId"; + private static final String AWS_ROLE_ACCESS_KEY_ID = "awsRoleAccessKeyId"; + private static final String AWS_ROLE_SECRET_ACCESS_KEY = "awsRoleSecretAccessKey"; + private static final String AWS_PROFILE_NAME = "awsProfileName"; + private static final String AWS_DEBUG_CREDS_NAME = "awsDebugCreds"; + private static final String AWS_STS_REGION_ENDPOINT = "awsStsRegionalEndpoint"; + /** + * If no options are passed in it should use the default credentials provider + * which should pick up the java system properties. + */ + @Test + public void testNoOptions() { + runDefaultTest(); + } + + private void runDefaultTest() { + runTestWithSystemPropertyCredentials(() -> { + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(Collections.emptyMap()); + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + + assertEquals(ACCESS_KEY_VALUE, credentials.accessKeyId()); + assertEquals(SECRET_KEY_VALUE, credentials.secretAccessKey()); + }, ACCESS_KEY_VALUE, SECRET_KEY_VALUE); + } + + /** + * If a profile name is passed in but there is no profile by that name + * it should still use the default credential provider. + */ + @Test + public void testMissingProfileName() { + runTestWithSystemPropertyCredentials(() -> { + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_PROFILE_NAME, "MISSING_PROFILE"); + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(optionsMap); + + AwsCredentials credentials = provider.resolveCredentials(); + + assertEquals(ACCESS_KEY_VALUE, credentials.accessKeyId()); + assertEquals(SECRET_KEY_VALUE, credentials.secretAccessKey()); + }, ACCESS_KEY_VALUE, SECRET_KEY_VALUE); + } + + /** + * If the credentials available to the default credential provider change, + * the new credentials should be picked up. + * + * @throws IOException + */ + @Test + public void testChangingCredentials() throws IOException { + runDefaultTest(); + + runTestWithSystemPropertyProfile(() -> { + ProfileFile profileFile = getProfileFile(); + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(Collections.emptyMap()) { + protected AwsCredentialsProvider getDefaultProvider() { + return AwsCredentialsProviderChain.of( + EnvironmentVariableCredentialsProvider.create(), + SystemPropertyCredentialsProvider.create(), + WebIdentityTokenFileCredentialsProvider.create(), + ProfileCredentialsProvider.builder().profileFile(profileFile).build(), + ContainerCredentialsProvider.builder().build(), + InstanceProfileCredentialsProvider.create() + ); + } + }; + + AwsCredentials credentials = provider.resolveCredentials(); + + assertEquals(PROFILE_ACCESS_KEY_VALUE, credentials.accessKeyId()); + assertEquals(PROFILE_SECRET_KEY_VALUE, credentials.secretAccessKey()); + }, TEST_PROFILE_NAME); + } + + @Test + public void testProfileName() { + ProfileFile profileFile = getProfileFile(); + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_PROFILE_NAME, "test_profile"); + STSAssumeRoleMSKCredentialProvider.ProviderBuilder providerBuilder = new STSAssumeRoleMSKCredentialProvider.ProviderBuilder(optionsMap) { + ProfileCredentialsProvider createEnhancedProfileCredentialsProvider(String profileName) { + assertEquals(TEST_PROFILE_NAME, profileName); + return ProfileCredentialsProvider.builder() + .profileFile(profileFile) + .profileName(TEST_PROFILE_NAME) + .build(); + } + }; + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(providerBuilder); + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + assertEquals(PROFILE_ACCESS_KEY_VALUE, credentials.accessKeyId()); + assertEquals(PROFILE_SECRET_KEY_VALUE, credentials.secretAccessKey()); + } + + @Test + public void testAwsRoleArn() { + StsAssumeRoleCredentialsProvider mockStsRoleProvider = Mockito + .mock(StsAssumeRoleCredentialsProvider.class); + Mockito.when(mockStsRoleProvider.resolveIdentity()) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsSessionCredentials.create(ACCESS_KEY_VALUE, SECRET_KEY_VALUE, SESSION_TOKEN))); + + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_ROLE_ARN, TEST_ROLE_ARN); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + + STSAssumeRoleMSKCredentialProvider.ProviderBuilder providerBuilder = getProviderBuilder(mockStsRoleProvider, optionsMap, + "aws-msk-iam-auth"); + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(providerBuilder); + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + validateBasicSessionCredentials(credentials); + + provider.close(); + Mockito.verify(mockStsRoleProvider, times(1)).close(); + } + + @Test + public void testAwsRoleArnWithAccessKey() { + StsAssumeRoleCredentialsProvider mockStsRoleProvider = Mockito + .mock(StsAssumeRoleCredentialsProvider.class); + Mockito.when(mockStsRoleProvider.resolveIdentity()) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsSessionCredentials.create(ACCESS_KEY_VALUE_TWO, SECRET_KEY_VALUE_TWO, SESSION_TOKEN))); + + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_ROLE_ARN, TEST_ROLE_ARN); + optionsMap.put(AWS_ROLE_ACCESS_KEY_ID, ACCESS_KEY_VALUE_TWO); + optionsMap.put(AWS_ROLE_SECRET_ACCESS_KEY, SECRET_KEY_VALUE_TWO); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + STSAssumeRoleMSKCredentialProvider.ProviderBuilder providerBuilder = getProviderBuilderWithCredentials(mockStsRoleProvider, optionsMap, + "aws-msk-iam-auth"); + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(providerBuilder); + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + validateBasicSessionCredentialsTwo(credentials); + + provider.close(); + Mockito.verify(mockStsRoleProvider, times(1)).close(); + } + + @Test + public void testAwsRoleArnWithDebugCreds() { + StsAssumeRoleCredentialsProvider mockStsRoleProvider = Mockito + .mock(StsAssumeRoleCredentialsProvider.class); + Mockito.when(mockStsRoleProvider.resolveIdentity()) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsSessionCredentials.create(ACCESS_KEY_VALUE, SECRET_KEY_VALUE, SESSION_TOKEN))); + + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_ROLE_ARN, TEST_ROLE_ARN); + optionsMap.put(AWS_DEBUG_CREDS_NAME, "true"); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + STSAssumeRoleMSKCredentialProvider.ProviderBuilder providerBuilder = getProviderBuilder(mockStsRoleProvider, optionsMap, + "aws-msk-iam-auth"); + + StsClient mockSts = Mockito.mock(StsClient.class); + Mockito.when(mockSts.getCallerIdentity()).thenReturn(GetCallerIdentityResponse.builder().userId("TEST_USER_ID").account("TEST_ACCOUNT").arn("TEST_ARN").build()); + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(providerBuilder) { + StsClient getStsClientForDebuggingCreds(AwsCredentials credentials) { + return mockSts; + } + }; + + assertTrue(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + validateBasicSessionCredentials(credentials); + + provider.close(); + Mockito.verify(mockStsRoleProvider, times(1)).close(); + Mockito.verify(mockSts, times(1)).getCallerIdentity(); + } + + @Test + public void testEcsCredsWithDebugCredsNoAccessToSts_Succeed() { + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_DEBUG_CREDS_NAME, "true"); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + + ContainerCredentialsProvider mockEcsCredsProvider = Mockito.mock(ContainerCredentialsProvider.class); + Mockito.when(mockEcsCredsProvider.resolveIdentity()) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsBasicCredentials.create(ACCESS_KEY_VALUE_TWO, SECRET_KEY_VALUE_TWO))); + + StsClient mockSts = Mockito.mock(StsClient.class); + Mockito.when(mockSts.getCallerIdentity()) + .thenThrow(SdkClientException.create("TEST TEST")); + + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(optionsMap) { + protected AwsCredentialsProvider getDefaultProvider() { + return mockEcsCredsProvider; + } + + StsClient getStsClientForDebuggingCreds(AwsCredentials credentials) { + return mockSts; + } + }; + assertTrue(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + + validateBasicCredentialsTwo(credentials); + + provider.close(); + Mockito.verify(mockSts, times(1)).getCallerIdentity(); + Mockito.verify(mockEcsCredsProvider, times(1)).resolveIdentity(); + Mockito.verifyNoMoreInteractions(mockEcsCredsProvider); + } + + @Test + public void testEc2CredsWithDebugCredsNoAccessToSts_Succeed() { + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_DEBUG_CREDS_NAME, "true"); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + + InstanceProfileCredentialsProvider mockEc2CredsProvider = Mockito.mock(InstanceProfileCredentialsProvider.class); + Mockito.when(mockEc2CredsProvider.resolveIdentity()) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsBasicCredentials.create(ACCESS_KEY_VALUE_TWO, SECRET_KEY_VALUE_TWO))); + + StsClient mockSts = Mockito.mock(StsClient.class); + Mockito.when(mockSts.getCallerIdentity()) + .thenThrow(SdkClientException.create("TEST TEST")); + + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(optionsMap) { + protected AwsCredentialsProvider getDefaultProvider() { + return mockEc2CredsProvider; + } + + StsClient getStsClientForDebuggingCreds(AwsCredentials credentials) { + return mockSts; + } + }; + assertTrue(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + + validateBasicCredentialsTwo(credentials); + + provider.close(); + Mockito.verify(mockSts, times(1)).getCallerIdentity(); + Mockito.verify(mockEc2CredsProvider, times(1)).resolveIdentity(); + Mockito.verifyNoMoreInteractions(mockEc2CredsProvider); + } + + @Test + public void testAwsRoleArnAndSessionName() { + StsAssumeRoleCredentialsProvider mockStsRoleProvider = Mockito + .mock(StsAssumeRoleCredentialsProvider.class); + Mockito.when(mockStsRoleProvider.resolveIdentity()) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsSessionCredentials.create(ACCESS_KEY_VALUE, SECRET_KEY_VALUE, SESSION_TOKEN))); + + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_ROLE_ARN, TEST_ROLE_ARN); + optionsMap.put("awsRoleSessionName", TEST_ROLE_SESSION_NAME); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + STSAssumeRoleMSKCredentialProvider.ProviderBuilder providerBuilder = getProviderBuilder(mockStsRoleProvider, optionsMap, + TEST_ROLE_SESSION_NAME); + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(providerBuilder); + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + validateBasicSessionCredentials(credentials); + + provider.close(); + Mockito.verify(mockStsRoleProvider, times(1)).close(); + } + + @Test + public void testAwsRoleArnSessionNameAndStsRegion() { + StsAssumeRoleCredentialsProvider mockStsRoleProvider = Mockito + .mock(StsAssumeRoleCredentialsProvider.class); + Mockito.when(mockStsRoleProvider.resolveIdentity()) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsSessionCredentials.create(ACCESS_KEY_VALUE, SECRET_KEY_VALUE, SESSION_TOKEN))); + + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_ROLE_ARN, TEST_ROLE_ARN); + optionsMap.put("awsRoleSessionName", TEST_ROLE_SESSION_NAME); + optionsMap.put("awsStsRegion", "eu-west-1"); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + STSAssumeRoleMSKCredentialProvider.ProviderBuilder providerBuilder = new STSAssumeRoleMSKCredentialProvider.ProviderBuilder(optionsMap) { + StsAssumeRoleCredentialsProvider createSTSRoleRegionalCredentialProvider(String roleArn, + String sessionName, String stsRegion, String stsRegionalEndpoint) { + assertEquals(TEST_ROLE_ARN, roleArn); + assertEquals(TEST_ROLE_SESSION_NAME, sessionName); + assertEquals("eu-west-1", stsRegion); + URI endpointConfiguration = buildEndpointConfiguration(Region.of(stsRegion), stsRegionalEndpoint); + assertEquals("https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com", endpointConfiguration.toString()); + return mockStsRoleProvider; + } + }; + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(providerBuilder); + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + validateBasicSessionCredentials(credentials); + + provider.close(); + Mockito.verify(mockStsRoleProvider, times(1)).close(); + } + + @Test + public void testAwsRoleArnSessionNameStsRegionAndRegionalEndpoint() { + StsAssumeRoleCredentialsProvider mockStsRoleProvider = Mockito + .mock(StsAssumeRoleCredentialsProvider.class); + Mockito.when(mockStsRoleProvider.resolveIdentity()) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsSessionCredentials.create(ACCESS_KEY_VALUE, SECRET_KEY_VALUE, SESSION_TOKEN))); + + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_ROLE_ARN, TEST_ROLE_ARN); + optionsMap.put("awsRoleSessionName", TEST_ROLE_SESSION_NAME); + optionsMap.put("awsStsRegion", "us-east-1"); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + STSAssumeRoleMSKCredentialProvider.ProviderBuilder providerBuilder = new STSAssumeRoleMSKCredentialProvider.ProviderBuilder(optionsMap) { + StsAssumeRoleCredentialsProvider createSTSRoleRegionalCredentialProvider(String roleArn, + String sessionName, + String stsRegion, + String stsRegionalEndpoint) { + assertEquals(TEST_ROLE_ARN, roleArn); + assertEquals("https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com", stsRegionalEndpoint); + assertEquals(TEST_ROLE_SESSION_NAME, sessionName); + assertEquals("us-east-1", stsRegion); + URI endpointConfiguration = buildEndpointConfiguration(Region.of(stsRegion), stsRegionalEndpoint); + assertEquals("https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com", endpointConfiguration.toString()); + return mockStsRoleProvider; + } + }; + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(providerBuilder); + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + validateBasicSessionCredentials(credentials); + + provider.close(); + Mockito.verify(mockStsRoleProvider, times(1)).close(); + } + + @Test + public void testAwsRoleArnSessionNameStsRegionAndExternalId() { + StsAssumeRoleCredentialsProvider mockStsRoleProvider = Mockito + .mock(StsAssumeRoleCredentialsProvider.class); + Mockito.when(mockStsRoleProvider.resolveIdentity()) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsSessionCredentials.create(ACCESS_KEY_VALUE, SECRET_KEY_VALUE, SESSION_TOKEN))); + + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_ROLE_ARN, TEST_ROLE_ARN); + optionsMap.put(AWS_ROLE_EXTERNAL_ID, TEST_ROLE_EXTERNAL_ID); + optionsMap.put("awsRoleSessionName", TEST_ROLE_SESSION_NAME); + optionsMap.put("awsStsRegion", "eu-west-1"); + + STSAssumeRoleMSKCredentialProvider.ProviderBuilder providerBuilder = new STSAssumeRoleMSKCredentialProvider.ProviderBuilder(optionsMap) { + StsAssumeRoleCredentialsProvider createSTSRoleCredentialProvider(String roleArn, + String externalId, + String sessionName, + String stsRegion) { + assertEquals(TEST_ROLE_ARN, roleArn); + assertEquals(TEST_ROLE_EXTERNAL_ID, externalId); + assertEquals(TEST_ROLE_SESSION_NAME, sessionName); + assertEquals("eu-west-1", stsRegion); + URI endpointConfiguration = buildEndpointConfiguration(Region.of(stsRegion)); + assertEquals("https://sts.eu-west-1.amazonaws.com", endpointConfiguration.toString()); + return mockStsRoleProvider; + } + }; + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(providerBuilder); + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + validateBasicSessionCredentials(credentials); + + provider.close(); + Mockito.verify(mockStsRoleProvider, times(1)).close(); + } + + @Test + public void testAwsRoleArnSessionNameStsRegionAndStsRegionalEndPoint() { + StsAssumeRoleCredentialsProvider mockStsRoleProvider = Mockito + .mock(StsAssumeRoleCredentialsProvider.class); + Mockito.when(mockStsRoleProvider.resolveIdentity()) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsSessionCredentials.create(ACCESS_KEY_VALUE, SECRET_KEY_VALUE, SESSION_TOKEN))); + + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_ROLE_ARN, TEST_ROLE_ARN); + optionsMap.put("awsRoleSessionName", TEST_ROLE_SESSION_NAME); + optionsMap.put("awsStsRegion", "us-east-1"); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + STSAssumeRoleMSKCredentialProvider.ProviderBuilder providerBuilder = new STSAssumeRoleMSKCredentialProvider.ProviderBuilder(optionsMap) { + StsAssumeRoleCredentialsProvider createSTSRoleRegionalCredentialProvider(String roleArn, + String sessionName, + String stsRegion, + String stsRegionalEndpoint) { + assertEquals(TEST_ROLE_ARN, roleArn); + assertEquals(TEST_ROLE_SESSION_NAME, sessionName); + assertEquals("us-east-1", stsRegion); + URI endpointConfiguration = buildEndpointConfiguration(Region.of(stsRegion), stsRegionalEndpoint); + assertEquals("https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com", endpointConfiguration.toString()); + return mockStsRoleProvider; + } + }; + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(providerBuilder); + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + validateBasicSessionCredentials(credentials); + + provider.close(); + Mockito.verify(mockStsRoleProvider, times(1)).close(); + } + + @Test + public void testProfileNameAndRoleArn() { + ProfileFile profileFile = getProfileFile(); + StsAssumeRoleCredentialsProvider mockStsRoleProvider = Mockito + .mock(StsAssumeRoleCredentialsProvider.class); + Mockito.when(mockStsRoleProvider.resolveIdentity()) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsSessionCredentials.create(ACCESS_KEY_VALUE_TWO, SECRET_KEY_VALUE_TWO, SESSION_TOKEN))); + + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_PROFILE_NAME, "test_profile"); + optionsMap.put(AWS_ROLE_ARN, TEST_ROLE_ARN); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + STSAssumeRoleMSKCredentialProvider.ProviderBuilder providerBuilder = new STSAssumeRoleMSKCredentialProvider.ProviderBuilder(optionsMap) { + ProfileCredentialsProvider createEnhancedProfileCredentialsProvider(String profileName) { + assertEquals(TEST_PROFILE_NAME, profileName); + return ProfileCredentialsProvider.builder().profileFile(profileFile) + .profileName(TEST_PROFILE_NAME) + .build(); + } + + StsAssumeRoleCredentialsProvider createSTSRoleRegionalCredentialProvider(String roleArn, + String sessionName, String stsRegion, String stsRegionalEndpoint) { + assertEquals(TEST_ROLE_ARN, roleArn); + assertEquals("aws-msk-iam-auth", sessionName); + assertEquals("https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com", stsRegionalEndpoint); + + return mockStsRoleProvider; + } + }; + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(providerBuilder); + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + provider.close(); + + assertEquals(PROFILE_ACCESS_KEY_VALUE, credentials.accessKeyId()); + assertEquals(PROFILE_SECRET_KEY_VALUE, credentials.secretAccessKey()); + Mockito.verify(mockStsRoleProvider, times(0)).resolveCredentials(); + Mockito.verify(mockStsRoleProvider, times(1)).close(); + } + + @Test + public void testRoleCredsWithTwoRetriableErrors() { + testRoleCredsWithRetriableErrors(2); + } + + @Test + public void testRoleCredsWithThreeRetriableErrors() { + testRoleCredsWithRetriableErrors(3); + } + + @Test + public void testRoleCredsWithFourRetriableErrors_ThrowsException() { + int numExceptions = 4; + StsAssumeRoleCredentialsProvider mockStsRoleProvider = setupMockStsRoleCredentialsProviderWithRetriableExceptions(numExceptions); + + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_ROLE_ARN, TEST_ROLE_ARN); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + STSAssumeRoleMSKCredentialProvider.ProviderBuilder providerBuilder = getProviderBuilder(mockStsRoleProvider, optionsMap, + "aws-msk-iam-auth"); + + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(providerBuilder) { + protected AwsCredentialsProvider getDefaultProvider() { + return EnvironmentVariableCredentialsProvider.create(); + } + }; + assertFalse(provider.getShouldDebugCreds()); + + assertThrows(SdkClientException.class, () -> provider.resolveCredentials()); + + Mockito.verify(mockStsRoleProvider, times(numExceptions)).resolveIdentity(); + Mockito.verifyNoMoreInteractions(mockStsRoleProvider); + } + + @Test + public void testEc2CredsWithTwoRetriableErrorsCustomRetry() { + testEc2CredsWithRetriableErrorsCustomRetry(2); + } + + @Test + public void testEc2CredsWithFiveRetriableErrorsCustomRetry() { + testEc2CredsWithRetriableErrorsCustomRetry(5); + } + + @Test + public void testEc2CredsWithSixRetriableErrorsCustomRetry_ThrowsException() { + int numExceptions = 6; + Map optionsMap = new HashMap<>(); + optionsMap.put("awsMaxRetries", "5"); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + AwsCredentialsProvider mockEc2CredsProvider = setupMockEc2DefaultProviderWithRetriableExceptions(numExceptions); + + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(optionsMap) { + protected AwsCredentialsProvider getDefaultProvider() { + return mockEc2CredsProvider; + } + }; + assertFalse(provider.getShouldDebugCreds()); + + assertThrows(SdkClientException.class, () -> provider.resolveCredentials()); + + Mockito.verify(mockEc2CredsProvider, times(numExceptions)).resolveIdentity(); + Mockito.verifyNoMoreInteractions(mockEc2CredsProvider); + } + + @Test + public void testEc2CredsWithOnrRetriableErrorsCustomZeroRetry_ThrowsException() { + int numExceptions = 1; + Map optionsMap = new HashMap<>(); + optionsMap.put("awsMaxRetries", "0"); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + AwsCredentialsProvider mockEc2CredsProvider = setupMockEc2DefaultProviderWithRetriableExceptions(numExceptions); + + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(optionsMap) { + protected AwsCredentialsProvider getDefaultProvider() { + return mockEc2CredsProvider; + } + }; + assertFalse(provider.getShouldDebugCreds()); + + assertThrows(SdkClientException.class, () -> provider.resolveCredentials()); + + Mockito.verify(mockEc2CredsProvider, times(numExceptions)).resolveIdentity(); + Mockito.verifyNoMoreInteractions(mockEc2CredsProvider); + } + + private void testEc2CredsWithRetriableErrorsCustomRetry(int numExceptions) { + Map optionsMap = new HashMap<>(); + optionsMap.put("awsMaxRetries", "5"); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + AwsCredentialsProvider mockEc2CredsProvider = setupMockEc2DefaultProviderWithRetriableExceptions(numExceptions); + + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(optionsMap) { + protected AwsCredentialsProvider getDefaultProvider() { + return mockEc2CredsProvider; + } + }; + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + + validateBasicCredentialsTwo(credentials); + + provider.close(); + Mockito.verify(mockEc2CredsProvider, times(numExceptions + 1)).resolveIdentity(); + Mockito.verifyNoMoreInteractions(mockEc2CredsProvider); + } + + @Test + public void testEcsCredsWithSixRetriableErrorsCustomRetry_ThrowsException() { + int numExceptions = 6; + Map optionsMap = new HashMap<>(); + optionsMap.put("awsMaxRetries", "5"); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + AwsCredentialsProvider mockEcsCredsProvider = setupMockEcsDefaultProviderWithRetriableExceptions(numExceptions); + + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(optionsMap) { + protected AwsCredentialsProvider getDefaultProvider() { + return mockEcsCredsProvider; + } + }; + assertFalse(provider.getShouldDebugCreds()); + + assertThrows(SdkClientException.class, () -> provider.resolveCredentials()); + + Mockito.verify(mockEcsCredsProvider, times(numExceptions)).resolveIdentity(); + Mockito.verifyNoMoreInteractions(mockEcsCredsProvider); + } + + @Test + public void testEcsCredsWithOnrRetriableErrorsCustomZeroRetry_ThrowsException() { + int numExceptions = 1; + Map optionsMap = new HashMap<>(); + optionsMap.put("awsMaxRetries", "0"); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + AwsCredentialsProvider mockEcsCredsProvider = setupMockEcsDefaultProviderWithRetriableExceptions(numExceptions); + + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(optionsMap) { + protected AwsCredentialsProvider getDefaultProvider() { + return mockEcsCredsProvider; + } + }; + assertFalse(provider.getShouldDebugCreds()); + + assertThrows(SdkClientException.class, () -> provider.resolveCredentials()); + + Mockito.verify(mockEcsCredsProvider, times(numExceptions)).resolveIdentity(); + Mockito.verifyNoMoreInteractions(mockEcsCredsProvider); + } + + private void testEcsCredsWithRetriableErrorsCustomRetry(int numExceptions) { + Map optionsMap = new HashMap<>(); + optionsMap.put("awsMaxRetries", "5"); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + AwsCredentialsProvider mockEcsCredsProvider = setupMockEcsDefaultProviderWithRetriableExceptions(numExceptions); + + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(optionsMap) { + protected AwsCredentialsProvider getDefaultProvider() { + return mockEcsCredsProvider; + } + }; + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + + validateBasicCredentialsTwo(credentials); + + provider.close(); + Mockito.verify(mockEcsCredsProvider, times(numExceptions + 1)).resolveIdentity(); + Mockito.verifyNoMoreInteractions(mockEcsCredsProvider); + } + + private void testRoleCredsWithRetriableErrors(int numExceptions) { + StsAssumeRoleCredentialsProvider mockStsRoleProvider = setupMockStsRoleCredentialsProviderWithRetriableExceptions( + numExceptions); + + Map optionsMap = new HashMap<>(); + optionsMap.put(AWS_ROLE_ARN, TEST_ROLE_ARN); + optionsMap.put(AWS_STS_REGION_ENDPOINT, "https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com"); + + STSAssumeRoleMSKCredentialProvider.ProviderBuilder providerBuilder = getProviderBuilder(mockStsRoleProvider, optionsMap, + "aws-msk-iam-auth"); + + STSAssumeRoleMSKCredentialProvider provider = new STSAssumeRoleMSKCredentialProvider(providerBuilder) { + protected AwsCredentialsProvider getDefaultProvider() { + return EnvironmentVariableCredentialsProvider.create(); + } + }; + assertFalse(provider.getShouldDebugCreds()); + + AwsCredentials credentials = provider.resolveCredentials(); + validateBasicSessionCredentials(credentials); + + provider.close(); + Mockito.verify(mockStsRoleProvider, times(numExceptions + 1)).resolveIdentity(); + Mockito.verify(mockStsRoleProvider, times(1)).close(); + Mockito.verifyNoMoreInteractions(mockStsRoleProvider); + } + + private STSAssumeRoleMSKCredentialProvider.ProviderBuilder getProviderBuilder(StsAssumeRoleCredentialsProvider mockStsRoleProvider, + Map optionsMap, String s) { + return new STSAssumeRoleMSKCredentialProvider.ProviderBuilder(optionsMap) { + StsAssumeRoleCredentialsProvider createSTSRoleRegionalCredentialProvider(String roleArn, + String sessionName, String stsRegion, String stsRegionalEndpoint) { + assertEquals(TEST_ROLE_ARN, roleArn); + assertEquals(s, sessionName); + assertEquals("https://vpce-4kujcrex.sts.us-east-1.vpce.amazonaws.com", stsRegionalEndpoint); + + return mockStsRoleProvider; + } + }; + } + + private STSAssumeRoleMSKCredentialProvider.ProviderBuilder getProviderBuilderWithCredentials(StsAssumeRoleCredentialsProvider mockStsRoleProvider, + Map optionsMap, String s) { + return new STSAssumeRoleMSKCredentialProvider.ProviderBuilder(optionsMap) { + StsAssumeRoleCredentialsProvider createSTSRoleCredentialProvider(String roleArn, + String sessionName, String stsRegion, + AwsCredentialsProvider credentials) { + assertEquals(TEST_ROLE_ARN, roleArn); + assertEquals(s, sessionName); + return mockStsRoleProvider; + } + }; + } + + private STSAssumeRoleMSKCredentialProvider.ProviderBuilder getProviderBuilderWithStsRegionalCredentials(StsAssumeRoleCredentialsProvider mockStsRoleProvider, + Map optionsMap, String s) { + return new STSAssumeRoleMSKCredentialProvider.ProviderBuilder(optionsMap) { + StsAssumeRoleCredentialsProvider createSTSRoleRegionalCredentialProvider(String roleArn, + String sessionName, String stsRegion, + String stsRegionalEndpoint) { + assertEquals(TEST_ROLE_ARN, roleArn); + assertEquals(s, sessionName); + return mockStsRoleProvider; + } + }; + } + + private void validateBasicSessionCredentials(AwsCredentials credentials) { + assertTrue(credentials instanceof AwsSessionCredentials); + AwsSessionCredentials sessionCredentials = (AwsSessionCredentials) credentials; + assertEquals(ACCESS_KEY_VALUE, sessionCredentials.accessKeyId()); + assertEquals(SECRET_KEY_VALUE, sessionCredentials.secretAccessKey()); + assertEquals(SESSION_TOKEN, sessionCredentials.sessionToken()); + } + + private void validateBasicSessionCredentialsTwo(AwsCredentials credentials) { + assertTrue(credentials instanceof AwsSessionCredentials); + AwsSessionCredentials sessionCredentials = (AwsSessionCredentials) credentials; + assertEquals(ACCESS_KEY_VALUE_TWO, sessionCredentials.accessKeyId()); + assertEquals(SECRET_KEY_VALUE_TWO, sessionCredentials.secretAccessKey()); + assertEquals(SESSION_TOKEN, sessionCredentials.sessionToken()); + } + + private void validateBasicCredentialsTwo(AwsCredentials credentials) { + assertTrue(credentials instanceof AwsBasicCredentials); + assertEquals(ACCESS_KEY_VALUE_TWO, credentials.accessKeyId()); + assertEquals(SECRET_KEY_VALUE_TWO, credentials.secretAccessKey()); + } + + private StsAssumeRoleCredentialsProvider setupMockStsRoleCredentialsProviderWithRetriableExceptions(int numErrors) { + SdkException[] exceptionsToThrow = getSdkBaseExceptions(numErrors); + + StsAssumeRoleCredentialsProvider mockStsRoleProvider = Mockito + .mock(StsAssumeRoleCredentialsProvider.class); + Mockito.when(mockStsRoleProvider.resolveIdentity()) + .thenThrow(exceptionsToThrow) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsSessionCredentials.create(ACCESS_KEY_VALUE, SECRET_KEY_VALUE, SESSION_TOKEN))); + return mockStsRoleProvider; + } + + private SdkException[] getSdkBaseExceptions(int numErrors) { + final SdkException exceptionFromProvider = SdkClientException.create("TEST TEST TEST"); + return IntStream.range(0, numErrors).mapToObj(i -> exceptionFromProvider) + .collect(Collectors.toList()).toArray(new SdkException[numErrors]); + } + + private AwsCredentialsProvider setupMockEcsDefaultProviderWithRetriableExceptions(int numErrors) { + SdkException[] exceptionsToThrow = getSdkBaseExceptions(numErrors); + ContainerCredentialsProvider mockEcsProvider = Mockito.mock(ContainerCredentialsProvider.class); + + Mockito.when(mockEcsProvider.resolveIdentity()) + .thenThrow(exceptionsToThrow) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsBasicCredentials.create(ACCESS_KEY_VALUE_TWO, SECRET_KEY_VALUE_TWO))); + return mockEcsProvider; + } + + private AwsCredentialsProvider setupMockEc2DefaultProviderWithRetriableExceptions(int numErrors) { + SdkException[] exceptionsToThrow = getSdkBaseExceptions(numErrors); + InstanceProfileCredentialsProvider mockEc2Provider = Mockito.mock(InstanceProfileCredentialsProvider.class); + + Mockito.when(mockEc2Provider.resolveIdentity()) + .thenThrow(exceptionsToThrow) + .thenAnswer(i -> CompletableFuture.completedFuture(AwsBasicCredentials.create(ACCESS_KEY_VALUE_TWO, SECRET_KEY_VALUE_TWO))); + return mockEc2Provider; + } + + private ProfileFile getProfileFile() { + return ProfileFile.builder() + .content(new File(getProfileResourceURL().getFile()).toPath()) + .type(ProfileFile.Type.CREDENTIALS) + .build(); + } + + private URL getProfileResourceURL() { + return getClass().getClassLoader().getResource("profile_config_file"); + } + +}