src

Go monorepo.
git clone git://code.dwrz.net/src
Log | Files | Refs

shared_config.go (48502B)


      1 package config
      2 
      3 import (
      4 	"bytes"
      5 	"context"
      6 	"errors"
      7 	"fmt"
      8 	"io"
      9 	"io/ioutil"
     10 	"os"
     11 	"path/filepath"
     12 	"strings"
     13 	"time"
     14 
     15 	"github.com/aws/aws-sdk-go-v2/aws"
     16 	"github.com/aws/aws-sdk-go-v2/feature/ec2/imds"
     17 	"github.com/aws/aws-sdk-go-v2/internal/ini"
     18 	"github.com/aws/aws-sdk-go-v2/internal/shareddefaults"
     19 	"github.com/aws/smithy-go/logging"
     20 	smithyrequestcompression "github.com/aws/smithy-go/private/requestcompression"
     21 )
     22 
     23 const (
     24 	// Prefix to use for filtering profiles. The profile prefix should only
     25 	// exist in the shared config file, not the credentials file.
     26 	profilePrefix = `profile `
     27 
     28 	// Prefix to be used for SSO sections. These are supposed to only exist in
     29 	// the shared config file, not the credentials file.
     30 	ssoSectionPrefix = `sso-session `
     31 
     32 	// Prefix for services section. It is referenced in profile via the services
     33 	// parameter to configure clients for service-specific parameters.
     34 	servicesPrefix = `services `
     35 
     36 	// string equivalent for boolean
     37 	endpointDiscoveryDisabled = `false`
     38 	endpointDiscoveryEnabled  = `true`
     39 	endpointDiscoveryAuto     = `auto`
     40 
     41 	// Static Credentials group
     42 	accessKeyIDKey  = `aws_access_key_id`     // group required
     43 	secretAccessKey = `aws_secret_access_key` // group required
     44 	sessionTokenKey = `aws_session_token`     // optional
     45 
     46 	// Assume Role Credentials group
     47 	roleArnKey             = `role_arn`          // group required
     48 	sourceProfileKey       = `source_profile`    // group required
     49 	credentialSourceKey    = `credential_source` // group required (or source_profile)
     50 	externalIDKey          = `external_id`       // optional
     51 	mfaSerialKey           = `mfa_serial`        // optional
     52 	roleSessionNameKey     = `role_session_name` // optional
     53 	roleDurationSecondsKey = "duration_seconds"  // optional
     54 
     55 	// AWS Single Sign-On (AWS SSO) group
     56 	ssoSessionNameKey = "sso_session"
     57 
     58 	ssoRegionKey   = "sso_region"
     59 	ssoStartURLKey = "sso_start_url"
     60 
     61 	ssoAccountIDKey = "sso_account_id"
     62 	ssoRoleNameKey  = "sso_role_name"
     63 
     64 	// Additional Config fields
     65 	regionKey = `region`
     66 
     67 	// endpoint discovery group
     68 	enableEndpointDiscoveryKey = `endpoint_discovery_enabled` // optional
     69 
     70 	// External Credential process
     71 	credentialProcessKey = `credential_process` // optional
     72 
     73 	// Web Identity Token File
     74 	webIdentityTokenFileKey = `web_identity_token_file` // optional
     75 
     76 	// S3 ARN Region Usage
     77 	s3UseARNRegionKey = "s3_use_arn_region"
     78 
     79 	ec2MetadataServiceEndpointModeKey = "ec2_metadata_service_endpoint_mode"
     80 
     81 	ec2MetadataServiceEndpointKey = "ec2_metadata_service_endpoint"
     82 
     83 	ec2MetadataV1DisabledKey = "ec2_metadata_v1_disabled"
     84 
     85 	// Use DualStack Endpoint Resolution
     86 	useDualStackEndpoint = "use_dualstack_endpoint"
     87 
     88 	// DefaultSharedConfigProfile is the default profile to be used when
     89 	// loading configuration from the config files if another profile name
     90 	// is not provided.
     91 	DefaultSharedConfigProfile = `default`
     92 
     93 	// S3 Disable Multi-Region AccessPoints
     94 	s3DisableMultiRegionAccessPointsKey = `s3_disable_multiregion_access_points`
     95 
     96 	useFIPSEndpointKey = "use_fips_endpoint"
     97 
     98 	defaultsModeKey = "defaults_mode"
     99 
    100 	// Retry options
    101 	retryMaxAttemptsKey = "max_attempts"
    102 	retryModeKey        = "retry_mode"
    103 
    104 	caBundleKey = "ca_bundle"
    105 
    106 	sdkAppID = "sdk_ua_app_id"
    107 
    108 	ignoreConfiguredEndpoints = "ignore_configured_endpoint_urls"
    109 
    110 	endpointURL = "endpoint_url"
    111 
    112 	servicesSectionKey = "services"
    113 
    114 	disableRequestCompression      = "disable_request_compression"
    115 	requestMinCompressionSizeBytes = "request_min_compression_size_bytes"
    116 
    117 	s3DisableExpressSessionAuthKey = "s3_disable_express_session_auth"
    118 )
    119 
    120 // defaultSharedConfigProfile allows for swapping the default profile for testing
    121 var defaultSharedConfigProfile = DefaultSharedConfigProfile
    122 
    123 // DefaultSharedCredentialsFilename returns the SDK's default file path
    124 // for the shared credentials file.
    125 //
    126 // Builds the shared config file path based on the OS's platform.
    127 //
    128 //   - Linux/Unix: $HOME/.aws/credentials
    129 //   - Windows: %USERPROFILE%\.aws\credentials
    130 func DefaultSharedCredentialsFilename() string {
    131 	return filepath.Join(shareddefaults.UserHomeDir(), ".aws", "credentials")
    132 }
    133 
    134 // DefaultSharedConfigFilename returns the SDK's default file path for
    135 // the shared config file.
    136 //
    137 // Builds the shared config file path based on the OS's platform.
    138 //
    139 //   - Linux/Unix: $HOME/.aws/config
    140 //   - Windows: %USERPROFILE%\.aws\config
    141 func DefaultSharedConfigFilename() string {
    142 	return filepath.Join(shareddefaults.UserHomeDir(), ".aws", "config")
    143 }
    144 
    145 // DefaultSharedConfigFiles is a slice of the default shared config files that
    146 // the will be used in order to load the SharedConfig.
    147 var DefaultSharedConfigFiles = []string{
    148 	DefaultSharedConfigFilename(),
    149 }
    150 
    151 // DefaultSharedCredentialsFiles is a slice of the default shared credentials
    152 // files that the will be used in order to load the SharedConfig.
    153 var DefaultSharedCredentialsFiles = []string{
    154 	DefaultSharedCredentialsFilename(),
    155 }
    156 
    157 // SSOSession provides the shared configuration parameters of the sso-session
    158 // section.
    159 type SSOSession struct {
    160 	Name        string
    161 	SSORegion   string
    162 	SSOStartURL string
    163 }
    164 
    165 func (s *SSOSession) setFromIniSection(section ini.Section) {
    166 	updateString(&s.Name, section, ssoSessionNameKey)
    167 	updateString(&s.SSORegion, section, ssoRegionKey)
    168 	updateString(&s.SSOStartURL, section, ssoStartURLKey)
    169 }
    170 
    171 // Services contains values configured in the services section
    172 // of the AWS configuration file.
    173 type Services struct {
    174 	// Services section values
    175 	// {"serviceId": {"key": "value"}}
    176 	// e.g. {"s3": {"endpoint_url": "example.com"}}
    177 	ServiceValues map[string]map[string]string
    178 }
    179 
    180 func (s *Services) setFromIniSection(section ini.Section) {
    181 	if s.ServiceValues == nil {
    182 		s.ServiceValues = make(map[string]map[string]string)
    183 	}
    184 	for _, service := range section.List() {
    185 		s.ServiceValues[service] = section.Map(service)
    186 	}
    187 }
    188 
    189 // SharedConfig represents the configuration fields of the SDK config files.
    190 type SharedConfig struct {
    191 	Profile string
    192 
    193 	// Credentials values from the config file. Both aws_access_key_id
    194 	// and aws_secret_access_key must be provided together in the same file
    195 	// to be considered valid. The values will be ignored if not a complete group.
    196 	// aws_session_token is an optional field that can be provided if both of the
    197 	// other two fields are also provided.
    198 	//
    199 	//	aws_access_key_id
    200 	//	aws_secret_access_key
    201 	//	aws_session_token
    202 	Credentials aws.Credentials
    203 
    204 	CredentialSource     string
    205 	CredentialProcess    string
    206 	WebIdentityTokenFile string
    207 
    208 	// SSO session options
    209 	SSOSessionName string
    210 	SSOSession     *SSOSession
    211 
    212 	// Legacy SSO session options
    213 	SSORegion   string
    214 	SSOStartURL string
    215 
    216 	// SSO fields not used
    217 	SSOAccountID string
    218 	SSORoleName  string
    219 
    220 	RoleARN             string
    221 	ExternalID          string
    222 	MFASerial           string
    223 	RoleSessionName     string
    224 	RoleDurationSeconds *time.Duration
    225 
    226 	SourceProfileName string
    227 	Source            *SharedConfig
    228 
    229 	// Region is the region the SDK should use for looking up AWS service endpoints
    230 	// and signing requests.
    231 	//
    232 	//	region = us-west-2
    233 	Region string
    234 
    235 	// EnableEndpointDiscovery can be enabled or disabled in the shared config
    236 	// by setting endpoint_discovery_enabled to true, or false respectively.
    237 	//
    238 	//	endpoint_discovery_enabled = true
    239 	EnableEndpointDiscovery aws.EndpointDiscoveryEnableState
    240 
    241 	// Specifies if the S3 service should allow ARNs to direct the region
    242 	// the client's requests are sent to.
    243 	//
    244 	// s3_use_arn_region=true
    245 	S3UseARNRegion *bool
    246 
    247 	// Specifies the EC2 Instance Metadata Service default endpoint selection
    248 	// mode (IPv4 or IPv6)
    249 	//
    250 	// ec2_metadata_service_endpoint_mode=IPv6
    251 	EC2IMDSEndpointMode imds.EndpointModeState
    252 
    253 	// Specifies the EC2 Instance Metadata Service endpoint to use. If
    254 	// specified it overrides EC2IMDSEndpointMode.
    255 	//
    256 	// ec2_metadata_service_endpoint=http://fd00:ec2::254
    257 	EC2IMDSEndpoint string
    258 
    259 	// Specifies that IMDS clients should not fallback to IMDSv1 if token
    260 	// requests fail.
    261 	//
    262 	// ec2_metadata_v1_disabled=true
    263 	EC2IMDSv1Disabled *bool
    264 
    265 	// Specifies if the S3 service should disable support for Multi-Region
    266 	// access-points
    267 	//
    268 	// s3_disable_multiregion_access_points=true
    269 	S3DisableMultiRegionAccessPoints *bool
    270 
    271 	// Specifies that SDK clients must resolve a dual-stack endpoint for
    272 	// services.
    273 	//
    274 	// use_dualstack_endpoint=true
    275 	UseDualStackEndpoint aws.DualStackEndpointState
    276 
    277 	// Specifies that SDK clients must resolve a FIPS endpoint for
    278 	// services.
    279 	//
    280 	// use_fips_endpoint=true
    281 	UseFIPSEndpoint aws.FIPSEndpointState
    282 
    283 	// Specifies which defaults mode should be used by services.
    284 	//
    285 	// defaults_mode=standard
    286 	DefaultsMode aws.DefaultsMode
    287 
    288 	// Specifies the maximum number attempts an API client will call an
    289 	// operation that fails with a retryable error.
    290 	//
    291 	// max_attempts=3
    292 	RetryMaxAttempts int
    293 
    294 	// Specifies the retry model the API client will be created with.
    295 	//
    296 	// retry_mode=standard
    297 	RetryMode aws.RetryMode
    298 
    299 	// Sets the path to a custom Credentials Authority (CA) Bundle PEM file
    300 	// that the SDK will use instead of the system's root CA bundle. Only use
    301 	// this if you want to configure the SDK to use a custom set of CAs.
    302 	//
    303 	// Enabling this option will attempt to merge the Transport into the SDK's
    304 	// HTTP client. If the client's Transport is not a http.Transport an error
    305 	// will be returned. If the Transport's TLS config is set this option will
    306 	// cause the SDK to overwrite the Transport's TLS config's  RootCAs value.
    307 	//
    308 	// Setting a custom HTTPClient in the aws.Config options will override this
    309 	// setting. To use this option and custom HTTP client, the HTTP client
    310 	// needs to be provided when creating the config. Not the service client.
    311 	//
    312 	//  ca_bundle=$HOME/my_custom_ca_bundle
    313 	CustomCABundle string
    314 
    315 	// aws sdk app ID that can be added to user agent header string
    316 	AppID string
    317 
    318 	// Flag used to disable configured endpoints.
    319 	IgnoreConfiguredEndpoints *bool
    320 
    321 	// Value to contain configured endpoints to be propagated to
    322 	// corresponding endpoint resolution field.
    323 	BaseEndpoint string
    324 
    325 	// Services section config.
    326 	ServicesSectionName string
    327 	Services            Services
    328 
    329 	// determine if request compression is allowed, default to false
    330 	// retrieved from config file's profile field disable_request_compression
    331 	DisableRequestCompression *bool
    332 
    333 	// inclusive threshold request body size to trigger compression,
    334 	// default to 10240 and must be within 0 and 10485760 bytes inclusive
    335 	// retrieved from config file's profile field request_min_compression_size_bytes
    336 	RequestMinCompressSizeBytes *int64
    337 
    338 	// Whether S3Express auth is disabled.
    339 	//
    340 	// This will NOT prevent requests from being made to S3Express buckets, it
    341 	// will only bypass the modified endpoint routing and signing behaviors
    342 	// associated with the feature.
    343 	S3DisableExpressAuth *bool
    344 }
    345 
    346 func (c SharedConfig) getDefaultsMode(ctx context.Context) (value aws.DefaultsMode, ok bool, err error) {
    347 	if len(c.DefaultsMode) == 0 {
    348 		return "", false, nil
    349 	}
    350 
    351 	return c.DefaultsMode, true, nil
    352 }
    353 
    354 // GetRetryMaxAttempts returns the maximum number of attempts an API client
    355 // created Retryer should attempt an operation call before failing.
    356 func (c SharedConfig) GetRetryMaxAttempts(ctx context.Context) (value int, ok bool, err error) {
    357 	if c.RetryMaxAttempts == 0 {
    358 		return 0, false, nil
    359 	}
    360 
    361 	return c.RetryMaxAttempts, true, nil
    362 }
    363 
    364 // GetRetryMode returns the model the API client should create its Retryer in.
    365 func (c SharedConfig) GetRetryMode(ctx context.Context) (value aws.RetryMode, ok bool, err error) {
    366 	if len(c.RetryMode) == 0 {
    367 		return "", false, nil
    368 	}
    369 
    370 	return c.RetryMode, true, nil
    371 }
    372 
    373 // GetS3UseARNRegion returns if the S3 service should allow ARNs to direct the region
    374 // the client's requests are sent to.
    375 func (c SharedConfig) GetS3UseARNRegion(ctx context.Context) (value, ok bool, err error) {
    376 	if c.S3UseARNRegion == nil {
    377 		return false, false, nil
    378 	}
    379 
    380 	return *c.S3UseARNRegion, true, nil
    381 }
    382 
    383 // GetEnableEndpointDiscovery returns if the enable_endpoint_discovery is set.
    384 func (c SharedConfig) GetEnableEndpointDiscovery(ctx context.Context) (value aws.EndpointDiscoveryEnableState, ok bool, err error) {
    385 	if c.EnableEndpointDiscovery == aws.EndpointDiscoveryUnset {
    386 		return aws.EndpointDiscoveryUnset, false, nil
    387 	}
    388 
    389 	return c.EnableEndpointDiscovery, true, nil
    390 }
    391 
    392 // GetS3DisableMultiRegionAccessPoints returns if the S3 service should disable support for Multi-Region
    393 // access-points.
    394 func (c SharedConfig) GetS3DisableMultiRegionAccessPoints(ctx context.Context) (value, ok bool, err error) {
    395 	if c.S3DisableMultiRegionAccessPoints == nil {
    396 		return false, false, nil
    397 	}
    398 
    399 	return *c.S3DisableMultiRegionAccessPoints, true, nil
    400 }
    401 
    402 // GetRegion returns the region for the profile if a region is set.
    403 func (c SharedConfig) getRegion(ctx context.Context) (string, bool, error) {
    404 	if len(c.Region) == 0 {
    405 		return "", false, nil
    406 	}
    407 	return c.Region, true, nil
    408 }
    409 
    410 // GetCredentialsProvider returns the credentials for a profile if they were set.
    411 func (c SharedConfig) getCredentialsProvider() (aws.Credentials, bool, error) {
    412 	return c.Credentials, true, nil
    413 }
    414 
    415 // GetEC2IMDSEndpointMode implements a EC2IMDSEndpointMode option resolver interface.
    416 func (c SharedConfig) GetEC2IMDSEndpointMode() (imds.EndpointModeState, bool, error) {
    417 	if c.EC2IMDSEndpointMode == imds.EndpointModeStateUnset {
    418 		return imds.EndpointModeStateUnset, false, nil
    419 	}
    420 
    421 	return c.EC2IMDSEndpointMode, true, nil
    422 }
    423 
    424 // GetEC2IMDSEndpoint implements a EC2IMDSEndpoint option resolver interface.
    425 func (c SharedConfig) GetEC2IMDSEndpoint() (string, bool, error) {
    426 	if len(c.EC2IMDSEndpoint) == 0 {
    427 		return "", false, nil
    428 	}
    429 
    430 	return c.EC2IMDSEndpoint, true, nil
    431 }
    432 
    433 // GetEC2IMDSV1FallbackDisabled implements an EC2IMDSV1FallbackDisabled option
    434 // resolver interface.
    435 func (c SharedConfig) GetEC2IMDSV1FallbackDisabled() (bool, bool) {
    436 	if c.EC2IMDSv1Disabled == nil {
    437 		return false, false
    438 	}
    439 
    440 	return *c.EC2IMDSv1Disabled, true
    441 }
    442 
    443 // GetUseDualStackEndpoint returns whether the service's dual-stack endpoint should be
    444 // used for requests.
    445 func (c SharedConfig) GetUseDualStackEndpoint(ctx context.Context) (value aws.DualStackEndpointState, found bool, err error) {
    446 	if c.UseDualStackEndpoint == aws.DualStackEndpointStateUnset {
    447 		return aws.DualStackEndpointStateUnset, false, nil
    448 	}
    449 
    450 	return c.UseDualStackEndpoint, true, nil
    451 }
    452 
    453 // GetUseFIPSEndpoint returns whether the service's FIPS endpoint should be
    454 // used for requests.
    455 func (c SharedConfig) GetUseFIPSEndpoint(ctx context.Context) (value aws.FIPSEndpointState, found bool, err error) {
    456 	if c.UseFIPSEndpoint == aws.FIPSEndpointStateUnset {
    457 		return aws.FIPSEndpointStateUnset, false, nil
    458 	}
    459 
    460 	return c.UseFIPSEndpoint, true, nil
    461 }
    462 
    463 // GetS3DisableExpressAuth returns the configured value for
    464 // [SharedConfig.S3DisableExpressAuth].
    465 func (c SharedConfig) GetS3DisableExpressAuth() (value, ok bool) {
    466 	if c.S3DisableExpressAuth == nil {
    467 		return false, false
    468 	}
    469 
    470 	return *c.S3DisableExpressAuth, true
    471 }
    472 
    473 // GetCustomCABundle returns the custom CA bundle's PEM bytes if the file was
    474 func (c SharedConfig) getCustomCABundle(context.Context) (io.Reader, bool, error) {
    475 	if len(c.CustomCABundle) == 0 {
    476 		return nil, false, nil
    477 	}
    478 
    479 	b, err := ioutil.ReadFile(c.CustomCABundle)
    480 	if err != nil {
    481 		return nil, false, err
    482 	}
    483 	return bytes.NewReader(b), true, nil
    484 }
    485 
    486 // getAppID returns the sdk app ID if set in shared config profile
    487 func (c SharedConfig) getAppID(context.Context) (string, bool, error) {
    488 	return c.AppID, len(c.AppID) > 0, nil
    489 }
    490 
    491 // GetIgnoreConfiguredEndpoints is used in knowing when to disable configured
    492 // endpoints feature.
    493 func (c SharedConfig) GetIgnoreConfiguredEndpoints(context.Context) (bool, bool, error) {
    494 	if c.IgnoreConfiguredEndpoints == nil {
    495 		return false, false, nil
    496 	}
    497 
    498 	return *c.IgnoreConfiguredEndpoints, true, nil
    499 }
    500 
    501 func (c SharedConfig) getBaseEndpoint(context.Context) (string, bool, error) {
    502 	return c.BaseEndpoint, len(c.BaseEndpoint) > 0, nil
    503 }
    504 
    505 // GetServiceBaseEndpoint is used to retrieve a normalized SDK ID for use
    506 // with configured endpoints.
    507 func (c SharedConfig) GetServiceBaseEndpoint(ctx context.Context, sdkID string) (string, bool, error) {
    508 	if service, ok := c.Services.ServiceValues[normalizeShared(sdkID)]; ok {
    509 		if endpt, ok := service[endpointURL]; ok {
    510 			return endpt, true, nil
    511 		}
    512 	}
    513 	return "", false, nil
    514 }
    515 
    516 func normalizeShared(sdkID string) string {
    517 	lower := strings.ToLower(sdkID)
    518 	return strings.ReplaceAll(lower, " ", "_")
    519 }
    520 
    521 func (c SharedConfig) getServicesObject(context.Context) (map[string]map[string]string, bool, error) {
    522 	return c.Services.ServiceValues, c.Services.ServiceValues != nil, nil
    523 }
    524 
    525 // loadSharedConfigIgnoreNotExist is an alias for loadSharedConfig with the
    526 // addition of ignoring when none of the files exist or when the profile
    527 // is not found in any of the files.
    528 func loadSharedConfigIgnoreNotExist(ctx context.Context, configs configs) (Config, error) {
    529 	cfg, err := loadSharedConfig(ctx, configs)
    530 	if err != nil {
    531 		if _, ok := err.(SharedConfigProfileNotExistError); ok {
    532 			return SharedConfig{}, nil
    533 		}
    534 		return nil, err
    535 	}
    536 
    537 	return cfg, nil
    538 }
    539 
    540 // loadSharedConfig uses the configs passed in to load the SharedConfig from file
    541 // The file names and profile name are sourced from the configs.
    542 //
    543 // If profile name is not provided DefaultSharedConfigProfile (default) will
    544 // be used.
    545 //
    546 // If shared config filenames are not provided DefaultSharedConfigFiles will
    547 // be used.
    548 //
    549 // Config providers used:
    550 // * sharedConfigProfileProvider
    551 // * sharedConfigFilesProvider
    552 func loadSharedConfig(ctx context.Context, configs configs) (Config, error) {
    553 	var profile string
    554 	var configFiles []string
    555 	var credentialsFiles []string
    556 	var ok bool
    557 	var err error
    558 
    559 	profile, ok, err = getSharedConfigProfile(ctx, configs)
    560 	if err != nil {
    561 		return nil, err
    562 	}
    563 	if !ok {
    564 		profile = defaultSharedConfigProfile
    565 	}
    566 
    567 	configFiles, ok, err = getSharedConfigFiles(ctx, configs)
    568 	if err != nil {
    569 		return nil, err
    570 	}
    571 
    572 	credentialsFiles, ok, err = getSharedCredentialsFiles(ctx, configs)
    573 	if err != nil {
    574 		return nil, err
    575 	}
    576 
    577 	// setup logger if log configuration warning is seti
    578 	var logger logging.Logger
    579 	logWarnings, found, err := getLogConfigurationWarnings(ctx, configs)
    580 	if err != nil {
    581 		return SharedConfig{}, err
    582 	}
    583 	if found && logWarnings {
    584 		logger, found, err = getLogger(ctx, configs)
    585 		if err != nil {
    586 			return SharedConfig{}, err
    587 		}
    588 		if !found {
    589 			logger = logging.NewStandardLogger(os.Stderr)
    590 		}
    591 	}
    592 
    593 	return LoadSharedConfigProfile(ctx, profile,
    594 		func(o *LoadSharedConfigOptions) {
    595 			o.Logger = logger
    596 			o.ConfigFiles = configFiles
    597 			o.CredentialsFiles = credentialsFiles
    598 		},
    599 	)
    600 }
    601 
    602 // LoadSharedConfigOptions struct contains optional values that can be used to load the config.
    603 type LoadSharedConfigOptions struct {
    604 
    605 	// CredentialsFiles are the shared credentials files
    606 	CredentialsFiles []string
    607 
    608 	// ConfigFiles are the shared config files
    609 	ConfigFiles []string
    610 
    611 	// Logger is the logger used to log shared config behavior
    612 	Logger logging.Logger
    613 }
    614 
    615 // LoadSharedConfigProfile retrieves the configuration from the list of files
    616 // using the profile provided. The order the files are listed will determine
    617 // precedence. Values in subsequent files will overwrite values defined in
    618 // earlier files.
    619 //
    620 // For example, given two files A and B. Both define credentials. If the order
    621 // of the files are A then B, B's credential values will be used instead of A's.
    622 //
    623 // If config files are not set, SDK will default to using a file at location `.aws/config` if present.
    624 // If credentials files are not set, SDK will default to using a file at location `.aws/credentials` if present.
    625 // No default files are set, if files set to an empty slice.
    626 //
    627 // You can read more about shared config and credentials file location at
    628 // https://docs.aws.amazon.com/credref/latest/refdocs/file-location.html#file-location
    629 func LoadSharedConfigProfile(ctx context.Context, profile string, optFns ...func(*LoadSharedConfigOptions)) (SharedConfig, error) {
    630 	var option LoadSharedConfigOptions
    631 	for _, fn := range optFns {
    632 		fn(&option)
    633 	}
    634 
    635 	if option.ConfigFiles == nil {
    636 		option.ConfigFiles = DefaultSharedConfigFiles
    637 	}
    638 
    639 	if option.CredentialsFiles == nil {
    640 		option.CredentialsFiles = DefaultSharedCredentialsFiles
    641 	}
    642 
    643 	// load shared configuration sections from shared configuration INI options
    644 	configSections, err := loadIniFiles(option.ConfigFiles)
    645 	if err != nil {
    646 		return SharedConfig{}, err
    647 	}
    648 
    649 	// check for profile prefix and drop duplicates or invalid profiles
    650 	err = processConfigSections(ctx, &configSections, option.Logger)
    651 	if err != nil {
    652 		return SharedConfig{}, err
    653 	}
    654 
    655 	// load shared credentials sections from shared credentials INI options
    656 	credentialsSections, err := loadIniFiles(option.CredentialsFiles)
    657 	if err != nil {
    658 		return SharedConfig{}, err
    659 	}
    660 
    661 	// check for profile prefix and drop duplicates or invalid profiles
    662 	err = processCredentialsSections(ctx, &credentialsSections, option.Logger)
    663 	if err != nil {
    664 		return SharedConfig{}, err
    665 	}
    666 
    667 	err = mergeSections(&configSections, credentialsSections)
    668 	if err != nil {
    669 		return SharedConfig{}, err
    670 	}
    671 
    672 	cfg := SharedConfig{}
    673 	profiles := map[string]struct{}{}
    674 
    675 	if err = cfg.setFromIniSections(profiles, profile, configSections, option.Logger); err != nil {
    676 		return SharedConfig{}, err
    677 	}
    678 
    679 	return cfg, nil
    680 }
    681 
    682 func processConfigSections(ctx context.Context, sections *ini.Sections, logger logging.Logger) error {
    683 	skipSections := map[string]struct{}{}
    684 
    685 	for _, section := range sections.List() {
    686 		if _, ok := skipSections[section]; ok {
    687 			continue
    688 		}
    689 
    690 		// drop sections from config file that do not have expected prefixes.
    691 		switch {
    692 		case strings.HasPrefix(section, profilePrefix):
    693 			// Rename sections to remove "profile " prefixing to match with
    694 			// credentials file. If default is already present, it will be
    695 			// dropped.
    696 			newName, err := renameProfileSection(section, sections, logger)
    697 			if err != nil {
    698 				return fmt.Errorf("failed to rename profile section, %w", err)
    699 			}
    700 			skipSections[newName] = struct{}{}
    701 
    702 		case strings.HasPrefix(section, ssoSectionPrefix):
    703 		case strings.HasPrefix(section, servicesPrefix):
    704 		case strings.EqualFold(section, "default"):
    705 		default:
    706 			// drop this section, as invalid profile name
    707 			sections.DeleteSection(section)
    708 
    709 			if logger != nil {
    710 				logger.Logf(logging.Debug, "A profile defined with name `%v` is ignored. "+
    711 					"For use within a shared configuration file, "+
    712 					"a non-default profile must have `profile ` "+
    713 					"prefixed to the profile name.",
    714 					section,
    715 				)
    716 			}
    717 		}
    718 	}
    719 	return nil
    720 }
    721 
    722 func renameProfileSection(section string, sections *ini.Sections, logger logging.Logger) (string, error) {
    723 	v, ok := sections.GetSection(section)
    724 	if !ok {
    725 		return "", fmt.Errorf("error processing profiles within the shared configuration files")
    726 	}
    727 
    728 	// delete section with profile as prefix
    729 	sections.DeleteSection(section)
    730 
    731 	// set the value to non-prefixed name in sections.
    732 	section = strings.TrimPrefix(section, profilePrefix)
    733 	if sections.HasSection(section) {
    734 		oldSection, _ := sections.GetSection(section)
    735 		v.Logs = append(v.Logs,
    736 			fmt.Sprintf("A non-default profile not prefixed with `profile ` found in %s, "+
    737 				"overriding non-default profile from %s",
    738 				v.SourceFile, oldSection.SourceFile))
    739 		sections.DeleteSection(section)
    740 	}
    741 
    742 	// assign non-prefixed name to section
    743 	v.Name = section
    744 	sections.SetSection(section, v)
    745 
    746 	return section, nil
    747 }
    748 
    749 func processCredentialsSections(ctx context.Context, sections *ini.Sections, logger logging.Logger) error {
    750 	for _, section := range sections.List() {
    751 		// drop profiles with prefix for credential files
    752 		if strings.HasPrefix(section, profilePrefix) {
    753 			// drop this section, as invalid profile name
    754 			sections.DeleteSection(section)
    755 
    756 			if logger != nil {
    757 				logger.Logf(logging.Debug,
    758 					"The profile defined with name `%v` is ignored. A profile with the `profile ` prefix is invalid "+
    759 						"for the shared credentials file.\n",
    760 					section,
    761 				)
    762 			}
    763 		}
    764 	}
    765 	return nil
    766 }
    767 
    768 func loadIniFiles(filenames []string) (ini.Sections, error) {
    769 	mergedSections := ini.NewSections()
    770 
    771 	for _, filename := range filenames {
    772 		sections, err := ini.OpenFile(filename)
    773 		var v *ini.UnableToReadFile
    774 		if ok := errors.As(err, &v); ok {
    775 			// Skip files which can't be opened and read for whatever reason.
    776 			// We treat such files as empty, and do not fall back to other locations.
    777 			continue
    778 		} else if err != nil {
    779 			return ini.Sections{}, SharedConfigLoadError{Filename: filename, Err: err}
    780 		}
    781 
    782 		// mergeSections into mergedSections
    783 		err = mergeSections(&mergedSections, sections)
    784 		if err != nil {
    785 			return ini.Sections{}, SharedConfigLoadError{Filename: filename, Err: err}
    786 		}
    787 	}
    788 
    789 	return mergedSections, nil
    790 }
    791 
    792 // mergeSections merges source section properties into destination section properties
    793 func mergeSections(dst *ini.Sections, src ini.Sections) error {
    794 	for _, sectionName := range src.List() {
    795 		srcSection, _ := src.GetSection(sectionName)
    796 
    797 		if (!srcSection.Has(accessKeyIDKey) && srcSection.Has(secretAccessKey)) ||
    798 			(srcSection.Has(accessKeyIDKey) && !srcSection.Has(secretAccessKey)) {
    799 			srcSection.Errors = append(srcSection.Errors,
    800 				fmt.Errorf("partial credentials found for profile %v", sectionName))
    801 		}
    802 
    803 		if !dst.HasSection(sectionName) {
    804 			dst.SetSection(sectionName, srcSection)
    805 			continue
    806 		}
    807 
    808 		// merge with destination srcSection
    809 		dstSection, _ := dst.GetSection(sectionName)
    810 
    811 		// errors should be overriden if any
    812 		dstSection.Errors = srcSection.Errors
    813 
    814 		// Access key id update
    815 		if srcSection.Has(accessKeyIDKey) && srcSection.Has(secretAccessKey) {
    816 			accessKey := srcSection.String(accessKeyIDKey)
    817 			secretKey := srcSection.String(secretAccessKey)
    818 
    819 			if dstSection.Has(accessKeyIDKey) {
    820 				dstSection.Logs = append(dstSection.Logs, newMergeKeyLogMessage(sectionName, accessKeyIDKey,
    821 					dstSection.SourceFile[accessKeyIDKey], srcSection.SourceFile[accessKeyIDKey]))
    822 			}
    823 
    824 			// update access key
    825 			v, err := ini.NewStringValue(accessKey)
    826 			if err != nil {
    827 				return fmt.Errorf("error merging access key, %w", err)
    828 			}
    829 			dstSection.UpdateValue(accessKeyIDKey, v)
    830 
    831 			// update secret key
    832 			v, err = ini.NewStringValue(secretKey)
    833 			if err != nil {
    834 				return fmt.Errorf("error merging secret key, %w", err)
    835 			}
    836 			dstSection.UpdateValue(secretAccessKey, v)
    837 
    838 			// update session token
    839 			if err = mergeStringKey(&srcSection, &dstSection, sectionName, sessionTokenKey); err != nil {
    840 				return err
    841 			}
    842 
    843 			// update source file to reflect where the static creds came from
    844 			dstSection.UpdateSourceFile(accessKeyIDKey, srcSection.SourceFile[accessKeyIDKey])
    845 			dstSection.UpdateSourceFile(secretAccessKey, srcSection.SourceFile[secretAccessKey])
    846 		}
    847 
    848 		stringKeys := []string{
    849 			roleArnKey,
    850 			sourceProfileKey,
    851 			credentialSourceKey,
    852 			externalIDKey,
    853 			mfaSerialKey,
    854 			roleSessionNameKey,
    855 			regionKey,
    856 			enableEndpointDiscoveryKey,
    857 			credentialProcessKey,
    858 			webIdentityTokenFileKey,
    859 			s3UseARNRegionKey,
    860 			s3DisableMultiRegionAccessPointsKey,
    861 			ec2MetadataServiceEndpointModeKey,
    862 			ec2MetadataServiceEndpointKey,
    863 			ec2MetadataV1DisabledKey,
    864 			useDualStackEndpoint,
    865 			useFIPSEndpointKey,
    866 			defaultsModeKey,
    867 			retryModeKey,
    868 			caBundleKey,
    869 			roleDurationSecondsKey,
    870 			retryMaxAttemptsKey,
    871 
    872 			ssoSessionNameKey,
    873 			ssoAccountIDKey,
    874 			ssoRegionKey,
    875 			ssoRoleNameKey,
    876 			ssoStartURLKey,
    877 		}
    878 		for i := range stringKeys {
    879 			if err := mergeStringKey(&srcSection, &dstSection, sectionName, stringKeys[i]); err != nil {
    880 				return err
    881 			}
    882 		}
    883 
    884 		// set srcSection on dst srcSection
    885 		*dst = dst.SetSection(sectionName, dstSection)
    886 	}
    887 
    888 	return nil
    889 }
    890 
    891 func mergeStringKey(srcSection *ini.Section, dstSection *ini.Section, sectionName, key string) error {
    892 	if srcSection.Has(key) {
    893 		srcValue := srcSection.String(key)
    894 		val, err := ini.NewStringValue(srcValue)
    895 		if err != nil {
    896 			return fmt.Errorf("error merging %s, %w", key, err)
    897 		}
    898 
    899 		if dstSection.Has(key) {
    900 			dstSection.Logs = append(dstSection.Logs, newMergeKeyLogMessage(sectionName, key,
    901 				dstSection.SourceFile[key], srcSection.SourceFile[key]))
    902 		}
    903 
    904 		dstSection.UpdateValue(key, val)
    905 		dstSection.UpdateSourceFile(key, srcSection.SourceFile[key])
    906 	}
    907 	return nil
    908 }
    909 
    910 func newMergeKeyLogMessage(sectionName, key, dstSourceFile, srcSourceFile string) string {
    911 	return fmt.Sprintf("For profile: %v, overriding %v value, defined in %v "+
    912 		"with a %v value found in a duplicate profile defined at file %v. \n",
    913 		sectionName, key, dstSourceFile, key, srcSourceFile)
    914 }
    915 
    916 // Returns an error if all of the files fail to load. If at least one file is
    917 // successfully loaded and contains the profile, no error will be returned.
    918 func (c *SharedConfig) setFromIniSections(profiles map[string]struct{}, profile string,
    919 	sections ini.Sections, logger logging.Logger) error {
    920 	c.Profile = profile
    921 
    922 	section, ok := sections.GetSection(profile)
    923 	if !ok {
    924 		return SharedConfigProfileNotExistError{
    925 			Profile: profile,
    926 		}
    927 	}
    928 
    929 	// if logs are appended to the section, log them
    930 	if section.Logs != nil && logger != nil {
    931 		for _, log := range section.Logs {
    932 			logger.Logf(logging.Debug, log)
    933 		}
    934 	}
    935 
    936 	// set config from the provided INI section
    937 	err := c.setFromIniSection(profile, section)
    938 	if err != nil {
    939 		return fmt.Errorf("error fetching config from profile, %v, %w", profile, err)
    940 	}
    941 
    942 	if _, ok := profiles[profile]; ok {
    943 		// if this is the second instance of the profile the Assume Role
    944 		// options must be cleared because they are only valid for the
    945 		// first reference of a profile. The self linked instance of the
    946 		// profile only have credential provider options.
    947 		c.clearAssumeRoleOptions()
    948 	} else {
    949 		// First time a profile has been seen. Assert if the credential type
    950 		// requires a role ARN, the ARN is also set
    951 		if err := c.validateCredentialsConfig(profile); err != nil {
    952 			return err
    953 		}
    954 	}
    955 
    956 	// if not top level profile and has credentials, return with credentials.
    957 	if len(profiles) != 0 && c.Credentials.HasKeys() {
    958 		return nil
    959 	}
    960 
    961 	profiles[profile] = struct{}{}
    962 
    963 	// validate no colliding credentials type are present
    964 	if err := c.validateCredentialType(); err != nil {
    965 		return err
    966 	}
    967 
    968 	// Link source profiles for assume roles
    969 	if len(c.SourceProfileName) != 0 {
    970 		// Linked profile via source_profile ignore credential provider
    971 		// options, the source profile must provide the credentials.
    972 		c.clearCredentialOptions()
    973 
    974 		srcCfg := &SharedConfig{}
    975 		err := srcCfg.setFromIniSections(profiles, c.SourceProfileName, sections, logger)
    976 		if err != nil {
    977 			// SourceProfileName that doesn't exist is an error in configuration.
    978 			if _, ok := err.(SharedConfigProfileNotExistError); ok {
    979 				err = SharedConfigAssumeRoleError{
    980 					RoleARN: c.RoleARN,
    981 					Profile: c.SourceProfileName,
    982 					Err:     err,
    983 				}
    984 			}
    985 			return err
    986 		}
    987 
    988 		if !srcCfg.hasCredentials() {
    989 			return SharedConfigAssumeRoleError{
    990 				RoleARN: c.RoleARN,
    991 				Profile: c.SourceProfileName,
    992 			}
    993 		}
    994 
    995 		c.Source = srcCfg
    996 	}
    997 
    998 	// If the profile contains an SSO session parameter, the session MUST exist
    999 	// as a section in the config file. Load the SSO session using the name
   1000 	// provided. If the session section is not found or incomplete an error
   1001 	// will be returned.
   1002 	if c.hasSSOTokenProviderConfiguration() {
   1003 		section, ok := sections.GetSection(ssoSectionPrefix + strings.TrimSpace(c.SSOSessionName))
   1004 		if !ok {
   1005 			return fmt.Errorf("failed to find SSO session section, %v", c.SSOSessionName)
   1006 		}
   1007 		var ssoSession SSOSession
   1008 		ssoSession.setFromIniSection(section)
   1009 		ssoSession.Name = c.SSOSessionName
   1010 		c.SSOSession = &ssoSession
   1011 	}
   1012 
   1013 	if len(c.ServicesSectionName) > 0 {
   1014 		if section, ok := sections.GetSection(servicesPrefix + c.ServicesSectionName); ok {
   1015 			var svcs Services
   1016 			svcs.setFromIniSection(section)
   1017 			c.Services = svcs
   1018 		}
   1019 	}
   1020 
   1021 	return nil
   1022 }
   1023 
   1024 // setFromIniSection loads the configuration from the profile section defined in
   1025 // the provided INI file. A SharedConfig pointer type value is used so that
   1026 // multiple config file loadings can be chained.
   1027 //
   1028 // Only loads complete logically grouped values, and will not set fields in cfg
   1029 // for incomplete grouped values in the config. Such as credentials. For example
   1030 // if a config file only includes aws_access_key_id but no aws_secret_access_key
   1031 // the aws_access_key_id will be ignored.
   1032 func (c *SharedConfig) setFromIniSection(profile string, section ini.Section) error {
   1033 	if len(section.Name) == 0 {
   1034 		sources := make([]string, 0)
   1035 		for _, v := range section.SourceFile {
   1036 			sources = append(sources, v)
   1037 		}
   1038 
   1039 		return fmt.Errorf("parsing error : could not find profile section name after processing files: %v", sources)
   1040 	}
   1041 
   1042 	if len(section.Errors) != 0 {
   1043 		var errStatement string
   1044 		for i, e := range section.Errors {
   1045 			errStatement = fmt.Sprintf("%d, %v\n", i+1, e.Error())
   1046 		}
   1047 		return fmt.Errorf("Error using profile: \n %v", errStatement)
   1048 	}
   1049 
   1050 	// Assume Role
   1051 	updateString(&c.RoleARN, section, roleArnKey)
   1052 	updateString(&c.ExternalID, section, externalIDKey)
   1053 	updateString(&c.MFASerial, section, mfaSerialKey)
   1054 	updateString(&c.RoleSessionName, section, roleSessionNameKey)
   1055 	updateString(&c.SourceProfileName, section, sourceProfileKey)
   1056 	updateString(&c.CredentialSource, section, credentialSourceKey)
   1057 	updateString(&c.Region, section, regionKey)
   1058 
   1059 	// AWS Single Sign-On (AWS SSO)
   1060 	// SSO session options
   1061 	updateString(&c.SSOSessionName, section, ssoSessionNameKey)
   1062 
   1063 	// Legacy SSO session options
   1064 	updateString(&c.SSORegion, section, ssoRegionKey)
   1065 	updateString(&c.SSOStartURL, section, ssoStartURLKey)
   1066 
   1067 	// SSO fields not used
   1068 	updateString(&c.SSOAccountID, section, ssoAccountIDKey)
   1069 	updateString(&c.SSORoleName, section, ssoRoleNameKey)
   1070 
   1071 	// we're retaining a behavioral quirk with this field that existed before
   1072 	// the removal of literal parsing for #2276:
   1073 	//   - if the key is missing, the config field will not be set
   1074 	//   - if the key is set to a non-numeric, the config field will be set to 0
   1075 	if section.Has(roleDurationSecondsKey) {
   1076 		if v, ok := section.Int(roleDurationSecondsKey); ok {
   1077 			c.RoleDurationSeconds = aws.Duration(time.Duration(v) * time.Second)
   1078 		} else {
   1079 			c.RoleDurationSeconds = aws.Duration(time.Duration(0))
   1080 		}
   1081 	}
   1082 
   1083 	updateString(&c.CredentialProcess, section, credentialProcessKey)
   1084 	updateString(&c.WebIdentityTokenFile, section, webIdentityTokenFileKey)
   1085 
   1086 	updateEndpointDiscoveryType(&c.EnableEndpointDiscovery, section, enableEndpointDiscoveryKey)
   1087 	updateBoolPtr(&c.S3UseARNRegion, section, s3UseARNRegionKey)
   1088 	updateBoolPtr(&c.S3DisableMultiRegionAccessPoints, section, s3DisableMultiRegionAccessPointsKey)
   1089 	updateBoolPtr(&c.S3DisableExpressAuth, section, s3DisableExpressSessionAuthKey)
   1090 
   1091 	if err := updateEC2MetadataServiceEndpointMode(&c.EC2IMDSEndpointMode, section, ec2MetadataServiceEndpointModeKey); err != nil {
   1092 		return fmt.Errorf("failed to load %s from shared config, %v", ec2MetadataServiceEndpointModeKey, err)
   1093 	}
   1094 	updateString(&c.EC2IMDSEndpoint, section, ec2MetadataServiceEndpointKey)
   1095 	updateBoolPtr(&c.EC2IMDSv1Disabled, section, ec2MetadataV1DisabledKey)
   1096 
   1097 	updateUseDualStackEndpoint(&c.UseDualStackEndpoint, section, useDualStackEndpoint)
   1098 	updateUseFIPSEndpoint(&c.UseFIPSEndpoint, section, useFIPSEndpointKey)
   1099 
   1100 	if err := updateDefaultsMode(&c.DefaultsMode, section, defaultsModeKey); err != nil {
   1101 		return fmt.Errorf("failed to load %s from shared config, %w", defaultsModeKey, err)
   1102 	}
   1103 
   1104 	if err := updateInt(&c.RetryMaxAttempts, section, retryMaxAttemptsKey); err != nil {
   1105 		return fmt.Errorf("failed to load %s from shared config, %w", retryMaxAttemptsKey, err)
   1106 	}
   1107 	if err := updateRetryMode(&c.RetryMode, section, retryModeKey); err != nil {
   1108 		return fmt.Errorf("failed to load %s from shared config, %w", retryModeKey, err)
   1109 	}
   1110 
   1111 	updateString(&c.CustomCABundle, section, caBundleKey)
   1112 
   1113 	// user agent app ID added to request User-Agent header
   1114 	updateString(&c.AppID, section, sdkAppID)
   1115 
   1116 	updateBoolPtr(&c.IgnoreConfiguredEndpoints, section, ignoreConfiguredEndpoints)
   1117 
   1118 	updateString(&c.BaseEndpoint, section, endpointURL)
   1119 
   1120 	if err := updateDisableRequestCompression(&c.DisableRequestCompression, section, disableRequestCompression); err != nil {
   1121 		return fmt.Errorf("failed to load %s from shared config, %w", disableRequestCompression, err)
   1122 	}
   1123 	if err := updateRequestMinCompressSizeBytes(&c.RequestMinCompressSizeBytes, section, requestMinCompressionSizeBytes); err != nil {
   1124 		return fmt.Errorf("failed to load %s from shared config, %w", requestMinCompressionSizeBytes, err)
   1125 	}
   1126 
   1127 	// Shared Credentials
   1128 	creds := aws.Credentials{
   1129 		AccessKeyID:     section.String(accessKeyIDKey),
   1130 		SecretAccessKey: section.String(secretAccessKey),
   1131 		SessionToken:    section.String(sessionTokenKey),
   1132 		Source:          fmt.Sprintf("SharedConfigCredentials: %s", section.SourceFile[accessKeyIDKey]),
   1133 	}
   1134 
   1135 	if creds.HasKeys() {
   1136 		c.Credentials = creds
   1137 	}
   1138 
   1139 	updateString(&c.ServicesSectionName, section, servicesSectionKey)
   1140 
   1141 	return nil
   1142 }
   1143 
   1144 func updateRequestMinCompressSizeBytes(bytes **int64, sec ini.Section, key string) error {
   1145 	if !sec.Has(key) {
   1146 		return nil
   1147 	}
   1148 
   1149 	v, ok := sec.Int(key)
   1150 	if !ok {
   1151 		return fmt.Errorf("invalid value for min request compression size bytes %s, need int64", sec.String(key))
   1152 	}
   1153 	if v < 0 || v > smithyrequestcompression.MaxRequestMinCompressSizeBytes {
   1154 		return fmt.Errorf("invalid range for min request compression size bytes %d, must be within 0 and 10485760 inclusively", v)
   1155 	}
   1156 	*bytes = new(int64)
   1157 	**bytes = v
   1158 	return nil
   1159 }
   1160 
   1161 func updateDisableRequestCompression(disable **bool, sec ini.Section, key string) error {
   1162 	if !sec.Has(key) {
   1163 		return nil
   1164 	}
   1165 
   1166 	v := sec.String(key)
   1167 	switch {
   1168 	case v == "true":
   1169 		*disable = new(bool)
   1170 		**disable = true
   1171 	case v == "false":
   1172 		*disable = new(bool)
   1173 		**disable = false
   1174 	default:
   1175 		return fmt.Errorf("invalid value for shared config profile field, %s=%s, need true or false", key, v)
   1176 	}
   1177 	return nil
   1178 }
   1179 
   1180 func (c SharedConfig) getRequestMinCompressSizeBytes(ctx context.Context) (int64, bool, error) {
   1181 	if c.RequestMinCompressSizeBytes == nil {
   1182 		return 0, false, nil
   1183 	}
   1184 	return *c.RequestMinCompressSizeBytes, true, nil
   1185 }
   1186 
   1187 func (c SharedConfig) getDisableRequestCompression(ctx context.Context) (bool, bool, error) {
   1188 	if c.DisableRequestCompression == nil {
   1189 		return false, false, nil
   1190 	}
   1191 	return *c.DisableRequestCompression, true, nil
   1192 }
   1193 
   1194 func updateDefaultsMode(mode *aws.DefaultsMode, section ini.Section, key string) error {
   1195 	if !section.Has(key) {
   1196 		return nil
   1197 	}
   1198 	value := section.String(key)
   1199 	if ok := mode.SetFromString(value); !ok {
   1200 		return fmt.Errorf("invalid value: %s", value)
   1201 	}
   1202 	return nil
   1203 }
   1204 
   1205 func updateRetryMode(mode *aws.RetryMode, section ini.Section, key string) (err error) {
   1206 	if !section.Has(key) {
   1207 		return nil
   1208 	}
   1209 	value := section.String(key)
   1210 	if *mode, err = aws.ParseRetryMode(value); err != nil {
   1211 		return err
   1212 	}
   1213 	return nil
   1214 }
   1215 
   1216 func updateEC2MetadataServiceEndpointMode(endpointMode *imds.EndpointModeState, section ini.Section, key string) error {
   1217 	if !section.Has(key) {
   1218 		return nil
   1219 	}
   1220 	value := section.String(key)
   1221 	return endpointMode.SetFromString(value)
   1222 }
   1223 
   1224 func (c *SharedConfig) validateCredentialsConfig(profile string) error {
   1225 	if err := c.validateCredentialsRequireARN(profile); err != nil {
   1226 		return err
   1227 	}
   1228 
   1229 	return nil
   1230 }
   1231 
   1232 func (c *SharedConfig) validateCredentialsRequireARN(profile string) error {
   1233 	var credSource string
   1234 
   1235 	switch {
   1236 	case len(c.SourceProfileName) != 0:
   1237 		credSource = sourceProfileKey
   1238 	case len(c.CredentialSource) != 0:
   1239 		credSource = credentialSourceKey
   1240 	case len(c.WebIdentityTokenFile) != 0:
   1241 		credSource = webIdentityTokenFileKey
   1242 	}
   1243 
   1244 	if len(credSource) != 0 && len(c.RoleARN) == 0 {
   1245 		return CredentialRequiresARNError{
   1246 			Type:    credSource,
   1247 			Profile: profile,
   1248 		}
   1249 	}
   1250 
   1251 	return nil
   1252 }
   1253 
   1254 func (c *SharedConfig) validateCredentialType() error {
   1255 	// Only one or no credential type can be defined.
   1256 	if !oneOrNone(
   1257 		len(c.SourceProfileName) != 0,
   1258 		len(c.CredentialSource) != 0,
   1259 		len(c.CredentialProcess) != 0,
   1260 		len(c.WebIdentityTokenFile) != 0,
   1261 	) {
   1262 		return fmt.Errorf("only one credential type may be specified per profile: source profile, credential source, credential process, web identity token")
   1263 	}
   1264 
   1265 	return nil
   1266 }
   1267 
   1268 func (c *SharedConfig) validateSSOConfiguration() error {
   1269 	if c.hasSSOTokenProviderConfiguration() {
   1270 		err := c.validateSSOTokenProviderConfiguration()
   1271 		if err != nil {
   1272 			return err
   1273 		}
   1274 		return nil
   1275 	}
   1276 
   1277 	if c.hasLegacySSOConfiguration() {
   1278 		err := c.validateLegacySSOConfiguration()
   1279 		if err != nil {
   1280 			return err
   1281 		}
   1282 	}
   1283 	return nil
   1284 }
   1285 
   1286 func (c *SharedConfig) validateSSOTokenProviderConfiguration() error {
   1287 	var missing []string
   1288 
   1289 	if len(c.SSOSessionName) == 0 {
   1290 		missing = append(missing, ssoSessionNameKey)
   1291 	}
   1292 
   1293 	if c.SSOSession == nil {
   1294 		missing = append(missing, ssoSectionPrefix)
   1295 	} else {
   1296 		if len(c.SSOSession.SSORegion) == 0 {
   1297 			missing = append(missing, ssoRegionKey)
   1298 		}
   1299 
   1300 		if len(c.SSOSession.SSOStartURL) == 0 {
   1301 			missing = append(missing, ssoStartURLKey)
   1302 		}
   1303 	}
   1304 
   1305 	if len(missing) > 0 {
   1306 		return fmt.Errorf("profile %q is configured to use SSO but is missing required configuration: %s",
   1307 			c.Profile, strings.Join(missing, ", "))
   1308 	}
   1309 
   1310 	if len(c.SSORegion) > 0 && c.SSORegion != c.SSOSession.SSORegion {
   1311 		return fmt.Errorf("%s in profile %q must match %s in %s", ssoRegionKey, c.Profile, ssoRegionKey, ssoSectionPrefix)
   1312 	}
   1313 
   1314 	if len(c.SSOStartURL) > 0 && c.SSOStartURL != c.SSOSession.SSOStartURL {
   1315 		return fmt.Errorf("%s in profile %q must match %s in %s", ssoStartURLKey, c.Profile, ssoStartURLKey, ssoSectionPrefix)
   1316 	}
   1317 
   1318 	return nil
   1319 }
   1320 
   1321 func (c *SharedConfig) validateLegacySSOConfiguration() error {
   1322 	var missing []string
   1323 
   1324 	if len(c.SSORegion) == 0 {
   1325 		missing = append(missing, ssoRegionKey)
   1326 	}
   1327 
   1328 	if len(c.SSOStartURL) == 0 {
   1329 		missing = append(missing, ssoStartURLKey)
   1330 	}
   1331 
   1332 	if len(c.SSOAccountID) == 0 {
   1333 		missing = append(missing, ssoAccountIDKey)
   1334 	}
   1335 
   1336 	if len(c.SSORoleName) == 0 {
   1337 		missing = append(missing, ssoRoleNameKey)
   1338 	}
   1339 
   1340 	if len(missing) > 0 {
   1341 		return fmt.Errorf("profile %q is configured to use SSO but is missing required configuration: %s",
   1342 			c.Profile, strings.Join(missing, ", "))
   1343 	}
   1344 	return nil
   1345 }
   1346 
   1347 func (c *SharedConfig) hasCredentials() bool {
   1348 	switch {
   1349 	case len(c.SourceProfileName) != 0:
   1350 	case len(c.CredentialSource) != 0:
   1351 	case len(c.CredentialProcess) != 0:
   1352 	case len(c.WebIdentityTokenFile) != 0:
   1353 	case c.hasSSOConfiguration():
   1354 	case c.Credentials.HasKeys():
   1355 	default:
   1356 		return false
   1357 	}
   1358 
   1359 	return true
   1360 }
   1361 
   1362 func (c *SharedConfig) hasSSOConfiguration() bool {
   1363 	return c.hasSSOTokenProviderConfiguration() || c.hasLegacySSOConfiguration()
   1364 }
   1365 
   1366 func (c *SharedConfig) hasSSOTokenProviderConfiguration() bool {
   1367 	return len(c.SSOSessionName) > 0
   1368 }
   1369 
   1370 func (c *SharedConfig) hasLegacySSOConfiguration() bool {
   1371 	return len(c.SSORegion) > 0 || len(c.SSOAccountID) > 0 || len(c.SSOStartURL) > 0 || len(c.SSORoleName) > 0
   1372 }
   1373 
   1374 func (c *SharedConfig) clearAssumeRoleOptions() {
   1375 	c.RoleARN = ""
   1376 	c.ExternalID = ""
   1377 	c.MFASerial = ""
   1378 	c.RoleSessionName = ""
   1379 	c.SourceProfileName = ""
   1380 }
   1381 
   1382 func (c *SharedConfig) clearCredentialOptions() {
   1383 	c.CredentialSource = ""
   1384 	c.CredentialProcess = ""
   1385 	c.WebIdentityTokenFile = ""
   1386 	c.Credentials = aws.Credentials{}
   1387 	c.SSOAccountID = ""
   1388 	c.SSORegion = ""
   1389 	c.SSORoleName = ""
   1390 	c.SSOStartURL = ""
   1391 }
   1392 
   1393 // SharedConfigLoadError is an error for the shared config file failed to load.
   1394 type SharedConfigLoadError struct {
   1395 	Filename string
   1396 	Err      error
   1397 }
   1398 
   1399 // Unwrap returns the underlying error that caused the failure.
   1400 func (e SharedConfigLoadError) Unwrap() error {
   1401 	return e.Err
   1402 }
   1403 
   1404 func (e SharedConfigLoadError) Error() string {
   1405 	return fmt.Sprintf("failed to load shared config file, %s, %v", e.Filename, e.Err)
   1406 }
   1407 
   1408 // SharedConfigProfileNotExistError is an error for the shared config when
   1409 // the profile was not find in the config file.
   1410 type SharedConfigProfileNotExistError struct {
   1411 	Filename []string
   1412 	Profile  string
   1413 	Err      error
   1414 }
   1415 
   1416 // Unwrap returns the underlying error that caused the failure.
   1417 func (e SharedConfigProfileNotExistError) Unwrap() error {
   1418 	return e.Err
   1419 }
   1420 
   1421 func (e SharedConfigProfileNotExistError) Error() string {
   1422 	return fmt.Sprintf("failed to get shared config profile, %s", e.Profile)
   1423 }
   1424 
   1425 // SharedConfigAssumeRoleError is an error for the shared config when the
   1426 // profile contains assume role information, but that information is invalid
   1427 // or not complete.
   1428 type SharedConfigAssumeRoleError struct {
   1429 	Profile string
   1430 	RoleARN string
   1431 	Err     error
   1432 }
   1433 
   1434 // Unwrap returns the underlying error that caused the failure.
   1435 func (e SharedConfigAssumeRoleError) Unwrap() error {
   1436 	return e.Err
   1437 }
   1438 
   1439 func (e SharedConfigAssumeRoleError) Error() string {
   1440 	return fmt.Sprintf("failed to load assume role %s, of profile %s, %v",
   1441 		e.RoleARN, e.Profile, e.Err)
   1442 }
   1443 
   1444 // CredentialRequiresARNError provides the error for shared config credentials
   1445 // that are incorrectly configured in the shared config or credentials file.
   1446 type CredentialRequiresARNError struct {
   1447 	// type of credentials that were configured.
   1448 	Type string
   1449 
   1450 	// Profile name the credentials were in.
   1451 	Profile string
   1452 }
   1453 
   1454 // Error satisfies the error interface.
   1455 func (e CredentialRequiresARNError) Error() string {
   1456 	return fmt.Sprintf(
   1457 		"credential type %s requires role_arn, profile %s",
   1458 		e.Type, e.Profile,
   1459 	)
   1460 }
   1461 
   1462 func oneOrNone(bs ...bool) bool {
   1463 	var count int
   1464 
   1465 	for _, b := range bs {
   1466 		if b {
   1467 			count++
   1468 			if count > 1 {
   1469 				return false
   1470 			}
   1471 		}
   1472 	}
   1473 
   1474 	return true
   1475 }
   1476 
   1477 // updateString will only update the dst with the value in the section key, key
   1478 // is present in the section.
   1479 func updateString(dst *string, section ini.Section, key string) {
   1480 	if !section.Has(key) {
   1481 		return
   1482 	}
   1483 	*dst = section.String(key)
   1484 }
   1485 
   1486 // updateInt will only update the dst with the value in the section key, key
   1487 // is present in the section.
   1488 //
   1489 // Down casts the INI integer value from a int64 to an int, which could be
   1490 // different bit size depending on platform.
   1491 func updateInt(dst *int, section ini.Section, key string) error {
   1492 	if !section.Has(key) {
   1493 		return nil
   1494 	}
   1495 
   1496 	v, ok := section.Int(key)
   1497 	if !ok {
   1498 		return fmt.Errorf("invalid value %s=%s, expect integer", key, section.String(key))
   1499 	}
   1500 
   1501 	*dst = int(v)
   1502 	return nil
   1503 }
   1504 
   1505 // updateBool will only update the dst with the value in the section key, key
   1506 // is present in the section.
   1507 func updateBool(dst *bool, section ini.Section, key string) {
   1508 	if !section.Has(key) {
   1509 		return
   1510 	}
   1511 
   1512 	// retains pre-#2276 behavior where non-bool value would resolve to false
   1513 	v, _ := section.Bool(key)
   1514 	*dst = v
   1515 }
   1516 
   1517 // updateBoolPtr will only update the dst with the value in the section key,
   1518 // key is present in the section.
   1519 func updateBoolPtr(dst **bool, section ini.Section, key string) {
   1520 	if !section.Has(key) {
   1521 		return
   1522 	}
   1523 
   1524 	// retains pre-#2276 behavior where non-bool value would resolve to false
   1525 	v, _ := section.Bool(key)
   1526 	*dst = new(bool)
   1527 	**dst = v
   1528 }
   1529 
   1530 // updateEndpointDiscoveryType will only update the dst with the value in the section, if
   1531 // a valid key and corresponding EndpointDiscoveryType is found.
   1532 func updateEndpointDiscoveryType(dst *aws.EndpointDiscoveryEnableState, section ini.Section, key string) {
   1533 	if !section.Has(key) {
   1534 		return
   1535 	}
   1536 
   1537 	value := section.String(key)
   1538 	if len(value) == 0 {
   1539 		return
   1540 	}
   1541 
   1542 	switch {
   1543 	case strings.EqualFold(value, endpointDiscoveryDisabled):
   1544 		*dst = aws.EndpointDiscoveryDisabled
   1545 	case strings.EqualFold(value, endpointDiscoveryEnabled):
   1546 		*dst = aws.EndpointDiscoveryEnabled
   1547 	case strings.EqualFold(value, endpointDiscoveryAuto):
   1548 		*dst = aws.EndpointDiscoveryAuto
   1549 	}
   1550 }
   1551 
   1552 // updateEndpointDiscoveryType will only update the dst with the value in the section, if
   1553 // a valid key and corresponding EndpointDiscoveryType is found.
   1554 func updateUseDualStackEndpoint(dst *aws.DualStackEndpointState, section ini.Section, key string) {
   1555 	if !section.Has(key) {
   1556 		return
   1557 	}
   1558 
   1559 	// retains pre-#2276 behavior where non-bool value would resolve to false
   1560 	if v, _ := section.Bool(key); v {
   1561 		*dst = aws.DualStackEndpointStateEnabled
   1562 	} else {
   1563 		*dst = aws.DualStackEndpointStateDisabled
   1564 	}
   1565 
   1566 	return
   1567 }
   1568 
   1569 // updateEndpointDiscoveryType will only update the dst with the value in the section, if
   1570 // a valid key and corresponding EndpointDiscoveryType is found.
   1571 func updateUseFIPSEndpoint(dst *aws.FIPSEndpointState, section ini.Section, key string) {
   1572 	if !section.Has(key) {
   1573 		return
   1574 	}
   1575 
   1576 	// retains pre-#2276 behavior where non-bool value would resolve to false
   1577 	if v, _ := section.Bool(key); v {
   1578 		*dst = aws.FIPSEndpointStateEnabled
   1579 	} else {
   1580 		*dst = aws.FIPSEndpointStateDisabled
   1581 	}
   1582 
   1583 	return
   1584 }