1
2
3
4
5 package externalaccount
6
7 import (
8 "context"
9 "encoding/json"
10 "errors"
11 "fmt"
12 "net/http"
13 "net/http/httptest"
14 neturl "net/url"
15 "reflect"
16 "strings"
17 "testing"
18 "time"
19 )
20
21 var defaultTime = time.Date(2011, 9, 9, 23, 36, 0, 0, time.UTC)
22 var secondDefaultTime = time.Date(2020, 8, 11, 6, 55, 22, 0, time.UTC)
23
24 type validateHeaders func(r *http.Request)
25
26 func setTime(testTime time.Time) func() time.Time {
27 return func() time.Time {
28 return testTime
29 }
30 }
31
32 func setEnvironment(env map[string]string) func(string) string {
33 return func(key string) string {
34 return env[key]
35 }
36 }
37
38 var defaultRequestSigner = &awsRequestSigner{
39 RegionName: "us-east-1",
40 AwsSecurityCredentials: &AwsSecurityCredentials{
41 AccessKeyID: "AKIDEXAMPLE",
42 SecretAccessKey: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
43 },
44 }
45
46 const (
47 accessKeyID = "ASIARD4OQDT6A77FR3CL"
48 secretAccessKey = "Y8AfSaucF37G4PpvfguKZ3/l7Id4uocLXxX0+VTx"
49 securityToken = "IQoJb3JpZ2luX2VjEIz//////////wEaCXVzLWVhc3QtMiJGMEQCIH7MHX/Oy/OB8OlLQa9GrqU1B914+iMikqWQW7vPCKlgAiA/Lsv8Jcafn14owfxXn95FURZNKaaphj0ykpmS+Ki+CSq0AwhlEAAaDDA3NzA3MTM5MTk5NiIMx9sAeP1ovlMTMKLjKpEDwuJQg41/QUKx0laTZYjPlQvjwSqS3OB9P1KAXPWSLkliVMMqaHqelvMF/WO/glv3KwuTfQsavRNs3v5pcSEm4SPO3l7mCs7KrQUHwGP0neZhIKxEXy+Ls//1C/Bqt53NL+LSbaGv6RPHaX82laz2qElphg95aVLdYgIFY6JWV5fzyjgnhz0DQmy62/Vi8pNcM2/VnxeCQ8CC8dRDSt52ry2v+nc77vstuI9xV5k8mPtnaPoJDRANh0bjwY5Sdwkbp+mGRUJBAQRlNgHUJusefXQgVKBCiyJY4w3Csd8Bgj9IyDV+Azuy1jQqfFZWgP68LSz5bURyIjlWDQunO82stZ0BgplKKAa/KJHBPCp8Qi6i99uy7qh76FQAqgVTsnDuU6fGpHDcsDSGoCls2HgZjZFPeOj8mmRhFk1Xqvkbjuz8V1cJk54d3gIJvQt8gD2D6yJQZecnuGWd5K2e2HohvCc8Fc9kBl1300nUJPV+k4tr/A5R/0QfEKOZL1/k5lf1g9CREnrM8LVkGxCgdYMxLQow1uTL+QU67AHRRSp5PhhGX4Rek+01vdYSnJCMaPhSEgcLqDlQkhk6MPsyT91QMXcWmyO+cAZwUPwnRamFepuP4K8k2KVXs/LIJHLELwAZ0ekyaS7CptgOqS7uaSTFG3U+vzFZLEnGvWQ7y9IPNQZ+Dffgh4p3vF4J68y9049sI6Sr5d5wbKkcbm8hdCDHZcv4lnqohquPirLiFQ3q7B17V9krMPu3mz1cg4Ekgcrn/E09NTsxAqD8NcZ7C7ECom9r+X3zkDOxaajW6hu3Az8hGlyylDaMiFfRbBJpTIlxp7jfa7CxikNgNtEKLH9iCzvuSg2vhA=="
50 )
51
52 var requestSignerWithToken = &awsRequestSigner{
53 RegionName: "us-east-2",
54 AwsSecurityCredentials: &AwsSecurityCredentials{
55 AccessKeyID: accessKeyID,
56 SecretAccessKey: secretAccessKey,
57 SessionToken: securityToken,
58 },
59 }
60
61 func setDefaultTime(req *http.Request) {
62
63
64 req.Header.Add("date", "Mon, 09 Sep 2011 23:36:00 GMT")
65 }
66
67 func testRequestSigner(t *testing.T, rs *awsRequestSigner, input, expectedOutput *http.Request) {
68 t.Helper()
69
70 err := rs.SignRequest(input)
71 if err != nil {
72 t.Errorf("unexpected error: %q", err.Error())
73 }
74
75 if got, want := input.URL.String(), expectedOutput.URL.String(); !reflect.DeepEqual(got, want) {
76 t.Errorf("url = %q, want %q", got, want)
77 }
78 if got, want := input.Method, expectedOutput.Method; !reflect.DeepEqual(got, want) {
79 t.Errorf("method = %q, want %q", got, want)
80 }
81 for header := range expectedOutput.Header {
82 if got, want := input.Header[header], expectedOutput.Header[header]; !reflect.DeepEqual(got, want) {
83 t.Errorf("header[%q] = %q, want %q", header, got, want)
84 }
85 }
86 }
87
88 func TestAWSv4Signature_GetRequest(t *testing.T) {
89 input, _ := http.NewRequest("GET", "https://host.foo.com", nil)
90 setDefaultTime(input)
91
92 output, _ := http.NewRequest("GET", "https://host.foo.com", nil)
93 output.Header = http.Header{
94 "Host": []string{"host.foo.com"},
95 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
96 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470"},
97 }
98
99 oldNow := now
100 defer func() { now = oldNow }()
101 now = setTime(defaultTime)
102
103 testRequestSigner(t, defaultRequestSigner, input, output)
104 }
105
106 func TestAWSv4Signature_GetRequestWithRelativePath(t *testing.T) {
107 input, _ := http.NewRequest("GET", "https://host.foo.com/foo/bar/../..", nil)
108 setDefaultTime(input)
109
110 output, _ := http.NewRequest("GET", "https://host.foo.com/foo/bar/../..", nil)
111 output.Header = http.Header{
112 "Host": []string{"host.foo.com"},
113 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
114 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470"},
115 }
116
117 oldNow := now
118 defer func() { now = oldNow }()
119 now = setTime(defaultTime)
120
121 testRequestSigner(t, defaultRequestSigner, input, output)
122 }
123
124 func TestAWSv4Signature_GetRequestWithDotPath(t *testing.T) {
125 input, _ := http.NewRequest("GET", "https://host.foo.com/./", nil)
126 setDefaultTime(input)
127
128 output, _ := http.NewRequest("GET", "https://host.foo.com/./", nil)
129 output.Header = http.Header{
130 "Host": []string{"host.foo.com"},
131 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
132 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b27ccfbfa7df52a200ff74193ca6e32d4b48b8856fab7ebf1c595d0670a7e470"},
133 }
134
135 oldNow := now
136 defer func() { now = oldNow }()
137 now = setTime(defaultTime)
138
139 testRequestSigner(t, defaultRequestSigner, input, output)
140 }
141
142 func TestAWSv4Signature_GetRequestWithPointlessDotPath(t *testing.T) {
143 input, _ := http.NewRequest("GET", "https://host.foo.com/./foo", nil)
144 setDefaultTime(input)
145
146 output, _ := http.NewRequest("GET", "https://host.foo.com/./foo", nil)
147 output.Header = http.Header{
148 "Host": []string{"host.foo.com"},
149 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
150 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=910e4d6c9abafaf87898e1eb4c929135782ea25bb0279703146455745391e63a"},
151 }
152
153 oldNow := now
154 defer func() { now = oldNow }()
155 now = setTime(defaultTime)
156
157 testRequestSigner(t, defaultRequestSigner, input, output)
158 }
159
160 func TestAWSv4Signature_GetRequestWithUtf8Path(t *testing.T) {
161 input, _ := http.NewRequest("GET", "https://host.foo.com/%E1%88%B4", nil)
162 setDefaultTime(input)
163
164 output, _ := http.NewRequest("GET", "https://host.foo.com/%E1%88%B4", nil)
165 output.Header = http.Header{
166 "Host": []string{"host.foo.com"},
167 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
168 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=8d6634c189aa8c75c2e51e106b6b5121bed103fdb351f7d7d4381c738823af74"},
169 }
170
171 oldNow := now
172 defer func() { now = oldNow }()
173 now = setTime(defaultTime)
174
175 testRequestSigner(t, defaultRequestSigner, input, output)
176 }
177
178 func TestAWSv4Signature_GetRequestWithDuplicateQuery(t *testing.T) {
179 input, _ := http.NewRequest("GET", "https://host.foo.com/?foo=Zoo&foo=aha", nil)
180 setDefaultTime(input)
181
182 output, _ := http.NewRequest("GET", "https://host.foo.com/?foo=Zoo&foo=aha", nil)
183 output.Header = http.Header{
184 "Host": []string{"host.foo.com"},
185 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
186 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=be7148d34ebccdc6423b19085378aa0bee970bdc61d144bd1a8c48c33079ab09"},
187 }
188
189 oldNow := now
190 defer func() { now = oldNow }()
191 now = setTime(defaultTime)
192
193 testRequestSigner(t, defaultRequestSigner, input, output)
194 }
195
196 func TestAWSv4Signature_GetRequestWithMisorderedQuery(t *testing.T) {
197 input, _ := http.NewRequest("GET", "https://host.foo.com/?foo=b&foo=a", nil)
198 setDefaultTime(input)
199
200 output, _ := http.NewRequest("GET", "https://host.foo.com/?foo=b&foo=a", nil)
201 output.Header = http.Header{
202 "Host": []string{"host.foo.com"},
203 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
204 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=feb926e49e382bec75c9d7dcb2a1b6dc8aa50ca43c25d2bc51143768c0875acc"},
205 }
206
207 oldNow := now
208 defer func() { now = oldNow }()
209 now = setTime(defaultTime)
210
211 testRequestSigner(t, defaultRequestSigner, input, output)
212 }
213
214 func TestAWSv4Signature_GetRequestWithUtf8Query(t *testing.T) {
215 input, _ := http.NewRequest("GET", "https://host.foo.com/?ሴ=bar", nil)
216 setDefaultTime(input)
217
218 output, _ := http.NewRequest("GET", "https://host.foo.com/?ሴ=bar", nil)
219 output.Header = http.Header{
220 "Host": []string{"host.foo.com"},
221 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
222 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=6fb359e9a05394cc7074e0feb42573a2601abc0c869a953e8c5c12e4e01f1a8c"},
223 }
224
225 oldNow := now
226 defer func() { now = oldNow }()
227 now = setTime(defaultTime)
228
229 testRequestSigner(t, defaultRequestSigner, input, output)
230 }
231
232 func TestAWSv4Signature_PostRequest(t *testing.T) {
233 input, _ := http.NewRequest("POST", "https://host.foo.com/", nil)
234 setDefaultTime(input)
235 input.Header.Add("ZOO", "zoobar")
236
237 output, _ := http.NewRequest("POST", "https://host.foo.com/", nil)
238 output.Header = http.Header{
239 "Host": []string{"host.foo.com"},
240 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
241 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=b7a95a52518abbca0964a999a880429ab734f35ebbf1235bd79a5de87756dc4a"},
242 "Zoo": []string{"zoobar"},
243 }
244
245 oldNow := now
246 defer func() { now = oldNow }()
247 now = setTime(defaultTime)
248
249 testRequestSigner(t, defaultRequestSigner, input, output)
250 }
251
252 func TestAWSv4Signature_PostRequestWithCapitalizedHeaderValue(t *testing.T) {
253 input, _ := http.NewRequest("POST", "https://host.foo.com/", nil)
254 setDefaultTime(input)
255 input.Header.Add("zoo", "ZOOBAR")
256
257 output, _ := http.NewRequest("POST", "https://host.foo.com/", nil)
258 output.Header = http.Header{
259 "Host": []string{"host.foo.com"},
260 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
261 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;zoo, Signature=273313af9d0c265c531e11db70bbd653f3ba074c1009239e8559d3987039cad7"},
262 "Zoo": []string{"ZOOBAR"},
263 }
264
265 oldNow := now
266 defer func() { now = oldNow }()
267 now = setTime(defaultTime)
268
269 testRequestSigner(t, defaultRequestSigner, input, output)
270 }
271
272 func TestAWSv4Signature_PostRequestPhfft(t *testing.T) {
273 input, _ := http.NewRequest("POST", "https://host.foo.com/", nil)
274 setDefaultTime(input)
275 input.Header.Add("p", "phfft")
276
277 output, _ := http.NewRequest("POST", "https://host.foo.com/", nil)
278 output.Header = http.Header{
279 "Host": []string{"host.foo.com"},
280 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
281 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host;p, Signature=debf546796015d6f6ded8626f5ce98597c33b47b9164cf6b17b4642036fcb592"},
282 "P": []string{"phfft"},
283 }
284
285 oldNow := now
286 defer func() { now = oldNow }()
287 now = setTime(defaultTime)
288
289 testRequestSigner(t, defaultRequestSigner, input, output)
290 }
291
292 func TestAWSv4Signature_PostRequestWithBody(t *testing.T) {
293 input, _ := http.NewRequest("POST", "https://host.foo.com/", strings.NewReader("foo=bar"))
294 setDefaultTime(input)
295 input.Header.Add("Content-Type", "application/x-www-form-urlencoded")
296
297 output, _ := http.NewRequest("POST", "https://host.foo.com/", nil)
298 output.Header = http.Header{
299 "Host": []string{"host.foo.com"},
300 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
301 "Content-Type": []string{"application/x-www-form-urlencoded"},
302 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=content-type;date;host, Signature=5a15b22cf462f047318703b92e6f4f38884e4a7ab7b1d6426ca46a8bd1c26cbc"},
303 }
304
305 oldNow := now
306 defer func() { now = oldNow }()
307 now = setTime(defaultTime)
308
309 testRequestSigner(t, defaultRequestSigner, input, output)
310 }
311
312 func TestAWSv4Signature_PostRequestWithQueryString(t *testing.T) {
313 input, _ := http.NewRequest("POST", "https://host.foo.com/?foo=bar", nil)
314 setDefaultTime(input)
315
316 output, _ := http.NewRequest("POST", "https://host.foo.com/?foo=bar", nil)
317 output.Header = http.Header{
318 "Host": []string{"host.foo.com"},
319 "Date": []string{"Mon, 09 Sep 2011 23:36:00 GMT"},
320 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=AKIDEXAMPLE/20110909/us-east-1/host/aws4_request, SignedHeaders=date;host, Signature=b6e3b79003ce0743a491606ba1035a804593b0efb1e20a11cba83f8c25a57a92"},
321 }
322
323 oldNow := now
324 defer func() { now = oldNow }()
325 now = setTime(defaultTime)
326
327 testRequestSigner(t, defaultRequestSigner, input, output)
328 }
329
330 func TestAWSv4Signature_GetRequestWithSecurityToken(t *testing.T) {
331 input, _ := http.NewRequest("GET", "https://ec2.us-east-2.amazonaws.com?Action=DescribeRegions&Version=2013-10-15", nil)
332
333 output, _ := http.NewRequest("GET", "https://ec2.us-east-2.amazonaws.com?Action=DescribeRegions&Version=2013-10-15", nil)
334 output.Header = http.Header{
335 "Host": []string{"ec2.us-east-2.amazonaws.com"},
336 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=" + accessKeyID + "/20200811/us-east-2/ec2/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=631ea80cddfaa545fdadb120dc92c9f18166e38a5c47b50fab9fce476e022855"},
337 "X-Amz-Date": []string{"20200811T065522Z"},
338 "X-Amz-Security-Token": []string{securityToken},
339 }
340
341 oldNow := now
342 defer func() { now = oldNow }()
343 now = setTime(secondDefaultTime)
344
345 testRequestSigner(t, requestSignerWithToken, input, output)
346 }
347
348 func TestAWSv4Signature_PostRequestWithSecurityToken(t *testing.T) {
349 input, _ := http.NewRequest("POST", "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", nil)
350
351 output, _ := http.NewRequest("POST", "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", nil)
352 output.Header = http.Header{
353 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=" + accessKeyID + "/20200811/us-east-2/sts/aws4_request, SignedHeaders=host;x-amz-date;x-amz-security-token, Signature=73452984e4a880ffdc5c392355733ec3f5ba310d5e0609a89244440cadfe7a7a"},
354 "Host": []string{"sts.us-east-2.amazonaws.com"},
355 "X-Amz-Date": []string{"20200811T065522Z"},
356 "X-Amz-Security-Token": []string{securityToken},
357 }
358
359 oldNow := now
360 defer func() { now = oldNow }()
361 now = setTime(secondDefaultTime)
362
363 testRequestSigner(t, requestSignerWithToken, input, output)
364 }
365
366 func TestAWSv4Signature_PostRequestWithSecurityTokenAndAdditionalHeaders(t *testing.T) {
367 requestParams := "{\"KeySchema\":[{\"KeyType\":\"HASH\",\"AttributeName\":\"Id\"}],\"TableName\":\"TestTable\",\"AttributeDefinitions\":[{\"AttributeName\":\"Id\",\"AttributeType\":\"S\"}],\"ProvisionedThroughput\":{\"WriteCapacityUnits\":5,\"ReadCapacityUnits\":5}}"
368 input, _ := http.NewRequest("POST", "https://dynamodb.us-east-2.amazonaws.com/", strings.NewReader(requestParams))
369 input.Header.Add("Content-Type", "application/x-amz-json-1.0")
370 input.Header.Add("x-amz-target", "DynamoDB_20120810.CreateTable")
371
372 output, _ := http.NewRequest("POST", "https://dynamodb.us-east-2.amazonaws.com/", strings.NewReader(requestParams))
373 output.Header = http.Header{
374 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=" + accessKeyID + "/20200811/us-east-2/dynamodb/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-security-token;x-amz-target, Signature=fdaa5b9cc9c86b80fe61eaf504141c0b3523780349120f2bd8145448456e0385"},
375 "Host": []string{"dynamodb.us-east-2.amazonaws.com"},
376 "X-Amz-Date": []string{"20200811T065522Z"},
377 "Content-Type": []string{"application/x-amz-json-1.0"},
378 "X-Amz-Target": []string{"DynamoDB_20120810.CreateTable"},
379 "X-Amz-Security-Token": []string{securityToken},
380 }
381
382 oldNow := now
383 defer func() { now = oldNow }()
384 now = setTime(secondDefaultTime)
385
386 testRequestSigner(t, requestSignerWithToken, input, output)
387 }
388
389 func TestAWSv4Signature_PostRequestWithAmzDateButNoSecurityToken(t *testing.T) {
390 var requestSigner = &awsRequestSigner{
391 RegionName: "us-east-2",
392 AwsSecurityCredentials: &AwsSecurityCredentials{
393 AccessKeyID: accessKeyID,
394 SecretAccessKey: secretAccessKey,
395 },
396 }
397
398 input, _ := http.NewRequest("POST", "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", nil)
399
400 output, _ := http.NewRequest("POST", "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15", nil)
401 output.Header = http.Header{
402 "Authorization": []string{"AWS4-HMAC-SHA256 Credential=" + accessKeyID + "/20200811/us-east-2/sts/aws4_request, SignedHeaders=host;x-amz-date, Signature=d095ba304919cd0d5570ba8a3787884ee78b860f268ed040ba23831d55536d56"},
403 "Host": []string{"sts.us-east-2.amazonaws.com"},
404 "X-Amz-Date": []string{"20200811T065522Z"},
405 }
406
407 oldNow := now
408 defer func() { now = oldNow }()
409 now = setTime(secondDefaultTime)
410
411 testRequestSigner(t, requestSigner, input, output)
412 }
413
414 type testAwsServer struct {
415 url string
416 securityCredentialURL string
417 regionURL string
418 regionalCredVerificationURL string
419 imdsv2SessionTokenUrl string
420
421 Credentials map[string]string
422
423 WriteRolename func(http.ResponseWriter, *http.Request)
424 WriteSecurityCredentials func(http.ResponseWriter, *http.Request)
425 WriteRegion func(http.ResponseWriter, *http.Request)
426 WriteIMDSv2SessionToken func(http.ResponseWriter, *http.Request)
427 }
428
429 func createAwsTestServer(url, regionURL, regionalCredVerificationURL, imdsv2SessionTokenUrl string, rolename, region string, credentials map[string]string, imdsv2SessionToken string, validateHeaders validateHeaders) *testAwsServer {
430 server := &testAwsServer{
431 url: url,
432 securityCredentialURL: fmt.Sprintf("%s/%s", url, rolename),
433 regionURL: regionURL,
434 regionalCredVerificationURL: regionalCredVerificationURL,
435 imdsv2SessionTokenUrl: imdsv2SessionTokenUrl,
436 Credentials: credentials,
437 WriteRolename: func(w http.ResponseWriter, r *http.Request) {
438 validateHeaders(r)
439 w.Write([]byte(rolename))
440 },
441 WriteRegion: func(w http.ResponseWriter, r *http.Request) {
442 validateHeaders(r)
443 w.Write([]byte(region))
444 },
445 WriteIMDSv2SessionToken: func(w http.ResponseWriter, r *http.Request) {
446 validateHeaders(r)
447 w.Write([]byte(imdsv2SessionToken))
448 },
449 }
450
451 server.WriteSecurityCredentials = func(w http.ResponseWriter, r *http.Request) {
452 validateHeaders(r)
453 jsonCredentials, _ := json.Marshal(server.Credentials)
454 w.Write(jsonCredentials)
455 }
456
457 return server
458 }
459
460 func createDefaultAwsTestServer() *testAwsServer {
461 return createAwsTestServer(
462 "/latest/meta-data/iam/security-credentials",
463 "/latest/meta-data/placement/availability-zone",
464 "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
465 "",
466 "gcp-aws-role",
467 "us-east-2b",
468 map[string]string{
469 "SecretAccessKey": secretAccessKey,
470 "AccessKeyId": accessKeyID,
471 "Token": securityToken,
472 },
473 "",
474 noHeaderValidation,
475 )
476 }
477
478 func createDefaultAwsTestServerWithImdsv2(t *testing.T) *testAwsServer {
479 validateSessionTokenHeaders := func(r *http.Request) {
480 if r.URL.Path == "/latest/api/token" {
481 headerValue := r.Header.Get(awsIMDSv2SessionTtlHeader)
482 if headerValue != awsIMDSv2SessionTtl {
483 t.Errorf("%q = \n%q\n want \n%q", awsIMDSv2SessionTtlHeader, headerValue, awsIMDSv2SessionTtl)
484 }
485 } else {
486 headerValue := r.Header.Get(awsIMDSv2SessionTokenHeader)
487 if headerValue != "sessiontoken" {
488 t.Errorf("%q = \n%q\n want \n%q", awsIMDSv2SessionTokenHeader, headerValue, "sessiontoken")
489 }
490 }
491 }
492
493 return createAwsTestServer(
494 "/latest/meta-data/iam/security-credentials",
495 "/latest/meta-data/placement/availability-zone",
496 "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
497 "/latest/api/token",
498 "gcp-aws-role",
499 "us-east-2b",
500 map[string]string{
501 "SecretAccessKey": secretAccessKey,
502 "AccessKeyId": accessKeyID,
503 "Token": securityToken,
504 },
505 "sessiontoken",
506 validateSessionTokenHeaders,
507 )
508 }
509
510 func (server *testAwsServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
511 switch p := r.URL.Path; p {
512 case server.url:
513 server.WriteRolename(w, r)
514 case server.securityCredentialURL:
515 server.WriteSecurityCredentials(w, r)
516 case server.regionURL:
517 server.WriteRegion(w, r)
518 case server.imdsv2SessionTokenUrl:
519 server.WriteIMDSv2SessionToken(w, r)
520 }
521 }
522
523 func notFound(w http.ResponseWriter, r *http.Request) {
524 w.WriteHeader(404)
525 w.Write([]byte("Not Found"))
526 }
527
528 func noHeaderValidation(r *http.Request) {}
529
530 func (server *testAwsServer) getCredentialSource(url string) *CredentialSource {
531 return &CredentialSource{
532 EnvironmentID: "aws1",
533 URL: url + server.url,
534 RegionURL: url + server.regionURL,
535 RegionalCredVerificationURL: server.regionalCredVerificationURL,
536 IMDSv2SessionTokenURL: url + server.imdsv2SessionTokenUrl,
537 }
538 }
539
540 func getExpectedSubjectToken(url, region, accessKeyID, secretAccessKey, securityToken string) string {
541 req, _ := http.NewRequest("POST", url, nil)
542 req.Header.Add("x-goog-cloud-target-resource", testFileConfig.Audience)
543 signer := &awsRequestSigner{
544 RegionName: region,
545 AwsSecurityCredentials: &AwsSecurityCredentials{
546 AccessKeyID: accessKeyID,
547 SecretAccessKey: secretAccessKey,
548 SessionToken: securityToken,
549 },
550 }
551 signer.SignRequest(req)
552
553 result := awsRequest{
554 URL: url,
555 Method: "POST",
556 Headers: []awsRequestHeader{
557 {
558 Key: "Authorization",
559 Value: req.Header.Get("Authorization"),
560 }, {
561 Key: "Host",
562 Value: req.Header.Get("Host"),
563 }, {
564 Key: "X-Amz-Date",
565 Value: req.Header.Get("X-Amz-Date"),
566 },
567 },
568 }
569
570 if securityToken != "" {
571 result.Headers = append(result.Headers, awsRequestHeader{
572 Key: "X-Amz-Security-Token",
573 Value: securityToken,
574 })
575 }
576
577 result.Headers = append(result.Headers, awsRequestHeader{
578 Key: "X-Goog-Cloud-Target-Resource",
579 Value: testFileConfig.Audience,
580 })
581
582 str, _ := json.Marshal(result)
583 return neturl.QueryEscape(string(str))
584 }
585
586 func TestAWSCredential_BasicRequest(t *testing.T) {
587 server := createDefaultAwsTestServer()
588 ts := httptest.NewServer(server)
589
590 tfc := testFileConfig
591 tfc.CredentialSource = server.getCredentialSource(ts.URL)
592 oldGetenv := getenv
593 oldNow := now
594 defer func() {
595 getenv = oldGetenv
596 now = oldNow
597 }()
598 getenv = setEnvironment(map[string]string{})
599 now = setTime(defaultTime)
600
601 base, err := tfc.parse(context.Background())
602 if err != nil {
603 t.Fatalf("parse() failed %v", err)
604 }
605
606 out, err := base.subjectToken()
607 if err != nil {
608 t.Fatalf("retrieveSubjectToken() failed: %v", err)
609 }
610
611 expected := getExpectedSubjectToken(
612 "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
613 "us-east-2",
614 accessKeyID,
615 secretAccessKey,
616 securityToken,
617 )
618
619 if got, want := out, expected; !reflect.DeepEqual(got, want) {
620 t.Errorf("subjectToken = \n%q\n want \n%q", got, want)
621 }
622 }
623
624 func TestAWSCredential_IMDSv2(t *testing.T) {
625 server := createDefaultAwsTestServerWithImdsv2(t)
626 ts := httptest.NewServer(server)
627
628 tfc := testFileConfig
629 tfc.CredentialSource = server.getCredentialSource(ts.URL)
630
631 oldGetenv := getenv
632 oldNow := now
633 defer func() {
634 getenv = oldGetenv
635 now = oldNow
636 }()
637 getenv = setEnvironment(map[string]string{})
638 now = setTime(defaultTime)
639
640 base, err := tfc.parse(context.Background())
641 if err != nil {
642 t.Fatalf("parse() failed %v", err)
643 }
644
645 out, err := base.subjectToken()
646 if err != nil {
647 t.Fatalf("retrieveSubjectToken() failed: %v", err)
648 }
649
650 expected := getExpectedSubjectToken(
651 "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
652 "us-east-2",
653 accessKeyID,
654 secretAccessKey,
655 securityToken,
656 )
657
658 if got, want := out, expected; !reflect.DeepEqual(got, want) {
659 t.Errorf("subjectToken = \n%q\n want \n%q", got, want)
660 }
661 }
662
663 func TestAWSCredential_BasicRequestWithoutSecurityToken(t *testing.T) {
664 server := createDefaultAwsTestServer()
665 ts := httptest.NewServer(server)
666 delete(server.Credentials, "Token")
667
668 tfc := testFileConfig
669 tfc.CredentialSource = server.getCredentialSource(ts.URL)
670
671 oldGetenv := getenv
672 oldNow := now
673 defer func() {
674 getenv = oldGetenv
675 now = oldNow
676 }()
677 getenv = setEnvironment(map[string]string{})
678 now = setTime(defaultTime)
679
680 base, err := tfc.parse(context.Background())
681 if err != nil {
682 t.Fatalf("parse() failed %v", err)
683 }
684
685 out, err := base.subjectToken()
686 if err != nil {
687 t.Fatalf("retrieveSubjectToken() failed: %v", err)
688 }
689
690 expected := getExpectedSubjectToken(
691 "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
692 "us-east-2",
693 accessKeyID,
694 secretAccessKey,
695 "",
696 )
697
698 if got, want := out, expected; !reflect.DeepEqual(got, want) {
699 t.Errorf("subjectToken = \n%q\n want \n%q", got, want)
700 }
701 }
702
703 func TestAWSCredential_BasicRequestWithEnv(t *testing.T) {
704 server := createDefaultAwsTestServer()
705 ts := httptest.NewServer(server)
706
707 tfc := testFileConfig
708 tfc.CredentialSource = server.getCredentialSource(ts.URL)
709
710 oldGetenv := getenv
711 oldNow := now
712 defer func() {
713 getenv = oldGetenv
714 now = oldNow
715 }()
716 getenv = setEnvironment(map[string]string{
717 "AWS_ACCESS_KEY_ID": "AKIDEXAMPLE",
718 "AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
719 "AWS_REGION": "us-west-1",
720 })
721 now = setTime(defaultTime)
722
723 base, err := tfc.parse(context.Background())
724 if err != nil {
725 t.Fatalf("parse() failed %v", err)
726 }
727
728 out, err := base.subjectToken()
729 if err != nil {
730 t.Fatalf("retrieveSubjectToken() failed: %v", err)
731 }
732
733 expected := getExpectedSubjectToken(
734 "https://sts.us-west-1.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
735 "us-west-1",
736 "AKIDEXAMPLE",
737 "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
738 "",
739 )
740
741 if got, want := out, expected; !reflect.DeepEqual(got, want) {
742 t.Errorf("subjectToken = \n%q\n want \n%q", got, want)
743 }
744 }
745
746 func TestAWSCredential_BasicRequestWithDefaultEnv(t *testing.T) {
747 server := createDefaultAwsTestServer()
748 ts := httptest.NewServer(server)
749
750 tfc := testFileConfig
751 tfc.CredentialSource = server.getCredentialSource(ts.URL)
752
753 oldGetenv := getenv
754 oldNow := now
755 defer func() {
756 getenv = oldGetenv
757 now = oldNow
758 }()
759 getenv = setEnvironment(map[string]string{
760 "AWS_ACCESS_KEY_ID": "AKIDEXAMPLE",
761 "AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
762 "AWS_REGION": "us-west-1",
763 })
764 now = setTime(defaultTime)
765
766 base, err := tfc.parse(context.Background())
767 if err != nil {
768 t.Fatalf("parse() failed %v", err)
769 }
770
771 out, err := base.subjectToken()
772 if err != nil {
773 t.Fatalf("retrieveSubjectToken() failed: %v", err)
774 }
775 expected := getExpectedSubjectToken(
776 "https://sts.us-west-1.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
777 "us-west-1",
778 "AKIDEXAMPLE",
779 "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
780 "",
781 )
782
783 if got, want := out, expected; !reflect.DeepEqual(got, want) {
784 t.Errorf("subjectToken = \n%q\n want \n%q", got, want)
785 }
786 }
787
788 func TestAWSCredential_BasicRequestWithTwoRegions(t *testing.T) {
789 server := createDefaultAwsTestServer()
790 ts := httptest.NewServer(server)
791
792 tfc := testFileConfig
793 tfc.CredentialSource = server.getCredentialSource(ts.URL)
794
795 oldGetenv := getenv
796 oldNow := now
797 defer func() {
798 getenv = oldGetenv
799 now = oldNow
800 }()
801 getenv = setEnvironment(map[string]string{
802 "AWS_ACCESS_KEY_ID": "AKIDEXAMPLE",
803 "AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
804 "AWS_REGION": "us-west-1",
805 "AWS_DEFAULT_REGION": "us-east-1",
806 })
807 now = setTime(defaultTime)
808
809 base, err := tfc.parse(context.Background())
810 if err != nil {
811 t.Fatalf("parse() failed %v", err)
812 }
813
814 out, err := base.subjectToken()
815 if err != nil {
816 t.Fatalf("retrieveSubjectToken() failed: %v", err)
817 }
818 expected := getExpectedSubjectToken(
819 "https://sts.us-west-1.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
820 "us-west-1",
821 "AKIDEXAMPLE",
822 "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
823 "",
824 )
825
826 if got, want := out, expected; !reflect.DeepEqual(got, want) {
827 t.Errorf("subjectToken = \n%q\n want \n%q", got, want)
828 }
829 }
830
831 func TestAWSCredential_RequestWithBadVersion(t *testing.T) {
832 server := createDefaultAwsTestServer()
833 ts := httptest.NewServer(server)
834
835 tfc := testFileConfig
836 tfc.CredentialSource = server.getCredentialSource(ts.URL)
837 tfc.CredentialSource.EnvironmentID = "aws3"
838
839 oldGetenv := getenv
840 defer func() {
841 getenv = oldGetenv
842 }()
843 getenv = setEnvironment(map[string]string{})
844
845 _, err := tfc.parse(context.Background())
846 if err == nil {
847 t.Fatalf("parse() should have failed")
848 }
849 if got, want := err.Error(), "oauth2/google/externalaccount: aws version '3' is not supported in the current build"; !reflect.DeepEqual(got, want) {
850 t.Errorf("subjectToken = %q, want %q", got, want)
851 }
852 }
853
854 func TestAWSCredential_RequestWithNoRegionURL(t *testing.T) {
855 server := createDefaultAwsTestServer()
856 ts := httptest.NewServer(server)
857
858 tfc := testFileConfig
859 tfc.CredentialSource = server.getCredentialSource(ts.URL)
860 tfc.CredentialSource.RegionURL = ""
861
862 oldGetenv := getenv
863 defer func() {
864 getenv = oldGetenv
865 }()
866 getenv = setEnvironment(map[string]string{})
867
868 base, err := tfc.parse(context.Background())
869 if err != nil {
870 t.Fatalf("parse() failed %v", err)
871 }
872
873 _, err = base.subjectToken()
874 if err == nil {
875 t.Fatalf("retrieveSubjectToken() should have failed")
876 }
877
878 if got, want := err.Error(), "oauth2/google/externalaccount: unable to determine AWS region"; !reflect.DeepEqual(got, want) {
879 t.Errorf("subjectToken = %q, want %q", got, want)
880 }
881 }
882
883 func TestAWSCredential_RequestWithBadRegionURL(t *testing.T) {
884 server := createDefaultAwsTestServer()
885 ts := httptest.NewServer(server)
886
887 server.WriteRegion = notFound
888
889 tfc := testFileConfig
890 tfc.CredentialSource = server.getCredentialSource(ts.URL)
891
892 oldGetenv := getenv
893 defer func() {
894 getenv = oldGetenv
895 }()
896 getenv = setEnvironment(map[string]string{})
897
898 base, err := tfc.parse(context.Background())
899 if err != nil {
900 t.Fatalf("parse() failed %v", err)
901 }
902
903 _, err = base.subjectToken()
904 if err == nil {
905 t.Fatalf("retrieveSubjectToken() should have failed")
906 }
907
908 if got, want := err.Error(), "oauth2/google/externalaccount: unable to retrieve AWS region - Not Found"; !reflect.DeepEqual(got, want) {
909 t.Errorf("subjectToken = %q, want %q", got, want)
910 }
911 }
912
913 func TestAWSCredential_RequestWithMissingCredential(t *testing.T) {
914 server := createDefaultAwsTestServer()
915 ts := httptest.NewServer(server)
916
917 server.WriteSecurityCredentials = func(w http.ResponseWriter, r *http.Request) {
918 w.Write([]byte("{}"))
919 }
920
921 tfc := testFileConfig
922 tfc.CredentialSource = server.getCredentialSource(ts.URL)
923
924 oldGetenv := getenv
925 defer func() {
926 getenv = oldGetenv
927 }()
928 getenv = setEnvironment(map[string]string{})
929
930 base, err := tfc.parse(context.Background())
931 if err != nil {
932 t.Fatalf("parse() failed %v", err)
933 }
934
935 _, err = base.subjectToken()
936 if err == nil {
937 t.Fatalf("retrieveSubjectToken() should have failed")
938 }
939
940 if got, want := err.Error(), "oauth2/google/externalaccount: missing AccessKeyId credential"; !reflect.DeepEqual(got, want) {
941 t.Errorf("subjectToken = %q, want %q", got, want)
942 }
943 }
944
945 func TestAWSCredential_RequestWithIncompleteCredential(t *testing.T) {
946 server := createDefaultAwsTestServer()
947 ts := httptest.NewServer(server)
948
949 server.WriteSecurityCredentials = func(w http.ResponseWriter, r *http.Request) {
950 w.Write([]byte(`{"AccessKeyId":"FOOBARBAS"}`))
951 }
952
953 tfc := testFileConfig
954 tfc.CredentialSource = server.getCredentialSource(ts.URL)
955
956 oldGetenv := getenv
957 defer func() {
958 getenv = oldGetenv
959 }()
960 getenv = setEnvironment(map[string]string{})
961
962 base, err := tfc.parse(context.Background())
963 if err != nil {
964 t.Fatalf("parse() failed %v", err)
965 }
966
967 _, err = base.subjectToken()
968 if err == nil {
969 t.Fatalf("retrieveSubjectToken() should have failed")
970 }
971
972 if got, want := err.Error(), "oauth2/google/externalaccount: missing SecretAccessKey credential"; !reflect.DeepEqual(got, want) {
973 t.Errorf("subjectToken = %q, want %q", got, want)
974 }
975 }
976
977 func TestAWSCredential_RequestWithNoCredentialURL(t *testing.T) {
978 server := createDefaultAwsTestServer()
979 ts := httptest.NewServer(server)
980
981 tfc := testFileConfig
982 tfc.CredentialSource = server.getCredentialSource(ts.URL)
983 tfc.CredentialSource.URL = ""
984
985 oldGetenv := getenv
986 defer func() {
987 getenv = oldGetenv
988 }()
989 getenv = setEnvironment(map[string]string{})
990
991 base, err := tfc.parse(context.Background())
992 if err != nil {
993 t.Fatalf("parse() failed %v", err)
994 }
995
996 _, err = base.subjectToken()
997 if err == nil {
998 t.Fatalf("retrieveSubjectToken() should have failed")
999 }
1000
1001 if got, want := err.Error(), "oauth2/google/externalaccount: unable to determine the AWS metadata server security credentials endpoint"; !reflect.DeepEqual(got, want) {
1002 t.Errorf("subjectToken = %q, want %q", got, want)
1003 }
1004 }
1005
1006 func TestAWSCredential_RequestWithBadCredentialURL(t *testing.T) {
1007 server := createDefaultAwsTestServer()
1008 ts := httptest.NewServer(server)
1009 server.WriteRolename = notFound
1010
1011 tfc := testFileConfig
1012 tfc.CredentialSource = server.getCredentialSource(ts.URL)
1013
1014 oldGetenv := getenv
1015 defer func() {
1016 getenv = oldGetenv
1017 }()
1018 getenv = setEnvironment(map[string]string{})
1019
1020 base, err := tfc.parse(context.Background())
1021 if err != nil {
1022 t.Fatalf("parse() failed %v", err)
1023 }
1024
1025 _, err = base.subjectToken()
1026 if err == nil {
1027 t.Fatalf("retrieveSubjectToken() should have failed")
1028 }
1029
1030 if got, want := err.Error(), "oauth2/google/externalaccount: unable to retrieve AWS role name - Not Found"; !reflect.DeepEqual(got, want) {
1031 t.Errorf("subjectToken = %q, want %q", got, want)
1032 }
1033 }
1034
1035 func TestAWSCredential_RequestWithBadFinalCredentialURL(t *testing.T) {
1036 server := createDefaultAwsTestServer()
1037 ts := httptest.NewServer(server)
1038 server.WriteSecurityCredentials = notFound
1039
1040 tfc := testFileConfig
1041 tfc.CredentialSource = server.getCredentialSource(ts.URL)
1042
1043 oldGetenv := getenv
1044 defer func() {
1045 getenv = oldGetenv
1046 }()
1047 getenv = setEnvironment(map[string]string{})
1048
1049 base, err := tfc.parse(context.Background())
1050 if err != nil {
1051 t.Fatalf("parse() failed %v", err)
1052 }
1053
1054 _, err = base.subjectToken()
1055 if err == nil {
1056 t.Fatalf("retrieveSubjectToken() should have failed")
1057 }
1058
1059 if got, want := err.Error(), "oauth2/google/externalaccount: unable to retrieve AWS security credentials - Not Found"; !reflect.DeepEqual(got, want) {
1060 t.Errorf("subjectToken = %q, want %q", got, want)
1061 }
1062 }
1063
1064 func TestAWSCredential_ShouldNotCallMetadataEndpointWhenCredsAreInEnv(t *testing.T) {
1065 server := createDefaultAwsTestServer()
1066 ts := httptest.NewServer(server)
1067
1068 metadataTs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
1069 t.Error("Metadata server should not have been called.")
1070 }))
1071
1072 tfc := testFileConfig
1073 tfc.CredentialSource = server.getCredentialSource(ts.URL)
1074 tfc.CredentialSource.IMDSv2SessionTokenURL = metadataTs.URL
1075
1076 oldGetenv := getenv
1077 oldNow := now
1078 defer func() {
1079 getenv = oldGetenv
1080 now = oldNow
1081 }()
1082 getenv = setEnvironment(map[string]string{
1083 "AWS_ACCESS_KEY_ID": "AKIDEXAMPLE",
1084 "AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
1085 "AWS_REGION": "us-west-1",
1086 })
1087 now = setTime(defaultTime)
1088
1089 base, err := tfc.parse(context.Background())
1090 if err != nil {
1091 t.Fatalf("parse() failed %v", err)
1092 }
1093
1094 out, err := base.subjectToken()
1095 if err != nil {
1096 t.Fatalf("retrieveSubjectToken() failed: %v", err)
1097 }
1098
1099 expected := getExpectedSubjectToken(
1100 "https://sts.us-west-1.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
1101 "us-west-1",
1102 "AKIDEXAMPLE",
1103 "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
1104 "",
1105 )
1106
1107 if got, want := out, expected; !reflect.DeepEqual(got, want) {
1108 t.Errorf("subjectToken = \n%q\n want \n%q", got, want)
1109 }
1110 }
1111
1112 func TestAWSCredential_ShouldCallMetadataEndpointWhenNoRegion(t *testing.T) {
1113 server := createDefaultAwsTestServerWithImdsv2(t)
1114 ts := httptest.NewServer(server)
1115
1116 tfc := testFileConfig
1117 tfc.CredentialSource = server.getCredentialSource(ts.URL)
1118
1119 oldGetenv := getenv
1120 oldNow := now
1121 defer func() {
1122 getenv = oldGetenv
1123 now = oldNow
1124 }()
1125 getenv = setEnvironment(map[string]string{
1126 "AWS_ACCESS_KEY_ID": accessKeyID,
1127 "AWS_SECRET_ACCESS_KEY": secretAccessKey,
1128 })
1129 now = setTime(defaultTime)
1130
1131 base, err := tfc.parse(context.Background())
1132 if err != nil {
1133 t.Fatalf("parse() failed %v", err)
1134 }
1135
1136 out, err := base.subjectToken()
1137 if err != nil {
1138 t.Fatalf("retrieveSubjectToken() failed: %v", err)
1139 }
1140
1141 expected := getExpectedSubjectToken(
1142 "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
1143 "us-east-2",
1144 accessKeyID,
1145 secretAccessKey,
1146 "",
1147 )
1148
1149 if got, want := out, expected; !reflect.DeepEqual(got, want) {
1150 t.Errorf("subjectToken = \n%q\n want \n%q", got, want)
1151 }
1152 }
1153
1154 func TestAWSCredential_ShouldCallMetadataEndpointWhenNoAccessKey(t *testing.T) {
1155 server := createDefaultAwsTestServerWithImdsv2(t)
1156 ts := httptest.NewServer(server)
1157
1158 tfc := testFileConfig
1159 tfc.CredentialSource = server.getCredentialSource(ts.URL)
1160
1161 oldGetenv := getenv
1162 oldNow := now
1163 defer func() {
1164 getenv = oldGetenv
1165 now = oldNow
1166 }()
1167 getenv = setEnvironment(map[string]string{
1168 "AWS_SECRET_ACCESS_KEY": "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
1169 "AWS_REGION": "us-west-1",
1170 })
1171 now = setTime(defaultTime)
1172
1173 base, err := tfc.parse(context.Background())
1174 if err != nil {
1175 t.Fatalf("parse() failed %v", err)
1176 }
1177
1178 out, err := base.subjectToken()
1179 if err != nil {
1180 t.Fatalf("retrieveSubjectToken() failed: %v", err)
1181 }
1182
1183 expected := getExpectedSubjectToken(
1184 "https://sts.us-west-1.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
1185 "us-west-1",
1186 accessKeyID,
1187 secretAccessKey,
1188 securityToken,
1189 )
1190
1191 if got, want := out, expected; !reflect.DeepEqual(got, want) {
1192 t.Errorf("subjectToken = \n%q\n want \n%q", got, want)
1193 }
1194 }
1195
1196 func TestAWSCredential_ShouldCallMetadataEndpointWhenNoSecretAccessKey(t *testing.T) {
1197 server := createDefaultAwsTestServerWithImdsv2(t)
1198 ts := httptest.NewServer(server)
1199
1200 tfc := testFileConfig
1201 tfc.CredentialSource = server.getCredentialSource(ts.URL)
1202
1203 oldGetenv := getenv
1204 oldNow := now
1205 defer func() {
1206 getenv = oldGetenv
1207 now = oldNow
1208 }()
1209 getenv = setEnvironment(map[string]string{
1210 "AWS_ACCESS_KEY_ID": "AKIDEXAMPLE",
1211 "AWS_REGION": "us-west-1",
1212 })
1213 now = setTime(defaultTime)
1214
1215 base, err := tfc.parse(context.Background())
1216 if err != nil {
1217 t.Fatalf("parse() failed %v", err)
1218 }
1219
1220 out, err := base.subjectToken()
1221 if err != nil {
1222 t.Fatalf("retrieveSubjectToken() failed: %v", err)
1223 }
1224
1225 expected := getExpectedSubjectToken(
1226 "https://sts.us-west-1.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
1227 "us-west-1",
1228 accessKeyID,
1229 secretAccessKey,
1230 securityToken,
1231 )
1232
1233 if got, want := out, expected; !reflect.DeepEqual(got, want) {
1234 t.Errorf("subjectToken = \n%q\n want \n%q", got, want)
1235 }
1236 }
1237
1238 func TestAWSCredential_ProgrammaticAuth(t *testing.T) {
1239 tfc := testFileConfig
1240 securityCredentials := AwsSecurityCredentials{
1241 AccessKeyID: accessKeyID,
1242 SecretAccessKey: secretAccessKey,
1243 SessionToken: securityToken,
1244 }
1245
1246 tfc.AwsSecurityCredentialsSupplier = testAwsSupplier{
1247 awsRegion: "us-east-2",
1248 err: nil,
1249 credentials: &securityCredentials,
1250 }
1251
1252 oldNow := now
1253 defer func() {
1254 now = oldNow
1255 }()
1256 now = setTime(defaultTime)
1257
1258 base, err := tfc.parse(context.Background())
1259 if err != nil {
1260 t.Fatalf("parse() failed %v", err)
1261 }
1262
1263 out, err := base.subjectToken()
1264 if err != nil {
1265 t.Fatalf("retrieveSubjectToken() failed: %v", err)
1266 }
1267
1268 expected := getExpectedSubjectToken(
1269 "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
1270 "us-east-2",
1271 accessKeyID,
1272 secretAccessKey,
1273 securityToken,
1274 )
1275
1276 if got, want := out, expected; !reflect.DeepEqual(got, want) {
1277 t.Errorf("subjectToken = \n%q\n want \n%q", got, want)
1278 }
1279 }
1280
1281 func TestAWSCredential_ProgrammaticAuthNoSessionToken(t *testing.T) {
1282 tfc := testFileConfig
1283 securityCredentials := AwsSecurityCredentials{
1284 AccessKeyID: accessKeyID,
1285 SecretAccessKey: secretAccessKey,
1286 }
1287
1288 tfc.AwsSecurityCredentialsSupplier = testAwsSupplier{
1289 awsRegion: "us-east-2",
1290 err: nil,
1291 credentials: &securityCredentials,
1292 }
1293
1294 oldNow := now
1295 defer func() {
1296 now = oldNow
1297 }()
1298 now = setTime(defaultTime)
1299
1300 base, err := tfc.parse(context.Background())
1301 if err != nil {
1302 t.Fatalf("parse() failed %v", err)
1303 }
1304
1305 out, err := base.subjectToken()
1306 if err != nil {
1307 t.Fatalf("retrieveSubjectToken() failed: %v", err)
1308 }
1309
1310 expected := getExpectedSubjectToken(
1311 "https://sts.us-east-2.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
1312 "us-east-2",
1313 accessKeyID,
1314 secretAccessKey,
1315 "",
1316 )
1317
1318 if got, want := out, expected; !reflect.DeepEqual(got, want) {
1319 t.Errorf("subjectToken = \n%q\n want \n%q", got, want)
1320 }
1321 }
1322
1323 func TestAWSCredential_ProgrammaticAuthError(t *testing.T) {
1324 tfc := testFileConfig
1325 testErr := errors.New("test error")
1326 tfc.AwsSecurityCredentialsSupplier = testAwsSupplier{
1327 awsRegion: "us-east-2",
1328 err: testErr,
1329 credentials: nil,
1330 }
1331
1332 base, err := tfc.parse(context.Background())
1333 if err != nil {
1334 t.Fatalf("parse() failed %v", err)
1335 }
1336
1337 _, err = base.subjectToken()
1338 if err == nil {
1339 t.Fatalf("subjectToken() should have failed")
1340 }
1341 if err != testErr {
1342 t.Errorf("error = %e, want %e", err, testErr)
1343 }
1344 }
1345
1346 func TestAWSCredential_ProgrammaticAuthRegionError(t *testing.T) {
1347 tfc := testFileConfig
1348 securityCredentials := AwsSecurityCredentials{
1349 AccessKeyID: accessKeyID,
1350 SecretAccessKey: secretAccessKey,
1351 }
1352
1353 testErr := errors.New("test")
1354 tfc.AwsSecurityCredentialsSupplier = testAwsSupplier{
1355 awsRegion: "",
1356 regionErr: testErr,
1357 credentials: &securityCredentials,
1358 }
1359
1360 base, err := tfc.parse(context.Background())
1361 if err != nil {
1362 t.Fatalf("parse() failed %v", err)
1363 }
1364
1365 _, err = base.subjectToken()
1366 if err == nil {
1367 t.Fatalf("subjectToken() should have failed")
1368 }
1369 if err != testErr {
1370 t.Errorf("error = %e, want %e", err, testErr)
1371 }
1372 }
1373
1374 func TestAWSCredential_ProgrammaticAuthOptions(t *testing.T) {
1375 tfc := testFileConfig
1376 securityCredentials := AwsSecurityCredentials{
1377 AccessKeyID: accessKeyID,
1378 SecretAccessKey: secretAccessKey,
1379 }
1380 expectedOptions := SupplierOptions{Audience: tfc.Audience, SubjectTokenType: tfc.SubjectTokenType}
1381
1382 tfc.AwsSecurityCredentialsSupplier = testAwsSupplier{
1383 awsRegion: "us-east-2",
1384 credentials: &securityCredentials,
1385 expectedOptions: &expectedOptions,
1386 }
1387
1388 base, err := tfc.parse(context.Background())
1389 if err != nil {
1390 t.Fatalf("parse() failed %v", err)
1391 }
1392
1393 _, err = base.subjectToken()
1394 if err != nil {
1395 t.Fatalf("subjectToken() failed %v", err)
1396 }
1397 }
1398
1399 func TestAWSCredential_ProgrammaticAuthContext(t *testing.T) {
1400 tfc := testFileConfig
1401 securityCredentials := AwsSecurityCredentials{
1402 AccessKeyID: accessKeyID,
1403 SecretAccessKey: secretAccessKey,
1404 }
1405 ctx := context.Background()
1406
1407 tfc.AwsSecurityCredentialsSupplier = testAwsSupplier{
1408 awsRegion: "us-east-2",
1409 credentials: &securityCredentials,
1410 expectedContext: ctx,
1411 }
1412
1413 base, err := tfc.parse(ctx)
1414 if err != nil {
1415 t.Fatalf("parse() failed %v", err)
1416 }
1417
1418 _, err = base.subjectToken()
1419 if err != nil {
1420 t.Fatalf("subjectToken() failed %v", err)
1421 }
1422 }
1423
1424 func TestAwsCredential_CredentialSourceType(t *testing.T) {
1425 server := createDefaultAwsTestServer()
1426 ts := httptest.NewServer(server)
1427
1428 tfc := testFileConfig
1429 tfc.CredentialSource = server.getCredentialSource(ts.URL)
1430
1431 base, err := tfc.parse(context.Background())
1432 if err != nil {
1433 t.Fatalf("parse() failed %v", err)
1434 }
1435
1436 if got, want := base.credentialSourceType(), "aws"; got != want {
1437 t.Errorf("got %v but want %v", got, want)
1438 }
1439 }
1440
1441 type testAwsSupplier struct {
1442 err error
1443 regionErr error
1444 awsRegion string
1445 credentials *AwsSecurityCredentials
1446 expectedOptions *SupplierOptions
1447 expectedContext context.Context
1448 }
1449
1450 func (supp testAwsSupplier) AwsRegion(ctx context.Context, options SupplierOptions) (string, error) {
1451 if supp.regionErr != nil {
1452 return "", supp.regionErr
1453 }
1454 if supp.expectedOptions != nil {
1455 if supp.expectedOptions.Audience != options.Audience {
1456 return "", errors.New("Audience does not match")
1457 }
1458 if supp.expectedOptions.SubjectTokenType != options.SubjectTokenType {
1459 return "", errors.New("Audience does not match")
1460 }
1461 }
1462 if supp.expectedContext != nil {
1463 if supp.expectedContext != ctx {
1464 return "", errors.New("Context does not match")
1465 }
1466 }
1467 return supp.awsRegion, nil
1468 }
1469
1470 func (supp testAwsSupplier) AwsSecurityCredentials(ctx context.Context, options SupplierOptions) (*AwsSecurityCredentials, error) {
1471 if supp.err != nil {
1472 return nil, supp.err
1473 }
1474 if supp.expectedOptions != nil {
1475 if supp.expectedOptions.Audience != options.Audience {
1476 return nil, errors.New("Audience does not match")
1477 }
1478 if supp.expectedOptions.SubjectTokenType != options.SubjectTokenType {
1479 return nil, errors.New("Audience does not match")
1480 }
1481 }
1482 if supp.expectedContext != nil {
1483 if supp.expectedContext != ctx {
1484 return nil, errors.New("Context does not match")
1485 }
1486 }
1487 return supp.credentials, nil
1488 }
1489
View as plain text