src

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

provider.go (6334B)


      1 // Package endpointcreds provides support for retrieving credentials from an
      2 // arbitrary HTTP endpoint.
      3 //
      4 // The credentials endpoint Provider can receive both static and refreshable
      5 // credentials that will expire. Credentials are static when an "Expiration"
      6 // value is not provided in the endpoint's response.
      7 //
      8 // Static credentials will never expire once they have been retrieved. The format
      9 // of the static credentials response:
     10 //
     11 //	{
     12 //	    "AccessKeyId" : "MUA...",
     13 //	    "SecretAccessKey" : "/7PC5om....",
     14 //	}
     15 //
     16 // Refreshable credentials will expire within the "ExpiryWindow" of the Expiration
     17 // value in the response. The format of the refreshable credentials response:
     18 //
     19 //	{
     20 //	    "AccessKeyId" : "MUA...",
     21 //	    "SecretAccessKey" : "/7PC5om....",
     22 //	    "Token" : "AQoDY....=",
     23 //	    "Expiration" : "2016-02-25T06:03:31Z"
     24 //	}
     25 //
     26 // Errors should be returned in the following format and only returned with 400
     27 // or 500 HTTP status codes.
     28 //
     29 //	{
     30 //	    "code": "ErrorCode",
     31 //	    "message": "Helpful error message."
     32 //	}
     33 package endpointcreds
     34 
     35 import (
     36 	"context"
     37 	"fmt"
     38 	"net/http"
     39 	"strings"
     40 
     41 	"github.com/aws/aws-sdk-go-v2/aws"
     42 	"github.com/aws/aws-sdk-go-v2/credentials/endpointcreds/internal/client"
     43 	"github.com/aws/smithy-go/middleware"
     44 )
     45 
     46 // ProviderName is the name of the credentials provider.
     47 const ProviderName = `CredentialsEndpointProvider`
     48 
     49 type getCredentialsAPIClient interface {
     50 	GetCredentials(context.Context, *client.GetCredentialsInput, ...func(*client.Options)) (*client.GetCredentialsOutput, error)
     51 }
     52 
     53 // Provider satisfies the aws.CredentialsProvider interface, and is a client to
     54 // retrieve credentials from an arbitrary endpoint.
     55 type Provider struct {
     56 	// The AWS Client to make HTTP requests to the endpoint with. The endpoint
     57 	// the request will be made to is provided by the aws.Config's
     58 	// EndpointResolver.
     59 	client getCredentialsAPIClient
     60 
     61 	options Options
     62 }
     63 
     64 // HTTPClient is a client for sending HTTP requests
     65 type HTTPClient interface {
     66 	Do(*http.Request) (*http.Response, error)
     67 }
     68 
     69 // Options is structure of configurable options for Provider
     70 type Options struct {
     71 	// Endpoint to retrieve credentials from. Required
     72 	Endpoint string
     73 
     74 	// HTTPClient to handle sending HTTP requests to the target endpoint.
     75 	HTTPClient HTTPClient
     76 
     77 	// Set of options to modify how the credentials operation is invoked.
     78 	APIOptions []func(*middleware.Stack) error
     79 
     80 	// The Retryer to be used for determining whether a failed requested should be retried
     81 	Retryer aws.Retryer
     82 
     83 	// Optional authorization token value if set will be used as the value of
     84 	// the Authorization header of the endpoint credential request.
     85 	//
     86 	// When constructed from environment, the provider will use the value of
     87 	// AWS_CONTAINER_AUTHORIZATION_TOKEN environment variable as the token
     88 	//
     89 	// Will be overridden if AuthorizationTokenProvider is configured
     90 	AuthorizationToken string
     91 
     92 	// Optional auth provider func to dynamically load the auth token from a file
     93 	// everytime a credential is retrieved
     94 	//
     95 	// When constructed from environment, the provider will read and use the content
     96 	// of the file pointed to by AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE environment variable
     97 	// as the auth token everytime credentials are retrieved
     98 	//
     99 	// Will override AuthorizationToken if configured
    100 	AuthorizationTokenProvider AuthTokenProvider
    101 
    102 	// The chain of providers that was used to create this provider
    103 	// These values are for reporting purposes and are not meant to be set up directly
    104 	CredentialSources []aws.CredentialSource
    105 }
    106 
    107 // AuthTokenProvider defines an interface to dynamically load a value to be passed
    108 // for the Authorization header of a credentials request.
    109 type AuthTokenProvider interface {
    110 	GetToken() (string, error)
    111 }
    112 
    113 // TokenProviderFunc is a func type implementing AuthTokenProvider interface
    114 // and enables customizing token provider behavior
    115 type TokenProviderFunc func() (string, error)
    116 
    117 // GetToken func retrieves auth token according to TokenProviderFunc implementation
    118 func (p TokenProviderFunc) GetToken() (string, error) {
    119 	return p()
    120 }
    121 
    122 // New returns a credentials Provider for retrieving AWS credentials
    123 // from arbitrary endpoint.
    124 func New(endpoint string, optFns ...func(*Options)) *Provider {
    125 	o := Options{
    126 		Endpoint: endpoint,
    127 	}
    128 
    129 	for _, fn := range optFns {
    130 		fn(&o)
    131 	}
    132 
    133 	p := &Provider{
    134 		client: client.New(client.Options{
    135 			HTTPClient: o.HTTPClient,
    136 			Endpoint:   o.Endpoint,
    137 			APIOptions: o.APIOptions,
    138 			Retryer:    o.Retryer,
    139 		}),
    140 		options: o,
    141 	}
    142 
    143 	return p
    144 }
    145 
    146 // Retrieve will attempt to request the credentials from the endpoint the Provider
    147 // was configured for. And error will be returned if the retrieval fails.
    148 func (p *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) {
    149 	resp, err := p.getCredentials(ctx)
    150 	if err != nil {
    151 		return aws.Credentials{}, fmt.Errorf("failed to load credentials, %w", err)
    152 	}
    153 
    154 	creds := aws.Credentials{
    155 		AccessKeyID:     resp.AccessKeyID,
    156 		SecretAccessKey: resp.SecretAccessKey,
    157 		SessionToken:    resp.Token,
    158 		Source:          ProviderName,
    159 		AccountID:       resp.AccountID,
    160 	}
    161 
    162 	if resp.Expiration != nil {
    163 		creds.CanExpire = true
    164 		creds.Expires = *resp.Expiration
    165 	}
    166 
    167 	return creds, nil
    168 }
    169 
    170 func (p *Provider) getCredentials(ctx context.Context) (*client.GetCredentialsOutput, error) {
    171 	authToken, err := p.resolveAuthToken()
    172 	if err != nil {
    173 		return nil, fmt.Errorf("resolve auth token: %v", err)
    174 	}
    175 
    176 	return p.client.GetCredentials(ctx, &client.GetCredentialsInput{
    177 		AuthorizationToken: authToken,
    178 	})
    179 }
    180 
    181 func (p *Provider) resolveAuthToken() (string, error) {
    182 	authToken := p.options.AuthorizationToken
    183 
    184 	var err error
    185 	if p.options.AuthorizationTokenProvider != nil {
    186 		authToken, err = p.options.AuthorizationTokenProvider.GetToken()
    187 		if err != nil {
    188 			return "", err
    189 		}
    190 	}
    191 
    192 	if strings.ContainsAny(authToken, "\r\n") {
    193 		return "", fmt.Errorf("authorization token contains invalid newline sequence")
    194 	}
    195 
    196 	return authToken, nil
    197 }
    198 
    199 var _ aws.CredentialProviderSource = (*Provider)(nil)
    200 
    201 // ProviderSources returns the credential chain that was used to construct this provider
    202 func (p *Provider) ProviderSources() []aws.CredentialSource {
    203 	if p.options.CredentialSources == nil {
    204 		return []aws.CredentialSource{aws.CredentialSourceHTTP}
    205 	}
    206 	return p.options.CredentialSources
    207 }