217 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
			
		
		
	
	
			217 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			Go
		
	
	
package s3
 | 
						|
 | 
						|
// Source: https://github.com/pivotal-golang/s3cli
 | 
						|
 | 
						|
// Copyright (c) 2013 Damien Le Berrigaud and Nick Wade
 | 
						|
 | 
						|
// Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
						|
// of this software and associated documentation files (the "Software"), to deal
 | 
						|
// in the Software without restriction, including without limitation the rights
 | 
						|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
						|
// copies of the Software, and to permit persons to whom the Software is
 | 
						|
// furnished to do so, subject to the following conditions:
 | 
						|
 | 
						|
// The above copyright notice and this permission notice shall be included in
 | 
						|
// all copies or substantial portions of the Software.
 | 
						|
 | 
						|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
						|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
						|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
						|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
						|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
						|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
						|
// THE SOFTWARE.
 | 
						|
 | 
						|
import (
 | 
						|
	"crypto/hmac"
 | 
						|
	"crypto/sha1"
 | 
						|
	"encoding/base64"
 | 
						|
	"net/http"
 | 
						|
	"net/url"
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/aws/aws-sdk-go/aws/corehandlers"
 | 
						|
	"github.com/aws/aws-sdk-go/aws/credentials"
 | 
						|
	"github.com/aws/aws-sdk-go/aws/request"
 | 
						|
	"github.com/aws/aws-sdk-go/service/s3"
 | 
						|
	log "github.com/sirupsen/logrus"
 | 
						|
)
 | 
						|
 | 
						|
type signer struct {
 | 
						|
	// Values that must be populated from the request
 | 
						|
	Request      *http.Request
 | 
						|
	Time         time.Time
 | 
						|
	Credentials  *credentials.Credentials
 | 
						|
	Query        url.Values
 | 
						|
	stringToSign string
 | 
						|
	signature    string
 | 
						|
}
 | 
						|
 | 
						|
var s3ParamsToSign = map[string]bool{
 | 
						|
	"acl":                          true,
 | 
						|
	"location":                     true,
 | 
						|
	"logging":                      true,
 | 
						|
	"notification":                 true,
 | 
						|
	"partNumber":                   true,
 | 
						|
	"policy":                       true,
 | 
						|
	"requestPayment":               true,
 | 
						|
	"torrent":                      true,
 | 
						|
	"uploadId":                     true,
 | 
						|
	"uploads":                      true,
 | 
						|
	"versionId":                    true,
 | 
						|
	"versioning":                   true,
 | 
						|
	"versions":                     true,
 | 
						|
	"response-content-type":        true,
 | 
						|
	"response-content-language":    true,
 | 
						|
	"response-expires":             true,
 | 
						|
	"response-cache-control":       true,
 | 
						|
	"response-content-disposition": true,
 | 
						|
	"response-content-encoding":    true,
 | 
						|
	"website":                      true,
 | 
						|
	"delete":                       true,
 | 
						|
}
 | 
						|
 | 
						|
// setv2Handlers will setup v2 signature signing on the S3 driver
 | 
						|
func setv2Handlers(svc *s3.S3) {
 | 
						|
	svc.Handlers.Build.PushBack(func(r *request.Request) {
 | 
						|
		parsedURL, err := url.Parse(r.HTTPRequest.URL.String())
 | 
						|
		if err != nil {
 | 
						|
			log.Fatalf("Failed to parse URL: %v", err)
 | 
						|
		}
 | 
						|
		r.HTTPRequest.URL.Opaque = parsedURL.Path
 | 
						|
	})
 | 
						|
 | 
						|
	svc.Handlers.Sign.Clear()
 | 
						|
	svc.Handlers.Sign.PushBack(Sign)
 | 
						|
	svc.Handlers.Sign.PushBackNamed(corehandlers.BuildContentLengthHandler)
 | 
						|
}
 | 
						|
 | 
						|
// Sign 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 Sign(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
 | 
						|
	}
 | 
						|
 | 
						|
	v2 := signer{
 | 
						|
		Request:     req.HTTPRequest,
 | 
						|
		Time:        req.Time,
 | 
						|
		Credentials: req.Config.Credentials,
 | 
						|
	}
 | 
						|
	v2.Sign()
 | 
						|
}
 | 
						|
 | 
						|
