Skip to content

Commit 930d60d

Browse files
author
Ryan Kohler
committed
more tests, refactoring tests, not submitting userProject when unneeded
1 parent 0a7e112 commit 930d60d

File tree

2 files changed

+130
-65
lines changed

2 files changed

+130
-65
lines changed

google/internal/externalaccount/basecredentials.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ func (c *Config) tokenSource(ctx context.Context, tokenURLValidPats []*regexp.Re
127127
if c.WorkforcePoolUserProject != "" {
128128
valid := validateWorkforceAudience(c.Audience)
129129
if !valid {
130-
return nil, fmt.Errorf("oauth2/google: invalid Workforce Pool Audience provided while constructing tokenSource")
130+
return nil, fmt.Errorf("oauth2/google: workforce_pool_user_project should not be set for non-workforce pool credentials")
131131
}
132132
}
133133

@@ -241,7 +241,9 @@ func (ts tokenSource) Token() (*oauth2.Token, error) {
241241
ClientSecret: conf.ClientSecret,
242242
}
243243
var options map[string]interface{}
244-
if conf.WorkforcePoolUserProject != "" {
244+
// Do not pass workforce_pool_user_project when client authentication is used.
245+
// The client ID is sufficient for determining the user project.
246+
if conf.WorkforcePoolUserProject != "" && conf.ClientID == "" {
245247
options = map[string]interface{}{
246248
"userProject": conf.WorkforcePoolUserProject,
247249
}

google/internal/externalaccount/basecredentials_test.go

Lines changed: 126 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"strings"
1313
"testing"
1414
"time"
15+
16+
"golang.org/x/oauth2"
1517
)
1618

1719
const (
@@ -35,117 +37,178 @@ var testConfig = Config{
3537
}
3638

3739
var (
38-
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"
39-
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"}`
40-
workforcePoolRequestBody = "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%3Ajwt"
41-
correctAT = "Sample.Access.Token"
42-
expiry int64 = 234852
40+
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"
41+
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"}`
42+
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%3Ajwt"
43+
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%3Ajwt"
44+
correctAT = "Sample.Access.Token"
45+
expiry int64 = 234852
4346
)
4447
var (
4548
testNow = func() time.Time { return time.Unix(expiry, 0) }
4649
)
4750

48-
func TestToken(t *testing.T) {
49-
targetServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
50-
if got, want := r.URL.String(), "/"; got != want {
51+
type testExchangeTokenServer struct {
52+
url string
53+
authorization string
54+
contentType string
55+
body string
56+
response string
57+
}
58+
59+
func run(t *testing.T, config *Config, tets *testExchangeTokenServer) (*oauth2.Token, error) {
60+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
61+
if got, want := r.URL.String(), tets.url; got != want {
5162
t.Errorf("URL.String(): got %v but want %v", got, want)
5263
}
5364
headerAuth := r.Header.Get("Authorization")
54-
if got, want := headerAuth, "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ="; got != want {
65+
if got, want := headerAuth, tets.authorization; got != want {
5566
t.Errorf("got %v but want %v", got, want)
5667
}
5768
headerContentType := r.Header.Get("Content-Type")
58-
if got, want := headerContentType, "application/x-www-form-urlencoded"; got != want {
69+
if got, want := headerContentType, tets.contentType; got != want {
5970
t.Errorf("got %v but want %v", got, want)
6071
}
6172
body, err := ioutil.ReadAll(r.Body)
6273
if err != nil {
6374
t.Fatalf("Failed reading request body: %s.", err)
6475
}
65-
if got, want := string(body), baseCredsRequestBody; got != want {
76+
if got, want := string(body), tets.body; got != want {
6677
t.Errorf("Unexpected exchange payload: got %v but want %v", got, want)
6778
}
6879
w.Header().Set("Content-Type", "application/json")
69-
w.Write([]byte(baseCredsResponseBody))
80+
w.Write([]byte(tets.response))
7081
}))
71-
defer targetServer.Close()
72-
73-
testConfig.TokenURL = targetServer.URL
74-
ourTS := tokenSource{
75-
ctx: context.Background(),
76-
conf: &testConfig,
77-
}
82+
defer server.Close()
83+
config.TokenURL = server.URL
7884

7985
oldNow := now
8086
defer func() { now = oldNow }()
8187
now = testNow
8288

83-
tok, err := ourTS.Token()
84-
if err != nil {
85-
t.Fatalf("Unexpected error: %e", err)
89+
ts := tokenSource{
90+
ctx: context.Background(),
91+
conf: config,
8692
}
93+
94+
return ts.Token()
95+
}
96+
97+
func validateToken(t *testing.T, tok *oauth2.Token) {
8798
if got, want := tok.AccessToken, correctAT; got != want {
8899
t.Errorf("Unexpected access token: got %v, but wanted %v", got, want)
89100
}
90101
if got, want := tok.TokenType, "Bearer"; got != want {
91102
t.Errorf("Unexpected TokenType: got %v, but wanted %v", got, want)
92103
}
93104

94-
if got, want := tok.Expiry, now().Add(time.Duration(3600)*time.Second); got != want {
105+
if got, want := tok.Expiry, testNow().Add(time.Duration(3600)*time.Second); got != want {
95106
t.Errorf("Unexpected Expiry: got %v, but wanted %v", got, want)
96107
}
97108
}
98109

99-
func TestWorkforcePoolToken(t *testing.T) {
100-
targetServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
101-
if got, want := r.URL.String(), "/"; got != want {
102-
t.Errorf("URL.String(): got %v but want %v", got, want)
103-
}
104-
headerAuth := r.Header.Get("Authorization")
105-
if got, want := headerAuth, "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ="; got != want {
106-
t.Errorf("got %v but want %v", got, want)
107-
}
108-
headerContentType := r.Header.Get("Content-Type")
109-
if got, want := headerContentType, "application/x-www-form-urlencoded"; got != want {
110-
t.Errorf("got %v but want %v", got, want)
111-
}
112-
body, err := ioutil.ReadAll(r.Body)
113-
if err != nil {
114-
t.Fatalf("Failed reading request body: %s.", err)
115-
}
116-
if got, want := string(body), workforcePoolRequestBody; got != want {
117-
t.Errorf("Unexpected exchange payload: got %v but want %v", got, want)
118-
}
119-
w.Header().Set("Content-Type", "application/json")
120-
w.Write([]byte(baseCredsResponseBody))
121-
}))
122-
defer targetServer.Close()
110+
func TestToken(t *testing.T) {
111+
config := Config{
112+
Audience: "32555940559.apps.googleusercontent.com",
113+
SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
114+
TokenInfoURL: "http://localhost:8080/v1/tokeninfo",
115+
ClientSecret: "notsosecret",
116+
ClientID: "rbrgnognrhongo3bi4gb9ghg9g",
117+
CredentialSource: testBaseCredSource,
118+
Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"},
119+
}
123120

124-
testConfig.TokenURL = targetServer.URL
125-
testConfig.WorkforcePoolUserProject = "myProject"
126-
testConfig.Audience = "//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id"
127-
ourTS := tokenSource{
128-
ctx: context.Background(),
129-
conf: &testConfig,
121+
server := testExchangeTokenServer{
122+
url: "/",
123+
authorization: "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=",
124+
contentType: "application/x-www-form-urlencoded",
125+
body: baseCredsRequestBody,
126+
response: baseCredsResponseBody,
130127
}
131128

132-
oldNow := now
133-
defer func() { now = oldNow }()
134-
now = testNow
129+
tok, err := run(t, &config, &server)
135130

136-
tok, err := ourTS.Token()
137131
if err != nil {
138132
t.Fatalf("Unexpected error: %e", err)
139133
}
140-
if got, want := tok.AccessToken, correctAT; got != want {
141-
t.Errorf("Unexpected access token: got %v, but wanted %v", got, want)
134+
validateToken(t, tok)
135+
}
136+
137+
func TestWorkforcePoolTokenWithClientID(t *testing.T) {
138+
config := Config{
139+
Audience: "//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id",
140+
SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
141+
TokenInfoURL: "http://localhost:8080/v1/tokeninfo",
142+
ClientSecret: "notsosecret",
143+
ClientID: "rbrgnognrhongo3bi4gb9ghg9g",
144+
CredentialSource: testBaseCredSource,
145+
Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"},
146+
WorkforcePoolUserProject: "myProject",
142147
}
143-
if got, want := tok.TokenType, "Bearer"; got != want {
144-
t.Errorf("Unexpected TokenType: got %v, but wanted %v", got, want)
148+
149+
server := testExchangeTokenServer{
150+
url: "/",
151+
authorization: "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=",
152+
contentType: "application/x-www-form-urlencoded",
153+
body: workforcePoolRequestBodyWithClientId,
154+
response: baseCredsResponseBody,
145155
}
146156

147-
if got, want := tok.Expiry, now().Add(time.Duration(3600)*time.Second); got != want {
148-
t.Errorf("Unexpected Expiry: got %v, but wanted %v", got, want)
157+
tok, err := run(t, &config, &server)
158+
159+
if err != nil {
160+
t.Fatalf("Unexpected error: %e", err)
161+
}
162+
validateToken(t, tok)
163+
}
164+
165+
func TestWorkforcePoolTokenWithoutClientID(t *testing.T) {
166+
config := Config{
167+
Audience: "//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id",
168+
SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
169+
TokenInfoURL: "http://localhost:8080/v1/tokeninfo",
170+
ClientSecret: "notsosecret",
171+
CredentialSource: testBaseCredSource,
172+
Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"},
173+
WorkforcePoolUserProject: "myProject",
174+
}
175+
176+
server := testExchangeTokenServer{
177+
url: "/",
178+
authorization: "",
179+
contentType: "application/x-www-form-urlencoded",
180+
body: workforcePoolRequestBodyWithoutClientId,
181+
response: baseCredsResponseBody,
182+
}
183+
184+
tok, err := run(t, &config, &server)
185+
186+
if err != nil {
187+
t.Fatalf("Unexpected error: %e", err)
188+
}
189+
validateToken(t, tok)
190+
}
191+
192+
func TestNonworkforceWithWorkforcePoolUserProject(t *testing.T) {
193+
config := Config{
194+
Audience: "32555940559.apps.googleusercontent.com",
195+
SubjectTokenType: "urn:ietf:params:oauth:token-type:jwt",
196+
TokenInfoURL: "http://localhost:8080/v1/tokeninfo",
197+
TokenURL: "https://sts.googleapis.com",
198+
ClientSecret: "notsosecret",
199+
ClientID: "rbrgnognrhongo3bi4gb9ghg9g",
200+
CredentialSource: testBaseCredSource,
201+
Scopes: []string{"https://www.googleapis.com/auth/devstorage.full_control"},
202+
WorkforcePoolUserProject: "myProject",
203+
}
204+
205+
_, err := config.TokenSource(context.Background())
206+
207+
if err == nil {
208+
t.Fatalf("Expected error but found none")
209+
}
210+
if got, want := err.Error(), "oauth2/google: workforce_pool_user_project should not be set for non-workforce pool credentials"; got != want {
211+
t.Errorf("Incorrect error received.\nExpected: %s\nRecieved: %s", want, got)
149212
}
150213
}
151214

0 commit comments

Comments
 (0)