diff --git a/awsauth.go b/awsauth.go index b126027..3175349 100644 --- a/awsauth.go +++ b/awsauth.go @@ -14,6 +14,7 @@ var Keys *Credentials type Credentials struct { AccessKeyID string SecretAccessKey string + SecurityToken string } // Sign signs a request bound for AWS. It automatically chooses the best @@ -39,6 +40,12 @@ func Sign(req *http.Request) *http.Request { // Sign4 signs a request with Signed Signature Version 4. func Sign4(req *http.Request) *http.Request { checkKeys() + + // Add the X-Amz-Security-Token header when using STS + if Keys.SecurityToken != "" { + req.Header.Set("X-Amz-Security-Token", Keys.SecurityToken) + } + prepareRequestV4(req) meta := new(metadata) @@ -119,6 +126,7 @@ type metadata struct { const ( envAccessKeyID = "AWS_ACCESS_KEY_ID" envSecretAccessKey = "AWS_SECRET_ACCESS_KEY" + envSecurityToken = "AWS_SECURITY_TOKEN" ) var awsSignVersion = map[string]int{ diff --git a/awsauth_test.go b/awsauth_test.go index 5a78a3a..ccd5d78 100644 --- a/awsauth_test.go +++ b/awsauth_test.go @@ -1,12 +1,12 @@ package awsauth import ( + . "github.com/smartystreets/goconvey/convey" "net/http" "net/url" "os" "strings" "testing" - . "github.com/smartystreets/goconvey/convey" ) func TestIntegration(t *testing.T) { diff --git a/common.go b/common.go index aa58c85..45e0c7b 100644 --- a/common.go +++ b/common.go @@ -46,6 +46,7 @@ func checkKeys() { Keys = &Credentials{ os.Getenv(envAccessKeyID), os.Getenv(envSecretAccessKey), + os.Getenv(envSecurityToken), } } } diff --git a/s3_test.go b/s3_test.go index c5e3f3d..adfe8c6 100644 --- a/s3_test.go +++ b/s3_test.go @@ -62,7 +62,7 @@ func test_headerRequestS3() *http.Request { } var ( - testCredS3 = &Credentials{"AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"} + testCredS3 = &Credentials{"AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", ""} expectedCanonAmzHeadersS3 = "x-amz-date:foobar\nx-amz-meta-something:more foobar\n" expectedCanonResourceS3 = "/johnsmith/photos/puppy.jpg" expectedStringToSignS3 = "GET\n\n\nTue, 27 Mar 2007 19:36:42 +0000\n/johnsmith/photos/puppy.jpg" diff --git a/sign2_test.go b/sign2_test.go index 7d00b5f..c1befb9 100644 --- a/sign2_test.go +++ b/sign2_test.go @@ -85,7 +85,7 @@ func test_unsignedRequestV2() *http.Request { } var ( - testCredV2 = &Credentials{"AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"} + testCredV2 = &Credentials{"AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", ""} exampleReqTsV2 = "2011-10-03T15:19:30" baseUrlV2 = "https://elasticmapreduce.amazonaws.com" canonicalQsV2 = "AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Action=DescribeJobFlows&SignatureMethod=HmacSHA256&SignatureVersion=2&Timestamp=2011-10-03T15%3A19%3A30&Version=2009-03-31" diff --git a/sign3_test.go b/sign3_test.go index a9d20fb..a0e13fe 100644 --- a/sign3_test.go +++ b/sign3_test.go @@ -1,11 +1,11 @@ package awsauth import ( + . "github.com/smartystreets/goconvey/convey" "net/http" "net/url" "testing" "time" - . "github.com/smartystreets/goconvey/convey" ) func TestSignature3(t *testing.T) { @@ -82,7 +82,7 @@ func test_unsignedRequestV3() *http.Request { } var ( - testCredV3 = &Credentials{"AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"} + testCredV3 = &Credentials{"AKIAIOSFODNN7EXAMPLE", "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY", ""} exampleReqTsV3 = "Thu, 14 Aug 2008 17:08:48 GMT" baseUrlV3 = "https://email.us-east-1.amazonaws.com" expectedStringToSignV3 = exampleReqTsV3 diff --git a/sign4_test.go b/sign4_test.go index 824522a..d6b68fc 100644 --- a/sign4_test.go +++ b/sign4_test.go @@ -1,11 +1,11 @@ package awsauth import ( + . "github.com/smartystreets/goconvey/convey" "net/http" "net/url" "strings" "testing" - . "github.com/smartystreets/goconvey/convey" ) func TestVersion4RequestPreparer(t *testing.T) { @@ -50,6 +50,26 @@ func TestVersion4RequestPreparer(t *testing.T) { }) } +func TestVersion4STSRequestPreparer(t *testing.T) { + Convey("Given a plain request with no custom headers", t, func() { + req := test_plainRequestV4(false) + + Convey("And a set of credentials with an STS token", func() { + Keys = testCredV4WithSTS + + Convey("It should include an X-Amz-Security-Token", func() { + actualSigned := Sign4(req) + actual := actualSigned.Header.Get("X-Amz-Security-Token") + + So(actual, ShouldNotBeBlank) + So(actual, ShouldEqual, testCredV4WithSTS.SecurityToken) + + }) + }) + }) + +} + func TestVersion4SigningTasks(t *testing.T) { // http://docs.aws.amazon.com/general/latest/gr/sigv4_signing.html @@ -166,6 +186,13 @@ var ( testCredV4 = &Credentials{ "AKIDEXAMPLE", "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", + "", + } + + testCredV4WithSTS = &Credentials{ + "AKIDEXAMPLE", + "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY", + "AQoDYXdzEHcaoAJ1Aqwx1Sum0iW2NQjXJcWlKR7vuB6lnAeGBaQnjDRZPVyniwc48ml5hx+0qiXenVJdfusMMl9XLhSncfhx9Rb1UF8IAOaQ+CkpWXvoH67YYN+93dgckSVgVEBRByTl/BvLOZhe0ii/pOWkuQtBm5T7lBHRe4Dfmxy9X6hd8L3FrWxgnGV3fWZ3j0gASdYXaa+VBJlU0E2/GmCzn3T+t2mjYaeoInAnYVKVpmVMOrh6lNAeETTOHElLopblSa7TAmROq5xHIyu4a9i2qwjERTwa3Yk4Jk6q7JYVA5Cu7kS8wKVml8LdzzCTsy+elJgvH+Jf6ivpaHt/En0AJ5PZUJDev2+Y5+9j4AYfrmXfm4L73DC1ZJFJrv+Yh+EXAMPLE=", } expectingV4 = map[string]string{