Support sink signature in a path
For certain cases where we can not afford to have query params like OAuth, sink signature can be placed at the end of the sink path
This commit is contained in:
parent
ef47a0d612
commit
af428e99f9
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -29,10 +30,10 @@ type (
|
||||
// Expect sink request to be of this method
|
||||
Method string `json:"mtd,omitempty"`
|
||||
|
||||
// OpUsed as an identifier, no validation of request params
|
||||
// Origin is as an identifier, no validation of request params
|
||||
Origin string `json:"origin,omitempty"`
|
||||
|
||||
// If set
|
||||
// Optional, signature expiration
|
||||
Expires *time.Time `json:"exp,omitempty"`
|
||||
|
||||
// When set it enables body processing (but limits it to that size!)
|
||||
@ -40,6 +41,10 @@ type (
|
||||
|
||||
// Acceptable content type
|
||||
ContentType string `json:"ct,omitempty"`
|
||||
|
||||
// Should we put signature in the path (true)
|
||||
// or in query string (false, default)
|
||||
SignatureInPath bool `json:"-"`
|
||||
}
|
||||
|
||||
sinkEventDispatcher interface {
|
||||
@ -71,9 +76,13 @@ func (svc sink) SignURL(surp SinkRequestUrlParams) (signedURL *url.URL, err erro
|
||||
var (
|
||||
params []byte
|
||||
sap = &sinkActionProps{sinkParams: &surp}
|
||||
|
||||
path = svc.GetPath()
|
||||
qs = url.Values{}
|
||||
)
|
||||
|
||||
err = func() error {
|
||||
|
||||
params, err = json.Marshal(surp)
|
||||
if err != nil {
|
||||
return SinkErrFailedToSign(sap).Wrap(err)
|
||||
@ -81,11 +90,17 @@ func (svc sink) SignURL(surp SinkRequestUrlParams) (signedURL *url.URL, err erro
|
||||
|
||||
surp.Method = strings.ToUpper(surp.Method)
|
||||
|
||||
v := url.Values{}
|
||||
signature := svc.signer.Sign(0, params) + SinkSignUrlParamDelimiter + base64.StdEncoding.EncodeToString(params)
|
||||
|
||||
v.Set(SinkSignUrlParamName, svc.signer.Sign(0, params)+SinkSignUrlParamDelimiter+base64.StdEncoding.EncodeToString(params))
|
||||
if surp.SignatureInPath {
|
||||
// Optional, use path for sink signature
|
||||
path = fmt.Sprintf("%s/%s=%s", path, SinkSignUrlParamName, signature)
|
||||
} else {
|
||||
// By default put signature in query string
|
||||
qs.Set(SinkSignUrlParamName, signature)
|
||||
}
|
||||
|
||||
signedURL = &url.URL{RawQuery: v.Encode(), Path: svc.GetPath()}
|
||||
signedURL = &url.URL{RawQuery: qs.Encode(), Path: path}
|
||||
return nil
|
||||
}()
|
||||
|
||||
@ -142,11 +157,26 @@ func (svc *sink) ProcessRequest(w http.ResponseWriter, r *http.Request) {
|
||||
// Verifies and extracts sink request params
|
||||
func (svc sink) handleRequest(r *http.Request) (*SinkRequestUrlParams, error) {
|
||||
var (
|
||||
srup *SinkRequestUrlParams
|
||||
srup = &SinkRequestUrlParams{}
|
||||
sap = &sinkActionProps{}
|
||||
qs = r.URL.Query()
|
||||
|
||||
param string
|
||||
)
|
||||
|
||||
param := r.URL.Query().Get(SinkSignUrlParamName)
|
||||
// try to find a signature
|
||||
if _, has := qs[SinkSignUrlParamName]; has {
|
||||
// first, in a query string
|
||||
param = r.URL.Query().Get(SinkSignUrlParamName)
|
||||
} else if i := strings.Index(r.URL.Path, SinkSignUrlParamName); i > -1 {
|
||||
// fallback to path, expecting signature to be at the end
|
||||
// offset string index by start of signature param name, length of param name, and = char
|
||||
param = r.URL.Path[i+len(SinkSignUrlParamName)+1:]
|
||||
|
||||
// this is more for consistency and cleaner tests
|
||||
srup.SignatureInPath = true
|
||||
}
|
||||
|
||||
if len(param) == 0 {
|
||||
return nil, SinkErrMissingSignature(sap)
|
||||
}
|
||||
@ -165,7 +195,6 @@ func (svc sink) handleRequest(r *http.Request) (*SinkRequestUrlParams, error) {
|
||||
return nil, SinkErrInvalidSignature(sap)
|
||||
}
|
||||
|
||||
srup = &SinkRequestUrlParams{}
|
||||
if err = json.Unmarshal(params, srup); err != nil {
|
||||
// Impossible scenario :)
|
||||
// How can we have verified signature of an invalid JSON ?!
|
||||
|
||||
@ -34,6 +34,18 @@ func Test_sink_SignURL(t *testing.T) {
|
||||
},
|
||||
wantSignedURL: "/sink?__sign=d8a8c5591acb0f5f6695ab6aa4a205a7066b3bf4_eyJtdGQiOiJQT1NUIiwib3JpZ2luIjoidGVzdCIsIm1icyI6MTAyNCwiY3QiOiJwbGFpbi90ZXh0In0%3D",
|
||||
},
|
||||
{
|
||||
name: "basic",
|
||||
surp: SinkRequestUrlParams{
|
||||
Method: "POST",
|
||||
Origin: "test",
|
||||
Expires: nil,
|
||||
MaxBodySize: 1024,
|
||||
ContentType: "plain/text",
|
||||
SignatureInPath: true,
|
||||
},
|
||||
wantSignedURL: "/sink/__sign=d8a8c5591acb0f5f6695ab6aa4a205a7066b3bf4_eyJtdGQiOiJQT1NUIiwib3JpZ2luIjoidGVzdCIsIm1icyI6MTAyNCwiY3QiOiJwbGFpbi90ZXh0In0=",
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
@ -45,11 +57,15 @@ func Test_sink_SignURL(t *testing.T) {
|
||||
|
||||
gotSignedURL, err := svc.SignURL(tt.surp)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("SignURL() error = %v, wantErr %v", err, tt.wantErr)
|
||||
t.Errorf("SignURL() \n"+
|
||||
" error: %v, \n"+
|
||||
"wantErr: %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if gotSignedURL.String() != tt.wantSignedURL {
|
||||
t.Errorf("SignURL() gotSignedURL = %v, want %v", gotSignedURL, tt.wantSignedURL)
|
||||
t.Errorf("SignURL() \n"+
|
||||
"gotSignedURL: %v\n"+
|
||||
" want: %v", gotSignedURL, tt.wantSignedURL)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -74,6 +90,9 @@ func Test_sink_handleRequest(t *testing.T) {
|
||||
|
||||
signParamsExp = SinkRequestUrlParams{Expires: &time.Time{}}
|
||||
signedUrlExp, _ = svc.SignURL(signParamsExp)
|
||||
|
||||
signParamsPath = SinkRequestUrlParams{SignatureInPath: true}
|
||||
signedUrlPath, _ = svc.SignURL(signParamsPath)
|
||||
)
|
||||
|
||||
var (
|
||||
@ -148,6 +167,12 @@ func Test_sink_handleRequest(t *testing.T) {
|
||||
withURL: signedUrl.String(),
|
||||
wantErr: SinkErrContentLengthExceedsMaxAllowedSize(),
|
||||
},
|
||||
{
|
||||
name: "signature in a path",
|
||||
withMethod: "POST",
|
||||
withURL: signedUrlPath.String(),
|
||||
wantParams: &signParamsPath,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user