diff options
| author | n1c00o <n@nc0.fr> | 2023-02-05 14:05:26 +0100 | 
|---|---|---|
| committer | Nicolas <34602094+n1c00o@users.noreply.github.com> | 2023-02-06 22:35:54 +0100 | 
| commit | b371cb11a5877ede8847351e95c7847b5024a551 (patch) | |
| tree | 958227cf8562503246976744b89370d389de5f66 /vendor/golang.org/x/oauth2/google | |
| parent | 03e0c597ad5f3539ad33976fe02c11a9e39f34d6 (diff) | |
Init Go module
Diffstat (limited to 'vendor/golang.org/x/oauth2/google')
18 files changed, 2636 insertions, 0 deletions
diff --git a/vendor/golang.org/x/oauth2/google/appengine.go b/vendor/golang.org/x/oauth2/google/appengine.go new file mode 100644 index 0000000..feb1157 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/appengine.go @@ -0,0 +1,38 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( +	"context" +	"time" + +	"golang.org/x/oauth2" +) + +// Set at init time by appengine_gen1.go. If nil, we're not on App Engine standard first generation (<= Go 1.9) or App Engine flexible. +var appengineTokenFunc func(c context.Context, scopes ...string) (token string, expiry time.Time, err error) + +// Set at init time by appengine_gen1.go. If nil, we're not on App Engine standard first generation (<= Go 1.9) or App Engine flexible. +var appengineAppIDFunc func(c context.Context) string + +// AppEngineTokenSource returns a token source that fetches tokens from either +// the current application's service account or from the metadata server, +// depending on the App Engine environment. See below for environment-specific +// details. If you are implementing a 3-legged OAuth 2.0 flow on App Engine that +// involves user accounts, see oauth2.Config instead. +// +// First generation App Engine runtimes (<= Go 1.9): +// AppEngineTokenSource returns a token source that fetches tokens issued to the +// current App Engine application's service account. The provided context must have +// come from appengine.NewContext. +// +// Second generation App Engine runtimes (>= Go 1.11) and App Engine flexible: +// AppEngineTokenSource is DEPRECATED on second generation runtimes and on the +// flexible environment. It delegates to ComputeTokenSource, and the provided +// context and scopes are not used. Please use DefaultTokenSource (or ComputeTokenSource, +// which DefaultTokenSource will use in this case) instead. +func AppEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource { +	return appEngineTokenSource(ctx, scope...) +} diff --git a/vendor/golang.org/x/oauth2/google/appengine_gen1.go b/vendor/golang.org/x/oauth2/google/appengine_gen1.go new file mode 100644 index 0000000..16c6c6b --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/appengine_gen1.go @@ -0,0 +1,78 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build appengine +// +build appengine + +// This file applies to App Engine first generation runtimes (<= Go 1.9). + +package google + +import ( +	"context" +	"sort" +	"strings" +	"sync" + +	"golang.org/x/oauth2" +	"google.golang.org/appengine" +) + +func init() { +	appengineTokenFunc = appengine.AccessToken +	appengineAppIDFunc = appengine.AppID +} + +// See comment on AppEngineTokenSource in appengine.go. +func appEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource { +	scopes := append([]string{}, scope...) +	sort.Strings(scopes) +	return &gaeTokenSource{ +		ctx:    ctx, +		scopes: scopes, +		key:    strings.Join(scopes, " "), +	} +} + +// aeTokens helps the fetched tokens to be reused until their expiration. +var ( +	aeTokensMu sync.Mutex +	aeTokens   = make(map[string]*tokenLock) // key is space-separated scopes +) + +type tokenLock struct { +	mu sync.Mutex // guards t; held while fetching or updating t +	t  *oauth2.Token +} + +type gaeTokenSource struct { +	ctx    context.Context +	scopes []string +	key    string // to aeTokens map; space-separated scopes +} + +func (ts *gaeTokenSource) Token() (*oauth2.Token, error) { +	aeTokensMu.Lock() +	tok, ok := aeTokens[ts.key] +	if !ok { +		tok = &tokenLock{} +		aeTokens[ts.key] = tok +	} +	aeTokensMu.Unlock() + +	tok.mu.Lock() +	defer tok.mu.Unlock() +	if tok.t.Valid() { +		return tok.t, nil +	} +	access, exp, err := appengineTokenFunc(ts.ctx, ts.scopes...) +	if err != nil { +		return nil, err +	} +	tok.t = &oauth2.Token{ +		AccessToken: access, +		Expiry:      exp, +	} +	return tok.t, nil +} diff --git a/vendor/golang.org/x/oauth2/google/appengine_gen2_flex.go b/vendor/golang.org/x/oauth2/google/appengine_gen2_flex.go new file mode 100644 index 0000000..a7e27b3 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/appengine_gen2_flex.go @@ -0,0 +1,28 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +//go:build !appengine +// +build !appengine + +// This file applies to App Engine second generation runtimes (>= Go 1.11) and App Engine flexible. + +package google + +import ( +	"context" +	"log" +	"sync" + +	"golang.org/x/oauth2" +) + +var logOnce sync.Once // only spam about deprecation once + +// See comment on AppEngineTokenSource in appengine.go. +func appEngineTokenSource(ctx context.Context, scope ...string) oauth2.TokenSource { +	logOnce.Do(func() { +		log.Print("google: AppEngineTokenSource is deprecated on App Engine standard second generation runtimes (>= Go 1.11) and App Engine flexible. Please use DefaultTokenSource or ComputeTokenSource.") +	}) +	return ComputeTokenSource("") +} diff --git a/vendor/golang.org/x/oauth2/google/default.go b/vendor/golang.org/x/oauth2/google/default.go new file mode 100644 index 0000000..7ed02cd --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/default.go @@ -0,0 +1,225 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( +	"context" +	"encoding/json" +	"fmt" +	"io/ioutil" +	"net/http" +	"os" +	"path/filepath" +	"runtime" + +	"cloud.google.com/go/compute/metadata" +	"golang.org/x/oauth2" +	"golang.org/x/oauth2/authhandler" +) + +// Credentials holds Google credentials, including "Application Default Credentials". +// For more details, see: +// https://developers.google.com/accounts/docs/application-default-credentials +// Credentials from external accounts (workload identity federation) are used to +// identify a particular application from an on-prem or non-Google Cloud platform +// including Amazon Web Services (AWS), Microsoft Azure or any identity provider +// that supports OpenID Connect (OIDC). +type Credentials struct { +	ProjectID   string // may be empty +	TokenSource oauth2.TokenSource + +	// JSON contains the raw bytes from a JSON credentials file. +	// This field may be nil if authentication is provided by the +	// environment and not with a credentials file, e.g. when code is +	// running on Google Cloud Platform. +	JSON []byte +} + +// DefaultCredentials is the old name of Credentials. +// +// Deprecated: use Credentials instead. +type DefaultCredentials = Credentials + +// CredentialsParams holds user supplied parameters that are used together +// with a credentials file for building a Credentials object. +type CredentialsParams struct { +	// Scopes is the list OAuth scopes. Required. +	// Example: https://www.googleapis.com/auth/cloud-platform +	Scopes []string + +	// Subject is the user email used for domain wide delegation (see +	// https://developers.google.com/identity/protocols/oauth2/service-account#delegatingauthority). +	// Optional. +	Subject string + +	// AuthHandler is the AuthorizationHandler used for 3-legged OAuth flow. Required for 3LO flow. +	AuthHandler authhandler.AuthorizationHandler + +	// State is a unique string used with AuthHandler. Required for 3LO flow. +	State string + +	// PKCE is used to support PKCE flow. Optional for 3LO flow. +	PKCE *authhandler.PKCEParams +} + +func (params CredentialsParams) deepCopy() CredentialsParams { +	paramsCopy := params +	paramsCopy.Scopes = make([]string, len(params.Scopes)) +	copy(paramsCopy.Scopes, params.Scopes) +	return paramsCopy +} + +// DefaultClient returns an HTTP Client that uses the +// DefaultTokenSource to obtain authentication credentials. +func DefaultClient(ctx context.Context, scope ...string) (*http.Client, error) { +	ts, err := DefaultTokenSource(ctx, scope...) +	if err != nil { +		return nil, err +	} +	return oauth2.NewClient(ctx, ts), nil +} + +// DefaultTokenSource returns the token source for +// "Application Default Credentials". +// It is a shortcut for FindDefaultCredentials(ctx, scope).TokenSource. +func DefaultTokenSource(ctx context.Context, scope ...string) (oauth2.TokenSource, error) { +	creds, err := FindDefaultCredentials(ctx, scope...) +	if err != nil { +		return nil, err +	} +	return creds.TokenSource, nil +} + +// FindDefaultCredentialsWithParams searches for "Application Default Credentials". +// +// It looks for credentials in the following places, +// preferring the first location found: +// +//  1. A JSON file whose path is specified by the +//     GOOGLE_APPLICATION_CREDENTIALS environment variable. +//     For workload identity federation, refer to +//     https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation on +//     how to generate the JSON configuration file for on-prem/non-Google cloud +//     platforms. +//  2. A JSON file in a location known to the gcloud command-line tool. +//     On Windows, this is %APPDATA%/gcloud/application_default_credentials.json. +//     On other systems, $HOME/.config/gcloud/application_default_credentials.json. +//  3. On Google App Engine standard first generation runtimes (<= Go 1.9) it uses +//     the appengine.AccessToken function. +//  4. On Google Compute Engine, Google App Engine standard second generation runtimes +//     (>= Go 1.11), and Google App Engine flexible environment, it fetches +//     credentials from the metadata server. +func FindDefaultCredentialsWithParams(ctx context.Context, params CredentialsParams) (*Credentials, error) { +	// Make defensive copy of the slices in params. +	params = params.deepCopy() + +	// First, try the environment variable. +	const envVar = "GOOGLE_APPLICATION_CREDENTIALS" +	if filename := os.Getenv(envVar); filename != "" { +		creds, err := readCredentialsFile(ctx, filename, params) +		if err != nil { +			return nil, fmt.Errorf("google: error getting credentials using %v environment variable: %v", envVar, err) +		} +		return creds, nil +	} + +	// Second, try a well-known file. +	filename := wellKnownFile() +	if creds, err := readCredentialsFile(ctx, filename, params); err == nil { +		return creds, nil +	} else if !os.IsNotExist(err) { +		return nil, fmt.Errorf("google: error getting credentials using well-known file (%v): %v", filename, err) +	} + +	// Third, if we're on a Google App Engine standard first generation runtime (<= Go 1.9) +	// use those credentials. App Engine standard second generation runtimes (>= Go 1.11) +	// and App Engine flexible use ComputeTokenSource and the metadata server. +	if appengineTokenFunc != nil { +		return &DefaultCredentials{ +			ProjectID:   appengineAppIDFunc(ctx), +			TokenSource: AppEngineTokenSource(ctx, params.Scopes...), +		}, nil +	} + +	// Fourth, if we're on Google Compute Engine, an App Engine standard second generation runtime, +	// or App Engine flexible, use the metadata server. +	if metadata.OnGCE() { +		id, _ := metadata.ProjectID() +		return &DefaultCredentials{ +			ProjectID:   id, +			TokenSource: ComputeTokenSource("", params.Scopes...), +		}, nil +	} + +	// None are found; return helpful error. +	const url = "https://developers.google.com/accounts/docs/application-default-credentials" +	return nil, fmt.Errorf("google: could not find default credentials. See %v for more information.", url) +} + +// FindDefaultCredentials invokes FindDefaultCredentialsWithParams with the specified scopes. +func FindDefaultCredentials(ctx context.Context, scopes ...string) (*Credentials, error) { +	var params CredentialsParams +	params.Scopes = scopes +	return FindDefaultCredentialsWithParams(ctx, params) +} + +// CredentialsFromJSONWithParams obtains Google credentials from a JSON value. The JSON can +// represent either a Google Developers Console client_credentials.json file (as in ConfigFromJSON), +// a Google Developers service account key file, a gcloud user credentials file (a.k.a. refresh +// token JSON), or the JSON configuration file for workload identity federation in non-Google cloud +// platforms (see https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation). +func CredentialsFromJSONWithParams(ctx context.Context, jsonData []byte, params CredentialsParams) (*Credentials, error) { +	// Make defensive copy of the slices in params. +	params = params.deepCopy() + +	// First, attempt to parse jsonData as a Google Developers Console client_credentials.json. +	config, _ := ConfigFromJSON(jsonData, params.Scopes...) +	if config != nil { +		return &Credentials{ +			ProjectID:   "", +			TokenSource: authhandler.TokenSourceWithPKCE(ctx, config, params.State, params.AuthHandler, params.PKCE), +			JSON:        jsonData, +		}, nil +	} + +	// Otherwise, parse jsonData as one of the other supported credentials files. +	var f credentialsFile +	if err := json.Unmarshal(jsonData, &f); err != nil { +		return nil, err +	} +	ts, err := f.tokenSource(ctx, params) +	if err != nil { +		return nil, err +	} +	ts = newErrWrappingTokenSource(ts) +	return &DefaultCredentials{ +		ProjectID:   f.ProjectID, +		TokenSource: ts, +		JSON:        jsonData, +	}, nil +} + +// CredentialsFromJSON invokes CredentialsFromJSONWithParams with the specified scopes. +func CredentialsFromJSON(ctx context.Context, jsonData []byte, scopes ...string) (*Credentials, error) { +	var params CredentialsParams +	params.Scopes = scopes +	return CredentialsFromJSONWithParams(ctx, jsonData, params) +} + +func wellKnownFile() string { +	const f = "application_default_credentials.json" +	if runtime.GOOS == "windows" { +		return filepath.Join(os.Getenv("APPDATA"), "gcloud", f) +	} +	return filepath.Join(guessUnixHomeDir(), ".config", "gcloud", f) +} + +func readCredentialsFile(ctx context.Context, filename string, params CredentialsParams) (*DefaultCredentials, error) { +	b, err := ioutil.ReadFile(filename) +	if err != nil { +		return nil, err +	} +	return CredentialsFromJSONWithParams(ctx, b, params) +} diff --git a/vendor/golang.org/x/oauth2/google/doc.go b/vendor/golang.org/x/oauth2/google/doc.go new file mode 100644 index 0000000..b3e7bc8 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/doc.go @@ -0,0 +1,84 @@ +// Copyright 2018 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package google provides support for making OAuth2 authorized and authenticated +// HTTP requests to Google APIs. It supports the Web server flow, client-side +// credentials, service accounts, Google Compute Engine service accounts, +// Google App Engine service accounts and workload identity federation +// from non-Google cloud platforms. +// +// A brief overview of the package follows. For more information, please read +// https://developers.google.com/accounts/docs/OAuth2 +// and +// https://developers.google.com/accounts/docs/application-default-credentials. +// For more information on using workload identity federation, refer to +// https://cloud.google.com/iam/docs/how-to#using-workload-identity-federation. +// +// # OAuth2 Configs +// +// Two functions in this package return golang.org/x/oauth2.Config values from Google credential +// data. Google supports two JSON formats for OAuth2 credentials: one is handled by ConfigFromJSON, +// the other by JWTConfigFromJSON. The returned Config can be used to obtain a TokenSource or +// create an http.Client. +// +// # Workload Identity Federation +// +// Using workload identity federation, your application can access Google Cloud +// resources from Amazon Web Services (AWS), Microsoft Azure or any identity +// provider that supports OpenID Connect (OIDC). +// Traditionally, applications running outside Google Cloud have used service +// account keys to access Google Cloud resources. Using identity federation, +// you can allow your workload to impersonate a service account. +// This lets you access Google Cloud resources directly, eliminating the +// maintenance and security burden associated with service account keys. +// +// Follow the detailed instructions on how to configure Workload Identity Federation +// in various platforms: +// +//	Amazon Web Services (AWS): https://cloud.google.com/iam/docs/access-resources-aws +//	Microsoft Azure: https://cloud.google.com/iam/docs/access-resources-azure +//	OIDC identity provider: https://cloud.google.com/iam/docs/access-resources-oidc +// +// For OIDC and SAML providers, the library can retrieve tokens in three ways: +// from a local file location (file-sourced credentials), from a server +// (URL-sourced credentials), or from a local executable (executable-sourced +// credentials). +// For file-sourced credentials, a background process needs to be continuously +// refreshing the file location with a new OIDC token prior to expiration. +// For tokens with one hour lifetimes, the token needs to be updated in the file +// every hour. The token can be stored directly as plain text or in JSON format. +// For URL-sourced credentials, a local server needs to host a GET endpoint to +// return the OIDC token. The response can be in plain text or JSON. +// Additional required request headers can also be specified. +// For executable-sourced credentials, an application needs to be available to +// output the OIDC token and other information in a JSON format. +// For more information on how these work (and how to implement +// executable-sourced credentials), please check out: +// https://cloud.google.com/iam/docs/using-workload-identity-federation#oidc +// +// # Credentials +// +// The Credentials type represents Google credentials, including Application Default +// Credentials. +// +// Use FindDefaultCredentials to obtain Application Default Credentials. +// FindDefaultCredentials looks in some well-known places for a credentials file, and +// will call AppEngineTokenSource or ComputeTokenSource as needed. +// +// Application Default Credentials also support workload identity federation to +// access Google Cloud resources from non-Google Cloud platforms including Amazon +// Web Services (AWS), Microsoft Azure or any identity provider that supports +// OpenID Connect (OIDC). Workload identity federation is recommended for +// non-Google Cloud environments as it avoids the need to download, manage and +// store service account private keys locally. +// +// DefaultClient and DefaultTokenSource are convenience methods. They first call FindDefaultCredentials, +// then use the credentials to construct an http.Client or an oauth2.TokenSource. +// +// Use CredentialsFromJSON to obtain credentials from either of the two JSON formats +// described in OAuth2 Configs, above. The TokenSource in the returned value is the +// same as the one obtained from the oauth2.Config returned from ConfigFromJSON or +// JWTConfigFromJSON, but the Credentials may contain additional information +// that is useful is some circumstances. +package google // import "golang.org/x/oauth2/google" diff --git a/vendor/golang.org/x/oauth2/google/error.go b/vendor/golang.org/x/oauth2/google/error.go new file mode 100644 index 0000000..d84dd00 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/error.go @@ -0,0 +1,64 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( +	"errors" + +	"golang.org/x/oauth2" +) + +// AuthenticationError indicates there was an error in the authentication flow. +// +// Use (*AuthenticationError).Temporary to check if the error can be retried. +type AuthenticationError struct { +	err *oauth2.RetrieveError +} + +func newAuthenticationError(err error) error { +	re := &oauth2.RetrieveError{} +	if !errors.As(err, &re) { +		return err +	} +	return &AuthenticationError{ +		err: re, +	} +} + +// Temporary indicates that the network error has one of the following status codes and may be retried: 500, 503, 408, or 429. +func (e *AuthenticationError) Temporary() bool { +	if e.err.Response == nil { +		return false +	} +	sc := e.err.Response.StatusCode +	return sc == 500 || sc == 503 || sc == 408 || sc == 429 +} + +func (e *AuthenticationError) Error() string { +	return e.err.Error() +} + +func (e *AuthenticationError) Unwrap() error { +	return e.err +} + +type errWrappingTokenSource struct { +	src oauth2.TokenSource +} + +func newErrWrappingTokenSource(ts oauth2.TokenSource) oauth2.TokenSource { +	return &errWrappingTokenSource{src: ts} +} + +// Token returns the current token if it's still valid, else will +// refresh the current token (using r.Context for HTTP client +// information) and return the new one. +func (s *errWrappingTokenSource) Token() (*oauth2.Token, error) { +	t, err := s.src.Token() +	if err != nil { +		return nil, newAuthenticationError(err) +	} +	return t, nil +} diff --git a/vendor/golang.org/x/oauth2/google/google.go b/vendor/golang.org/x/oauth2/google/google.go new file mode 100644 index 0000000..8df0c49 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/google.go @@ -0,0 +1,277 @@ +// Copyright 2014 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( +	"context" +	"encoding/json" +	"errors" +	"fmt" +	"net/url" +	"strings" +	"time" + +	"cloud.google.com/go/compute/metadata" +	"golang.org/x/oauth2" +	"golang.org/x/oauth2/google/internal/externalaccount" +	"golang.org/x/oauth2/jwt" +) + +// Endpoint is Google's OAuth 2.0 default endpoint. +var Endpoint = oauth2.Endpoint{ +	AuthURL:   "https://accounts.google.com/o/oauth2/auth", +	TokenURL:  "https://oauth2.googleapis.com/token", +	AuthStyle: oauth2.AuthStyleInParams, +} + +// JWTTokenURL is Google's OAuth 2.0 token URL to use with the JWT flow. +const JWTTokenURL = "https://oauth2.googleapis.com/token" + +// ConfigFromJSON uses a Google Developers Console client_credentials.json +// file to construct a config. +// client_credentials.json can be downloaded from +// https://console.developers.google.com, under "Credentials". Download the Web +// application credentials in the JSON format and provide the contents of the +// file as jsonKey. +func ConfigFromJSON(jsonKey []byte, scope ...string) (*oauth2.Config, error) { +	type cred struct { +		ClientID     string   `json:"client_id"` +		ClientSecret string   `json:"client_secret"` +		RedirectURIs []string `json:"redirect_uris"` +		AuthURI      string   `json:"auth_uri"` +		TokenURI     string   `json:"token_uri"` +	} +	var j struct { +		Web       *cred `json:"web"` +		Installed *cred `json:"installed"` +	} +	if err := json.Unmarshal(jsonKey, &j); err != nil { +		return nil, err +	} +	var c *cred +	switch { +	case j.Web != nil: +		c = j.Web +	case j.Installed != nil: +		c = j.Installed +	default: +		return nil, fmt.Errorf("oauth2/google: no credentials found") +	} +	if len(c.RedirectURIs) < 1 { +		return nil, errors.New("oauth2/google: missing redirect URL in the client_credentials.json") +	} +	return &oauth2.Config{ +		ClientID:     c.ClientID, +		ClientSecret: c.ClientSecret, +		RedirectURL:  c.RedirectURIs[0], +		Scopes:       scope, +		Endpoint: oauth2.Endpoint{ +			AuthURL:  c.AuthURI, +			TokenURL: c.TokenURI, +		}, +	}, nil +} + +// JWTConfigFromJSON uses a Google Developers service account JSON key file to read +// the credentials that authorize and authenticate the requests. +// Create a service account on "Credentials" for your project at +// https://console.developers.google.com to download a JSON key file. +func JWTConfigFromJSON(jsonKey []byte, scope ...string) (*jwt.Config, error) { +	var f credentialsFile +	if err := json.Unmarshal(jsonKey, &f); err != nil { +		return nil, err +	} +	if f.Type != serviceAccountKey { +		return nil, fmt.Errorf("google: read JWT from JSON credentials: 'type' field is %q (expected %q)", f.Type, serviceAccountKey) +	} +	scope = append([]string(nil), scope...) // copy +	return f.jwtConfig(scope, ""), nil +} + +// JSON key file types. +const ( +	serviceAccountKey          = "service_account" +	userCredentialsKey         = "authorized_user" +	externalAccountKey         = "external_account" +	impersonatedServiceAccount = "impersonated_service_account" +) + +// credentialsFile is the unmarshalled representation of a credentials file. +type credentialsFile struct { +	Type string `json:"type"` + +	// Service Account fields +	ClientEmail  string `json:"client_email"` +	PrivateKeyID string `json:"private_key_id"` +	PrivateKey   string `json:"private_key"` +	AuthURL      string `json:"auth_uri"` +	TokenURL     string `json:"token_uri"` +	ProjectID    string `json:"project_id"` + +	// User Credential fields +	// (These typically come from gcloud auth.) +	ClientSecret string `json:"client_secret"` +	ClientID     string `json:"client_id"` +	RefreshToken string `json:"refresh_token"` + +	// External Account fields +	Audience                       string                           `json:"audience"` +	SubjectTokenType               string                           `json:"subject_token_type"` +	TokenURLExternal               string                           `json:"token_url"` +	TokenInfoURL                   string                           `json:"token_info_url"` +	ServiceAccountImpersonationURL string                           `json:"service_account_impersonation_url"` +	ServiceAccountImpersonation    serviceAccountImpersonationInfo  `json:"service_account_impersonation"` +	Delegates                      []string                         `json:"delegates"` +	CredentialSource               externalaccount.CredentialSource `json:"credential_source"` +	QuotaProjectID                 string                           `json:"quota_project_id"` +	WorkforcePoolUserProject       string                           `json:"workforce_pool_user_project"` + +	// Service account impersonation +	SourceCredentials *credentialsFile `json:"source_credentials"` +} + +type serviceAccountImpersonationInfo struct { +	TokenLifetimeSeconds int `json:"token_lifetime_seconds"` +} + +func (f *credentialsFile) jwtConfig(scopes []string, subject string) *jwt.Config { +	cfg := &jwt.Config{ +		Email:        f.ClientEmail, +		PrivateKey:   []byte(f.PrivateKey), +		PrivateKeyID: f.PrivateKeyID, +		Scopes:       scopes, +		TokenURL:     f.TokenURL, +		Subject:      subject, // This is the user email to impersonate +		Audience:     f.Audience, +	} +	if cfg.TokenURL == "" { +		cfg.TokenURL = JWTTokenURL +	} +	return cfg +} + +func (f *credentialsFile) tokenSource(ctx context.Context, params CredentialsParams) (oauth2.TokenSource, error) { +	switch f.Type { +	case serviceAccountKey: +		cfg := f.jwtConfig(params.Scopes, params.Subject) +		return cfg.TokenSource(ctx), nil +	case userCredentialsKey: +		cfg := &oauth2.Config{ +			ClientID:     f.ClientID, +			ClientSecret: f.ClientSecret, +			Scopes:       params.Scopes, +			Endpoint: oauth2.Endpoint{ +				AuthURL:   f.AuthURL, +				TokenURL:  f.TokenURL, +				AuthStyle: oauth2.AuthStyleInParams, +			}, +		} +		if cfg.Endpoint.AuthURL == "" { +			cfg.Endpoint.AuthURL = Endpoint.AuthURL +		} +		if cfg.Endpoint.TokenURL == "" { +			cfg.Endpoint.TokenURL = Endpoint.TokenURL +		} +		tok := &oauth2.Token{RefreshToken: f.RefreshToken} +		return cfg.TokenSource(ctx, tok), nil +	case externalAccountKey: +		cfg := &externalaccount.Config{ +			Audience:                       f.Audience, +			SubjectTokenType:               f.SubjectTokenType, +			TokenURL:                       f.TokenURLExternal, +			TokenInfoURL:                   f.TokenInfoURL, +			ServiceAccountImpersonationURL: f.ServiceAccountImpersonationURL, +			ServiceAccountImpersonationLifetimeSeconds: f.ServiceAccountImpersonation.TokenLifetimeSeconds, +			ClientSecret:             f.ClientSecret, +			ClientID:                 f.ClientID, +			CredentialSource:         f.CredentialSource, +			QuotaProjectID:           f.QuotaProjectID, +			Scopes:                   params.Scopes, +			WorkforcePoolUserProject: f.WorkforcePoolUserProject, +		} +		return cfg.TokenSource(ctx) +	case impersonatedServiceAccount: +		if f.ServiceAccountImpersonationURL == "" || f.SourceCredentials == nil { +			return nil, errors.New("missing 'source_credentials' field or 'service_account_impersonation_url' in credentials") +		} + +		ts, err := f.SourceCredentials.tokenSource(ctx, params) +		if err != nil { +			return nil, err +		} +		imp := externalaccount.ImpersonateTokenSource{ +			Ctx:       ctx, +			URL:       f.ServiceAccountImpersonationURL, +			Scopes:    params.Scopes, +			Ts:        ts, +			Delegates: f.Delegates, +		} +		return oauth2.ReuseTokenSource(nil, imp), nil +	case "": +		return nil, errors.New("missing 'type' field in credentials") +	default: +		return nil, fmt.Errorf("unknown credential type: %q", f.Type) +	} +} + +// ComputeTokenSource returns a token source that fetches access tokens +// from Google Compute Engine (GCE)'s metadata server. It's only valid to use +// this token source if your program is running on a GCE instance. +// If no account is specified, "default" is used. +// If no scopes are specified, a set of default scopes are automatically granted. +// Further information about retrieving access tokens from the GCE metadata +// server can be found at https://cloud.google.com/compute/docs/authentication. +func ComputeTokenSource(account string, scope ...string) oauth2.TokenSource { +	return oauth2.ReuseTokenSource(nil, computeSource{account: account, scopes: scope}) +} + +type computeSource struct { +	account string +	scopes  []string +} + +func (cs computeSource) Token() (*oauth2.Token, error) { +	if !metadata.OnGCE() { +		return nil, errors.New("oauth2/google: can't get a token from the metadata service; not running on GCE") +	} +	acct := cs.account +	if acct == "" { +		acct = "default" +	} +	tokenURI := "instance/service-accounts/" + acct + "/token" +	if len(cs.scopes) > 0 { +		v := url.Values{} +		v.Set("scopes", strings.Join(cs.scopes, ",")) +		tokenURI = tokenURI + "?" + v.Encode() +	} +	tokenJSON, err := metadata.Get(tokenURI) +	if err != nil { +		return nil, err +	} +	var res struct { +		AccessToken  string `json:"access_token"` +		ExpiresInSec int    `json:"expires_in"` +		TokenType    string `json:"token_type"` +	} +	err = json.NewDecoder(strings.NewReader(tokenJSON)).Decode(&res) +	if err != nil { +		return nil, fmt.Errorf("oauth2/google: invalid token JSON from metadata: %v", err) +	} +	if res.ExpiresInSec == 0 || res.AccessToken == "" { +		return nil, fmt.Errorf("oauth2/google: incomplete token received from metadata") +	} +	tok := &oauth2.Token{ +		AccessToken: res.AccessToken, +		TokenType:   res.TokenType, +		Expiry:      time.Now().Add(time.Duration(res.ExpiresInSec) * time.Second), +	} +	// NOTE(cbro): add hidden metadata about where the token is from. +	// This is needed for detection by client libraries to know that credentials come from the metadata server. +	// This may be removed in a future version of this library. +	return tok.WithExtra(map[string]interface{}{ +		"oauth2.google.tokenSource":    "compute-metadata", +		"oauth2.google.serviceAccount": acct, +	}), nil +} diff --git a/vendor/golang.org/x/oauth2/google/internal/externalaccount/aws.go b/vendor/golang.org/x/oauth2/google/internal/externalaccount/aws.go new file mode 100644 index 0000000..e917195 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/internal/externalaccount/aws.go @@ -0,0 +1,530 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package externalaccount + +import ( +	"bytes" +	"context" +	"crypto/hmac" +	"crypto/sha256" +	"encoding/hex" +	"encoding/json" +	"errors" +	"fmt" +	"io" +	"io/ioutil" +	"net/http" +	"net/url" +	"os" +	"path" +	"sort" +	"strings" +	"time" + +	"golang.org/x/oauth2" +) + +type awsSecurityCredentials struct { +	AccessKeyID     string `json:"AccessKeyID"` +	SecretAccessKey string `json:"SecretAccessKey"` +	SecurityToken   string `json:"Token"` +} + +// awsRequestSigner is a utility class to sign http requests using a AWS V4 signature. +type awsRequestSigner struct { +	RegionName             string +	AwsSecurityCredentials awsSecurityCredentials +} + +// getenv aliases os.Getenv for testing +var getenv = os.Getenv + +const ( +	// AWS Signature Version 4 signing algorithm identifier. +	awsAlgorithm = "AWS4-HMAC-SHA256" + +	// The termination string for the AWS credential scope value as defined in +	// https://docs.aws.amazon.com/general/latest/gr/sigv4-create-string-to-sign.html +	awsRequestType = "aws4_request" + +	// The AWS authorization header name for the security session token if available. +	awsSecurityTokenHeader = "x-amz-security-token" + +	// The name of the header containing the session token for metadata endpoint calls +	awsIMDSv2SessionTokenHeader = "X-aws-ec2-metadata-token" + +	awsIMDSv2SessionTtlHeader = "X-aws-ec2-metadata-token-ttl-seconds" + +	awsIMDSv2SessionTtl = "300" + +	// The AWS authorization header name for the auto-generated date. +	awsDateHeader = "x-amz-date" + +	awsTimeFormatLong  = "20060102T150405Z" +	awsTimeFormatShort = "20060102" +) + +func getSha256(input []byte) (string, error) { +	hash := sha256.New() +	if _, err := hash.Write(input); err != nil { +		return "", err +	} +	return hex.EncodeToString(hash.Sum(nil)), nil +} + +func getHmacSha256(key, input []byte) ([]byte, error) { +	hash := hmac.New(sha256.New, key) +	if _, err := hash.Write(input); err != nil { +		return nil, err +	} +	return hash.Sum(nil), nil +} + +func cloneRequest(r *http.Request) *http.Request { +	r2 := new(http.Request) +	*r2 = *r +	if r.Header != nil { +		r2.Header = make(http.Header, len(r.Header)) + +		// Find total number of values. +		headerCount := 0 +		for _, headerValues := range r.Header { +			headerCount += len(headerValues) +		} +		copiedHeaders := make([]string, headerCount) // shared backing array for headers' values + +		for headerKey, headerValues := range r.Header { +			headerCount = copy(copiedHeaders, headerValues) +			r2.Header[headerKey] = copiedHeaders[:headerCount:headerCount] +			copiedHeaders = copiedHeaders[headerCount:] +		} +	} +	return r2 +} + +func canonicalPath(req *http.Request) string { +	result := req.URL.EscapedPath() +	if result == "" { +		return "/" +	} +	return path.Clean(result) +} + +func canonicalQuery(req *http.Request) string { +	queryValues := req.URL.Query() +	for queryKey := range queryValues { +		sort.Strings(queryValues[queryKey]) +	} +	return queryValues.Encode() +} + +func canonicalHeaders(req *http.Request) (string, string) { +	// Header keys need to be sorted alphabetically. +	var headers []string +	lowerCaseHeaders := make(http.Header) +	for k, v := range req.Header { +		k := strings.ToLower(k) +		if _, ok := lowerCaseHeaders[k]; ok { +			// include additional values +			lowerCaseHeaders[k] = append(lowerCaseHeaders[k], v...) +		} else { +			headers = append(headers, k) +			lowerCaseHeaders[k] = v +		} +	} +	sort.Strings(headers) + +	var fullHeaders bytes.Buffer +	for _, header := range headers { +		headerValue := strings.Join(lowerCaseHeaders[header], ",") +		fullHeaders.WriteString(header) +		fullHeaders.WriteRune(':') +		fullHeaders.WriteString(headerValue) +		fullHeaders.WriteRune('\n') +	} + +	return strings.Join(headers, ";"), fullHeaders.String() +} + +func requestDataHash(req *http.Request) (string, error) { +	var requestData []byte +	if req.Body != nil { +		requestBody, err := req.GetBody() +		if err != nil { +			return "", err +		} +		defer requestBody.Close() + +		requestData, err = ioutil.ReadAll(io.LimitReader(requestBody, 1<<20)) +		if err != nil { +			return "", err +		} +	} + +	return getSha256(requestData) +} + +func requestHost(req *http.Request) string { +	if req.Host != "" { +		return req.Host +	} +	return req.URL.Host +} + +func canonicalRequest(req *http.Request, canonicalHeaderColumns, canonicalHeaderData string) (string, error) { +	dataHash, err := requestDataHash(req) +	if err != nil { +		return "", err +	} + +	return fmt.Sprintf("%s\n%s\n%s\n%s\n%s\n%s", req.Method, canonicalPath(req), canonicalQuery(req), canonicalHeaderData, canonicalHeaderColumns, dataHash), nil +} + +// SignRequest adds the appropriate headers to an http.Request +// or returns an error if something prevented this. +func (rs *awsRequestSigner) SignRequest(req *http.Request) error { +	signedRequest := cloneRequest(req) +	timestamp := now() + +	signedRequest.Header.Add("host", requestHost(req)) + +	if rs.AwsSecurityCredentials.SecurityToken != "" { +		signedRequest.Header.Add(awsSecurityTokenHeader, rs.AwsSecurityCredentials.SecurityToken) +	} + +	if signedRequest.Header.Get("date") == "" { +		signedRequest.Header.Add(awsDateHeader, timestamp.Format(awsTimeFormatLong)) +	} + +	authorizationCode, err := rs.generateAuthentication(signedRequest, timestamp) +	if err != nil { +		return err +	} +	signedRequest.Header.Set("Authorization", authorizationCode) + +	req.Header = signedRequest.Header +	return nil +} + +func (rs *awsRequestSigner) generateAuthentication(req *http.Request, timestamp time.Time) (string, error) { +	canonicalHeaderColumns, canonicalHeaderData := canonicalHeaders(req) + +	dateStamp := timestamp.Format(awsTimeFormatShort) +	serviceName := "" +	if splitHost := strings.Split(requestHost(req), "."); len(splitHost) > 0 { +		serviceName = splitHost[0] +	} + +	credentialScope := fmt.Sprintf("%s/%s/%s/%s", dateStamp, rs.RegionName, serviceName, awsRequestType) + +	requestString, err := canonicalRequest(req, canonicalHeaderColumns, canonicalHeaderData) +	if err != nil { +		return "", err +	} +	requestHash, err := getSha256([]byte(requestString)) +	if err != nil { +		return "", err +	} + +	stringToSign := fmt.Sprintf("%s\n%s\n%s\n%s", awsAlgorithm, timestamp.Format(awsTimeFormatLong), credentialScope, requestHash) + +	signingKey := []byte("AWS4" + rs.AwsSecurityCredentials.SecretAccessKey) +	for _, signingInput := range []string{ +		dateStamp, rs.RegionName, serviceName, awsRequestType, stringToSign, +	} { +		signingKey, err = getHmacSha256(signingKey, []byte(signingInput)) +		if err != nil { +			return "", err +		} +	} + +	return fmt.Sprintf("%s Credential=%s/%s, SignedHeaders=%s, Signature=%s", awsAlgorithm, rs.AwsSecurityCredentials.AccessKeyID, credentialScope, canonicalHeaderColumns, hex.EncodeToString(signingKey)), nil +} + +type awsCredentialSource struct { +	EnvironmentID               string +	RegionURL                   string +	RegionalCredVerificationURL string +	CredVerificationURL         string +	IMDSv2SessionTokenURL       string +	TargetResource              string +	requestSigner               *awsRequestSigner +	region                      string +	ctx                         context.Context +	client                      *http.Client +} + +type awsRequestHeader struct { +	Key   string `json:"key"` +	Value string `json:"value"` +} + +type awsRequest struct { +	URL     string             `json:"url"` +	Method  string             `json:"method"` +	Headers []awsRequestHeader `json:"headers"` +} + +func (cs awsCredentialSource) doRequest(req *http.Request) (*http.Response, error) { +	if cs.client == nil { +		cs.client = oauth2.NewClient(cs.ctx, nil) +	} +	return cs.client.Do(req.WithContext(cs.ctx)) +} + +func (cs awsCredentialSource) subjectToken() (string, error) { +	if cs.requestSigner == nil { +		awsSessionToken, err := cs.getAWSSessionToken() +		if err != nil { +			return "", err +		} + +		headers := make(map[string]string) +		if awsSessionToken != "" { +			headers[awsIMDSv2SessionTokenHeader] = awsSessionToken +		} + +		awsSecurityCredentials, err := cs.getSecurityCredentials(headers) +		if err != nil { +			return "", err +		} + +		if cs.region, err = cs.getRegion(headers); err != nil { +			return "", err +		} + +		cs.requestSigner = &awsRequestSigner{ +			RegionName:             cs.region, +			AwsSecurityCredentials: awsSecurityCredentials, +		} +	} + +	// Generate the signed request to AWS STS GetCallerIdentity API. +	// Use the required regional endpoint. Otherwise, the request will fail. +	req, err := http.NewRequest("POST", strings.Replace(cs.RegionalCredVerificationURL, "{region}", cs.region, 1), nil) +	if err != nil { +		return "", err +	} +	// The full, canonical resource name of the workload identity pool +	// provider, with or without the HTTPS prefix. +	// Including this header as part of the signature is recommended to +	// ensure data integrity. +	if cs.TargetResource != "" { +		req.Header.Add("x-goog-cloud-target-resource", cs.TargetResource) +	} +	cs.requestSigner.SignRequest(req) + +	/* +	   The GCP STS endpoint expects the headers to be formatted as: +	   # [ +	   #   {key: 'x-amz-date', value: '...'}, +	   #   {key: 'Authorization', value: '...'}, +	   #   ... +	   # ] +	   # And then serialized as: +	   # quote(json.dumps({ +	   #   url: '...', +	   #   method: 'POST', +	   #   headers: [{key: 'x-amz-date', value: '...'}, ...] +	   # })) +	*/ + +	awsSignedReq := awsRequest{ +		URL:    req.URL.String(), +		Method: "POST", +	} +	for headerKey, headerList := range req.Header { +		for _, headerValue := range headerList { +			awsSignedReq.Headers = append(awsSignedReq.Headers, awsRequestHeader{ +				Key:   headerKey, +				Value: headerValue, +			}) +		} +	} +	sort.Slice(awsSignedReq.Headers, func(i, j int) bool { +		headerCompare := strings.Compare(awsSignedReq.Headers[i].Key, awsSignedReq.Headers[j].Key) +		if headerCompare == 0 { +			return strings.Compare(awsSignedReq.Headers[i].Value, awsSignedReq.Headers[j].Value) < 0 +		} +		return headerCompare < 0 +	}) + +	result, err := json.Marshal(awsSignedReq) +	if err != nil { +		return "", err +	} +	return url.QueryEscape(string(result)), nil +} + +func (cs *awsCredentialSource) getAWSSessionToken() (string, error) { +	if cs.IMDSv2SessionTokenURL == "" { +		return "", nil +	} + +	req, err := http.NewRequest("PUT", cs.IMDSv2SessionTokenURL, nil) +	if err != nil { +		return "", err +	} + +	req.Header.Add(awsIMDSv2SessionTtlHeader, awsIMDSv2SessionTtl) + +	resp, err := cs.doRequest(req) +	if err != nil { +		return "", err +	} +	defer resp.Body.Close() + +	respBody, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) +	if err != nil { +		return "", err +	} + +	if resp.StatusCode != 200 { +		return "", fmt.Errorf("oauth2/google: unable to retrieve AWS session token - %s", string(respBody)) +	} + +	return string(respBody), nil +} + +func (cs *awsCredentialSource) getRegion(headers map[string]string) (string, error) { +	if envAwsRegion := getenv("AWS_REGION"); envAwsRegion != "" { +		return envAwsRegion, nil +	} +	if envAwsRegion := getenv("AWS_DEFAULT_REGION"); envAwsRegion != "" { +		return envAwsRegion, nil +	} + +	if cs.RegionURL == "" { +		return "", errors.New("oauth2/google: unable to determine AWS region") +	} + +	req, err := http.NewRequest("GET", cs.RegionURL, nil) +	if err != nil { +		return "", err +	} + +	for name, value := range headers { +		req.Header.Add(name, value) +	} + +	resp, err := cs.doRequest(req) +	if err != nil { +		return "", err +	} +	defer resp.Body.Close() + +	respBody, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) +	if err != nil { +		return "", err +	} + +	if resp.StatusCode != 200 { +		return "", fmt.Errorf("oauth2/google: unable to retrieve AWS region - %s", string(respBody)) +	} + +	// This endpoint will return the region in format: us-east-2b. +	// Only the us-east-2 part should be used. +	respBodyEnd := 0 +	if len(respBody) > 1 { +		respBodyEnd = len(respBody) - 1 +	} +	return string(respBody[:respBodyEnd]), nil +} + +func (cs *awsCredentialSource) getSecurityCredentials(headers map[string]string) (result awsSecurityCredentials, err error) { +	if accessKeyID := getenv("AWS_ACCESS_KEY_ID"); accessKeyID != "" { +		if secretAccessKey := getenv("AWS_SECRET_ACCESS_KEY"); secretAccessKey != "" { +			return awsSecurityCredentials{ +				AccessKeyID:     accessKeyID, +				SecretAccessKey: secretAccessKey, +				SecurityToken:   getenv("AWS_SESSION_TOKEN"), +			}, nil +		} +	} + +	roleName, err := cs.getMetadataRoleName(headers) +	if err != nil { +		return +	} + +	credentials, err := cs.getMetadataSecurityCredentials(roleName, headers) +	if err != nil { +		return +	} + +	if credentials.AccessKeyID == "" { +		return result, errors.New("oauth2/google: missing AccessKeyId credential") +	} + +	if credentials.SecretAccessKey == "" { +		return result, errors.New("oauth2/google: missing SecretAccessKey credential") +	} + +	return credentials, nil +} + +func (cs *awsCredentialSource) getMetadataSecurityCredentials(roleName string, headers map[string]string) (awsSecurityCredentials, error) { +	var result awsSecurityCredentials + +	req, err := http.NewRequest("GET", fmt.Sprintf("%s/%s", cs.CredVerificationURL, roleName), nil) +	if err != nil { +		return result, err +	} +	req.Header.Add("Content-Type", "application/json") + +	for name, value := range headers { +		req.Header.Add(name, value) +	} + +	resp, err := cs.doRequest(req) +	if err != nil { +		return result, err +	} +	defer resp.Body.Close() + +	respBody, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) +	if err != nil { +		return result, err +	} + +	if resp.StatusCode != 200 { +		return result, fmt.Errorf("oauth2/google: unable to retrieve AWS security credentials - %s", string(respBody)) +	} + +	err = json.Unmarshal(respBody, &result) +	return result, err +} + +func (cs *awsCredentialSource) getMetadataRoleName(headers map[string]string) (string, error) { +	if cs.CredVerificationURL == "" { +		return "", errors.New("oauth2/google: unable to determine the AWS metadata server security credentials endpoint") +	} + +	req, err := http.NewRequest("GET", cs.CredVerificationURL, nil) +	if err != nil { +		return "", err +	} + +	for name, value := range headers { +		req.Header.Add(name, value) +	} + +	resp, err := cs.doRequest(req) +	if err != nil { +		return "", err +	} +	defer resp.Body.Close() + +	respBody, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) +	if err != nil { +		return "", err +	} + +	if resp.StatusCode != 200 { +		return "", fmt.Errorf("oauth2/google: unable to retrieve AWS role name - %s", string(respBody)) +	} + +	return string(respBody), nil +} diff --git a/vendor/golang.org/x/oauth2/google/internal/externalaccount/basecredentials.go b/vendor/golang.org/x/oauth2/google/internal/externalaccount/basecredentials.go new file mode 100644 index 0000000..9fc3553 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/internal/externalaccount/basecredentials.go @@ -0,0 +1,293 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package externalaccount + +import ( +	"context" +	"fmt" +	"net/http" +	"net/url" +	"regexp" +	"strconv" +	"strings" +	"time" + +	"golang.org/x/oauth2" +) + +// now aliases time.Now for testing +var now = func() time.Time { +	return time.Now().UTC() +} + +// Config stores the configuration for fetching tokens with external credentials. +type Config struct { +	// Audience is the Secure Token Service (STS) audience which contains the resource name for the workload +	// identity pool or the workforce pool and the provider identifier in that pool. +	Audience string +	// SubjectTokenType is the STS token type based on the Oauth2.0 token exchange spec +	// e.g. `urn:ietf:params:oauth:token-type:jwt`. +	SubjectTokenType string +	// TokenURL is the STS token exchange endpoint. +	TokenURL string +	// TokenInfoURL is the token_info endpoint used to retrieve the account related information ( +	// user attributes like account identifier, eg. email, username, uid, etc). This is +	// needed for gCloud session account identification. +	TokenInfoURL string +	// ServiceAccountImpersonationURL is the URL for the service account impersonation request. This is only +	// required for workload identity pools when APIs to be accessed have not integrated with UberMint. +	ServiceAccountImpersonationURL string +	// ServiceAccountImpersonationLifetimeSeconds is the number of seconds the service account impersonation +	// token will be valid for. +	ServiceAccountImpersonationLifetimeSeconds int +	// ClientSecret is currently only required if token_info endpoint also +	// needs to be called with the generated GCP access token. When provided, STS will be +	// called with additional basic authentication using client_id as username and client_secret as password. +	ClientSecret string +	// ClientID is only required in conjunction with ClientSecret, as described above. +	ClientID string +	// CredentialSource contains the necessary information to retrieve the token itself, as well +	// as some environmental information. +	CredentialSource CredentialSource +	// QuotaProjectID is injected by gCloud. If the value is non-empty, the Auth libraries +	// will set the x-goog-user-project which overrides the project associated with the credentials. +	QuotaProjectID string +	// Scopes contains the desired scopes for the returned access token. +	Scopes []string +	// The optional workforce pool user project number when the credential +	// corresponds to a workforce pool and not a workload identity pool. +	// The underlying principal must still have serviceusage.services.use IAM +	// permission to use the project for billing/quota. +	WorkforcePoolUserProject string +} + +// Each element consists of a list of patterns.  validateURLs checks for matches +// that include all elements in a given list, in that order. + +var ( +	validTokenURLPatterns = []*regexp.Regexp{ +		// The complicated part in the middle matches any number of characters that +		// aren't period, spaces, or slashes. +		regexp.MustCompile(`(?i)^[^\.\s\/\\]+\.sts\.googleapis\.com$`), +		regexp.MustCompile(`(?i)^sts\.googleapis\.com$`), +		regexp.MustCompile(`(?i)^sts\.[^\.\s\/\\]+\.googleapis\.com$`), +		regexp.MustCompile(`(?i)^[^\.\s\/\\]+-sts\.googleapis\.com$`), +		regexp.MustCompile(`(?i)^sts-[^\.\s\/\\]+\.p\.googleapis\.com$`), +	} +	validImpersonateURLPatterns = []*regexp.Regexp{ +		regexp.MustCompile(`^[^\.\s\/\\]+\.iamcredentials\.googleapis\.com$`), +		regexp.MustCompile(`^iamcredentials\.googleapis\.com$`), +		regexp.MustCompile(`^iamcredentials\.[^\.\s\/\\]+\.googleapis\.com$`), +		regexp.MustCompile(`^[^\.\s\/\\]+-iamcredentials\.googleapis\.com$`), +		regexp.MustCompile(`^iamcredentials-[^\.\s\/\\]+\.p\.googleapis\.com$`), +	} +	validWorkforceAudiencePattern *regexp.Regexp = regexp.MustCompile(`//iam\.googleapis\.com/locations/[^/]+/workforcePools/`) +) + +func validateURL(input string, patterns []*regexp.Regexp, scheme string) bool { +	parsed, err := url.Parse(input) +	if err != nil { +		return false +	} +	if !strings.EqualFold(parsed.Scheme, scheme) { +		return false +	} +	toTest := parsed.Host + +	for _, pattern := range patterns { +		if pattern.MatchString(toTest) { +			return true +		} +	} +	return false +} + +func validateWorkforceAudience(input string) bool { +	return validWorkforceAudiencePattern.MatchString(input) +} + +// TokenSource Returns an external account TokenSource struct. This is to be called by package google to construct a google.Credentials. +func (c *Config) TokenSource(ctx context.Context) (oauth2.TokenSource, error) { +	return c.tokenSource(ctx, validTokenURLPatterns, validImpersonateURLPatterns, "https") +} + +// tokenSource is a private function that's directly called by some of the tests, +// because the unit test URLs are mocked, and would otherwise fail the +// validity check. +func (c *Config) tokenSource(ctx context.Context, tokenURLValidPats []*regexp.Regexp, impersonateURLValidPats []*regexp.Regexp, scheme string) (oauth2.TokenSource, error) { +	valid := validateURL(c.TokenURL, tokenURLValidPats, scheme) +	if !valid { +		return nil, fmt.Errorf("oauth2/google: invalid TokenURL provided while constructing tokenSource") +	} + +	if c.ServiceAccountImpersonationURL != "" { +		valid := validateURL(c.ServiceAccountImpersonationURL, impersonateURLValidPats, scheme) +		if !valid { +			return nil, fmt.Errorf("oauth2/google: invalid ServiceAccountImpersonationURL provided while constructing tokenSource") +		} +	} + +	if c.WorkforcePoolUserProject != "" { +		valid := validateWorkforceAudience(c.Audience) +		if !valid { +			return nil, fmt.Errorf("oauth2/google: workforce_pool_user_project should not be set for non-workforce pool credentials") +		} +	} + +	ts := tokenSource{ +		ctx:  ctx, +		conf: c, +	} +	if c.ServiceAccountImpersonationURL == "" { +		return oauth2.ReuseTokenSource(nil, ts), nil +	} +	scopes := c.Scopes +	ts.conf.Scopes = []string{"https://www.googleapis.com/auth/cloud-platform"} +	imp := ImpersonateTokenSource{ +		Ctx:                  ctx, +		URL:                  c.ServiceAccountImpersonationURL, +		Scopes:               scopes, +		Ts:                   oauth2.ReuseTokenSource(nil, ts), +		TokenLifetimeSeconds: c.ServiceAccountImpersonationLifetimeSeconds, +	} +	return oauth2.ReuseTokenSource(nil, imp), nil +} + +// Subject token file types. +const ( +	fileTypeText = "text" +	fileTypeJSON = "json" +) + +type format struct { +	// Type is either "text" or "json". When not provided "text" type is assumed. +	Type string `json:"type"` +	// SubjectTokenFieldName is only required for JSON format. This would be "access_token" for azure. +	SubjectTokenFieldName string `json:"subject_token_field_name"` +} + +// CredentialSource stores the information necessary to retrieve the credentials for the STS exchange. +// One field amongst File, URL, and Executable should be filled, depending on the kind of credential in question. +// The EnvironmentID should start with AWS if being used for an AWS credential. +type CredentialSource struct { +	File string `json:"file"` + +	URL     string            `json:"url"` +	Headers map[string]string `json:"headers"` + +	Executable *ExecutableConfig `json:"executable"` + +	EnvironmentID               string `json:"environment_id"` +	RegionURL                   string `json:"region_url"` +	RegionalCredVerificationURL string `json:"regional_cred_verification_url"` +	CredVerificationURL         string `json:"cred_verification_url"` +	IMDSv2SessionTokenURL       string `json:"imdsv2_session_token_url"` +	Format                      format `json:"format"` +} + +type ExecutableConfig struct { +	Command       string `json:"command"` +	TimeoutMillis *int   `json:"timeout_millis"` +	OutputFile    string `json:"output_file"` +} + +// parse determines the type of CredentialSource needed. +func (c *Config) parse(ctx context.Context) (baseCredentialSource, error) { +	if len(c.CredentialSource.EnvironmentID) > 3 && c.CredentialSource.EnvironmentID[:3] == "aws" { +		if awsVersion, err := strconv.Atoi(c.CredentialSource.EnvironmentID[3:]); err == nil { +			if awsVersion != 1 { +				return nil, fmt.Errorf("oauth2/google: aws version '%d' is not supported in the current build", awsVersion) +			} + +			awsCredSource := awsCredentialSource{ +				EnvironmentID:               c.CredentialSource.EnvironmentID, +				RegionURL:                   c.CredentialSource.RegionURL, +				RegionalCredVerificationURL: c.CredentialSource.RegionalCredVerificationURL, +				CredVerificationURL:         c.CredentialSource.URL, +				TargetResource:              c.Audience, +				ctx:                         ctx, +			} +			if c.CredentialSource.IMDSv2SessionTokenURL != "" { +				awsCredSource.IMDSv2SessionTokenURL = c.CredentialSource.IMDSv2SessionTokenURL +			} + +			return awsCredSource, nil +		} +	} else if c.CredentialSource.File != "" { +		return fileCredentialSource{File: c.CredentialSource.File, Format: c.CredentialSource.Format}, nil +	} else if c.CredentialSource.URL != "" { +		return urlCredentialSource{URL: c.CredentialSource.URL, Headers: c.CredentialSource.Headers, Format: c.CredentialSource.Format, ctx: ctx}, nil +	} else if c.CredentialSource.Executable != nil { +		return CreateExecutableCredential(ctx, c.CredentialSource.Executable, c) +	} +	return nil, fmt.Errorf("oauth2/google: unable to parse credential source") +} + +type baseCredentialSource interface { +	subjectToken() (string, error) +} + +// tokenSource is the source that handles external credentials. It is used to retrieve Tokens. +type tokenSource struct { +	ctx  context.Context +	conf *Config +} + +// Token allows tokenSource to conform to the oauth2.TokenSource interface. +func (ts tokenSource) Token() (*oauth2.Token, error) { +	conf := ts.conf + +	credSource, err := conf.parse(ts.ctx) +	if err != nil { +		return nil, err +	} +	subjectToken, err := credSource.subjectToken() + +	if err != nil { +		return nil, err +	} +	stsRequest := stsTokenExchangeRequest{ +		GrantType:          "urn:ietf:params:oauth:grant-type:token-exchange", +		Audience:           conf.Audience, +		Scope:              conf.Scopes, +		RequestedTokenType: "urn:ietf:params:oauth:token-type:access_token", +		SubjectToken:       subjectToken, +		SubjectTokenType:   conf.SubjectTokenType, +	} +	header := make(http.Header) +	header.Add("Content-Type", "application/x-www-form-urlencoded") +	clientAuth := clientAuthentication{ +		AuthStyle:    oauth2.AuthStyleInHeader, +		ClientID:     conf.ClientID, +		ClientSecret: conf.ClientSecret, +	} +	var options map[string]interface{} +	// Do not pass workforce_pool_user_project when client authentication is used. +	// The client ID is sufficient for determining the user project. +	if conf.WorkforcePoolUserProject != "" && conf.ClientID == "" { +		options = map[string]interface{}{ +			"userProject": conf.WorkforcePoolUserProject, +		} +	} +	stsResp, err := exchangeToken(ts.ctx, conf.TokenURL, &stsRequest, clientAuth, header, options) +	if err != nil { +		return nil, err +	} + +	accessToken := &oauth2.Token{ +		AccessToken: stsResp.AccessToken, +		TokenType:   stsResp.TokenType, +	} +	if stsResp.ExpiresIn < 0 { +		return nil, fmt.Errorf("oauth2/google: got invalid expiry from security token service") +	} else if stsResp.ExpiresIn >= 0 { +		accessToken.Expiry = now().Add(time.Duration(stsResp.ExpiresIn) * time.Second) +	} + +	if stsResp.RefreshToken != "" { +		accessToken.RefreshToken = stsResp.RefreshToken +	} +	return accessToken, nil +} diff --git a/vendor/golang.org/x/oauth2/google/internal/externalaccount/clientauth.go b/vendor/golang.org/x/oauth2/google/internal/externalaccount/clientauth.go new file mode 100644 index 0000000..99987ce --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/internal/externalaccount/clientauth.go @@ -0,0 +1,45 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package externalaccount + +import ( +	"encoding/base64" +	"net/http" +	"net/url" + +	"golang.org/x/oauth2" +) + +// clientAuthentication represents an OAuth client ID and secret and the mechanism for passing these credentials as stated in rfc6749#2.3.1. +type clientAuthentication struct { +	// AuthStyle can be either basic or request-body +	AuthStyle    oauth2.AuthStyle +	ClientID     string +	ClientSecret string +} + +// InjectAuthentication is used to add authentication to a Secure Token Service exchange +// request.  It modifies either the passed url.Values or http.Header depending on the desired +// authentication format. +func (c *clientAuthentication) InjectAuthentication(values url.Values, headers http.Header) { +	if c.ClientID == "" || c.ClientSecret == "" || values == nil || headers == nil { +		return +	} + +	switch c.AuthStyle { +	case oauth2.AuthStyleInHeader: // AuthStyleInHeader corresponds to basic authentication as defined in rfc7617#2 +		plainHeader := c.ClientID + ":" + c.ClientSecret +		headers.Add("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte(plainHeader))) +	case oauth2.AuthStyleInParams: // AuthStyleInParams corresponds to request-body authentication with ClientID and ClientSecret in the message body. +		values.Set("client_id", c.ClientID) +		values.Set("client_secret", c.ClientSecret) +	case oauth2.AuthStyleAutoDetect: +		values.Set("client_id", c.ClientID) +		values.Set("client_secret", c.ClientSecret) +	default: +		values.Set("client_id", c.ClientID) +		values.Set("client_secret", c.ClientSecret) +	} +} diff --git a/vendor/golang.org/x/oauth2/google/internal/externalaccount/err.go b/vendor/golang.org/x/oauth2/google/internal/externalaccount/err.go new file mode 100644 index 0000000..233a78c --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/internal/externalaccount/err.go @@ -0,0 +1,18 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package externalaccount + +import "fmt" + +// Error for handling OAuth related error responses as stated in rfc6749#5.2. +type Error struct { +	Code        string +	URI         string +	Description string +} + +func (err *Error) Error() string { +	return fmt.Sprintf("got error code %s from %s: %s", err.Code, err.URI, err.Description) +} diff --git a/vendor/golang.org/x/oauth2/google/internal/externalaccount/executablecredsource.go b/vendor/golang.org/x/oauth2/google/internal/externalaccount/executablecredsource.go new file mode 100644 index 0000000..579bcce --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/internal/externalaccount/executablecredsource.go @@ -0,0 +1,309 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package externalaccount + +import ( +	"bytes" +	"context" +	"encoding/json" +	"errors" +	"fmt" +	"io" +	"io/ioutil" +	"os" +	"os/exec" +	"regexp" +	"strings" +	"time" +) + +var serviceAccountImpersonationRE = regexp.MustCompile("https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/(.*@.*):generateAccessToken") + +const ( +	executableSupportedMaxVersion = 1 +	defaultTimeout                = 30 * time.Second +	timeoutMinimum                = 5 * time.Second +	timeoutMaximum                = 120 * time.Second +	executableSource              = "response" +	outputFileSource              = "output file" +) + +type nonCacheableError struct { +	message string +} + +func (nce nonCacheableError) Error() string { +	return nce.message +} + +func missingFieldError(source, field string) error { +	return fmt.Errorf("oauth2/google: %v missing `%q` field", source, field) +} + +func jsonParsingError(source, data string) error { +	return fmt.Errorf("oauth2/google: unable to parse %v\nResponse: %v", source, data) +} + +func malformedFailureError() error { +	return nonCacheableError{"oauth2/google: response must include `error` and `message` fields when unsuccessful"} +} + +func userDefinedError(code, message string) error { +	return nonCacheableError{fmt.Sprintf("oauth2/google: response contains unsuccessful response: (%v) %v", code, message)} +} + +func unsupportedVersionError(source string, version int) error { +	return fmt.Errorf("oauth2/google: %v contains unsupported version: %v", source, version) +} + +func tokenExpiredError() error { +	return nonCacheableError{"oauth2/google: the token returned by the executable is expired"} +} + +func tokenTypeError(source string) error { +	return fmt.Errorf("oauth2/google: %v contains unsupported token type", source) +} + +func exitCodeError(exitCode int) error { +	return fmt.Errorf("oauth2/google: executable command failed with exit code %v", exitCode) +} + +func executableError(err error) error { +	return fmt.Errorf("oauth2/google: executable command failed: %v", err) +} + +func executablesDisallowedError() error { +	return errors.New("oauth2/google: executables need to be explicitly allowed (set GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES to '1') to run") +} + +func timeoutRangeError() error { +	return errors.New("oauth2/google: invalid `timeout_millis` field — executable timeout must be between 5 and 120 seconds") +} + +func commandMissingError() error { +	return errors.New("oauth2/google: missing `command` field — executable command must be provided") +} + +type environment interface { +	existingEnv() []string +	getenv(string) string +	run(ctx context.Context, command string, env []string) ([]byte, error) +	now() time.Time +} + +type runtimeEnvironment struct{} + +func (r runtimeEnvironment) existingEnv() []string { +	return os.Environ() +} + +func (r runtimeEnvironment) getenv(key string) string { +	return os.Getenv(key) +} + +func (r runtimeEnvironment) now() time.Time { +	return time.Now().UTC() +} + +func (r runtimeEnvironment) run(ctx context.Context, command string, env []string) ([]byte, error) { +	splitCommand := strings.Fields(command) +	cmd := exec.CommandContext(ctx, splitCommand[0], splitCommand[1:]...) +	cmd.Env = env + +	var stdout, stderr bytes.Buffer +	cmd.Stdout = &stdout +	cmd.Stderr = &stderr + +	if err := cmd.Run(); err != nil { +		if ctx.Err() == context.DeadlineExceeded { +			return nil, context.DeadlineExceeded +		} + +		if exitError, ok := err.(*exec.ExitError); ok { +			return nil, exitCodeError(exitError.ExitCode()) +		} + +		return nil, executableError(err) +	} + +	bytesStdout := bytes.TrimSpace(stdout.Bytes()) +	if len(bytesStdout) > 0 { +		return bytesStdout, nil +	} +	return bytes.TrimSpace(stderr.Bytes()), nil +} + +type executableCredentialSource struct { +	Command    string +	Timeout    time.Duration +	OutputFile string +	ctx        context.Context +	config     *Config +	env        environment +} + +// CreateExecutableCredential creates an executableCredentialSource given an ExecutableConfig. +// It also performs defaulting and type conversions. +func CreateExecutableCredential(ctx context.Context, ec *ExecutableConfig, config *Config) (executableCredentialSource, error) { +	if ec.Command == "" { +		return executableCredentialSource{}, commandMissingError() +	} + +	result := executableCredentialSource{} +	result.Command = ec.Command +	if ec.TimeoutMillis == nil { +		result.Timeout = defaultTimeout +	} else { +		result.Timeout = time.Duration(*ec.TimeoutMillis) * time.Millisecond +		if result.Timeout < timeoutMinimum || result.Timeout > timeoutMaximum { +			return executableCredentialSource{}, timeoutRangeError() +		} +	} +	result.OutputFile = ec.OutputFile +	result.ctx = ctx +	result.config = config +	result.env = runtimeEnvironment{} +	return result, nil +} + +type executableResponse struct { +	Version        int    `json:"version,omitempty"` +	Success        *bool  `json:"success,omitempty"` +	TokenType      string `json:"token_type,omitempty"` +	ExpirationTime int64  `json:"expiration_time,omitempty"` +	IdToken        string `json:"id_token,omitempty"` +	SamlResponse   string `json:"saml_response,omitempty"` +	Code           string `json:"code,omitempty"` +	Message        string `json:"message,omitempty"` +} + +func (cs executableCredentialSource) parseSubjectTokenFromSource(response []byte, source string, now int64) (string, error) { +	var result executableResponse +	if err := json.Unmarshal(response, &result); err != nil { +		return "", jsonParsingError(source, string(response)) +	} + +	if result.Version == 0 { +		return "", missingFieldError(source, "version") +	} + +	if result.Success == nil { +		return "", missingFieldError(source, "success") +	} + +	if !*result.Success { +		if result.Code == "" || result.Message == "" { +			return "", malformedFailureError() +		} +		return "", userDefinedError(result.Code, result.Message) +	} + +	if result.Version > executableSupportedMaxVersion || result.Version < 0 { +		return "", unsupportedVersionError(source, result.Version) +	} + +	if result.ExpirationTime == 0 && cs.OutputFile != "" { +		return "", missingFieldError(source, "expiration_time") +	} + +	if result.TokenType == "" { +		return "", missingFieldError(source, "token_type") +	} + +	if result.ExpirationTime != 0 && result.ExpirationTime < now { +		return "", tokenExpiredError() +	} + +	if result.TokenType == "urn:ietf:params:oauth:token-type:jwt" || result.TokenType == "urn:ietf:params:oauth:token-type:id_token" { +		if result.IdToken == "" { +			return "", missingFieldError(source, "id_token") +		} +		return result.IdToken, nil +	} + +	if result.TokenType == "urn:ietf:params:oauth:token-type:saml2" { +		if result.SamlResponse == "" { +			return "", missingFieldError(source, "saml_response") +		} +		return result.SamlResponse, nil +	} + +	return "", tokenTypeError(source) +} + +func (cs executableCredentialSource) subjectToken() (string, error) { +	if token, err := cs.getTokenFromOutputFile(); token != "" || err != nil { +		return token, err +	} + +	return cs.getTokenFromExecutableCommand() +} + +func (cs executableCredentialSource) getTokenFromOutputFile() (token string, err error) { +	if cs.OutputFile == "" { +		// This ExecutableCredentialSource doesn't use an OutputFile. +		return "", nil +	} + +	file, err := os.Open(cs.OutputFile) +	if err != nil { +		// No OutputFile found. Hasn't been created yet, so skip it. +		return "", nil +	} +	defer file.Close() + +	data, err := ioutil.ReadAll(io.LimitReader(file, 1<<20)) +	if err != nil || len(data) == 0 { +		// Cachefile exists, but no data found. Get new credential. +		return "", nil +	} + +	token, err = cs.parseSubjectTokenFromSource(data, outputFileSource, cs.env.now().Unix()) +	if err != nil { +		if _, ok := err.(nonCacheableError); ok { +			// If the cached token is expired we need a new token, +			// and if the cache contains a failure, we need to try again. +			return "", nil +		} + +		// There was an error in the cached token, and the developer should be aware of it. +		return "", err +	} +	// Token parsing succeeded.  Use found token. +	return token, nil +} + +func (cs executableCredentialSource) executableEnvironment() []string { +	result := cs.env.existingEnv() +	result = append(result, fmt.Sprintf("GOOGLE_EXTERNAL_ACCOUNT_AUDIENCE=%v", cs.config.Audience)) +	result = append(result, fmt.Sprintf("GOOGLE_EXTERNAL_ACCOUNT_TOKEN_TYPE=%v", cs.config.SubjectTokenType)) +	result = append(result, "GOOGLE_EXTERNAL_ACCOUNT_INTERACTIVE=0") +	if cs.config.ServiceAccountImpersonationURL != "" { +		matches := serviceAccountImpersonationRE.FindStringSubmatch(cs.config.ServiceAccountImpersonationURL) +		if matches != nil { +			result = append(result, fmt.Sprintf("GOOGLE_EXTERNAL_ACCOUNT_IMPERSONATED_EMAIL=%v", matches[1])) +		} +	} +	if cs.OutputFile != "" { +		result = append(result, fmt.Sprintf("GOOGLE_EXTERNAL_ACCOUNT_OUTPUT_FILE=%v", cs.OutputFile)) +	} +	return result +} + +func (cs executableCredentialSource) getTokenFromExecutableCommand() (string, error) { +	// For security reasons, we need our consumers to set this environment variable to allow executables to be run. +	if cs.env.getenv("GOOGLE_EXTERNAL_ACCOUNT_ALLOW_EXECUTABLES") != "1" { +		return "", executablesDisallowedError() +	} + +	ctx, cancel := context.WithDeadline(cs.ctx, cs.env.now().Add(cs.Timeout)) +	defer cancel() + +	output, err := cs.env.run(ctx, cs.Command, cs.executableEnvironment()) +	if err != nil { +		return "", err +	} +	return cs.parseSubjectTokenFromSource(output, executableSource, cs.env.now().Unix()) +} diff --git a/vendor/golang.org/x/oauth2/google/internal/externalaccount/filecredsource.go b/vendor/golang.org/x/oauth2/google/internal/externalaccount/filecredsource.go new file mode 100644 index 0000000..e953ddb --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/internal/externalaccount/filecredsource.go @@ -0,0 +1,57 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package externalaccount + +import ( +	"bytes" +	"encoding/json" +	"errors" +	"fmt" +	"io" +	"io/ioutil" +	"os" +) + +type fileCredentialSource struct { +	File   string +	Format format +} + +func (cs fileCredentialSource) subjectToken() (string, error) { +	tokenFile, err := os.Open(cs.File) +	if err != nil { +		return "", fmt.Errorf("oauth2/google: failed to open credential file %q", cs.File) +	} +	defer tokenFile.Close() +	tokenBytes, err := ioutil.ReadAll(io.LimitReader(tokenFile, 1<<20)) +	if err != nil { +		return "", fmt.Errorf("oauth2/google: failed to read credential file: %v", err) +	} +	tokenBytes = bytes.TrimSpace(tokenBytes) +	switch cs.Format.Type { +	case "json": +		jsonData := make(map[string]interface{}) +		err = json.Unmarshal(tokenBytes, &jsonData) +		if err != nil { +			return "", fmt.Errorf("oauth2/google: failed to unmarshal subject token file: %v", err) +		} +		val, ok := jsonData[cs.Format.SubjectTokenFieldName] +		if !ok { +			return "", errors.New("oauth2/google: provided subject_token_field_name not found in credentials") +		} +		token, ok := val.(string) +		if !ok { +			return "", errors.New("oauth2/google: improperly formatted subject token") +		} +		return token, nil +	case "text": +		return string(tokenBytes), nil +	case "": +		return string(tokenBytes), nil +	default: +		return "", errors.New("oauth2/google: invalid credential_source file format type") +	} + +} diff --git a/vendor/golang.org/x/oauth2/google/internal/externalaccount/impersonate.go b/vendor/golang.org/x/oauth2/google/internal/externalaccount/impersonate.go new file mode 100644 index 0000000..54c8f20 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/internal/externalaccount/impersonate.go @@ -0,0 +1,105 @@ +// Copyright 2021 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package externalaccount + +import ( +	"bytes" +	"context" +	"encoding/json" +	"fmt" +	"io" +	"io/ioutil" +	"net/http" +	"time" + +	"golang.org/x/oauth2" +) + +// generateAccesstokenReq is used for service account impersonation +type generateAccessTokenReq struct { +	Delegates []string `json:"delegates,omitempty"` +	Lifetime  string   `json:"lifetime,omitempty"` +	Scope     []string `json:"scope,omitempty"` +} + +type impersonateTokenResponse struct { +	AccessToken string `json:"accessToken"` +	ExpireTime  string `json:"expireTime"` +} + +// ImpersonateTokenSource uses a source credential, stored in Ts, to request an access token to the provided URL. +// Scopes can be defined when the access token is requested. +type ImpersonateTokenSource struct { +	// Ctx is the execution context of the impersonation process +	// used to perform http call to the URL. Required +	Ctx context.Context +	// Ts is the source credential used to generate a token on the +	// impersonated service account. Required. +	Ts oauth2.TokenSource + +	// URL is the endpoint to call to generate a token +	// on behalf the service account. Required. +	URL string +	// Scopes that the impersonated credential should have. Required. +	Scopes []string +	// Delegates are the service account email addresses in a delegation chain. +	// Each service account must be granted roles/iam.serviceAccountTokenCreator +	// on the next service account in the chain. Optional. +	Delegates []string +	// TokenLifetimeSeconds is the number of seconds the impersonation token will +	// be valid for. +	TokenLifetimeSeconds int +} + +// Token performs the exchange to get a temporary service account token to allow access to GCP. +func (its ImpersonateTokenSource) Token() (*oauth2.Token, error) { +	lifetimeString := "3600s" +	if its.TokenLifetimeSeconds != 0 { +		lifetimeString = fmt.Sprintf("%ds", its.TokenLifetimeSeconds) +	} +	reqBody := generateAccessTokenReq{ +		Lifetime:  lifetimeString, +		Scope:     its.Scopes, +		Delegates: its.Delegates, +	} +	b, err := json.Marshal(reqBody) +	if err != nil { +		return nil, fmt.Errorf("oauth2/google: unable to marshal request: %v", err) +	} +	client := oauth2.NewClient(its.Ctx, its.Ts) +	req, err := http.NewRequest("POST", its.URL, bytes.NewReader(b)) +	if err != nil { +		return nil, fmt.Errorf("oauth2/google: unable to create impersonation request: %v", err) +	} +	req = req.WithContext(its.Ctx) +	req.Header.Set("Content-Type", "application/json") + +	resp, err := client.Do(req) +	if err != nil { +		return nil, fmt.Errorf("oauth2/google: unable to generate access token: %v", err) +	} +	defer resp.Body.Close() +	body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) +	if err != nil { +		return nil, fmt.Errorf("oauth2/google: unable to read body: %v", err) +	} +	if c := resp.StatusCode; c < 200 || c > 299 { +		return nil, fmt.Errorf("oauth2/google: status code %d: %s", c, body) +	} + +	var accessTokenResp impersonateTokenResponse +	if err := json.Unmarshal(body, &accessTokenResp); err != nil { +		return nil, fmt.Errorf("oauth2/google: unable to parse response: %v", err) +	} +	expiry, err := time.Parse(time.RFC3339, accessTokenResp.ExpireTime) +	if err != nil { +		return nil, fmt.Errorf("oauth2/google: unable to parse expiry: %v", err) +	} +	return &oauth2.Token{ +		AccessToken: accessTokenResp.AccessToken, +		Expiry:      expiry, +		TokenType:   "Bearer", +	}, nil +} diff --git a/vendor/golang.org/x/oauth2/google/internal/externalaccount/sts_exchange.go b/vendor/golang.org/x/oauth2/google/internal/externalaccount/sts_exchange.go new file mode 100644 index 0000000..e6fcae5 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/internal/externalaccount/sts_exchange.go @@ -0,0 +1,107 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package externalaccount + +import ( +	"context" +	"encoding/json" +	"fmt" +	"io" +	"io/ioutil" +	"net/http" +	"net/url" +	"strconv" +	"strings" + +	"golang.org/x/oauth2" +) + +// exchangeToken performs an oauth2 token exchange with the provided endpoint. +// The first 4 fields are all mandatory.  headers can be used to pass additional +// headers beyond the bare minimum required by the token exchange.  options can +// be used to pass additional JSON-structured options to the remote server. +func exchangeToken(ctx context.Context, endpoint string, request *stsTokenExchangeRequest, authentication clientAuthentication, headers http.Header, options map[string]interface{}) (*stsTokenExchangeResponse, error) { + +	client := oauth2.NewClient(ctx, nil) + +	data := url.Values{} +	data.Set("audience", request.Audience) +	data.Set("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange") +	data.Set("requested_token_type", "urn:ietf:params:oauth:token-type:access_token") +	data.Set("subject_token_type", request.SubjectTokenType) +	data.Set("subject_token", request.SubjectToken) +	data.Set("scope", strings.Join(request.Scope, " ")) +	if options != nil { +		opts, err := json.Marshal(options) +		if err != nil { +			return nil, fmt.Errorf("oauth2/google: failed to marshal additional options: %v", err) +		} +		data.Set("options", string(opts)) +	} + +	authentication.InjectAuthentication(data, headers) +	encodedData := data.Encode() + +	req, err := http.NewRequest("POST", endpoint, strings.NewReader(encodedData)) +	if err != nil { +		return nil, fmt.Errorf("oauth2/google: failed to properly build http request: %v", err) + +	} +	req = req.WithContext(ctx) +	for key, list := range headers { +		for _, val := range list { +			req.Header.Add(key, val) +		} +	} +	req.Header.Add("Content-Length", strconv.Itoa(len(encodedData))) + +	resp, err := client.Do(req) + +	if err != nil { +		return nil, fmt.Errorf("oauth2/google: invalid response from Secure Token Server: %v", err) +	} +	defer resp.Body.Close() + +	body, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) +	if err != nil { +		return nil, err +	} +	if c := resp.StatusCode; c < 200 || c > 299 { +		return nil, fmt.Errorf("oauth2/google: status code %d: %s", c, body) +	} +	var stsResp stsTokenExchangeResponse +	err = json.Unmarshal(body, &stsResp) +	if err != nil { +		return nil, fmt.Errorf("oauth2/google: failed to unmarshal response body from Secure Token Server: %v", err) + +	} + +	return &stsResp, nil +} + +// stsTokenExchangeRequest contains fields necessary to make an oauth2 token exchange. +type stsTokenExchangeRequest struct { +	ActingParty struct { +		ActorToken     string +		ActorTokenType string +	} +	GrantType          string +	Resource           string +	Audience           string +	Scope              []string +	RequestedTokenType string +	SubjectToken       string +	SubjectTokenType   string +} + +// stsTokenExchangeResponse is used to decode the remote server response during an oauth2 token exchange. +type stsTokenExchangeResponse struct { +	AccessToken     string `json:"access_token"` +	IssuedTokenType string `json:"issued_token_type"` +	TokenType       string `json:"token_type"` +	ExpiresIn       int    `json:"expires_in"` +	Scope           string `json:"scope"` +	RefreshToken    string `json:"refresh_token"` +} diff --git a/vendor/golang.org/x/oauth2/google/internal/externalaccount/urlcredsource.go b/vendor/golang.org/x/oauth2/google/internal/externalaccount/urlcredsource.go new file mode 100644 index 0000000..16dca65 --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/internal/externalaccount/urlcredsource.go @@ -0,0 +1,75 @@ +// Copyright 2020 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package externalaccount + +import ( +	"context" +	"encoding/json" +	"errors" +	"fmt" +	"io" +	"io/ioutil" +	"net/http" + +	"golang.org/x/oauth2" +) + +type urlCredentialSource struct { +	URL     string +	Headers map[string]string +	Format  format +	ctx     context.Context +} + +func (cs urlCredentialSource) subjectToken() (string, error) { +	client := oauth2.NewClient(cs.ctx, nil) +	req, err := http.NewRequest("GET", cs.URL, nil) +	if err != nil { +		return "", fmt.Errorf("oauth2/google: HTTP request for URL-sourced credential failed: %v", err) +	} +	req = req.WithContext(cs.ctx) + +	for key, val := range cs.Headers { +		req.Header.Add(key, val) +	} +	resp, err := client.Do(req) +	if err != nil { +		return "", fmt.Errorf("oauth2/google: invalid response when retrieving subject token: %v", err) +	} +	defer resp.Body.Close() + +	respBody, err := ioutil.ReadAll(io.LimitReader(resp.Body, 1<<20)) +	if err != nil { +		return "", fmt.Errorf("oauth2/google: invalid body in subject token URL query: %v", err) +	} +	if c := resp.StatusCode; c < 200 || c > 299 { +		return "", fmt.Errorf("oauth2/google: status code %d: %s", c, respBody) +	} + +	switch cs.Format.Type { +	case "json": +		jsonData := make(map[string]interface{}) +		err = json.Unmarshal(respBody, &jsonData) +		if err != nil { +			return "", fmt.Errorf("oauth2/google: failed to unmarshal subject token file: %v", err) +		} +		val, ok := jsonData[cs.Format.SubjectTokenFieldName] +		if !ok { +			return "", errors.New("oauth2/google: provided subject_token_field_name not found in credentials") +		} +		token, ok := val.(string) +		if !ok { +			return "", errors.New("oauth2/google: improperly formatted subject token") +		} +		return token, nil +	case "text": +		return string(respBody), nil +	case "": +		return string(respBody), nil +	default: +		return "", errors.New("oauth2/google: invalid credential_source file format type") +	} + +} diff --git a/vendor/golang.org/x/oauth2/google/jwt.go b/vendor/golang.org/x/oauth2/google/jwt.go new file mode 100644 index 0000000..e89e6ae --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/jwt.go @@ -0,0 +1,102 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( +	"crypto/rsa" +	"fmt" +	"strings" +	"time" + +	"golang.org/x/oauth2" +	"golang.org/x/oauth2/internal" +	"golang.org/x/oauth2/jws" +) + +// JWTAccessTokenSourceFromJSON uses a Google Developers service account JSON +// key file to read the credentials that authorize and authenticate the +// requests, and returns a TokenSource that does not use any OAuth2 flow but +// instead creates a JWT and sends that as the access token. +// The audience is typically a URL that specifies the scope of the credentials. +// +// Note that this is not a standard OAuth flow, but rather an +// optimization supported by a few Google services. +// Unless you know otherwise, you should use JWTConfigFromJSON instead. +func JWTAccessTokenSourceFromJSON(jsonKey []byte, audience string) (oauth2.TokenSource, error) { +	return newJWTSource(jsonKey, audience, nil) +} + +// JWTAccessTokenSourceWithScope uses a Google Developers service account JSON +// key file to read the credentials that authorize and authenticate the +// requests, and returns a TokenSource that does not use any OAuth2 flow but +// instead creates a JWT and sends that as the access token. +// The scope is typically a list of URLs that specifies the scope of the +// credentials. +// +// Note that this is not a standard OAuth flow, but rather an +// optimization supported by a few Google services. +// Unless you know otherwise, you should use JWTConfigFromJSON instead. +func JWTAccessTokenSourceWithScope(jsonKey []byte, scope ...string) (oauth2.TokenSource, error) { +	return newJWTSource(jsonKey, "", scope) +} + +func newJWTSource(jsonKey []byte, audience string, scopes []string) (oauth2.TokenSource, error) { +	if len(scopes) == 0 && audience == "" { +		return nil, fmt.Errorf("google: missing scope/audience for JWT access token") +	} + +	cfg, err := JWTConfigFromJSON(jsonKey) +	if err != nil { +		return nil, fmt.Errorf("google: could not parse JSON key: %v", err) +	} +	pk, err := internal.ParseKey(cfg.PrivateKey) +	if err != nil { +		return nil, fmt.Errorf("google: could not parse key: %v", err) +	} +	ts := &jwtAccessTokenSource{ +		email:    cfg.Email, +		audience: audience, +		scopes:   scopes, +		pk:       pk, +		pkID:     cfg.PrivateKeyID, +	} +	tok, err := ts.Token() +	if err != nil { +		return nil, err +	} +	rts := newErrWrappingTokenSource(oauth2.ReuseTokenSource(tok, ts)) +	return rts, nil +} + +type jwtAccessTokenSource struct { +	email, audience string +	scopes          []string +	pk              *rsa.PrivateKey +	pkID            string +} + +func (ts *jwtAccessTokenSource) Token() (*oauth2.Token, error) { +	iat := time.Now() +	exp := iat.Add(time.Hour) +	scope := strings.Join(ts.scopes, " ") +	cs := &jws.ClaimSet{ +		Iss:   ts.email, +		Sub:   ts.email, +		Aud:   ts.audience, +		Scope: scope, +		Iat:   iat.Unix(), +		Exp:   exp.Unix(), +	} +	hdr := &jws.Header{ +		Algorithm: "RS256", +		Typ:       "JWT", +		KeyID:     string(ts.pkID), +	} +	msg, err := jws.Encode(hdr, cs, ts.pk) +	if err != nil { +		return nil, fmt.Errorf("google: could not encode JWT: %v", err) +	} +	return &oauth2.Token{AccessToken: msg, TokenType: "Bearer", Expiry: exp}, nil +} diff --git a/vendor/golang.org/x/oauth2/google/sdk.go b/vendor/golang.org/x/oauth2/google/sdk.go new file mode 100644 index 0000000..456224b --- /dev/null +++ b/vendor/golang.org/x/oauth2/google/sdk.go @@ -0,0 +1,201 @@ +// Copyright 2015 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package google + +import ( +	"bufio" +	"context" +	"encoding/json" +	"errors" +	"fmt" +	"io" +	"net/http" +	"os" +	"os/user" +	"path/filepath" +	"runtime" +	"strings" +	"time" + +	"golang.org/x/oauth2" +) + +type sdkCredentials struct { +	Data []struct { +		Credential struct { +			ClientID     string     `json:"client_id"` +			ClientSecret string     `json:"client_secret"` +			AccessToken  string     `json:"access_token"` +			RefreshToken string     `json:"refresh_token"` +			TokenExpiry  *time.Time `json:"token_expiry"` +		} `json:"credential"` +		Key struct { +			Account string `json:"account"` +			Scope   string `json:"scope"` +		} `json:"key"` +	} +} + +// An SDKConfig provides access to tokens from an account already +// authorized via the Google Cloud SDK. +type SDKConfig struct { +	conf         oauth2.Config +	initialToken *oauth2.Token +} + +// NewSDKConfig creates an SDKConfig for the given Google Cloud SDK +// account. If account is empty, the account currently active in +// Google Cloud SDK properties is used. +// Google Cloud SDK credentials must be created by running `gcloud auth` +// before using this function. +// The Google Cloud SDK is available at https://cloud.google.com/sdk/. +func NewSDKConfig(account string) (*SDKConfig, error) { +	configPath, err := sdkConfigPath() +	if err != nil { +		return nil, fmt.Errorf("oauth2/google: error getting SDK config path: %v", err) +	} +	credentialsPath := filepath.Join(configPath, "credentials") +	f, err := os.Open(credentialsPath) +	if err != nil { +		return nil, fmt.Errorf("oauth2/google: failed to load SDK credentials: %v", err) +	} +	defer f.Close() + +	var c sdkCredentials +	if err := json.NewDecoder(f).Decode(&c); err != nil { +		return nil, fmt.Errorf("oauth2/google: failed to decode SDK credentials from %q: %v", credentialsPath, err) +	} +	if len(c.Data) == 0 { +		return nil, fmt.Errorf("oauth2/google: no credentials found in %q, run `gcloud auth login` to create one", credentialsPath) +	} +	if account == "" { +		propertiesPath := filepath.Join(configPath, "properties") +		f, err := os.Open(propertiesPath) +		if err != nil { +			return nil, fmt.Errorf("oauth2/google: failed to load SDK properties: %v", err) +		} +		defer f.Close() +		ini, err := parseINI(f) +		if err != nil { +			return nil, fmt.Errorf("oauth2/google: failed to parse SDK properties %q: %v", propertiesPath, err) +		} +		core, ok := ini["core"] +		if !ok { +			return nil, fmt.Errorf("oauth2/google: failed to find [core] section in %v", ini) +		} +		active, ok := core["account"] +		if !ok { +			return nil, fmt.Errorf("oauth2/google: failed to find %q attribute in %v", "account", core) +		} +		account = active +	} + +	for _, d := range c.Data { +		if account == "" || d.Key.Account == account { +			if d.Credential.AccessToken == "" && d.Credential.RefreshToken == "" { +				return nil, fmt.Errorf("oauth2/google: no token available for account %q", account) +			} +			var expiry time.Time +			if d.Credential.TokenExpiry != nil { +				expiry = *d.Credential.TokenExpiry +			} +			return &SDKConfig{ +				conf: oauth2.Config{ +					ClientID:     d.Credential.ClientID, +					ClientSecret: d.Credential.ClientSecret, +					Scopes:       strings.Split(d.Key.Scope, " "), +					Endpoint:     Endpoint, +					RedirectURL:  "oob", +				}, +				initialToken: &oauth2.Token{ +					AccessToken:  d.Credential.AccessToken, +					RefreshToken: d.Credential.RefreshToken, +					Expiry:       expiry, +				}, +			}, nil +		} +	} +	return nil, fmt.Errorf("oauth2/google: no such credentials for account %q", account) +} + +// Client returns an HTTP client using Google Cloud SDK credentials to +// authorize requests. The token will auto-refresh as necessary. The +// underlying http.RoundTripper will be obtained using the provided +// context. The returned client and its Transport should not be +// modified. +func (c *SDKConfig) Client(ctx context.Context) *http.Client { +	return &http.Client{ +		Transport: &oauth2.Transport{ +			Source: c.TokenSource(ctx), +		}, +	} +} + +// TokenSource returns an oauth2.TokenSource that retrieve tokens from +// Google Cloud SDK credentials using the provided context. +// It will returns the current access token stored in the credentials, +// and refresh it when it expires, but it won't update the credentials +// with the new access token. +func (c *SDKConfig) TokenSource(ctx context.Context) oauth2.TokenSource { +	return c.conf.TokenSource(ctx, c.initialToken) +} + +// Scopes are the OAuth 2.0 scopes the current account is authorized for. +func (c *SDKConfig) Scopes() []string { +	return c.conf.Scopes +} + +func parseINI(ini io.Reader) (map[string]map[string]string, error) { +	result := map[string]map[string]string{ +		"": {}, // root section +	} +	scanner := bufio.NewScanner(ini) +	currentSection := "" +	for scanner.Scan() { +		line := strings.TrimSpace(scanner.Text()) +		if strings.HasPrefix(line, ";") { +			// comment. +			continue +		} +		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") { +			currentSection = strings.TrimSpace(line[1 : len(line)-1]) +			result[currentSection] = map[string]string{} +			continue +		} +		parts := strings.SplitN(line, "=", 2) +		if len(parts) == 2 && parts[0] != "" { +			result[currentSection][strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1]) +		} +	} +	if err := scanner.Err(); err != nil { +		return nil, fmt.Errorf("error scanning ini: %v", err) +	} +	return result, nil +} + +// sdkConfigPath tries to guess where the gcloud config is located. +// It can be overridden during tests. +var sdkConfigPath = func() (string, error) { +	if runtime.GOOS == "windows" { +		return filepath.Join(os.Getenv("APPDATA"), "gcloud"), nil +	} +	homeDir := guessUnixHomeDir() +	if homeDir == "" { +		return "", errors.New("unable to get current user home directory: os/user lookup failed; $HOME is empty") +	} +	return filepath.Join(homeDir, ".config", "gcloud"), nil +} + +func guessUnixHomeDir() string { +	// Prefer $HOME over user.Current due to glibc bug: golang.org/issue/13470 +	if v := os.Getenv("HOME"); v != "" { +		return v +	} +	// Else, fall back to user.Current: +	if u, err := user.Current(); err == nil { +		return u.HomeDir +	} +	return "" +}  | 
