1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
|
package awsauth
import (
"fmt"
"net/http"
"net/url"
"testing"
"time"
. "github.com/smartystreets/goconvey/convey"
)
func TestSignatureS3(t *testing.T) {
// http://docs.aws.amazon.com/AmazonS3/2006-03-01/dev/RESTAuthentication.html
// Note: S3 now supports signed signature version 4
// (but signed URL requests still utilize a lot of the same functionality)
Convey("Given a GET request to Amazon S3", t, func() {
keys := *testCredS3
request := test_plainRequestS3()
// Mock time
now = func() time.Time {
parsed, _ := time.Parse(timeFormatS3, exampleReqTsS3)
return parsed
}
Convey("The request should be prepared with a Date header", func() {
prepareRequestS3(request)
So(request.Header.Get("Date"), ShouldEqual, exampleReqTsS3)
})
Convey("The CanonicalizedAmzHeaders should be built properly", func() {
req2 := test_headerRequestS3()
actual := canonicalAmzHeadersS3(req2)
So(actual, ShouldEqual, expectedCanonAmzHeadersS3)
})
Convey("The CanonicalizedResource should be built properly", func() {
actual := canonicalResourceS3(request)
So(actual, ShouldEqual, expectedCanonResourceS3)
})
Convey("The string to sign should be correct", func() {
actual := stringToSignS3(request)
So(actual, ShouldEqual, expectedStringToSignS3)
})
Convey("The final signature string should be exactly correct", func() {
actual := signatureS3(stringToSignS3(request), keys)
So(actual, ShouldEqual, "bWq2s1WEIj+Ydj0vQ697zp+IXMU=")
})
})
Convey("Given a GET request for a resource on S3 for query string authentication", t, func() {
keys := *testCredS3
request, _ := http.NewRequest("GET", "https://johnsmith.s3.amazonaws.com/johnsmith/photos/puppy.jpg", nil)
now = func() time.Time {
parsed, _ := time.Parse(timeFormatS3, exampleReqTsS3)
return parsed
}
Convey("The string to sign should be correct", func() {
actual := stringToSignS3Url("GET", now(), request.URL.Path)
So(actual, ShouldEqual, expectedStringToSignS3Url)
})
Convey("The signature of string to sign should be correct", func() {
actual := signatureS3(expectedStringToSignS3Url, keys)
So(actual, ShouldEqual, "R2K/+9bbnBIbVDCs7dqlz3XFtBQ=")
})
Convey("The finished signed URL should be correct", func() {
expiry := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
So(SignS3Url(request, expiry, keys).URL.String(), ShouldEqual, expectedSignedS3Url)
})
})
}
func TestS3STSRequestPreparer(t *testing.T) {
Convey("Given a plain request with no custom headers", t, func() {
request := test_plainRequestS3()
Convey("And a set of credentials with an STS token", func() {
keys := *testCredS3WithSTS
Convey("It should include an X-Amz-Security-Token when the request is signed", func() {
actualSigned := SignS3(request, keys)
actual := actualSigned.Header.Get("X-Amz-Security-Token")
So(actual, ShouldNotBeBlank)
So(actual, ShouldEqual, testCredS3WithSTS.SecurityToken)
})
})
})
}
func test_plainRequestS3() *http.Request {
request, _ := http.NewRequest("GET", "https://johnsmith.s3.amazonaws.com/photos/puppy.jpg", nil)
return request
}
func test_headerRequestS3() *http.Request {
request := test_plainRequestS3()
request.Header.Set("X-Amz-Meta-Something", "more foobar")
request.Header.Set("X-Amz-Date", "foobar")
request.Header.Set("X-Foobar", "nanoo-nanoo")
return request
}
func TestCanonical(t *testing.T) {
expectedCanonicalString := "PUT\nc8fdb181845a4ca6b8fec737b3581d76\ntext/html\nThu, 17 Nov 2005 18:49:58 GMT\nx-amz-magic:abracadabra\nx-amz-meta-author:foo@bar.com\n/quotes/nelson"
origUrl := "https://s3.amazonaws.com/"
resource := "/quotes/nelson"
u, _ := url.ParseRequestURI(origUrl)
u.Path = resource
urlStr := fmt.Sprintf("%v", u)
request, _ := http.NewRequest("PUT", urlStr, nil)
request.Header.Add("Content-Md5", "c8fdb181845a4ca6b8fec737b3581d76")
request.Header.Add("Content-Type", "text/html")
request.Header.Add("Date", "Thu, 17 Nov 2005 18:49:58 GMT")
request.Header.Add("X-Amz-Meta-Author", "foo@bar.com")
request.Header.Add("X-Amz-Magic", "abracadabra")
if stringToSignS3(request) != expectedCanonicalString {
t.Errorf("----Got\n***%s***\n----Expected\n***%s***", stringToSignS3(request), expectedCanonicalString)
}
}
var (
testCredS3 = &Credentials{
AccessKeyID: "AKIAIOSFODNN7EXAMPLE",
SecretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
}
testCredS3WithSTS = &Credentials{
AccessKeyID: "AKIDEXAMPLE",
SecretAccessKey: "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY",
SecurityToken: "AQoDYXdzEHcaoAJ1Aqwx1Sum0iW2NQjXJcWlKR7vuB6lnAeGBaQnjDRZPVyniwc48ml5hx+0qiXenVJdfusMMl9XLhSncfhx9Rb1UF8IAOaQ+CkpWXvoH67YYN+93dgckSVgVEBRByTl/BvLOZhe0ii/pOWkuQtBm5T7lBHRe4Dfmxy9X6hd8L3FrWxgnGV3fWZ3j0gASdYXaa+VBJlU0E2/GmCzn3T+t2mjYaeoInAnYVKVpmVMOrh6lNAeETTOHElLopblSa7TAmROq5xHIyu4a9i2qwjERTwa3Yk4Jk6q7JYVA5Cu7kS8wKVml8LdzzCTsy+elJgvH+Jf6ivpaHt/En0AJ5PZUJDev2+Y5+9j4AYfrmXfm4L73DC1ZJFJrv+Yh+EXAMPLE=",
}
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"
expectedStringToSignS3Url = "GET\n\n\n1175024202\n/johnsmith/photos/puppy.jpg"
expectedSignedS3Url = "https://johnsmith.s3.amazonaws.com/johnsmith/photos/puppy.jpg?AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Expires=1257894000&Signature=X%2FarTLAJP08uP1Bsap52rwmsVok%3D"
exampleReqTsS3 = "Tue, 27 Mar 2007 19:36:42 +0000"
)
|