Switch to using the dep tool and update all the dependencies

This commit is contained in:
Nick Craig-Wood 2017-05-11 15:39:54 +01:00
parent 5135ff73cb
commit 98c2d2c41b
5321 changed files with 4483201 additions and 5922 deletions

View file

@ -0,0 +1,180 @@
package v2
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"net/http"
"net/url"
"sort"
"strings"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/request"
)
var (
errInvalidMethod = errors.New("v2 signer only handles HTTP POST")
)
const (
signatureVersion = "2"
signatureMethod = "HmacSHA256"
timeFormat = "2006-01-02T15:04:05Z"
)
type signer struct {
// Values that must be populated from the request
Request *http.Request
Time time.Time
Credentials *credentials.Credentials
Debug aws.LogLevelType
Logger aws.Logger
Query url.Values
stringToSign string
signature string
}
// SignRequestHandler is a named request handler the SDK will use to sign
// service client request with using the V4 signature.
var SignRequestHandler = request.NamedHandler{
Name: "v2.SignRequestHandler", Fn: SignSDKRequest,
}
// SignSDKRequest requests with signature version 2.
//
// Will sign the requests with the service config's Credentials object
// Signing is skipped if the credentials is the credentials.AnonymousCredentials
// object.
func SignSDKRequest(req *request.Request) {
// If the request does not need to be signed ignore the signing of the
// request if the AnonymousCredentials object is used.
if req.Config.Credentials == credentials.AnonymousCredentials {
return
}
if req.HTTPRequest.Method != "POST" && req.HTTPRequest.Method != "GET" {
// The V2 signer only supports GET and POST
req.Error = errInvalidMethod
return
}
v2 := signer{
Request: req.HTTPRequest,
Time: req.Time,
Credentials: req.Config.Credentials,
Debug: req.Config.LogLevel.Value(),
Logger: req.Config.Logger,
}
req.Error = v2.Sign()
if req.Error != nil {
return
}
if req.HTTPRequest.Method == "POST" {
// Set the body of the request based on the modified query parameters
req.SetStringBody(v2.Query.Encode())
// Now that the body has changed, remove any Content-Length header,
// because it will be incorrect
req.HTTPRequest.ContentLength = 0
req.HTTPRequest.Header.Del("Content-Length")
} else {
req.HTTPRequest.URL.RawQuery = v2.Query.Encode()
}
}
func (v2 *signer) Sign() error {
credValue, err := v2.Credentials.Get()
if err != nil {
return err
}
if v2.Request.Method == "POST" {
// Parse the HTTP request to obtain the query parameters that will
// be used to build the string to sign. Note that because the HTTP
// request will need to be modified, the PostForm and Form properties
// are reset to nil after parsing.
v2.Request.ParseForm()
v2.Query = v2.Request.PostForm
v2.Request.PostForm = nil
v2.Request.Form = nil
} else {
v2.Query = v2.Request.URL.Query()
}
// Set new query parameters
v2.Query.Set("AWSAccessKeyId", credValue.AccessKeyID)
v2.Query.Set("SignatureVersion", signatureVersion)
v2.Query.Set("SignatureMethod", signatureMethod)
v2.Query.Set("Timestamp", v2.Time.UTC().Format(timeFormat))
if credValue.SessionToken != "" {
v2.Query.Set("SecurityToken", credValue.SessionToken)
}
// in case this is a retry, ensure no signature present
v2.Query.Del("Signature")
method := v2.Request.Method
host := v2.Request.URL.Host
path := v2.Request.URL.Path
if path == "" {
path = "/"
}
// obtain all of the query keys and sort them
queryKeys := make([]string, 0, len(v2.Query))
for key := range v2.Query {
queryKeys = append(queryKeys, key)
}
sort.Strings(queryKeys)
// build URL-encoded query keys and values
queryKeysAndValues := make([]string, len(queryKeys))
for i, key := range queryKeys {
k := strings.Replace(url.QueryEscape(key), "+", "%20", -1)
v := strings.Replace(url.QueryEscape(v2.Query.Get(key)), "+", "%20", -1)
queryKeysAndValues[i] = k + "=" + v
}
// join into one query string
query := strings.Join(queryKeysAndValues, "&")
// build the canonical string for the V2 signature
v2.stringToSign = strings.Join([]string{
method,
host,
path,
query,
}, "\n")
hash := hmac.New(sha256.New, []byte(credValue.SecretAccessKey))
hash.Write([]byte(v2.stringToSign))
v2.signature = base64.StdEncoding.EncodeToString(hash.Sum(nil))
v2.Query.Set("Signature", v2.signature)
if v2.Debug.Matches(aws.LogDebugWithSigning) {
v2.logSigningInfo()
}
return nil
}
const logSignInfoMsg = `DEBUG: Request Signature:
---[ STRING TO SIGN ]--------------------------------
%s
---[ SIGNATURE ]-------------------------------------
%s
-----------------------------------------------------`
func (v2 *signer) logSigningInfo() {
msg := fmt.Sprintf(logSignInfoMsg, v2.stringToSign, v2.Query.Get("Signature"))
v2.Logger.Log(msg)
}

View file

