commonMain.aws.sdk.kotlin.runtime.auth.credentials.ProcessCredentialsProvider.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aws-config-jvm Show documentation
Show all versions of aws-config-jvm Show documentation
Support for AWS configuration
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0
*/
package aws.sdk.kotlin.runtime.auth.credentials
import aws.sdk.kotlin.runtime.auth.credentials.internal.credentials
import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials
import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProvider
import aws.smithy.kotlin.runtime.auth.awscredentials.CredentialsProviderException
import aws.smithy.kotlin.runtime.collections.Attributes
import aws.smithy.kotlin.runtime.serde.json.JsonDeserializer
import aws.smithy.kotlin.runtime.telemetry.logging.logger
import aws.smithy.kotlin.runtime.time.Clock
import aws.smithy.kotlin.runtime.time.Instant
import aws.smithy.kotlin.runtime.util.PlatformProvider
import kotlin.coroutines.coroutineContext
internal expect suspend fun executeCommand(
command: String,
platformProvider: PlatformProvider,
maxOutputLengthBytes: Long,
timeoutMillis: Long,
clock: Clock = Clock.System,
): Pair
private const val PROVIDER_NAME = "Process"
/**
* [CredentialsProvider] that invokes a command to retrieve [Credentials] from stdout.
* If the exit code is non-zero, log and throw an exception including the stderr from the command.
*
* WARNING: This [CredentialsProvider] sources credentials from an external process.
* This can potentially be dangerous, so proceed with caution. Other credential providers should be preferred if possible.
* If using this option, secure the config file and any process/script files using security best practices for your operating system.
* Additionally, make sure to follow command-line conventions such as surrounding filenames / paths with quotes if they contain spaces.
* See the public documentation for a full list of expected conventions.
* @see Process Credentials
*
* @param credentialProcess the command to invoke to retrieve credentials
* @param platformProvider the platform provider
* @param maxOutputLengthBytes the maximum output of the process in bytes
* @param timeoutMillis the timeout of the process
* @throws RuntimeException if the process returns larger than [maxOutputLengthBytes] or takes longer than [timeoutMillis] to return [Credentials]
*/
public class ProcessCredentialsProvider(
private val credentialProcess: String,
private val platformProvider: PlatformProvider = PlatformProvider.System,
private val maxOutputLengthBytes: Long = 64 * 1024,
private val timeoutMillis: Long = 60_000,
) : CredentialsProvider {
override suspend fun resolve(attributes: Attributes): Credentials {
val logger = coroutineContext.logger()
val (exitCode, output) = try {
executeCommand(credentialProcess, platformProvider, maxOutputLengthBytes, timeoutMillis)
} catch (ex: Exception) {
throw CredentialsProviderException("Failed to execute command", ex)
}
if (exitCode != 0) {
logger.warn { "Command completed with nonzero exit code $exitCode: $output" }
throw CredentialsProviderException("Command completed with nonzero exit code $exitCode: $output")
}
val payload = output.encodeToByteArray()
val deserializer = JsonDeserializer(payload)
return when (val resp = deserializeJsonProcessCredentials(deserializer)) {
is JsonCredentialsResponse.SessionCredentials -> credentials(
resp.accessKeyId,
resp.secretAccessKey,
resp.sessionToken,
resp.expiration ?: Instant.MAX_VALUE,
PROVIDER_NAME,
resp.accountId,
)
else -> throw CredentialsProviderException("Credentials response was not of expected format")
}
}
}