mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
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:
parent
ca5ca8332b
commit
1e53316241
@ -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
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user