From d11b8164fa3ce59823633cdb6924597d03eaa724 Mon Sep 17 00:00:00 2001 From: Mark Fine Date: Sat, 13 Jul 2013 21:34:20 -0400 Subject: [PATCH 1/3] Signed URL path. --- sign.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/sign.go b/sign.go index 2ddff2c..862e5b6 100644 --- a/sign.go +++ b/sign.go @@ -12,6 +12,7 @@ import ( "net/http" "sort" "strings" + "time" ) var signParams = map[string]bool{ @@ -71,6 +72,10 @@ func Sign(r *http.Request, k Keys) { DefaultService.Sign(r, k) } +func SignURL(r *http.Request, t time.Time, k Keys) { + DefaultService.SignURL(r, t, k) +} + // Service represents an S3-compatible service. type Service struct { Domain string // service root domain, used to extract subdomain from an http.Request and pass it to Bucket @@ -89,6 +94,17 @@ func (s *Service) Sign(r *http.Request, k Keys) { r.Header.Set("Authorization", "AWS "+k.AccessKey+":"+string(sig)) } +func (s *Service) SignURL(r *http.Request, t time.Time, k Keys) { + if k.SecurityToken != "" { + r.Header.Set("X-Amz-Security-Token", k.SecurityToken) + } + h := hmac.New(sha1.New, []byte(k.SecretKey)) + s.writeSigURLData(h, r, t) + sig := make([]byte, base64.StdEncoding.EncodedLen(h.Size())) + base64.StdEncoding.Encode(sig, h.Sum(nil)) + r.URL.RawQuery = "AWSAccessKeyId="+k.AccessKey+"&Signature="+string(sig)+"&Expires="+string(t.Unix()) +} + func (s *Service) writeSigData(w io.Writer, r *http.Request) { w.Write([]byte(r.Method)) w.Write([]byte{'\n'}) @@ -104,6 +120,19 @@ func (s *Service) writeSigData(w io.Writer, r *http.Request) { s.writeResource(w, r) } +func (s *Service) writeSigURLData(w io.Writer, r *http.Request, t time.Time) { + w.Write([]byte(r.Method)) + w.Write([]byte{'\n'}) + w.Write([]byte(r.Header.Get("content-md5"))) + w.Write([]byte{'\n'}) + w.Write([]byte(r.Header.Get("content-type"))) + w.Write([]byte{'\n'}) + w.Write([]byte(string(t.Unix()))) + w.Write([]byte{'\n'}) + writeAmzHeaders(w, r) + s.writeResource(w, r) +} + func (s *Service) writeResource(w io.Writer, r *http.Request) { s.writeVhostBucket(w, strings.ToLower(r.Host)) path := r.URL.RequestURI() From c4377ed87481eb54d04bdb72f7f7c78978c23c37 Mon Sep 17 00:00:00 2001 From: Mark Fine Date: Sat, 13 Jul 2013 21:42:47 -0400 Subject: [PATCH 2/3] move over to SignQuery. --- sign.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sign.go b/sign.go index 862e5b6..e922c23 100644 --- a/sign.go +++ b/sign.go @@ -72,8 +72,8 @@ func Sign(r *http.Request, k Keys) { DefaultService.Sign(r, k) } -func SignURL(r *http.Request, t time.Time, k Keys) { - DefaultService.SignURL(r, t, k) +func SignQuery(r *http.Request, t time.Time, k Keys) { + DefaultService.SignQuery(r, t, k) } // Service represents an S3-compatible service. @@ -94,12 +94,12 @@ func (s *Service) Sign(r *http.Request, k Keys) { r.Header.Set("Authorization", "AWS "+k.AccessKey+":"+string(sig)) } -func (s *Service) SignURL(r *http.Request, t time.Time, k Keys) { +func (s *Service) SignQuery(r *http.Request, t time.Time, k Keys) { if k.SecurityToken != "" { r.Header.Set("X-Amz-Security-Token", k.SecurityToken) } h := hmac.New(sha1.New, []byte(k.SecretKey)) - s.writeSigURLData(h, r, t) + s.writeSigQueryData(h, r, t) sig := make([]byte, base64.StdEncoding.EncodedLen(h.Size())) base64.StdEncoding.Encode(sig, h.Sum(nil)) r.URL.RawQuery = "AWSAccessKeyId="+k.AccessKey+"&Signature="+string(sig)+"&Expires="+string(t.Unix()) @@ -120,7 +120,7 @@ func (s *Service) writeSigData(w io.Writer, r *http.Request) { s.writeResource(w, r) } -func (s *Service) writeSigURLData(w io.Writer, r *http.Request, t time.Time) { +func (s *Service) writeSigQueryData(w io.Writer, r *http.Request, t time.Time) { w.Write([]byte(r.Method)) w.Write([]byte{'\n'}) w.Write([]byte(r.Header.Get("content-md5"))) From 12635f798ef1e9b8e0ed60384195b0473702c7bf Mon Sep 17 00:00:00 2001 From: matope Date: Thu, 4 Dec 2014 22:18:22 +0900 Subject: [PATCH 3/3] Add SignQuery and TestQuerySign --- sign.go | 10 ++++++-- sign_test.go | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/sign.go b/sign.go index e922c23..bc303ff 100644 --- a/sign.go +++ b/sign.go @@ -10,7 +10,9 @@ import ( "encoding/base64" "io" "net/http" + "net/url" "sort" + "strconv" "strings" "time" ) @@ -102,7 +104,11 @@ func (s *Service) SignQuery(r *http.Request, t time.Time, k Keys) { s.writeSigQueryData(h, r, t) sig := make([]byte, base64.StdEncoding.EncodedLen(h.Size())) base64.StdEncoding.Encode(sig, h.Sum(nil)) - r.URL.RawQuery = "AWSAccessKeyId="+k.AccessKey+"&Signature="+string(sig)+"&Expires="+string(t.Unix()) + if r.URL.RawQuery == "" { + r.URL.RawQuery = "AWSAccessKeyId=" + k.AccessKey + "&Signature=" + url.QueryEscape(string(sig)) + "&Expires=" + strconv.FormatInt(t.Unix(), 10) + } else { + r.URL.RawQuery += "&AWSAccessKeyId=" + k.AccessKey + "&Signature=" + url.QueryEscape(string(sig)) + "&Expires=" + strconv.FormatInt(t.Unix(), 10) + } } func (s *Service) writeSigData(w io.Writer, r *http.Request) { @@ -127,7 +133,7 @@ func (s *Service) writeSigQueryData(w io.Writer, r *http.Request, t time.Time) { w.Write([]byte{'\n'}) w.Write([]byte(r.Header.Get("content-type"))) w.Write([]byte{'\n'}) - w.Write([]byte(string(t.Unix()))) + w.Write([]byte(strconv.FormatInt(t.Unix(), 10))) w.Write([]byte{'\n'}) writeAmzHeaders(w, r) s.writeResource(w, r) diff --git a/sign_test.go b/sign_test.go index 9187c81..f8f1b17 100644 --- a/sign_test.go +++ b/sign_test.go @@ -4,6 +4,7 @@ import ( "bytes" "net/http" "testing" + "time" ) var exKeys = Keys{ @@ -297,3 +298,70 @@ func TestVhostBucket(t *testing.T) { } } } + +var signQueryTest = []struct { + service *Service + method string + url string + more http.Header + expires int64 + expBuf string + expQuery string +}{ + { + DefaultService, + "GET", + "http://johnsmith.s3.amazonaws.com/photos/puppy.jpg", + nil, + 1175139620, + "GET\n\n\n1175139620\n/johnsmith/photos/puppy.jpg", + "AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Signature=NpgCjnDzrM%2BWFzoENXmpNDUsSn8%3D&Expires=1175139620", + }, { + DefaultService, + "GET", + "http://s3.amazonaws.com/johnsmith/photos/puppy.jpg", + nil, + 1175139620, + "GET\n\n\n1175139620\n/johnsmith/photos/puppy.jpg", + "AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Signature=NpgCjnDzrM%2BWFzoENXmpNDUsSn8%3D&Expires=1175139620", + }, { + DefaultService, + "GET", + "http://johnsmith.s3.amazonaws.com/photos/puppy.jpg?acl", + nil, + 1175139620, + "GET\n\n\n1175139620\n/johnsmith/photos/puppy.jpg?acl", + "acl&AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&Signature=2t9CVTYWEqyKpbsimoCqHNLfxsA%3D&Expires=1175139620", + }, +} + +func TestQuerySign(t *testing.T) { + for _, ts := range signQueryTest { + r, err := http.NewRequest(ts.method, ts.url, nil) + if err != nil { + panic(err) + } + + for k, vs := range ts.more { + for _, v := range vs { + r.Header.Add(k, v) + } + } + var buf bytes.Buffer + ts.service.writeSigQueryData(&buf, r, time.Unix(ts.expires, 0)) + if buf.String() != ts.expBuf { + t.Errorf("in %s:", r.Method) + t.Logf("url %s", r.URL.String()) + t.Logf("exp %q", ts.expBuf) + t.Logf("got %q", buf.String()) + } + + ts.service.SignQuery(r, time.Unix(ts.expires, 0), exKeys) + if got := r.URL.RawQuery; got != ts.expQuery { + t.Errorf("in %s:", r.Method) + t.Logf("url %s", r.URL.String()) + t.Logf("exp %q", ts.expQuery) + t.Logf("got %q", got) + } + } +}