v4_test.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. package v4
  2. import (
  3. "net/http"
  4. "strings"
  5. "testing"
  6. "time"
  7. "github.com/aws/aws-sdk-go/aws"
  8. "github.com/aws/aws-sdk-go/aws/credentials"
  9. "github.com/aws/aws-sdk-go/aws/request"
  10. "github.com/aws/aws-sdk-go/aws/service"
  11. "github.com/stretchr/testify/assert"
  12. )
  13. func buildSigner(serviceName string, region string, signTime time.Time, expireTime time.Duration, body string) signer {
  14. endpoint := "https://" + serviceName + "." + region + ".amazonaws.com"
  15. reader := strings.NewReader(body)
  16. req, _ := http.NewRequest("POST", endpoint, reader)
  17. req.URL.Opaque = "//example.org/bucket/key-._~,!@#$%^&*()"
  18. req.Header.Add("X-Amz-Target", "prefix.Operation")
  19. req.Header.Add("Content-Type", "application/x-amz-json-1.0")
  20. req.Header.Add("Content-Length", string(len(body)))
  21. req.Header.Add("X-Amz-Meta-Other-Header", "some-value=!@#$%^&* (+)")
  22. return signer{
  23. Request: req,
  24. Time: signTime,
  25. ExpireTime: expireTime,
  26. Query: req.URL.Query(),
  27. Body: reader,
  28. ServiceName: serviceName,
  29. Region: region,
  30. Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"),
  31. }
  32. }
  33. func removeWS(text string) string {
  34. text = strings.Replace(text, " ", "", -1)
  35. text = strings.Replace(text, "\n", "", -1)
  36. text = strings.Replace(text, "\t", "", -1)
  37. return text
  38. }
  39. func assertEqual(t *testing.T, expected, given string) {
  40. if removeWS(expected) != removeWS(given) {
  41. t.Errorf("\nExpected: %s\nGiven: %s", expected, given)
  42. }
  43. }
  44. func TestPresignRequest(t *testing.T) {
  45. signer := buildSigner("dynamodb", "us-east-1", time.Unix(0, 0), 300*time.Second, "{}")
  46. signer.sign()
  47. expectedDate := "19700101T000000Z"
  48. expectedHeaders := "host;x-amz-meta-other-header;x-amz-target"
  49. expectedSig := "5eeedebf6f995145ce56daa02902d10485246d3defb34f97b973c1f40ab82d36"
  50. expectedCred := "AKID/19700101/us-east-1/dynamodb/aws4_request"
  51. q := signer.Request.URL.Query()
  52. assert.Equal(t, expectedSig, q.Get("X-Amz-Signature"))
  53. assert.Equal(t, expectedCred, q.Get("X-Amz-Credential"))
  54. assert.Equal(t, expectedHeaders, q.Get("X-Amz-SignedHeaders"))
  55. assert.Equal(t, expectedDate, q.Get("X-Amz-Date"))
  56. }
  57. func TestSignRequest(t *testing.T) {
  58. signer := buildSigner("dynamodb", "us-east-1", time.Unix(0, 0), 0, "{}")
  59. signer.sign()
  60. expectedDate := "19700101T000000Z"
  61. expectedSig := "AWS4-HMAC-SHA256 Credential=AKID/19700101/us-east-1/dynamodb/aws4_request, SignedHeaders=host;x-amz-date;x-amz-meta-other-header;x-amz-security-token;x-amz-target, Signature=69ada33fec48180dab153576e4dd80c4e04124f80dda3eccfed8a67c2b91ed5e"
  62. q := signer.Request.Header
  63. assert.Equal(t, expectedSig, q.Get("Authorization"))
  64. assert.Equal(t, expectedDate, q.Get("X-Amz-Date"))
  65. }
  66. func TestSignEmptyBody(t *testing.T) {
  67. signer := buildSigner("dynamodb", "us-east-1", time.Now(), 0, "")
  68. signer.Body = nil
  69. signer.sign()
  70. hash := signer.Request.Header.Get("X-Amz-Content-Sha256")
  71. assert.Equal(t, "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", hash)
  72. }
  73. func TestSignBody(t *testing.T) {
  74. signer := buildSigner("dynamodb", "us-east-1", time.Now(), 0, "hello")
  75. signer.sign()
  76. hash := signer.Request.Header.Get("X-Amz-Content-Sha256")
  77. assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", hash)
  78. }
  79. func TestSignSeekedBody(t *testing.T) {
  80. signer := buildSigner("dynamodb", "us-east-1", time.Now(), 0, " hello")
  81. signer.Body.Read(make([]byte, 3)) // consume first 3 bytes so body is now "hello"
  82. signer.sign()
  83. hash := signer.Request.Header.Get("X-Amz-Content-Sha256")
  84. assert.Equal(t, "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824", hash)
  85. start, _ := signer.Body.Seek(0, 1)
  86. assert.Equal(t, int64(3), start)
  87. }
  88. func TestPresignEmptyBodyS3(t *testing.T) {
  89. signer := buildSigner("s3", "us-east-1", time.Now(), 5*time.Minute, "hello")
  90. signer.sign()
  91. hash := signer.Request.Header.Get("X-Amz-Content-Sha256")
  92. assert.Equal(t, "UNSIGNED-PAYLOAD", hash)
  93. }
  94. func TestSignPrecomputedBodyChecksum(t *testing.T) {
  95. signer := buildSigner("dynamodb", "us-east-1", time.Now(), 0, "hello")
  96. signer.Request.Header.Set("X-Amz-Content-Sha256", "PRECOMPUTED")
  97. signer.sign()
  98. hash := signer.Request.Header.Get("X-Amz-Content-Sha256")
  99. assert.Equal(t, "PRECOMPUTED", hash)
  100. }
  101. func TestAnonymousCredentials(t *testing.T) {
  102. svc := service.New(&aws.Config{Credentials: credentials.AnonymousCredentials})
  103. r := svc.NewRequest(
  104. &request.Operation{
  105. Name: "BatchGetItem",
  106. HTTPMethod: "POST",
  107. HTTPPath: "/",
  108. },
  109. nil,
  110. nil,
  111. )
  112. Sign(r)
  113. urlQ := r.HTTPRequest.URL.Query()
  114. assert.Empty(t, urlQ.Get("X-Amz-Signature"))
  115. assert.Empty(t, urlQ.Get("X-Amz-Credential"))
  116. assert.Empty(t, urlQ.Get("X-Amz-SignedHeaders"))
  117. assert.Empty(t, urlQ.Get("X-Amz-Date"))
  118. hQ := r.HTTPRequest.Header
  119. assert.Empty(t, hQ.Get("Authorization"))
  120. assert.Empty(t, hQ.Get("X-Amz-Date"))
  121. }
  122. func TestIgnoreResignRequestWithValidCreds(t *testing.T) {
  123. svc := service.New(&aws.Config{
  124. Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"),
  125. Region: aws.String("us-west-2"),
  126. })
  127. r := svc.NewRequest(
  128. &request.Operation{
  129. Name: "BatchGetItem",
  130. HTTPMethod: "POST",
  131. HTTPPath: "/",
  132. },
  133. nil,
  134. nil,
  135. )
  136. Sign(r)
  137. sig := r.HTTPRequest.Header.Get("Authorization")
  138. Sign(r)
  139. assert.Equal(t, sig, r.HTTPRequest.Header.Get("Authorization"))
  140. }
  141. func TestIgnorePreResignRequestWithValidCreds(t *testing.T) {
  142. svc := service.New(&aws.Config{
  143. Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"),
  144. Region: aws.String("us-west-2"),
  145. })
  146. r := svc.NewRequest(
  147. &request.Operation{
  148. Name: "BatchGetItem",
  149. HTTPMethod: "POST",
  150. HTTPPath: "/",
  151. },
  152. nil,
  153. nil,
  154. )
  155. r.ExpireTime = time.Minute * 10
  156. Sign(r)
  157. sig := r.HTTPRequest.Header.Get("X-Amz-Signature")
  158. Sign(r)
  159. assert.Equal(t, sig, r.HTTPRequest.Header.Get("X-Amz-Signature"))
  160. }
  161. func TestResignRequestExpiredCreds(t *testing.T) {
  162. creds := credentials.NewStaticCredentials("AKID", "SECRET", "SESSION")
  163. svc := service.New(&aws.Config{Credentials: creds})
  164. r := svc.NewRequest(
  165. &request.Operation{
  166. Name: "BatchGetItem",
  167. HTTPMethod: "POST",
  168. HTTPPath: "/",
  169. },
  170. nil,
  171. nil,
  172. )
  173. Sign(r)
  174. querySig := r.HTTPRequest.Header.Get("Authorization")
  175. creds.Expire()
  176. Sign(r)
  177. assert.NotEqual(t, querySig, r.HTTPRequest.Header.Get("Authorization"))
  178. }
  179. func TestPreResignRequestExpiredCreds(t *testing.T) {
  180. provider := &credentials.StaticProvider{credentials.Value{"AKID", "SECRET", "SESSION"}}
  181. creds := credentials.NewCredentials(provider)
  182. svc := service.New(&aws.Config{Credentials: creds})
  183. r := svc.NewRequest(
  184. &request.Operation{
  185. Name: "BatchGetItem",
  186. HTTPMethod: "POST",
  187. HTTPPath: "/",
  188. },
  189. nil,
  190. nil,
  191. )
  192. r.ExpireTime = time.Minute * 10
  193. Sign(r)
  194. querySig := r.HTTPRequest.URL.Query().Get("X-Amz-Signature")
  195. creds.Expire()
  196. r.Time = time.Now().Add(time.Hour * 48)
  197. Sign(r)
  198. assert.NotEqual(t, querySig, r.HTTPRequest.URL.Query().Get("X-Amz-Signature"))
  199. }
  200. func BenchmarkPresignRequest(b *testing.B) {
  201. signer := buildSigner("dynamodb", "us-east-1", time.Now(), 300*time.Second, "{}")
  202. for i := 0; i < b.N; i++ {
  203. signer.sign()
  204. }
  205. }
  206. func BenchmarkSignRequest(b *testing.B) {
  207. signer := buildSigner("dynamodb", "us-east-1", time.Now(), 0, "{}")
  208. for i := 0; i < b.N; i++ {
  209. signer.sign()
  210. }
  211. }