@ -0,0 +1,195 @@
package v2
import (
"bytes"
"net/http"
"net/url"
"os"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/request"
"github.com/aws/aws-sdk-go/awstesting"
"github.com/stretchr/testify/assert"
)
type signerBuilder struct {
ServiceName string
Region string
SignTime time.Time
Query url.Values
Method string
SessionToken string
}
func (sb signerBuilder) BuildSigner() signer {
endpoint := "https://" + sb.ServiceName + "." + sb.Region + ".amazonaws.com"
var req *http.Request
if sb.Method == "POST" {
body := []byte(sb.Query.Encode())
reader := bytes.NewReader(body)
req, _ = http.NewRequest(sb.Method, endpoint, reader)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Content-Length", string(len(body)))
} else {
req, _ = http.NewRequest(sb.Method, endpoint, nil)
req.URL.RawQuery = sb.Query.Encode()
}
sig := signer{
Request: req,
Time: sb.SignTime,
Credentials: credentials.NewStaticCredentials(
"AKID",
"SECRET",
sb.SessionToken),
}
if os.Getenv("DEBUG") != "" {
sig.Debug = aws.LogDebug
sig.Logger = aws.NewDefaultLogger()
}
return sig
}
func TestSignRequestWithAndWithoutSession(t *testing.T) {
assert := assert.New(t)
// have to create more than once, so use a function
newQuery := func() url.Values {
query := make(url.Values)
query.Add("Action", "CreateDomain")
query.Add("DomainName", "TestDomain-1437033376")
query.Add("Version", "2009-04-15")
return query
}
// create request without a SecurityToken (session) in the credentials
query := newQuery()
timestamp := time.Date(2015, 7, 16, 7, 56, 16, 0, time.UTC)
builder := signerBuilder{
Method: "POST",
ServiceName: "sdb",
Region: "ap-southeast-2",
SignTime: timestamp,
Query: query,
}
signer := builder.BuildSigner()
err := signer.Sign()
assert.NoError(err)
assert.Equal("tm4dX8Ks7pzFSVHz7qHdoJVXKRLuC4gWz9eti60d8ks=", signer.signature)
assert.Equal(8, len(signer.Query))
assert.Equal("AKID", signer.Query.Get("AWSAccessKeyId"))
assert.Equal("2015-07-16T07:56:16Z", signer.Query.Get("Timestamp"))
assert.Equal("HmacSHA256", signer.Query.Get("SignatureMethod"))
assert.Equal("2", signer.Query.Get("SignatureVersion"))
assert.Equal("tm4dX8Ks7pzFSVHz7qHdoJVXKRLuC4gWz9eti60d8ks=", signer.Query.Get("Signature"))
assert.Equal("CreateDomain", signer.Query.Get("Action"))
assert.Equal("TestDomain-1437033376", signer.Query.Get("DomainName"))
assert.Equal("2009-04-15", signer.Query.Get("Version"))
// should not have a SecurityToken parameter
_, ok := signer.Query["SecurityToken"]
assert.False(ok)
// now sign again, this time with a security token (session)
query = newQuery()
builder.SessionToken = "SESSION"
signer = builder.BuildSigner()
err = signer.Sign()
assert.NoError(err)
assert.Equal("Ch6qv3rzXB1SLqY2vFhsgA1WQ9rnQIE2WJCigOvAJwI=", signer.signature)
assert.Equal(9, len(signer.Query)) // expect one more parameter
assert.Equal("Ch6qv3rzXB1SLqY2vFhsgA1WQ9rnQIE2WJCigOvAJwI=", signer.Query.Get("Signature"))
assert.Equal("SESSION", signer.Query.Get("SecurityToken"))
}
func TestMoreComplexSignRequest(t *testing.T) {
assert := assert.New(t)
query := make(url.Values)
query.Add("Action", "PutAttributes")
query.Add("DomainName", "TestDomain-1437041569")
query.Add("Version", "2009-04-15")
query.Add("Attribute.2.Name", "Attr2")
query.Add("Attribute.2.Value", "Value2")
query.Add("Attribute.2.Replace", "true")
query.Add("Attribute.1.Name", "Attr1-%\\+ %")
query.Add("Attribute.1.Value", " \tValue1 +!@#$%^&*(){}[]\"';:?/.>,<\x12\x00")
query.Add("Attribute.1.Replace", "true")
query.Add("ItemName", "Item 1")
timestamp := time.Date(2015, 7, 16, 10, 12, 51, 0, time.UTC)
builder := signerBuilder{
Method: "POST",
ServiceName: "sdb",
Region: "ap-southeast-2",
SignTime: timestamp,
Query: query,
SessionToken: "SESSION",
}
signer := builder.BuildSigner()
err := signer.Sign()
assert.NoError(err)
assert.Equal("WNdE62UJKLKoA6XncVY/9RDbrKmcVMdQPQOTAs8SgwQ=", signer.signature)
}
func TestGet(t *testing.T) {
assert := assert.New(t)
svc := awstesting.NewClient(&aws.Config{
Credentials: credentials.NewStaticCredentials("AKID", "SECRET", "SESSION"),
Region: aws.String("ap-southeast-2"),
})
r := svc.NewRequest(
&request.Operation{
Name: "OpName",
HTTPMethod: "GET",
HTTPPath: "/",
},
nil,
nil,
)
r.Build()
assert.Equal("GET", r.HTTPRequest.Method)
assert.Equal("", r.HTTPRequest.URL.Query().Get("Signature"))
SignSDKRequest(r)
assert.NoError(r.Error)
t.Logf("Signature: %s", r.HTTPRequest.URL.Query().Get("Signature"))
assert.NotEqual("", r.HTTPRequest.URL.Query().Get("Signature"))
}
func TestAnonymousCredentials(t *testing.T) {
assert := assert.New(t)
svc := awstesting.NewClient(&aws.Config{
Credentials: credentials.AnonymousCredentials,
Region: aws.String("ap-southeast-2"),
})
r := svc.NewRequest(
&request.Operation{
Name: "PutAttributes",
HTTPMethod: "POST",
HTTPPath: "/",
},
nil,
nil,
)
r.Build()
SignSDKRequest(r)
req := r.HTTPRequest
req.ParseForm()
assert.Empty(req.PostForm.Get("Signature"))
}