src

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

provider.go (5760B)


      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 
    103 // AuthTokenProvider defines an interface to dynamically load a value to be passed
    104 // for the Authorization header of a credentials request.
    105 type AuthTokenProvider interface {
    106 	GetToken() (string, error)
    107 }
    108 
    109 // TokenProviderFunc is a func type implementing AuthTokenProvider interface
    110 // and enables customizing token provider behavior
    111 type TokenProviderFunc func() (string, error)
    112 
    113 // GetToken func retrieves auth token according to TokenProviderFunc implementation
    114 func (p TokenProviderFunc) GetToken() (string, error) {
    115 	return p()
    116 }
    117 
    118 // New returns a credentials Provider for retrieving AWS credentials
    119 // from arbitrary endpoint.
    120 func New(endpoint string, optFns ...func(*Options)) *Provider {
    121 	o := Options{
    122 		Endpoint: endpoint,
    123 	}
    124 
    125 	for _, fn := range optFns {
    126 		fn(&o)
    127 	}
    128 
    129 	p := &Provider{
    130 		client: client.New(client.Options{
    131 			HTTPClient: o.HTTPClient,
    132 			Endpoint:   o.Endpoint,
    133 			APIOptions: o.APIOptions,
    134 			Retryer:    o.Retryer,
    135 		}),
    136 		options: o,
    137 	}
    138 
    139 	return p
    140 }
    141 
    142 // Retrieve will attempt to request the credentials from the endpoint the Provider
    143 // was configured for. And error will be returned if the retrieval fails.
    144 func (p *Provider) Retrieve(ctx context.Context) (aws.Credentials, error) {
    145 	resp, err := p.getCredentials(ctx)
    146 	if err != nil {
    147 		return aws.Credentials{}, fmt.Errorf("failed to load credentials, %w", err)
    148 	}
    149 
    150 	creds := aws.Credentials{
    151 		AccessKeyID:     resp.AccessKeyID,
    152 		SecretAccessKey: resp.SecretAccessKey,
    153 		SessionToken:    resp.Token,
    154 		Source:          ProviderName,
    155 	}
    156 
    157 	if resp.Expiration != nil {
    158 		creds.CanExpire = true
    159 		creds.Expires = *resp.Expiration
    160 	}
    161 
    162 	return creds, nil
    163 }
    164 
    165 func (p *Provider) getCredentials(ctx context.Context) (*client.GetCredentialsOutput, error) {
    166 	authToken, err := p.resolveAuthToken()
    167 	if err != nil {
    168 		return nil, fmt.Errorf("resolve auth token: %v", err)
    169 	}
    170 
    171 	return p.client.GetCredentials(ctx, &client.GetCredentialsInput{
    172 		AuthorizationToken: authToken,
    173 	})
    174 }
    175 
    176 func (p *Provider) resolveAuthToken() (string, error) {
    177 	authToken := p.options.AuthorizationToken
    178 
    179 	var err error
    180 	if p.options.AuthorizationTokenProvider != nil {
    181 		authToken, err = p.options.AuthorizationTokenProvider.GetToken()
    182 		if err != nil {
    183 			return "", err
    184 		}
    185 	}
    186 
    187 	if strings.ContainsAny(authToken, "\r\n") {
    188 		return "", fmt.Errorf("authorization token contains invalid newline sequence")
    189 	}
    190 
    191 	return authToken, nil
    192 }