diff --git a/IntegrationTests/Services/AWSS3IntegrationTests/ProcessCredentialsProviderTests.swift b/IntegrationTests/Services/AWSS3IntegrationTests/ProcessCredentialsProviderTests.swift new file mode 100644 index 00000000000..800bb1805f2 --- /dev/null +++ b/IntegrationTests/Services/AWSS3IntegrationTests/ProcessCredentialsProviderTests.swift @@ -0,0 +1,37 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation +import XCTest +import AWSS3 +import AWSClientRuntime + +// Please provide your-access-key and your-secret-key in Resources/credenitals +class ProcessCredentialProviderTests: XCTestCase { + + var client: S3Client! + + override func setUp() async throws { + // Setup ProcessCredentialsProvider + let processCredentialsProvider = try ProcessCredentialsProvider( + configFilePath: Bundle.module.path(forResource: "config", ofType: nil)!, + credentialsFilePath: Bundle.module.path(forResource: "credentials", ofType: nil)! + ) + + // Setup S3ClientConfiguration to use ProcessCredentialsProvider + let testConfig = try await S3Client.S3ClientConfiguration() + testConfig.credentialsProvider = processCredentialsProvider + + // Initialize our S3 client with the specified configuration + client = S3Client(config: testConfig) + } + + // This test calls listBuckets() and forces S3Client to use ProcessCredentialsProvider + func test_listBuckets() async throws { + _ = try await client.listBuckets(input: ListBucketsInput()) + } +} diff --git a/IntegrationTests/Services/AWSS3IntegrationTests/Resources/config b/IntegrationTests/Services/AWSS3IntegrationTests/Resources/config new file mode 100644 index 00000000000..a8c11c6e8c4 --- /dev/null +++ b/IntegrationTests/Services/AWSS3IntegrationTests/Resources/config @@ -0,0 +1,2 @@ +[default] +region = us-east-1 diff --git a/IntegrationTests/Services/AWSS3IntegrationTests/Resources/credentials b/IntegrationTests/Services/AWSS3IntegrationTests/Resources/credentials new file mode 100644 index 00000000000..fc0f8143f41 --- /dev/null +++ b/IntegrationTests/Services/AWSS3IntegrationTests/Resources/credentials @@ -0,0 +1,2 @@ +[default] +credential_process = echo '{"Version": 1, "AccessKeyId": "your-access-key", "SecretAccessKey": "your-secret-key"}' diff --git a/Sources/Core/AWSClientRuntime/Auth/CredentialsProviders/ProcessCredentialsProvider.swift b/Sources/Core/AWSClientRuntime/Auth/CredentialsProviders/ProcessCredentialsProvider.swift new file mode 100644 index 00000000000..750e8e55b36 --- /dev/null +++ b/Sources/Core/AWSClientRuntime/Auth/CredentialsProviders/ProcessCredentialsProvider.swift @@ -0,0 +1,51 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import AwsCommonRuntimeKit +import ClientRuntime +import Foundation + +/// The process credentials provider sources credentials from running a command or process. +/// The command to run is sourced from a profile in the AWS config file, using the standard +/// profile selection rules. The profile key the command is read from is "credential_process." +/// E.g.: +/// [default] +/// credential_process=/opt/amazon/bin/my-credential-fetcher --argsA=abc +/// On successfully running the command, the output should be a json data with the following +/// format: +/// { +/// "Version": 1, +/// "AccessKeyId": "accesskey", +/// "SecretAccessKey": "secretAccessKey" +/// "SessionToken": "....", +/// "Expiration": "2019-05-29T00:21:43Z" +/// } +/// Version here identifies the command output format version. +public struct ProcessCredentialsProvider: CredentialsSourcedByCRT { + let crtCredentialsProvider: CRTCredentialsProvider + + /// Creates a credentials provider that gets credentials from running a command or process. + /// + /// - Parameters: + /// - profileName: The profile name to use. If not provided it will be resolved internally via the `AWS_PROFILE` environment variable or defaulted to `default` if not configured. + /// - configFilePath: The path to the configuration file to use. If not provided it will be resolved internally via the `AWS_CONFIG_FILE` environment variable or defaulted to `~/.aws/config` if not configured. + /// - credentialsFilePath: The path to the shared credentials file to use. If not provided it will be resolved internally via the `AWS_SHARED_CREDENTIALS_FILE` environment variable or defaulted `~/.aws/credentials` if not configured. + public init( + profileName: String? = nil, + configFilePath: String? = nil, + credentialsFilePath: String? = nil + ) throws { + let fileBasedConfig = try CRTFileBasedConfiguration( + configFilePath: configFilePath, + credentialsFilePath: credentialsFilePath + ) + self.crtCredentialsProvider = try CRTCredentialsProvider(source: .process( + fileBasedConfiguration: fileBasedConfig, + profileFileNameOverride: profileName + )) + } +} diff --git a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/CachedCredentialsProviderTests.swift b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/CachedCredentialsProviderTests.swift index 88dddd69355..0aeab234a9e 100644 --- a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/CachedCredentialsProviderTests.swift +++ b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/CachedCredentialsProviderTests.swift @@ -12,22 +12,22 @@ import XCTest @_spi(FileBasedConfig) @testable import AWSClientRuntime class CachedCredentialsProviderTests: XCTestCase { - func testGetCredentials() async { + func testGetCredentials() async throws { var counter: Int = 0 let coreProvider = MockCredentialsProvider { counter += 1 return .init(accessKey: "some_access_key", secret: "some_secret") } - let subject = try! CachedCredentialsProvider( + let subject = try CachedCredentialsProvider( source: coreProvider, refreshTime: 1 ) - _ = try! await subject.getCredentials() - _ = try! await subject.getCredentials() - _ = try! await subject.getCredentials() - _ = try! await subject.getCredentials() + _ = try await subject.getCredentials() + _ = try await subject.getCredentials() + _ = try await subject.getCredentials() + _ = try await subject.getCredentials() XCTAssertEqual(counter, 1) diff --git a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/CustomCredentialsProviderTests.swift b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/CustomCredentialsProviderTests.swift index a9496b8f8b4..6b5ed1bbdbb 100644 --- a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/CustomCredentialsProviderTests.swift +++ b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/CustomCredentialsProviderTests.swift @@ -12,10 +12,10 @@ import XCTest @_spi(FileBasedConfig) @testable import AWSClientRuntime class CustomCredentialsProviderTests: XCTestCase { - func testGetCredentials() async { + func testGetCredentials() async throws { let mockProvider = MockCredentialsProvider() - let subject = try! CustomCredentialsProvider(mockProvider) - let credentials = try! await subject.getCredentials() + let subject = try CustomCredentialsProvider(mockProvider) + let credentials = try await subject.getCredentials() XCTAssertEqual(credentials.accessKey, "some_access_key") XCTAssertEqual(credentials.secret, "some_secret") diff --git a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/DefaultChainCredentialsProviderTests.swift b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/DefaultChainCredentialsProviderTests.swift index 1a9211fadf8..d235f90e467 100644 --- a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/DefaultChainCredentialsProviderTests.swift +++ b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/DefaultChainCredentialsProviderTests.swift @@ -12,23 +12,19 @@ import XCTest @_spi(FileBasedConfig) @testable import AWSClientRuntime class DefaultChainCredentialsProviderTests: XCTestCase { - func testGetCredentials() async { + func testGetCredentials() async throws { setenv("AWS_ACCESS_KEY_ID", "some_access_key_b", 1) setenv("AWS_SECRET_ACCESS_KEY", "some_secret_b", 1) - + defer { unsetenv("AWS_ACCESS_KEY_ID") unsetenv("AWS_SECRET_ACCESS_KEY") } - - do { - let subject = try DefaultChainCredentialsProvider() - let credentials = try await subject.getCredentials() - XCTAssertEqual(credentials.accessKey, "some_access_key_b") - XCTAssertEqual(credentials.secret, "some_secret_b") - } catch { - XCTFail("Failed to create credentials") - } + let subject = try DefaultChainCredentialsProvider() + let credentials = try await subject.getCredentials() + + XCTAssertEqual(credentials.accessKey, "some_access_key_b") + XCTAssertEqual(credentials.secret, "some_secret_b") } } diff --git a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/EnvironmentCredentialsProviderTests.swift b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/EnvironmentCredentialsProviderTests.swift index f170633a44f..7e7c739c38d 100644 --- a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/EnvironmentCredentialsProviderTests.swift +++ b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/EnvironmentCredentialsProviderTests.swift @@ -12,23 +12,19 @@ import XCTest @_spi(FileBasedConfig) @testable import AWSClientRuntime class EnvironmentCredentialsProviderTests: XCTestCase { - func testGetCredentials() async { + func testGetCredentials() async throws { setenv("AWS_ACCESS_KEY_ID", "some_access_key_a", 1) setenv("AWS_SECRET_ACCESS_KEY", "some_secret_a", 1) - + defer { unsetenv("AWS_ACCESS_KEY_ID") unsetenv("AWS_SECRET_ACCESS_KEY") } + + let subject = try EnvironmentCredentialsProvider() + let credentials = try await subject.getCredentials() - do { - let subject = try EnvironmentCredentialsProvider() - let credentials = try await subject.getCredentials() - - XCTAssertEqual(credentials.accessKey, "some_access_key_a") - XCTAssertEqual(credentials.secret, "some_secret_a") - } catch { - XCTFail("Failed to create credentials") - } + XCTAssertEqual(credentials.accessKey, "some_access_key_a") + XCTAssertEqual(credentials.secret, "some_secret_a") } } diff --git a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/ProcessCredentialsProviderTests.swift b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/ProcessCredentialsProviderTests.swift new file mode 100644 index 00000000000..79c671a08ef --- /dev/null +++ b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/ProcessCredentialsProviderTests.swift @@ -0,0 +1,46 @@ +// +// Copyright Amazon.com Inc. or its affiliates. +// All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import ClientRuntime +import Foundation +import XCTest + +@_spi(FileBasedConfig) @testable import AWSClientRuntime + +// Test fails on CI build with macos-11, Xcode_13.2.1, platform=iOS Simulator but not on later versions +// ProcessCredentialsProvider is not useful on iOS platform so this test will remain disabled for now +#if !os(iOS) +class ProcessCredentialsProviderTests: XCTestCase { + let configPath = Bundle.module.path(forResource: "config", ofType: nil)! + let credentialsPath = Bundle.module.path(forResource: "credentials", ofType: nil)! + + func testGetCredentialsWithDefaultProfile() async throws { + let subject = try ProcessCredentialsProvider( + configFilePath: configPath, + credentialsFilePath: credentialsPath + ) + let credentials = try await subject.getCredentials() + + XCTAssertEqual("AccessKey123", credentials.accessKey) + XCTAssertEqual("SecretAccessKey123", credentials.secret) + XCTAssertEqual("SessionToken123", credentials.sessionToken) + } + + func testGetCredentialsWithNamedProfileFromConfigFile() async throws { + let subject = try ProcessCredentialsProvider( + profileName: "credentials-process-config-tests-profile", + configFilePath: configPath, + credentialsFilePath: credentialsPath + ) + let credentials = try await subject.getCredentials() + + XCTAssertEqual("AccessKey123", credentials.accessKey) + XCTAssertEqual("SecretAccessKey123", credentials.secret) + XCTAssertEqual("SessionToken123", credentials.sessionToken) + } +} +#endif diff --git a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/ProfileCredentialsProviderTests.swift b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/ProfileCredentialsProviderTests.swift index 7ec12e39cf1..5a682722f0d 100644 --- a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/ProfileCredentialsProviderTests.swift +++ b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/ProfileCredentialsProviderTests.swift @@ -15,36 +15,36 @@ class ProfileCredentialsProviderTests: XCTestCase { let configPath = Bundle.module.path(forResource: "config", ofType: nil)! let credentialsPath = Bundle.module.path(forResource: "credentials", ofType: nil)! - func testGetCredentialsWithDefaultProfile() async { - let subject = try! ProfileCredentialsProvider( + func testGetCredentialsWithDefaultProfile() async throws { + let subject = try ProfileCredentialsProvider( configFilePath: configPath, credentialsFilePath: credentialsPath ) - let credentials = try! await subject.getCredentials() + let credentials = try await subject.getCredentials() XCTAssertEqual(credentials.accessKey, "access_key_default_cred") XCTAssertEqual(credentials.secret, "secret_default_cred") } - func testGetCredentialsWithNamedProfileFromConfigFile() async { - let subject = try! ProfileCredentialsProvider( + func testGetCredentialsWithNamedProfileFromConfigFile() async throws { + let subject = try ProfileCredentialsProvider( profileName: "credentials-provider-config-tests-profile", configFilePath: configPath, credentialsFilePath: credentialsPath ) - let credentials = try! await subject.getCredentials() + let credentials = try await subject.getCredentials() XCTAssertEqual(credentials.accessKey, "access_key_profile_config") XCTAssertEqual(credentials.secret, "secret_profile_config") } - func testGetCredentialsWithNamedProfileFromCredentialsFile() async { - let subject = try! ProfileCredentialsProvider( + func testGetCredentialsWithNamedProfileFromCredentialsFile() async throws { + let subject = try ProfileCredentialsProvider( profileName: "credentials-provider-creds-tests-profile", configFilePath: configPath, credentialsFilePath: credentialsPath ) - let credentials = try! await subject.getCredentials() + let credentials = try await subject.getCredentials() XCTAssertEqual(credentials.accessKey, "access_key_profile_cred") XCTAssertEqual(credentials.secret, "secret_profile_cred") diff --git a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/StaticCredentialsProviderTests.swift b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/StaticCredentialsProviderTests.swift index 79d687dce83..62391e6d923 100644 --- a/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/StaticCredentialsProviderTests.swift +++ b/Tests/Core/AWSClientRuntimeTests/Auth/CredentialsProvidersTests/StaticCredentialsProviderTests.swift @@ -12,12 +12,12 @@ import XCTest @_spi(FileBasedConfig) @testable import AWSClientRuntime class StaticCredentialsProviderTests: XCTestCase { - func testGetCredentials() async { - let subject = try! StaticCredentialsProvider(.init( + func testGetCredentials() async throws { + let subject = try StaticCredentialsProvider(.init( accessKey: "some_access_key", secret: "some_secret" )) - let credentials = try! await subject.getCredentials() + let credentials = try await subject.getCredentials() XCTAssertEqual(credentials.accessKey, "some_access_key") XCTAssertEqual(credentials.secret, "some_secret") diff --git a/Tests/Core/AWSClientRuntimeTests/Resources/config b/Tests/Core/AWSClientRuntimeTests/Resources/config index c5c97d10cdb..003dd64438a 100644 --- a/Tests/Core/AWSClientRuntimeTests/Resources/config +++ b/Tests/Core/AWSClientRuntimeTests/Resources/config @@ -1,7 +1,13 @@ [default] aws_access_key_id = access_key_default_config aws_secret_access_key = secret_default_config +credential_process = echo '{"Version": 1, "AccessKeyId": "AccessKey123", "SecretAccessKey": "SecretAccessKey123", "SessionToken": "SessionToken123","Expiration":"2020-02-25T06:03:31Z"}' [profile credentials-provider-config-tests-profile] aws_access_key_id = access_key_profile_config aws_secret_access_key = secret_profile_config + +[profile credentials-process-config-tests-profile] +aws_access_key_id = access_key_profile_config +aws_secret_access_key = secret_profile_config +credential_process = echo '{"Version": 1, "AccessKeyId": "AccessKey123", "SecretAccessKey": "SecretAccessKey123", "SessionToken": "SessionToken123","Expiration":"2020-02-25T06:03:31Z"}'