diff --git a/config/env_config.go b/config/env_config.go index e932c63dfb4..2c1f380030f 100644 --- a/config/env_config.go +++ b/config/env_config.go @@ -11,6 +11,7 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" + "github.com/aws/aws-sdk-go-v2/internal/shareddefaults" smithyrequestcompression "github.com/aws/smithy-go/private/requestcompression" ) @@ -340,8 +341,8 @@ func NewEnvConfig() (EnvConfig, error) { setStringFromEnvVal(&cfg.Region, regionEnvKeys) setStringFromEnvVal(&cfg.SharedConfigProfile, profileEnvKeys) - cfg.SharedCredentialsFile = os.Getenv(awsSharedCredentialsFileEnv) - cfg.SharedConfigFile = os.Getenv(awsConfigFileEnv) + cfg.SharedCredentialsFile = shareddefaults.ExpandHomePath(os.Getenv(awsSharedCredentialsFileEnv)) + cfg.SharedConfigFile = shareddefaults.ExpandHomePath(os.Getenv(awsConfigFileEnv)) cfg.CustomCABundle = os.Getenv(awsCABundleEnv) diff --git a/config/env_config_test.go b/config/env_config_test.go index e8784f4374b..a3bb5ee6454 100644 --- a/config/env_config_test.go +++ b/config/env_config_test.go @@ -576,6 +576,17 @@ func TestNewEnvConfig(t *testing.T) { AuthSchemePreference: []string{"sigv4a", "sigv4"}, }, }, + 55: { + Env: map[string]string{ + "HOME": "/home/user", + "AWS_SHARED_CREDENTIALS_FILE": "~/.aws/credentials", + "AWS_CONFIG_FILE": "~/.aws/config", + }, + Config: EnvConfig{ + SharedCredentialsFile: "/home/user/.aws/credentials", + SharedConfigFile: "/home/user/.aws/config", + }, + }, } for i, c := range cases { diff --git a/internal/shareddefaults/expand_home_test.go b/internal/shareddefaults/expand_home_test.go new file mode 100644 index 00000000000..beac18fd5bc --- /dev/null +++ b/internal/shareddefaults/expand_home_test.go @@ -0,0 +1,67 @@ +//go:build !windows +// +build !windows + +package shareddefaults_test + +import ( + "os" + "path/filepath" + "testing" + + "github.com/aws/aws-sdk-go-v2/internal/awstesting" + "github.com/aws/aws-sdk-go-v2/internal/shareddefaults" +) + +func TestExpandHomePath(t *testing.T) { + restoreEnv := awstesting.StashEnv() + defer awstesting.PopEnv(restoreEnv) + + os.Setenv("HOME", "/home/user") + + cases := map[string]struct { + Input string + Expect string + }{ + "empty": { + Input: "", + Expect: "", + }, + "absolute path unchanged": { + Input: "/absolute/path/to/config", + Expect: "/absolute/path/to/config", + }, + "relative path unchanged": { + Input: "relative/path/to/config", + Expect: "relative/path/to/config", + }, + "tilde only": { + Input: "~", + Expect: "/home/user", + }, + "tilde with slash": { + Input: "~/.aws/config", + Expect: filepath.Join("/home/user", ".aws", "config"), + }, + "tilde with nested path": { + Input: "~/projects/my app/.aws/config", + Expect: filepath.Join("/home/user", "projects", "my app", ".aws", "config"), + }, + "tilde not at start unchanged": { + Input: "/some/~/path", + Expect: "/some/~/path", + }, + "tilde without slash unchanged": { + Input: "~username/.aws/config", + Expect: "~username/.aws/config", + }, + } + + for name, c := range cases { + t.Run(name, func(t *testing.T) { + actual := shareddefaults.ExpandHomePath(c.Input) + if c.Expect != actual { + t.Errorf("expect %q, got %q", c.Expect, actual) + } + }) + } +} diff --git a/internal/shareddefaults/shared_config.go b/internal/shareddefaults/shared_config.go index c96b717e08a..1a158153fc0 100644 --- a/internal/shareddefaults/shared_config.go +++ b/internal/shareddefaults/shared_config.go @@ -4,6 +4,7 @@ import ( "os" "os/user" "path/filepath" + "strings" ) // SharedCredentialsFilename returns the SDK's default file path @@ -28,6 +29,25 @@ func SharedConfigFilename() string { return filepath.Join(UserHomeDir(), ".aws", "config") } +// ExpandHomePath expands a leading ~ in the path to the user's home +// directory. This is necessary because the Go os package does not +// perform shell-style tilde expansion, unlike Python's +// os.path.expanduser which is used by botocore. +// +// Per the AWS SDKs and Tools Reference Guide, ~ followed by / (or the +// platform-specific path separator) at the start of a file path should +// resolve to the home directory: +// https://docs.aws.amazon.com/sdkref/latest/guide/file-location.html +func ExpandHomePath(path string) string { + if path == "~" { + return UserHomeDir() + } + if strings.HasPrefix(path, "~/") || strings.HasPrefix(path, "~"+string(filepath.Separator)) { + return filepath.Join(UserHomeDir(), path[2:]) + } + return path +} + // UserHomeDir returns the home directory for the user the process is // running under. func UserHomeDir() string {