forked from Mirrors/oauth2
google/externalaccount: add support for workforce pool credentials
Workforce pools (external account credentials for non-Google users) are
organization-level resources which means that issued workforce pool tokens
will not have any client project ID on token exchange as currently designed.
"To use a Google API, the client must identify the application to the server.
If the API requires authentication, the client must also identify the principal
running the application."
The application here is the client project. The token will identify the user
principal but not the application. This will result in APIs rejecting requests
authenticated with these tokens.
Note that passing a x-goog-user-project override header on API request is
still not sufficient. The token is still expected to have a client project.
As a result, we have extended the spec to support an additional
workforce_pool_user_project for these credentials (workforce pools) which will
be passed when exchanging an external token for a Google Access token. After the
exchange, the issued access token will use the supplied project as the client
project. The underlying principal must still have serviceusage.services.use
IAM permission to use the project for billing/quota.
This field is not needed for flows with basic client authentication (e.g. client
ID is supplied). The client ID is sufficient to determine the client project and
any additionally supplied workforce_pool_user_project value will be ignored.
Note that this feature is not usable yet publicly.
Change-Id: I8311d7783e4048c260cbb68e90d3565df864d7e0
GitHub-Last-Rev: a6dc5ebc95
GitHub-Pull-Request: golang/oauth2#520
Reviewed-on: https://go-review.googlesource.com/c/oauth2/+/353393
Reviewed-by: Cody Oss <codyoss@google.com>
Reviewed-by: Bassam Ojeil <bojeil@google.com>
Trust: Cody Oss <codyoss@google.com>
Trust: Tyler Bui-Palsulich <tbp@google.com>
Run-TryBot: Cody Oss <codyoss@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
parent
2bc19b1117
commit
6b3c2da341
|
@ -123,6 +123,7 @@ type credentialsFile struct {
|
||||||
ServiceAccountImpersonationURL string `json:"service_account_impersonation_url"`
|
ServiceAccountImpersonationURL string `json:"service_account_impersonation_url"`
|
||||||
CredentialSource externalaccount.CredentialSource `json:"credential_source"`
|
CredentialSource externalaccount.CredentialSource `json:"credential_source"`
|
||||||
QuotaProjectID string `json:"quota_project_id"`
|
QuotaProjectID string `json:"quota_project_id"`
|
||||||
|
WorkforcePoolUserProject string `json:"workforce_pool_user_project"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *credentialsFile) jwtConfig(scopes []string, subject string) *jwt.Config {
|
func (f *credentialsFile) jwtConfig(scopes []string, subject string) *jwt.Config {
|
||||||
|
@ -176,6 +177,7 @@ func (f *credentialsFile) tokenSource(ctx context.Context, params CredentialsPar
|
||||||
CredentialSource: f.CredentialSource,
|
CredentialSource: f.CredentialSource,
|
||||||
QuotaProjectID: f.QuotaProjectID,
|
QuotaProjectID: f.QuotaProjectID,
|
||||||
Scopes: params.Scopes,
|
Scopes: params.Scopes,
|
||||||
|
WorkforcePoolUserProject: f.WorkforcePoolUserProject,
|
||||||
}
|
}
|
||||||
return cfg.TokenSource(ctx)
|
return cfg.TokenSource(ctx)
|
||||||
case "":
|
case "":
|
||||||
|
|
|
@ -53,6 +53,11 @@ type Config struct {
|
||||||
QuotaProjectID string
|
QuotaProjectID string
|
||||||
// Scopes contains the desired scopes for the returned access token.
|
// Scopes contains the desired scopes for the returned access token.
|
||||||
Scopes []string
|
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
|
// Each element consists of a list of patterns. validateURLs checks for matches
|
||||||
|
@ -73,6 +78,7 @@ var (
|
||||||
regexp.MustCompile(`^iamcredentials\.[^\.\s\/\\]+\.googleapis\.com$`),
|
regexp.MustCompile(`^iamcredentials\.[^\.\s\/\\]+\.googleapis\.com$`),
|
||||||
regexp.MustCompile(`^[^\.\s\/\\]+-iamcredentials\.googleapis\.com$`),
|
regexp.MustCompile(`^[^\.\s\/\\]+-iamcredentials\.googleapis\.com$`),
|
||||||
}
|
}
|
||||||
|
validWorkforceAudiencePattern *regexp.Regexp = regexp.MustCompile(`//iam\.googleapis\.com/locations/[^/]+/workforcePools/`)
|
||||||
)
|
)
|
||||||
|
|
||||||
func validateURL(input string, patterns []*regexp.Regexp, scheme string) bool {
|
func validateURL(input string, patterns []*regexp.Regexp, scheme string) bool {
|
||||||
|
@ -86,14 +92,17 @@ func validateURL(input string, patterns []*regexp.Regexp, scheme string) bool {
|
||||||
toTest := parsed.Host
|
toTest := parsed.Host
|
||||||
|
|
||||||
for _, pattern := range patterns {
|
for _, pattern := range patterns {
|
||||||
|
if pattern.MatchString(toTest) {
|
||||||
if valid := pattern.MatchString(toTest); valid {
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
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.
|
// 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) {
|
func (c *Config) TokenSource(ctx context.Context) (oauth2.TokenSource, error) {
|
||||||
return c.tokenSource(ctx, validTokenURLPatterns, validImpersonateURLPatterns, "https")
|
return c.tokenSource(ctx, validTokenURLPatterns, validImpersonateURLPatterns, "https")
|
||||||
|
@ -115,6 +124,13 @@ func (c *Config) tokenSource(ctx context.Context, tokenURLValidPats []*regexp.Re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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{
|
ts := tokenSource{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
conf: c,
|
conf: c,
|
||||||
|
@ -224,7 +240,15 @@ func (ts tokenSource) Token() (*oauth2.Token, error) {
|
||||||
ClientID: conf.ClientID,
|
ClientID: conf.ClientID,
|
||||||
ClientSecret: conf.ClientSecret,
|
ClientSecret: conf.ClientSecret,
|
||||||
}
|
}
|
||||||
stsResp, err := exchangeToken(ts.ctx, conf.TokenURL, &stsRequest, clientAuth, header, nil)
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/oauth2"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -35,8 +37,10 @@ var testConfig = Config{
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
baseCredsRequestBody = "audience=32555940559.apps.googleusercontent.com&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=street123&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Ajwt"
|
baseCredsRequestBody = "audience=32555940559.apps.googleusercontent.com&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=street123&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aid_token"
|
||||||
baseCredsResponseBody = `{"access_token":"Sample.Access.Token","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":3600,"scope":"https://www.googleapis.com/auth/cloud-platform"}`
|
baseCredsResponseBody = `{"access_token":"Sample.Access.Token","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","token_type":"Bearer","expires_in":3600,"scope":"https://www.googleapis.com/auth/cloud-platform"}`
|
||||||
|
workforcePoolRequestBodyWithClientId = "audience=%2F%2Fiam.googleapis.com%2Flocations%2Feu%2FworkforcePools%2Fpool-id%2Fproviders%2Fprovider-id&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=street123&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aid_token"
|
||||||
|
workforcePoolRequestBodyWithoutClientId = "audience=%2F%2Fiam.googleapis.com%2Flocations%2Feu%2FworkforcePools%2Fpool-id%2Fproviders%2Fprovider-id&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Atoken-exchange&options=%7B%22userProject%22%3A%22myProject%22%7D&requested_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aaccess_token&scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fdevstorage.full_control&subject_token=street123&subject_token_type=urn%3Aietf%3Aparams%3Aoauth%3Atoken-type%3Aid_token"
|
||||||
correctAT = "Sample.Access.Token"
|
correctAT = "Sample.Access.Token"
|
||||||
expiry int64 = 234852
|
expiry int64 = 234852
|
||||||
)
|
)
|
||||||
|
@ -44,46 +48,53 @@ var (
|
||||||
testNow = func() time.Time { return time.Unix(expiry, 0) }
|
testNow = func() time.Time { return time.Unix(expiry, 0) }
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestToken(t *testing.T) {
|
type testExchangeTokenServer struct {
|
||||||
|
url string
|
||||||
|
authorization string
|
||||||
|
contentType string
|
||||||
|
body string
|
||||||
|
response string
|
||||||
|
}
|
||||||
|
|
||||||
targetServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
func run(t *testing.T, config *Config, tets *testExchangeTokenServer) (*oauth2.Token, error) {
|
||||||
if got, want := r.URL.String(), "/"; got != want {
|
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if got, want := r.URL.String(), tets.url; got != want {
|
||||||
t.Errorf("URL.String(): got %v but want %v", got, want)
|
t.Errorf("URL.String(): got %v but want %v", got, want)
|
||||||
}
|
}
|
||||||
headerAuth := r.Header.Get("Authorization")
|
headerAuth := r.Header.Get("Authorization")
|
||||||
if got, want := headerAuth, "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ="; got != want {
|
if got, want := headerAuth, tets.authorization; got != want {
|
||||||
t.Errorf("got %v but want %v", got, want)
|
t.Errorf("got %v but want %v", got, want)
|
||||||
}
|
}
|
||||||
headerContentType := r.Header.Get("Content-Type")
|
headerContentType := r.Header.Get("Content-Type")
|
||||||
if got, want := headerContentType, "application/x-www-form-urlencoded"; got != want {
|
if got, want := headerContentType, tets.contentType; got != want {
|
||||||
t.Errorf("got %v but want %v", got, want)
|
t.Errorf("got %v but want %v", got, want)
|
||||||
}
|
}
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed reading request body: %s.", err)
|
t.Fatalf("Failed reading request body: %s.", err)
|
||||||
}
|
}
|
||||||
if got, want := string(body), baseCredsRequestBody; got != want {
|
if got, want := string(body), tets.body; got != want {
|
||||||
t.Errorf("Unexpected exchange payload: got %v but want %v", got, want)
|
t.Errorf("Unexpected exchange payload: got %v but want %v", got, want)
|
||||||
}
|
}
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.Write([]byte(baseCredsResponseBody))
|
w.Write([]byte(tets.response))
|
||||||
}))
|
}))
|
||||||
defer targetServer.Close()
|
defer server.Close()
|
||||||
|
config.TokenURL = server.URL
|
||||||
testConfig.TokenURL = targetServer.URL
|
|
||||||
ourTS := tokenSource{
|
|
||||||
ctx: context.Background(),
|
|
||||||
conf: &testConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
oldNow := now
|
oldNow := now
|
||||||
defer func() { now = oldNow }()
|
defer func() { now = oldNow }()
|
||||||
now = testNow
|
now = testNow
|
||||||
|
|
||||||
tok, err := ourTS.Token()
|
ts := tokenSource{
|
||||||
if err != nil {
|
ctx: context.Background(),
|
||||||
t.Fatalf("Unexpected error: %e", err)
|
conf: config,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ts.Token()
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateToken(t *testing.T, tok *oauth2.Token) {
|
||||||
if got, want := tok.AccessToken, correctAT; got != want {
|
if got, want := tok.AccessToken, correctAT; got != want {
|
||||||
t.Errorf("Unexpected access token: got %v, but wanted %v", got, want)
|
t.Errorf("Unexpected access token: got %v, but wanted %v", got, want)
|
||||||
}
|
}
|
||||||
|
@ -91,10 +102,110 @@ func TestToken(t *testing.T) {
|
||||||
t.Errorf("Unexpected TokenType: got %v, but wanted %v", got, want)
|
t.Errorf("Unexpected TokenType: got %v, but wanted %v", got, want)
|
||||||
}
|
}
|
||||||
|
|
||||||
if got, want := tok.Expiry, now().Add(time.Duration(3600)*time.Second); got != want {
|
if got, want := tok.Expiry, testNow().Add(time.Duration(3600)*time.Second); got != want {
|
||||||
t.Errorf("Unexpected Expiry: got %v, but wanted %v", got, want)
|
t.Errorf("Unexpected Expiry: got %v, but wanted %v", got, want)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToken(t *testing.T) {
|
||||||
|
config := Config{
|
||||||
|
Audience: "32555940559.apps.googleusercontent.com",
|
||||||
|
SubjectTokenType: "urn:ietf:params:oauth:token-type:id_token",
|
||||||
|
ClientSecret: "notsosecret",
|
||||||
|
ClientID: "rbrgnognrhongo3bi4gb9ghg9g",
|
||||||
|
CredentialSource: testBaseCredSource,
|
||||||
|
Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"},
|
||||||
|
}
|
||||||
|
|
||||||
|
server := testExchangeTokenServer{
|
||||||
|
url: "/",
|
||||||
|
authorization: "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=",
|
||||||
|
contentType: "application/x-www-form-urlencoded",
|
||||||
|
body: baseCredsRequestBody,
|
||||||
|
response: baseCredsResponseBody,
|
||||||
|
}
|
||||||
|
|
||||||
|
tok, err := run(t, &config, &server)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %e", err)
|
||||||
|
}
|
||||||
|
validateToken(t, tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWorkforcePoolTokenWithClientID(t *testing.T) {
|
||||||
|
config := Config{
|
||||||
|
Audience: "//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id",
|
||||||
|
SubjectTokenType: "urn:ietf:params:oauth:token-type:id_token",
|
||||||
|
ClientSecret: "notsosecret",
|
||||||
|
ClientID: "rbrgnognrhongo3bi4gb9ghg9g",
|
||||||
|
CredentialSource: testBaseCredSource,
|
||||||
|
Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"},
|
||||||
|
WorkforcePoolUserProject: "myProject",
|
||||||
|
}
|
||||||
|
|
||||||
|
server := testExchangeTokenServer{
|
||||||
|
url: "/",
|
||||||
|
authorization: "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=",
|
||||||
|
contentType: "application/x-www-form-urlencoded",
|
||||||
|
body: workforcePoolRequestBodyWithClientId,
|
||||||
|
response: baseCredsResponseBody,
|
||||||
|
}
|
||||||
|
|
||||||
|
tok, err := run(t, &config, &server)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %e", err)
|
||||||
|
}
|
||||||
|
validateToken(t, tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWorkforcePoolTokenWithoutClientID(t *testing.T) {
|
||||||
|
config := Config{
|
||||||
|
Audience: "//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id",
|
||||||
|
SubjectTokenType: "urn:ietf:params:oauth:token-type:id_token",
|
||||||
|
ClientSecret: "notsosecret",
|
||||||
|
CredentialSource: testBaseCredSource,
|
||||||
|
Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"},
|
||||||
|
WorkforcePoolUserProject: "myProject",
|
||||||
|
}
|
||||||
|
|
||||||
|
server := testExchangeTokenServer{
|
||||||
|
url: "/",
|
||||||
|
authorization: "",
|
||||||
|
contentType: "application/x-www-form-urlencoded",
|
||||||
|
body: workforcePoolRequestBodyWithoutClientId,
|
||||||
|
response: baseCredsResponseBody,
|
||||||
|
}
|
||||||
|
|
||||||
|
tok, err := run(t, &config, &server)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Unexpected error: %e", err)
|
||||||
|
}
|
||||||
|
validateToken(t, tok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNonworkforceWithWorkforcePoolUserProject(t *testing.T) {
|
||||||
|
config := Config{
|
||||||
|
Audience: "32555940559.apps.googleusercontent.com",
|
||||||
|
SubjectTokenType: "urn:ietf:params:oauth:token-type:id_token",
|
||||||
|
TokenURL: "https://sts.googleapis.com",
|
||||||
|
ClientSecret: "notsosecret",
|
||||||
|
ClientID: "rbrgnognrhongo3bi4gb9ghg9g",
|
||||||
|
CredentialSource: testBaseCredSource,
|
||||||
|
Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"},
|
||||||
|
WorkforcePoolUserProject: "myProject",
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := config.TokenSource(context.Background())
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected error but found none")
|
||||||
|
}
|
||||||
|
if got, want := err.Error(), "oauth2/google: workforce_pool_user_project should not be set for non-workforce pool credentials"; got != want {
|
||||||
|
t.Errorf("Incorrect error received.\nExpected: %s\nRecieved: %s", want, got)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateURLTokenURL(t *testing.T) {
|
func TestValidateURLTokenURL(t *testing.T) {
|
||||||
|
@ -210,3 +321,41 @@ func TestValidateURLImpersonateURL(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestWorkforcePoolCreation(t *testing.T) {
|
||||||
|
var audienceValidatyTests = []struct {
|
||||||
|
audience string
|
||||||
|
expectSuccess bool
|
||||||
|
}{
|
||||||
|
{"//iam.googleapis.com/locations/global/workforcePools/pool-id/providers/provider-id", true},
|
||||||
|
{"//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id", true},
|
||||||
|
{"//iam.googleapis.com/locations/eu/workforcePools/workloadIdentityPools/providers/provider-id", true},
|
||||||
|
{"identitynamespace:1f12345:my_provider", false},
|
||||||
|
{"//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/pool-id/providers/provider-id", false},
|
||||||
|
{"//iam.googleapis.com/projects/123456/locations/eu/workloadIdentityPools/pool-id/providers/provider-id", false},
|
||||||
|
{"//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/workforcePools/providers/provider-id", false},
|
||||||
|
{"//iamgoogleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id", false},
|
||||||
|
{"//iam.googleapiscom/locations/eu/workforcePools/pool-id/providers/provider-id", false},
|
||||||
|
{"//iam.googleapis.com/locations/workforcePools/pool-id/providers/provider-id", false},
|
||||||
|
{"//iam.googleapis.com/locations/eu/workforcePool/pool-id/providers/provider-id", false},
|
||||||
|
{"//iam.googleapis.com/locations//workforcePool/pool-id/providers/provider-id", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
for _, tt := range audienceValidatyTests {
|
||||||
|
t.Run(" "+tt.audience, func(t *testing.T) { // We prepend a space ahead of the test input when outputting for sake of readability.
|
||||||
|
config := testConfig
|
||||||
|
config.TokenURL = "https://sts.googleapis.com" // Setting the most basic acceptable tokenURL
|
||||||
|
config.ServiceAccountImpersonationURL = "https://iamcredentials.googleapis.com"
|
||||||
|
config.Audience = tt.audience
|
||||||
|
config.WorkforcePoolUserProject = "myProject"
|
||||||
|
_, err := config.TokenSource(ctx)
|
||||||
|
|
||||||
|
if tt.expectSuccess && err != nil {
|
||||||
|
t.Errorf("got %v but want nil", err)
|
||||||
|
} else if !tt.expectSuccess && err == nil {
|
||||||
|
t.Errorf("got nil but expected an error")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue