Add tests for presigned-get (#2767)

* web-handlers: support for presigned-get json-rpc call for MinioBrowser's "share" feature.

* Add tests for presigned-get
This commit is contained in:
Krishna Srinivas 2016-09-23 01:24:49 -07:00 committed by Harshavardhana
parent ca5ca8332b
commit 1e53316241
2 changed files with 151 additions and 0 deletions

View File

@ -27,6 +27,7 @@ import (
"path" "path"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"time" "time"
jwtgo "github.com/dgrijalva/jwt-go" jwtgo "github.com/dgrijalva/jwt-go"
@ -669,3 +670,67 @@ func (web *webAPIHandlers) SetBucketPolicy(r *http.Request, args *SetBucketPolic
reply.UIVersion = miniobrowser.UIVersion reply.UIVersion = miniobrowser.UIVersion
return nil return nil
} }
// PresignedGetArgs - presigned-get API args.
type PresignedGetArgs struct {
// Host header required for signed headers.
HostName string `json:"host"`
// Bucket name of the object to be presigned.
BucketName string `json:"bucket"`
// Object name to be presigned.
ObjectName string `json:"object"`
}
// PresignedGetRep - presigned-get URL reply.
type PresignedGetRep struct {
// Presigned URL of the object.
URL string `json:"url"`
}
// PresignedGET - returns presigned-Get url.
func (web *webAPIHandlers) PresignedGet(r *http.Request, args *PresignedGetArgs, reply *PresignedGetRep) error {
if !isJWTReqAuthenticated(r) {
return &json2.Error{Message: "Unauthorized request"}
}
if args.BucketName == "" || args.ObjectName == "" {
return &json2.Error{Message: "Required arguments: Host, Bucket, Object"}
}
reply.URL = presignedGet(args.HostName, args.BucketName, args.ObjectName)
return nil
}
// Returns presigned url for GET method.
func presignedGet(host, bucket, object string) string {
cred := serverConfig.GetCredential()
region := serverConfig.GetRegion()
accessKey := cred.AccessKeyID
secretKey := cred.SecretAccessKey
date := time.Now().UTC()
dateStr := date.Format("20060102T150405Z")
credential := fmt.Sprintf("%s/%s", accessKey, getScope(date, region))
query := strings.Join([]string{
"X-Amz-Algorithm=" + signV4Algorithm,
"X-Amz-Credential=" + strings.Replace(credential, "/", "%2F", -1),
"X-Amz-Date=" + dateStr,
"X-Amz-Expires=" + "604800", // Default set to be expire in 7days.
"X-Amz-SignedHeaders=host",
}, "&")
path := "/" + path.Join(bucket, object)
// Headers are empty, since "host" is the only header required to be signed for Presigned URLs.
var extractedSignedHeaders http.Header
canonicalRequest := getCanonicalRequest(extractedSignedHeaders, unsignedPayload, query, path, "GET", host)
stringToSign := getStringToSign(canonicalRequest, date, region)
signingKey := getSigningKey(secretKey, date, region)
signature := getSignature(signingKey, stringToSign)
// Construct the final presigned URL.
return host + path + "?" + query + "&" + "X-Amz-Signature=" + signature
}

View File

@ -717,6 +717,92 @@ func testDownloadWebHandler(obj ObjectLayer, instanceType string, t TestErrHandl
} }
} }
// Wrapper for calling PresignedGet handler
func TestWebHandlerPresignedGetHandler(t *testing.T) {
ExecObjectLayerTest(t, testWebPresignedGetHandler)
}
func testWebPresignedGetHandler(obj ObjectLayer, instanceType string, t TestErrHandler) {
// Register the API end points with XL/FS object layer.
apiRouter := initTestWebRPCEndPoint(obj)
// initialize the server and obtain the credentials and root.
// credentials are necessary to sign the HTTP request.
rootPath, err := newTestConfig("us-east-1")
if err != nil {
t.Fatalf("Init Test config failed")
}
// remove the root folder after the test ends.
defer removeAll(rootPath)
credentials := serverConfig.GetCredential()
authorization, err := getWebRPCToken(apiRouter, credentials.AccessKeyID, credentials.SecretAccessKey)
if err != nil {
t.Fatal("Cannot authenticate")
}
rec := httptest.NewRecorder()
bucketName := getRandomBucketName()
objectName := "object"
objectSize := 1024
// Create bucket.
err = obj.MakeBucket(bucketName)
if err != nil {
// failed to create newbucket, abort.
t.Fatalf("%s : %s", instanceType, err)
}
data := bytes.Repeat([]byte("a"), objectSize)
_, err = obj.PutObject(bucketName, objectName, int64(len(data)), bytes.NewReader(data), map[string]string{"md5Sum": "c9a34cfc85d982698c6ac89f76071abd"})
if err != nil {
t.Fatalf("Was not able to upload an object, %v", err)
}
presignGetReq := PresignedGetArgs{
HostName: "",
BucketName: bucketName,
ObjectName: objectName,
}
presignGetRep := &PresignedGetRep{}
req, err := newTestWebRPCRequest("Web.PresignedGet", authorization, presignGetReq)
if err != nil {
t.Fatalf("Failed to create HTTP request: <ERROR> %v", err)
}
apiRouter.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("Expected the response status to be 200, but instead found `%d`", rec.Code)
}
err = getTestWebRPCResponse(rec, &presignGetRep)
if err != nil {
t.Fatalf("Failed, %v", err)
}
// Register the API end points with XL/FS object layer.
apiRouter = initTestAPIEndPoints(obj, []string{"GetObject"})
// Initialize a new api recorder.
arec := httptest.NewRecorder()
req, err = newTestRequest("GET", presignGetRep.URL, 0, nil)
req.Header.Del("x-amz-content-sha256")
if err != nil {
t.Fatal("Failed to initialized a new request", err)
}
apiRouter.ServeHTTP(arec, req)
if arec.Code != http.StatusOK {
t.Fatalf("Expected the response status to be 200, but instead found `%d`", arec.Code)
}
savedData, err := ioutil.ReadAll(arec.Body)
if err != nil {
t.Fatal("Reading body failed", err)
}
if !bytes.Equal(data, savedData) {
t.Fatal("Read data is not equal was what was expected")
}
}
// Wrapper for calling GetBucketPolicy Handler // Wrapper for calling GetBucketPolicy Handler
func TestWebHandlerGetBucketPolicyHandler(t *testing.T) { func TestWebHandlerGetBucketPolicyHandler(t *testing.T) {
ExecObjectLayerTest(t, testWebGetBucketPolicyHandler) ExecObjectLayerTest(t, testWebGetBucketPolicyHandler)