* update github.com/blevesearch/bleve v2.0.2 -> v2.0.3 * github.com/denisenkom/go-mssqldb v0.9.0 -> v0.10.0 * github.com/editorconfig/editorconfig-core-go v2.4.1 -> v2.4.2 * github.com/go-chi/cors v1.1.1 -> v1.2.0 * github.com/go-git/go-billy v5.0.0 -> v5.1.0 * github.com/go-git/go-git v5.2.0 -> v5.3.0 * github.com/go-ldap/ldap v3.2.4 -> v3.3.0 * github.com/go-redis/redis v8.6.0 -> v8.8.2 * github.com/go-sql-driver/mysql v1.5.0 -> v1.6.0 * github.com/go-swagger/go-swagger v0.26.1 -> v0.27.0 * github.com/lib/pq v1.9.0 -> v1.10.1 * github.com/mattn/go-sqlite3 v1.14.6 -> v1.14.7 * github.com/go-testfixtures/testfixtures v3.5.0 -> v3.6.0 * github.com/issue9/identicon v1.0.1 -> v1.2.0 * github.com/klauspost/compress v1.11.8 -> v1.12.1 * github.com/mgechev/revive v1.0.3 -> v1.0.6 * github.com/microcosm-cc/bluemonday v1.0.7 -> v1.0.8 * github.com/niklasfasching/go-org v1.4.0 -> v1.5.0 * github.com/olivere/elastic v7.0.22 -> v7.0.24 * github.com/pelletier/go-toml v1.8.1 -> v1.9.0 * github.com/prometheus/client_golang v1.9.0 -> v1.10.0 * github.com/xanzy/go-gitlab v0.44.0 -> v0.48.0 * github.com/yuin/goldmark v1.3.3 -> v1.3.5 * github.com/6543/go-version v1.2.4 -> v1.3.1 * do github.com/lib/pq v1.10.0 -> v1.10.1 again ...
163 lines
5.3 KiB
Go
Vendored
163 lines
5.3 KiB
Go
Vendored
// 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"
|
|
"golang.org/x/oauth2"
|
|
"net/http"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
// 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 string
|
|
SubjectTokenType string
|
|
TokenURL string
|
|
TokenInfoURL string
|
|
ServiceAccountImpersonationURL string
|
|
ClientSecret string
|
|
ClientID string
|
|
CredentialSource CredentialSource
|
|
QuotaProjectID string
|
|
Scopes []string
|
|
}
|
|
|
|
// 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 {
|
|
ts := tokenSource{
|
|
ctx: ctx,
|
|
conf: c,
|
|
}
|
|
if c.ServiceAccountImpersonationURL == "" {
|
|
return oauth2.ReuseTokenSource(nil, ts)
|
|
}
|
|
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),
|
|
}
|
|
return oauth2.ReuseTokenSource(nil, imp)
|
|
}
|
|
|
|
// 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.
|
|
type CredentialSource struct {
|
|
File string `json:"file"`
|
|
|
|
URL string `json:"url"`
|
|
Headers map[string]string `json:"headers"`
|
|
|
|
EnvironmentID string `json:"environment_id"`
|
|
RegionURL string `json:"region_url"`
|
|
RegionalCredVerificationURL string `json:"regional_cred_verification_url"`
|
|
CredVerificationURL string `json:"cred_verification_url"`
|
|
Format format `json:"format"`
|
|
}
|
|
|
|
// 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)
|
|
}
|
|
return awsCredentialSource{
|
|
EnvironmentID: c.CredentialSource.EnvironmentID,
|
|
RegionURL: c.CredentialSource.RegionURL,
|
|
RegionalCredVerificationURL: c.CredentialSource.RegionalCredVerificationURL,
|
|
CredVerificationURL: c.CredentialSource.URL,
|
|
TargetResource: c.Audience,
|
|
ctx: ctx,
|
|
}, 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
|
|
}
|
|
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.
|
|
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,
|
|
}
|
|
stsResp, err := exchangeToken(ts.ctx, conf.TokenURL, &stsRequest, clientAuth, header, nil)
|
|
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
|
|
}
|