...

Source file src/github.com/aws/aws-sdk-go-v2/config/shared_config.go

Documentation: github.com/aws/aws-sdk-go-v2/config

     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  }
  1585  

View as plain text