@@ -12,6 +12,8 @@ import (
12
12
"strings"
13
13
"testing"
14
14
"time"
15
+
16
+ "golang.org/x/oauth2"
15
17
)
16
18
17
19
const (
@@ -35,66 +37,175 @@ var testConfig = Config{
35
37
}
36
38
37
39
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
- correctAT = "Sample.Access.Token"
41
- 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%3Aid_token"
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%3Aid_token"
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%3Aid_token"
44
+ correctAT = "Sample.Access.Token"
45
+ expiry int64 = 234852
42
46
)
43
47
var (
44
48
testNow = func () time.Time { return time .Unix (expiry , 0 ) }
45
49
)
46
50
47
- func TestToken (t * testing.T ) {
51
+ type testExchangeTokenServer struct {
52
+ url string
53
+ authorization string
54
+ contentType string
55
+ body string
56
+ response string
57
+ }
48
58
49
- targetServer := httptest .NewServer (http .HandlerFunc (func (w http.ResponseWriter , r * http.Request ) {
50
- if got , want := r .URL .String (), "/" ; got != want {
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 {
51
62
t .Errorf ("URL.String(): got %v but want %v" , got , want )
52
63
}
53
64
headerAuth := r .Header .Get ("Authorization" )
54
- if got , want := headerAuth , "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=" ; got != want {
65
+ if got , want := headerAuth , tets . authorization ; got != want {
55
66
t .Errorf ("got %v but want %v" , got , want )
56
67
}
57
68
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 {
59
70
t .Errorf ("got %v but want %v" , got , want )
60
71
}
61
72
body , err := ioutil .ReadAll (r .Body )
62
73
if err != nil {
63
74
t .Fatalf ("Failed reading request body: %s." , err )
64
75
}
65
- if got , want := string (body ), baseCredsRequestBody ; got != want {
76
+ if got , want := string (body ), tets . body ; got != want {
66
77
t .Errorf ("Unexpected exchange payload: got %v but want %v" , got , want )
67
78
}
68
79
w .Header ().Set ("Content-Type" , "application/json" )
69
- w .Write ([]byte (baseCredsResponseBody ))
80
+ w .Write ([]byte (tets . response ))
70
81
}))
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
78
84
79
85
oldNow := now
80
86
defer func () { now = oldNow }()
81
87
now = testNow
82
88
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 ,
86
92
}
93
+
94
+ return ts .Token ()
95
+ }
96
+
97
+ func validateToken (t * testing.T , tok * oauth2.Token ) {
87
98
if got , want := tok .AccessToken , correctAT ; got != want {
88
99
t .Errorf ("Unexpected access token: got %v, but wanted %v" , got , want )
89
100
}
90
101
if got , want := tok .TokenType , "Bearer" ; got != want {
91
102
t .Errorf ("Unexpected TokenType: got %v, but wanted %v" , got , want )
92
103
}
93
104
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 {
95
106
t .Errorf ("Unexpected Expiry: got %v, but wanted %v" , got , want )
96
107
}
108
+ }
109
+
110
+ func TestToken (t * testing.T ) {
111
+ config := Config {
112
+ Audience : "32555940559.apps.googleusercontent.com" ,
113
+ SubjectTokenType : "urn:ietf:params:oauth:token-type:id_token" ,
114
+ ClientSecret : "notsosecret" ,
115
+ ClientID : "rbrgnognrhongo3bi4gb9ghg9g" ,
116
+ CredentialSource : testBaseCredSource ,
117
+ Scopes : []string {"https://www.googleapis.com/auth/devstorage.full_control" },
118
+ }
119
+
120
+ server := testExchangeTokenServer {
121
+ url : "/" ,
122
+ authorization : "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=" ,
123
+ contentType : "application/x-www-form-urlencoded" ,
124
+ body : baseCredsRequestBody ,
125
+ response : baseCredsResponseBody ,
126
+ }
127
+
128
+ tok , err := run (t , & config , & server )
97
129
130
+ if err != nil {
131
+ t .Fatalf ("Unexpected error: %e" , err )
132
+ }
133
+ validateToken (t , tok )
134
+ }
135
+
136
+ func TestWorkforcePoolTokenWithClientID (t * testing.T ) {
137
+ config := Config {
138
+ Audience : "//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id" ,
139
+ SubjectTokenType : "urn:ietf:params:oauth:token-type:id_token" ,
140
+ ClientSecret : "notsosecret" ,
141
+ ClientID : "rbrgnognrhongo3bi4gb9ghg9g" ,
142
+ CredentialSource : testBaseCredSource ,
143
+ Scopes : []string {"https://www.googleapis.com/auth/devstorage.full_control" },
144
+ WorkforcePoolUserProject : "myProject" ,
145
+ }
146
+
147
+ server := testExchangeTokenServer {
148
+ url : "/" ,
149
+ authorization : "Basic cmJyZ25vZ25yaG9uZ28zYmk0Z2I5Z2hnOWc6bm90c29zZWNyZXQ=" ,
150
+ contentType : "application/x-www-form-urlencoded" ,
151
+ body : workforcePoolRequestBodyWithClientId ,
152
+ response : baseCredsResponseBody ,
153
+ }
154
+
155
+ tok , err := run (t , & config , & server )
156
+
157
+ if err != nil {
158
+ t .Fatalf ("Unexpected error: %e" , err )
159
+ }
160
+ validateToken (t , tok )
161
+ }
162
+
163
+ func TestWorkforcePoolTokenWithoutClientID (t * testing.T ) {
164
+ config := Config {
165
+ Audience : "//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id" ,
166
+ SubjectTokenType : "urn:ietf:params:oauth:token-type:id_token" ,
167
+ ClientSecret : "notsosecret" ,
168
+ CredentialSource : testBaseCredSource ,
169
+ Scopes : []string {"https://www.googleapis.com/auth/devstorage.full_control" },
170
+ WorkforcePoolUserProject : "myProject" ,
171
+ }
172
+
173
+ server := testExchangeTokenServer {
174
+ url : "/" ,
175
+ authorization : "" ,
176
+ contentType : "application/x-www-form-urlencoded" ,
177
+ body : workforcePoolRequestBodyWithoutClientId ,
178
+ response : baseCredsResponseBody ,
179
+ }
180
+
181
+ tok , err := run (t , & config , & server )
182
+
183
+ if err != nil {
184
+ t .Fatalf ("Unexpected error: %e" , err )
185
+ }
186
+ validateToken (t , tok )
187
+ }
188
+
189
+ func TestNonworkforceWithWorkforcePoolUserProject (t * testing.T ) {
190
+ config := Config {
191
+ Audience : "32555940559.apps.googleusercontent.com" ,
192
+ SubjectTokenType : "urn:ietf:params:oauth:token-type:id_token" ,
193
+ TokenURL : "https://sts.googleapis.com" ,
194
+ ClientSecret : "notsosecret" ,
195
+ ClientID : "rbrgnognrhongo3bi4gb9ghg9g" ,
196
+ CredentialSource : testBaseCredSource ,
197
+ Scopes : []string {"https://www.googleapis.com/auth/devstorage.full_control" },
198
+ WorkforcePoolUserProject : "myProject" ,
199
+ }
200
+
201
+ _ , err := config .TokenSource (context .Background ())
202
+
203
+ if err == nil {
204
+ t .Fatalf ("Expected error but found none" )
205
+ }
206
+ if got , want := err .Error (), "oauth2/google: workforce_pool_user_project should not be set for non-workforce pool credentials" ; got != want {
207
+ t .Errorf ("Incorrect error received.\n Expected: %s\n Recieved: %s" , want , got )
208
+ }
98
209
}
99
210
100
211
func TestValidateURLTokenURL (t * testing.T ) {
@@ -210,3 +321,41 @@ func TestValidateURLImpersonateURL(t *testing.T) {
210
321
})
211
322
}
212
323
}
324
+
325
+ func TestWorkforcePoolCreation (t * testing.T ) {
326
+ var audienceValidatyTests = []struct {
327
+ audience string
328
+ expectSuccess bool
329
+ }{
330
+ {"//iam.googleapis.com/locations/global/workforcePools/pool-id/providers/provider-id" , true },
331
+ {"//iam.googleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id" , true },
332
+ {"//iam.googleapis.com/locations/eu/workforcePools/workloadIdentityPools/providers/provider-id" , true },
333
+ {"identitynamespace:1f12345:my_provider" , false },
334
+ {"//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/pool-id/providers/provider-id" , false },
335
+ {"//iam.googleapis.com/projects/123456/locations/eu/workloadIdentityPools/pool-id/providers/provider-id" , false },
336
+ {"//iam.googleapis.com/projects/123456/locations/global/workloadIdentityPools/workforcePools/providers/provider-id" , false },
337
+ {"//iamgoogleapis.com/locations/eu/workforcePools/pool-id/providers/provider-id" , false },
338
+ {"//iam.googleapiscom/locations/eu/workforcePools/pool-id/providers/provider-id" , false },
339
+ {"//iam.googleapis.com/locations/workforcePools/pool-id/providers/provider-id" , false },
340
+ {"//iam.googleapis.com/locations/eu/workforcePool/pool-id/providers/provider-id" , false },
341
+ {"//iam.googleapis.com/locations//workforcePool/pool-id/providers/provider-id" , false },
342
+ }
343
+
344
+ ctx := context .Background ()
345
+ for _ , tt := range audienceValidatyTests {
346
+ t .Run (" " + tt .audience , func (t * testing.T ) { // We prepend a space ahead of the test input when outputting for sake of readability.
347
+ config := testConfig
348
+ config .TokenURL = "https://sts.googleapis.com" // Setting the most basic acceptable tokenURL
349
+ config .ServiceAccountImpersonationURL = "https://iamcredentials.googleapis.com"
350
+ config .Audience = tt .audience
351
+ config .WorkforcePoolUserProject = "myProject"
352
+ _ , err := config .TokenSource (ctx )
353
+
354
+ if tt .expectSuccess && err != nil {
355
+ t .Errorf ("got %v but want nil" , err )
356
+ } else if ! tt .expectSuccess && err == nil {
357
+ t .Errorf ("got nil but expected an error" )
358
+ }
359
+ })
360
+ }
361
+ }
0 commit comments