shared_config.go raw

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