func (v2 *signer) Sign() error {
 | 
						|
	credValue, err := v2.Credentials.Get()
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	accessKey := credValue.AccessKeyID
 | 
						|
	var (
 | 
						|
		md5, ctype, date, xamz string
 | 
						|
		xamzDate               bool
 | 
						|
		sarray                 []string
 | 
						|
		smap                   map[string]string
 | 
						|
		sharray                []string
 | 
						|
	)
 | 
						|
 | 
						|
	headers := v2.Request.Header
 | 
						|
	params := v2.Request.URL.Query()
 | 
						|
	parsedURL, err := url.Parse(v2.Request.URL.String())
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
	host, canonicalPath := parsedURL.Host, parsedURL.Path
 | 
						|
	v2.Request.Header["Host"] = []string{host}
 | 
						|
	v2.Request.Header["date"] = []string{v2.Time.In(time.UTC).Format(time.RFC1123)}
 | 
						|
	if credValue.SessionToken != "" {
 | 
						|
		v2.Request.Header["x-amz-security-token"] = []string{credValue.SessionToken}
 | 
						|
	}
 | 
						|
 | 
						|
	smap = make(map[string]string)
 | 
						|
	for k, v := range headers {
 | 
						|
		k = strings.ToLower(k)
 | 
						|
		switch k {
 | 
						|
		case "content-md5":
 | 
						|
			md5 = v[0]
 | 
						|
		case "content-type":
 | 
						|
			ctype = v[0]
 | 
						|
		case "date":
 | 
						|
			if !xamzDate {
 | 
						|
				date = v[0]
 | 
						|
			}
 | 
						|
		default:
 | 
						|
			if strings.HasPrefix(k, "x-amz-") {
 | 
						|
				vall := strings.Join(v, ",")
 | 
						|
				smap[k] = k + ":" + vall
 | 
						|
				if k == "x-amz-date" {
 | 
						|
					xamzDate = true
 | 
						|
					date = ""
 | 
						|
				}
 | 
						|
				sharray = append(sharray, k)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if len(sharray) > 0 {
 | 
						|
		sort.StringSlice(sharray).Sort()
 | 
						|
		for _, h := range sharray {
 | 
						|
			sarray = append(sarray, smap[h])
 | 
						|
		}
 | 
						|
		xamz = strings.Join(sarray, "\n") + "\n"
 | 
						|
	}
 | 
						|
 | 
						|
	expires := false
 | 
						|
	if v, ok := params["Expires"]; ok {
 | 
						|
		expires = true
 | 
						|
		date = v[0]
 | 
						|
		params["AWSAccessKeyId"] = []string{accessKey}
 | 
						|
	}
 | 
						|
 | 
						|
	sarray = sarray[0:0]
 | 
						|
	for k, v := range params {
 | 
						|
		if s3ParamsToSign[k] {
 | 
						|
			for _, vi := range v {
 | 
						|
				if vi == "" {
 | 
						|
					sarray = append(sarray, k)
 | 
						|
				} else {
 | 
						|
					sarray = append(sarray, k+"="+vi)
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
	if len(sarray) > 0 {
 | 
						|
		sort.StringSlice(sarray).Sort()
 | 
						|
		canonicalPath = canonicalPath + "?" + strings.Join(sarray, "&")
 | 
						|
	}
 | 
						|
 | 
						|
	v2.stringToSign = strings.Join([]string{
 | 
						|
		v2.Request.Method,
 | 
						|
		md5,
 | 
						|
		ctype,
 | 
						|
		date,
 | 
						|
		xamz + canonicalPath,
 | 
						|
	}, "\n")
 | 
						|
	hash := hmac.New(sha1.New, []byte(credValue.SecretAccessKey))
 | 
						|
	hash.Write([]byte(v2.stringToSign))
 | 
						|
	v2.signature = base64.StdEncoding.EncodeToString(hash.Sum(nil))
 | 
						|
 | 
						|
	if expires {
 | 
						|
		params["Signature"] = []string{v2.signature}
 | 
						|
	} else {
 | 
						|
		headers["Authorization"] = []string{"AWS " + accessKey + ":" + v2.signature}
 | 
						|
	}
 | 
						|
 | 
						|
	log.WithFields(log.Fields{
 | 
						|
		"string-to-sign": v2.stringToSign,
 | 
						|
		"signature":      v2.signature,
 | 
						|
	}).Debugln("request signature")
 | 
						|
	return nil
 | 
						|
}
 |