mirror of
https://github.com/minio/minio.git
synced 2024-12-24 22:25:54 -05:00
signature: Add legacy signature v2 support transparently. (#2811)
Add new tests as well.
This commit is contained in:
parent
9fb1c89f81
commit
5885ffc8ae
@ -42,12 +42,24 @@ func isRequestSignatureV4(r *http.Request) bool {
|
|||||||
return strings.HasPrefix(r.Header.Get("Authorization"), signV4Algorithm)
|
return strings.HasPrefix(r.Header.Get("Authorization"), signV4Algorithm)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify if request has AWS Signature Version '2'.
|
||||||
|
func isRequestSignatureV2(r *http.Request) bool {
|
||||||
|
return (!strings.HasPrefix(r.Header.Get("Authorization"), signV4Algorithm) &&
|
||||||
|
strings.HasPrefix(r.Header.Get("Authorization"), signV2Algorithm))
|
||||||
|
}
|
||||||
|
|
||||||
// Verify if request has AWS PreSign Version '4'.
|
// Verify if request has AWS PreSign Version '4'.
|
||||||
func isRequestPresignedSignatureV4(r *http.Request) bool {
|
func isRequestPresignedSignatureV4(r *http.Request) bool {
|
||||||
_, ok := r.URL.Query()["X-Amz-Credential"]
|
_, ok := r.URL.Query()["X-Amz-Credential"]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify request has AWS PreSign Version '2'.
|
||||||
|
func isRequestPresignedSignatureV2(r *http.Request) bool {
|
||||||
|
_, ok := r.URL.Query()["AWSAccessKeyId"]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
// Verify if request has AWS Post policy Signature Version '4'.
|
// Verify if request has AWS Post policy Signature Version '4'.
|
||||||
func isRequestPostPolicySignatureV4(r *http.Request) bool {
|
func isRequestPostPolicySignatureV4(r *http.Request) bool {
|
||||||
return strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") && r.Method == "POST"
|
return strings.Contains(r.Header.Get("Content-Type"), "multipart/form-data") && r.Method == "POST"
|
||||||
@ -66,15 +78,21 @@ const (
|
|||||||
authTypeUnknown authType = iota
|
authTypeUnknown authType = iota
|
||||||
authTypeAnonymous
|
authTypeAnonymous
|
||||||
authTypePresigned
|
authTypePresigned
|
||||||
|
authTypePresignedV2
|
||||||
authTypePostPolicy
|
authTypePostPolicy
|
||||||
authTypeStreamingSigned
|
authTypeStreamingSigned
|
||||||
authTypeSigned
|
authTypeSigned
|
||||||
|
authTypeSignedV2
|
||||||
authTypeJWT
|
authTypeJWT
|
||||||
)
|
)
|
||||||
|
|
||||||
// Get request authentication type.
|
// Get request authentication type.
|
||||||
func getRequestAuthType(r *http.Request) authType {
|
func getRequestAuthType(r *http.Request) authType {
|
||||||
if isRequestSignStreamingV4(r) {
|
if isRequestSignatureV2(r) {
|
||||||
|
return authTypeSignedV2
|
||||||
|
} else if isRequestPresignedSignatureV2(r) {
|
||||||
|
return authTypePresignedV2
|
||||||
|
} else if isRequestSignStreamingV4(r) {
|
||||||
return authTypeStreamingSigned
|
return authTypeStreamingSigned
|
||||||
} else if isRequestSignatureV4(r) {
|
} else if isRequestSignatureV4(r) {
|
||||||
return authTypeSigned
|
return authTypeSigned
|
||||||
@ -104,6 +122,14 @@ func sumMD5(data []byte) []byte {
|
|||||||
return hash.Sum(nil)
|
return hash.Sum(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify if request has valid AWS Signature Version '2'.
|
||||||
|
func isReqAuthenticatedV2(r *http.Request) (s3Error APIErrorCode) {
|
||||||
|
if isRequestSignatureV2(r) {
|
||||||
|
return doesSignV2Match(r)
|
||||||
|
}
|
||||||
|
return doesPresignV2SignatureMatch(r)
|
||||||
|
}
|
||||||
|
|
||||||
// Verify if request has valid AWS Signature Version '4'.
|
// Verify if request has valid AWS Signature Version '4'.
|
||||||
func isReqAuthenticated(r *http.Request, region string) (s3Error APIErrorCode) {
|
func isReqAuthenticated(r *http.Request, region string) (s3Error APIErrorCode) {
|
||||||
if r == nil {
|
if r == nil {
|
||||||
@ -111,6 +137,7 @@ func isReqAuthenticated(r *http.Request, region string) (s3Error APIErrorCode) {
|
|||||||
}
|
}
|
||||||
payload, err := ioutil.ReadAll(r.Body)
|
payload, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
errorIf(err, "Unable to read request body for signature verification")
|
||||||
return ErrInternalError
|
return ErrInternalError
|
||||||
}
|
}
|
||||||
// Verify Content-Md5, if payload is set.
|
// Verify Content-Md5, if payload is set.
|
||||||
@ -144,7 +171,6 @@ func isReqAuthenticated(r *http.Request, region string) (s3Error APIErrorCode) {
|
|||||||
// request headers and body are used to calculate the signature validating
|
// request headers and body are used to calculate the signature validating
|
||||||
// the client signature present in request.
|
// the client signature present in request.
|
||||||
func checkAuth(r *http.Request) APIErrorCode {
|
func checkAuth(r *http.Request) APIErrorCode {
|
||||||
// Validates the request for both Presigned and Signed
|
|
||||||
return checkAuthWithRegion(r, serverConfig.GetRegion())
|
return checkAuthWithRegion(r, serverConfig.GetRegion())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,11 +178,14 @@ func checkAuth(r *http.Request) APIErrorCode {
|
|||||||
func checkAuthWithRegion(r *http.Request, region string) APIErrorCode {
|
func checkAuthWithRegion(r *http.Request, region string) APIErrorCode {
|
||||||
// Validates the request for both Presigned and Signed.
|
// Validates the request for both Presigned and Signed.
|
||||||
aType := getRequestAuthType(r)
|
aType := getRequestAuthType(r)
|
||||||
if aType != authTypePresigned && aType != authTypeSigned {
|
switch aType {
|
||||||
|
case authTypeSignedV2, authTypePresignedV2: // Signature V2.
|
||||||
|
return isReqAuthenticatedV2(r)
|
||||||
|
case authTypeSigned, authTypePresigned: // Signature V4.
|
||||||
|
return isReqAuthenticated(r, region)
|
||||||
|
}
|
||||||
// For all unhandled auth types return error AccessDenied.
|
// For all unhandled auth types return error AccessDenied.
|
||||||
return ErrAccessDenied
|
return ErrAccessDenied
|
||||||
}
|
|
||||||
return isReqAuthenticated(r, region)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// authHandler - handles all the incoming authorization headers and validates them if possible.
|
// authHandler - handles all the incoming authorization headers and validates them if possible.
|
||||||
@ -173,7 +202,9 @@ func setAuthHandler(h http.Handler) http.Handler {
|
|||||||
var supportedS3AuthTypes = map[authType]struct{}{
|
var supportedS3AuthTypes = map[authType]struct{}{
|
||||||
authTypeAnonymous: {},
|
authTypeAnonymous: {},
|
||||||
authTypePresigned: {},
|
authTypePresigned: {},
|
||||||
|
authTypePresignedV2: {},
|
||||||
authTypeSigned: {},
|
authTypeSigned: {},
|
||||||
|
authTypeSignedV2: {},
|
||||||
authTypePostPolicy: {},
|
authTypePostPolicy: {},
|
||||||
authTypeStreamingSigned: {},
|
authTypeStreamingSigned: {},
|
||||||
}
|
}
|
||||||
|
@ -151,19 +151,29 @@ func TestS3SupportedAuthType(t *testing.T) {
|
|||||||
authT: authTypeStreamingSigned,
|
authT: authTypeStreamingSigned,
|
||||||
pass: true,
|
pass: true,
|
||||||
},
|
},
|
||||||
// Test 6 - JWT is not supported s3 type.
|
// Test 6 - supported s3 type with signature v2.
|
||||||
|
{
|
||||||
|
authT: authTypeSignedV2,
|
||||||
|
pass: true,
|
||||||
|
},
|
||||||
|
// Test 7 - supported s3 type with presign v2.
|
||||||
|
{
|
||||||
|
authT: authTypePresignedV2,
|
||||||
|
pass: true,
|
||||||
|
},
|
||||||
|
// Test 8 - JWT is not supported s3 type.
|
||||||
{
|
{
|
||||||
authT: authTypeJWT,
|
authT: authTypeJWT,
|
||||||
pass: false,
|
pass: false,
|
||||||
},
|
},
|
||||||
// Test 7 - unknown auth header is not supported s3 type.
|
// Test 9 - unknown auth header is not supported s3 type.
|
||||||
{
|
{
|
||||||
authT: authTypeUnknown,
|
authT: authTypeUnknown,
|
||||||
pass: false,
|
pass: false,
|
||||||
},
|
},
|
||||||
// Test 8 - some new auth type is not supported s3 type.
|
// Test 10 - some new auth type is not supported s3 type.
|
||||||
{
|
{
|
||||||
authT: authType(7),
|
authT: authType(9),
|
||||||
pass: false,
|
pass: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -210,6 +220,39 @@ func TestIsRequestUnsignedPayload(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsRequestPresignedSignatureV2(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
inputQueryKey string
|
||||||
|
inputQueryValue string
|
||||||
|
expectedResult bool
|
||||||
|
}{
|
||||||
|
// Test case - 1.
|
||||||
|
// Test case with query key "AWSAccessKeyId" set.
|
||||||
|
{"", "", false},
|
||||||
|
// Test case - 2.
|
||||||
|
{"AWSAccessKeyId", "", true},
|
||||||
|
// Test case - 3.
|
||||||
|
{"X-Amz-Content-Sha256", "", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, testCase := range testCases {
|
||||||
|
// creating an input HTTP request.
|
||||||
|
// Only the query parameters are relevant for this particular test.
|
||||||
|
inputReq, err := http.NewRequest("GET", "http://example.com", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error initializing input HTTP request: %v", err)
|
||||||
|
}
|
||||||
|
q := inputReq.URL.Query()
|
||||||
|
q.Add(testCase.inputQueryKey, testCase.inputQueryValue)
|
||||||
|
inputReq.URL.RawQuery = q.Encode()
|
||||||
|
|
||||||
|
actualResult := isRequestPresignedSignatureV2(inputReq)
|
||||||
|
if testCase.expectedResult != actualResult {
|
||||||
|
t.Errorf("Test %d: Expected the result to `%v`, but instead got `%v`", i+1, testCase.expectedResult, actualResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TestIsRequestPresignedSignatureV4 - Test validates the logic for presign signature verision v4 detection.
|
// TestIsRequestPresignedSignatureV4 - Test validates the logic for presign signature verision v4 detection.
|
||||||
func TestIsRequestPresignedSignatureV4(t *testing.T) {
|
func TestIsRequestPresignedSignatureV4(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
@ -258,7 +301,7 @@ func mustNewRequest(method string, urlStr string, contentLength int64, body io.R
|
|||||||
func mustNewSignedRequest(method string, urlStr string, contentLength int64, body io.ReadSeeker, t *testing.T) *http.Request {
|
func mustNewSignedRequest(method string, urlStr string, contentLength int64, body io.ReadSeeker, t *testing.T) *http.Request {
|
||||||
req := mustNewRequest(method, urlStr, contentLength, body, t)
|
req := mustNewRequest(method, urlStr, contentLength, body, t)
|
||||||
cred := serverConfig.GetCredential()
|
cred := serverConfig.GetCredential()
|
||||||
if err := signRequest(req, cred.AccessKeyID, cred.SecretAccessKey); err != nil {
|
if err := signRequestV4(req, cred.AccessKeyID, cred.SecretAccessKey); err != nil {
|
||||||
t.Fatalf("Unable to inititalized new signed http request %s", err)
|
t.Fatalf("Unable to inititalized new signed http request %s", err)
|
||||||
}
|
}
|
||||||
return req
|
return req
|
||||||
|
@ -81,6 +81,13 @@ func (api objectAPIHandlers) ListObjectsV2Handler(w http.ResponseWriter, r *http
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypeSigned, authTypePresigned:
|
case authTypeSigned, authTypePresigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
@ -148,6 +155,13 @@ func (api objectAPIHandlers) ListObjectsV1Handler(w http.ResponseWriter, r *http
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypeSigned, authTypePresigned:
|
case authTypeSigned, authTypePresigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
@ -93,6 +93,13 @@ func (api objectAPIHandlers) GetBucketLocationHandler(w http.ResponseWriter, r *
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypeSigned, authTypePresigned:
|
case authTypeSigned, authTypePresigned:
|
||||||
if s3Error := isReqAuthenticated(r, "us-east-1"); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, "us-east-1"); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
@ -149,6 +156,13 @@ func (api objectAPIHandlers) ListMultipartUploadsHandler(w http.ResponseWriter,
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
@ -198,6 +212,7 @@ func (api objectAPIHandlers) ListBucketsHandler(w http.ResponseWriter, r *http.R
|
|||||||
|
|
||||||
// List buckets does not support bucket policies, no need to enforce it.
|
// List buckets does not support bucket policies, no need to enforce it.
|
||||||
// Proceed to validate signature.
|
// Proceed to validate signature.
|
||||||
|
// Validates the request for both Presigned and Signed.
|
||||||
if s3Error := checkAuthWithRegion(r, ""); s3Error != ErrNone {
|
if s3Error := checkAuthWithRegion(r, ""); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
@ -243,6 +258,13 @@ func (api objectAPIHandlers) DeleteMultipleObjectsHandler(w http.ResponseWriter,
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
@ -490,6 +512,13 @@ func (api objectAPIHandlers) HeadBucketHandler(w http.ResponseWriter, r *http.Re
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
@ -94,7 +94,7 @@ func testGetBucketLocationHandler(obj ObjectLayer, instanceType string, t TestEr
|
|||||||
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
// construct HTTP request for Get bucket location.
|
// construct HTTP request for Get bucket location.
|
||||||
req, err := newTestSignedRequest("GET", getBucketLocationURL("", testCase.bucketName), 0, nil, testCase.accessKey, testCase.secretKey)
|
req, err := newTestSignedRequestV4("GET", getBucketLocationURL("", testCase.bucketName), 0, nil, testCase.accessKey, testCase.secretKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %d: %s: Failed to create HTTP request for GetBucketLocationHandler: <ERROR> %v", i+1, instanceType, err)
|
t.Fatalf("Test %d: %s: Failed to create HTTP request for GetBucketLocationHandler: <ERROR> %v", i+1, instanceType, err)
|
||||||
}
|
}
|
||||||
@ -187,7 +187,7 @@ func testHeadBucketHandler(obj ObjectLayer, instanceType string, t TestErrHandle
|
|||||||
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
// construct HTTP request for HEAD bucket.
|
// construct HTTP request for HEAD bucket.
|
||||||
req, err := newTestSignedRequest("HEAD", getHEADBucketURL("", testCase.bucketName), 0, nil, testCase.accessKey, testCase.secretKey)
|
req, err := newTestSignedRequestV4("HEAD", getHEADBucketURL("", testCase.bucketName), 0, nil, testCase.accessKey, testCase.secretKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %d: %s: Failed to create HTTP request for HeadBucketHandler: <ERROR> %v", i+1, instanceType, err)
|
t.Fatalf("Test %d: %s: Failed to create HTTP request for HeadBucketHandler: <ERROR> %v", i+1, instanceType, err)
|
||||||
}
|
}
|
||||||
@ -272,7 +272,7 @@ func testListMultipartUploadsHandler(obj ObjectLayer, instanceType string, t Tes
|
|||||||
|
|
||||||
// construct HTTP request for List multipart uploads endpoint.
|
// construct HTTP request for List multipart uploads endpoint.
|
||||||
u := getListMultipartUploadsURLWithParams("", testCase.bucket, testCase.prefix, testCase.keyMarker, testCase.uploadIDMarker, testCase.delimiter, testCase.maxUploads)
|
u := getListMultipartUploadsURLWithParams("", testCase.bucket, testCase.prefix, testCase.keyMarker, testCase.uploadIDMarker, testCase.delimiter, testCase.maxUploads)
|
||||||
req, gerr := newTestSignedRequest("GET", u, 0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
req, gerr := newTestSignedRequestV4("GET", u, 0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if gerr != nil {
|
if gerr != nil {
|
||||||
t.Fatalf("Test %d: %s: Failed to create HTTP request for ListMultipartUploadsHandler: <ERROR> %v", i+1, instanceType, gerr)
|
t.Fatalf("Test %d: %s: Failed to create HTTP request for ListMultipartUploadsHandler: <ERROR> %v", i+1, instanceType, gerr)
|
||||||
}
|
}
|
||||||
@ -289,7 +289,7 @@ func testListMultipartUploadsHandler(obj ObjectLayer, instanceType string, t Tes
|
|||||||
|
|
||||||
// construct HTTP request for List multipart uploads endpoint.
|
// construct HTTP request for List multipart uploads endpoint.
|
||||||
u := getListMultipartUploadsURLWithParams("", bucketName, "", "", "", "", "")
|
u := getListMultipartUploadsURLWithParams("", bucketName, "", "", "", "", "")
|
||||||
req, err := newTestSignedRequest("GET", u, 0, nil, "", "") // Generate an anonymous request.
|
req, err := newTestSignedRequestV4("GET", u, 0, nil, "", "") // Generate an anonymous request.
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %s: Failed to create HTTP request for ListMultipartUploadsHandler: <ERROR> %v", instanceType, err)
|
t.Fatalf("Test %s: Failed to create HTTP request for ListMultipartUploadsHandler: <ERROR> %v", instanceType, err)
|
||||||
}
|
}
|
||||||
@ -359,7 +359,7 @@ func testListBucketsHandler(obj ObjectLayer, instanceType string, t TestErrHandl
|
|||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
req, lerr := newTestSignedRequest("GET", getListBucketURL(""), 0, nil, testCase.accessKey, testCase.secretKey)
|
req, lerr := newTestSignedRequestV4("GET", getListBucketURL(""), 0, nil, testCase.accessKey, testCase.secretKey)
|
||||||
if lerr != nil {
|
if lerr != nil {
|
||||||
t.Fatalf("Test %d: %s: Failed to create HTTP request for ListBucketsHandler: <ERROR> %v", i+1, instanceType, lerr)
|
t.Fatalf("Test %d: %s: Failed to create HTTP request for ListBucketsHandler: <ERROR> %v", i+1, instanceType, lerr)
|
||||||
}
|
}
|
||||||
|
@ -221,7 +221,7 @@ func testGetBucketNotificationHandler(obj ObjectLayer, instanceType string, t Te
|
|||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
|
|
||||||
// Prepare notification config for one of the test cases.
|
// Prepare notification config for one of the test cases.
|
||||||
req, err := newTestSignedRequest("PUT", getPutBucketNotificationURL("", randBucket),
|
req, err := newTestSignedRequestV4("PUT", getPutBucketNotificationURL("", randBucket),
|
||||||
int64(len(sampleNotificationBytes)), bytes.NewReader(sampleNotificationBytes),
|
int64(len(sampleNotificationBytes)), bytes.NewReader(sampleNotificationBytes),
|
||||||
credentials.AccessKeyID, credentials.SecretAccessKey)
|
credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -251,7 +251,7 @@ func testGetBucketNotificationHandler(obj ObjectLayer, instanceType string, t Te
|
|||||||
signatureMismatchCode := getAPIError(ErrContentSHA256Mismatch).Code
|
signatureMismatchCode := getAPIError(ErrContentSHA256Mismatch).Code
|
||||||
for i, test := range testCases {
|
for i, test := range testCases {
|
||||||
testRec := httptest.NewRecorder()
|
testRec := httptest.NewRecorder()
|
||||||
testReq, tErr := newTestSignedRequest("GET", getGetBucketNotificationURL("", test.bucketName),
|
testReq, tErr := newTestSignedRequestV4("GET", getGetBucketNotificationURL("", test.bucketName),
|
||||||
int64(0), nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
int64(0), nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if tErr != nil {
|
if tErr != nil {
|
||||||
t.Fatalf("Test %d: %s: Failed to create HTTP testRequest for GetBucketNotification: <ERROR> %v",
|
t.Fatalf("Test %d: %s: Failed to create HTTP testRequest for GetBucketNotification: <ERROR> %v",
|
||||||
@ -308,7 +308,7 @@ func testGetBucketNotificationHandler(obj ObjectLayer, instanceType string, t Te
|
|||||||
"PutBucketNotificationHandler",
|
"PutBucketNotificationHandler",
|
||||||
})
|
})
|
||||||
testRec := httptest.NewRecorder()
|
testRec := httptest.NewRecorder()
|
||||||
testReq, tErr := newTestSignedRequest("GET", getGetBucketNotificationURL("", randBucket),
|
testReq, tErr := newTestSignedRequestV4("GET", getGetBucketNotificationURL("", randBucket),
|
||||||
int64(0), nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
int64(0), nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if tErr != nil {
|
if tErr != nil {
|
||||||
t.Fatalf("Test %d: %s: Failed to create HTTP testRequest for GetBucketNotification: <ERROR> %v",
|
t.Fatalf("Test %d: %s: Failed to create HTTP testRequest for GetBucketNotification: <ERROR> %v",
|
||||||
@ -391,7 +391,7 @@ func testPutBucketNotificationHandler(obj ObjectLayer, instanceType string, t Te
|
|||||||
}
|
}
|
||||||
for i, test := range testCases {
|
for i, test := range testCases {
|
||||||
testRec := httptest.NewRecorder()
|
testRec := httptest.NewRecorder()
|
||||||
testReq, tErr := newTestSignedRequest("PUT", getPutBucketNotificationURL("", test.bucketName),
|
testReq, tErr := newTestSignedRequestV4("PUT", getPutBucketNotificationURL("", test.bucketName),
|
||||||
int64(len(test.expectedNotificationBytes)), bytes.NewReader(test.expectedNotificationBytes),
|
int64(len(test.expectedNotificationBytes)), bytes.NewReader(test.expectedNotificationBytes),
|
||||||
credentials.AccessKeyID, credentials.SecretAccessKey)
|
credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if tErr != nil {
|
if tErr != nil {
|
||||||
@ -416,7 +416,7 @@ func testPutBucketNotificationHandler(obj ObjectLayer, instanceType string, t Te
|
|||||||
switch test.kind {
|
switch test.kind {
|
||||||
case CompareBytes:
|
case CompareBytes:
|
||||||
|
|
||||||
testReq, tErr = newTestSignedRequest("GET", getGetBucketNotificationURL("", test.bucketName),
|
testReq, tErr = newTestSignedRequestV4("GET", getGetBucketNotificationURL("", test.bucketName),
|
||||||
int64(0), nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
int64(0), nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if tErr != nil {
|
if tErr != nil {
|
||||||
t.Fatalf("Test %d: %s: Failed to create HTTP testRequest for GetBucketNotification: <ERROR> %v",
|
t.Fatalf("Test %d: %s: Failed to create HTTP testRequest for GetBucketNotification: <ERROR> %v",
|
||||||
@ -464,7 +464,7 @@ func testPutBucketNotificationHandler(obj ObjectLayer, instanceType string, t Te
|
|||||||
"PutBucketNotificationHandler",
|
"PutBucketNotificationHandler",
|
||||||
})
|
})
|
||||||
testRec := httptest.NewRecorder()
|
testRec := httptest.NewRecorder()
|
||||||
testReq, tErr := newTestSignedRequest("PUT", getPutBucketNotificationURL("", randBucket),
|
testReq, tErr := newTestSignedRequestV4("PUT", getPutBucketNotificationURL("", randBucket),
|
||||||
int64(len(sampleNotificationBytes)), bytes.NewReader(sampleNotificationBytes),
|
int64(len(sampleNotificationBytes)), bytes.NewReader(sampleNotificationBytes),
|
||||||
credentials.AccessKeyID, credentials.SecretAccessKey)
|
credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if tErr != nil {
|
if tErr != nil {
|
||||||
@ -526,7 +526,7 @@ func testListenBucketNotificationHandler(obj ObjectLayer, instanceType string, t
|
|||||||
instanceType, err)
|
instanceType, err)
|
||||||
}
|
}
|
||||||
testRec := httptest.NewRecorder()
|
testRec := httptest.NewRecorder()
|
||||||
testReq, tErr := newTestSignedRequest("PUT", getPutBucketNotificationURL("", randBucket),
|
testReq, tErr := newTestSignedRequestV4("PUT", getPutBucketNotificationURL("", randBucket),
|
||||||
int64(len(sampleNotificationBytes)), bytes.NewReader(sampleNotificationBytes),
|
int64(len(sampleNotificationBytes)), bytes.NewReader(sampleNotificationBytes),
|
||||||
credentials.AccessKeyID, credentials.SecretAccessKey)
|
credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if tErr != nil {
|
if tErr != nil {
|
||||||
@ -562,7 +562,7 @@ func testListenBucketNotificationHandler(obj ObjectLayer, instanceType string, t
|
|||||||
|
|
||||||
for i, test := range testCases {
|
for i, test := range testCases {
|
||||||
testRec = httptest.NewRecorder()
|
testRec = httptest.NewRecorder()
|
||||||
testReq, tErr = newTestSignedRequest("GET",
|
testReq, tErr = newTestSignedRequestV4("GET",
|
||||||
getListenBucketNotificationURL("", test.bucketName, test.prefix, test.suffix, test.events),
|
getListenBucketNotificationURL("", test.bucketName, test.prefix, test.suffix, test.events),
|
||||||
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if tErr != nil {
|
if tErr != nil {
|
||||||
@ -611,7 +611,7 @@ func testListenBucketNotificationHandler(obj ObjectLayer, instanceType string, t
|
|||||||
"ListenBucketNotificationHandler",
|
"ListenBucketNotificationHandler",
|
||||||
})
|
})
|
||||||
testRec = httptest.NewRecorder()
|
testRec = httptest.NewRecorder()
|
||||||
testReq, tErr = newTestSignedRequest("GET",
|
testReq, tErr = newTestSignedRequestV4("GET",
|
||||||
getListenBucketNotificationURL("", randBucket, "", "*.jpg", []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:*"}),
|
getListenBucketNotificationURL("", randBucket, "", "*.jpg", []string{"s3:ObjectCreated:*", "s3:ObjectRemoved:*"}),
|
||||||
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if tErr != nil {
|
if tErr != nil {
|
||||||
@ -670,7 +670,7 @@ func testRemoveNotificationConfig(obj ObjectLayer, instanceType string, t TestEr
|
|||||||
}
|
}
|
||||||
// Set sample bucket notification on randBucket.
|
// Set sample bucket notification on randBucket.
|
||||||
testRec := httptest.NewRecorder()
|
testRec := httptest.NewRecorder()
|
||||||
testReq, tErr := newTestSignedRequest("PUT", getPutBucketNotificationURL("", randBucket),
|
testReq, tErr := newTestSignedRequestV4("PUT", getPutBucketNotificationURL("", randBucket),
|
||||||
int64(len(sampleNotificationBytes)), bytes.NewReader(sampleNotificationBytes),
|
int64(len(sampleNotificationBytes)), bytes.NewReader(sampleNotificationBytes),
|
||||||
credentials.AccessKeyID, credentials.SecretAccessKey)
|
credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if tErr != nil {
|
if tErr != nil {
|
||||||
|
@ -132,19 +132,15 @@ func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *ht
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
// PutBucketPolicy does not support bucket policies, use checkAuth to validate signature.
|
||||||
bucket := vars["bucket"]
|
if s3Error := checkAuth(r); s3Error != ErrNone {
|
||||||
switch getRequestAuthType(r) {
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
default:
|
|
||||||
// For all unknown auth types return error.
|
|
||||||
writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path)
|
|
||||||
return
|
|
||||||
case authTypePresigned, authTypeSigned:
|
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
|
||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
// If Content-Length is unknown or zero, deny the
|
// If Content-Length is unknown or zero, deny the
|
||||||
// request. PutBucketPolicy always needs a Content-Length if
|
// request. PutBucketPolicy always needs a Content-Length if
|
||||||
@ -214,20 +210,15 @@ func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
// DeleteBucketPolicy does not support bucket policies, use checkAuth to validate signature.
|
||||||
bucket := vars["bucket"]
|
if s3Error := checkAuth(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
switch getRequestAuthType(r) {
|
|
||||||
default:
|
|
||||||
// For all unknown auth types return error.
|
|
||||||
writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path)
|
|
||||||
return
|
|
||||||
case authTypePresigned, authTypeSigned:
|
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
|
||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
// Delete bucket access policy.
|
// Delete bucket access policy.
|
||||||
if err := removeBucketPolicy(bucket, objAPI); err != nil {
|
if err := removeBucketPolicy(bucket, objAPI); err != nil {
|
||||||
@ -260,20 +251,15 @@ func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *ht
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
// GetBucketPolicy does not support bucket policies, use checkAuth to validate signature.
|
||||||
bucket := vars["bucket"]
|
if s3Error := checkAuth(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
switch getRequestAuthType(r) {
|
|
||||||
default:
|
|
||||||
// For all unknown auth types return error.
|
|
||||||
writeErrorResponse(w, r, ErrAccessDenied, r.URL.Path)
|
|
||||||
return
|
|
||||||
case authTypePresigned, authTypeSigned:
|
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
|
||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
bucket := vars["bucket"]
|
||||||
|
|
||||||
// Read bucket access policy.
|
// Read bucket access policy.
|
||||||
policy, err := readBucketPolicy(bucket, objAPI)
|
policy, err := readBucketPolicy(bucket, objAPI)
|
||||||
|
@ -289,18 +289,32 @@ func testPutBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestErrH
|
|||||||
// obtain the put bucket policy request body.
|
// obtain the put bucket policy request body.
|
||||||
bucketPolicyStr := fmt.Sprintf(bucketPolicyTemplate, testCase.bucketName, testCase.bucketName)
|
bucketPolicyStr := fmt.Sprintf(bucketPolicyTemplate, testCase.bucketName, testCase.bucketName)
|
||||||
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
rec := httptest.NewRecorder()
|
recV4 := httptest.NewRecorder()
|
||||||
// construct HTTP request for PUT bucket policy endpoint.
|
// construct HTTP request for PUT bucket policy endpoint.
|
||||||
req, err := newTestSignedRequest("PUT", getPutPolicyURL("", testCase.bucketName),
|
reqV4, err := newTestSignedRequestV4("PUT", getPutPolicyURL("", testCase.bucketName),
|
||||||
int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testCase.accessKey, testCase.secretKey)
|
int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testCase.accessKey, testCase.secretKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %d: %s: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, instanceType, err)
|
t.Fatalf("Test %d: %s: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, instanceType, err)
|
||||||
}
|
}
|
||||||
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
|
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
|
||||||
// Call the ServeHTTP to execute the handler.
|
// Call the ServeHTTP to execute the handler.
|
||||||
apiRouter.ServeHTTP(rec, req)
|
apiRouter.ServeHTTP(recV4, reqV4)
|
||||||
if rec.Code != testCase.expectedRespStatus {
|
if recV4.Code != testCase.expectedRespStatus {
|
||||||
t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, rec.Code)
|
t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, recV4.Code)
|
||||||
|
}
|
||||||
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
|
recV2 := httptest.NewRecorder()
|
||||||
|
// construct HTTP request for PUT bucket policy endpoint.
|
||||||
|
reqV2, err := newTestSignedRequestV2("PUT", getPutPolicyURL("", testCase.bucketName),
|
||||||
|
int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testCase.accessKey, testCase.secretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Test %d: %s: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, instanceType, err)
|
||||||
|
}
|
||||||
|
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
|
||||||
|
// Call the ServeHTTP to execute the handler.
|
||||||
|
apiRouter.ServeHTTP(recV2, reqV2)
|
||||||
|
if recV2.Code != testCase.expectedRespStatus {
|
||||||
|
t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, recV2.Code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -357,18 +371,32 @@ func testGetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestErrH
|
|||||||
// obtain the put bucket policy request body.
|
// obtain the put bucket policy request body.
|
||||||
bucketPolicyStr := fmt.Sprintf(bucketPolicyTemplate, testPolicy.bucketName, testPolicy.bucketName)
|
bucketPolicyStr := fmt.Sprintf(bucketPolicyTemplate, testPolicy.bucketName, testPolicy.bucketName)
|
||||||
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
rec := httptest.NewRecorder()
|
recV4 := httptest.NewRecorder()
|
||||||
// construct HTTP request for PUT bucket policy endpoint.
|
// construct HTTP request for PUT bucket policy endpoint.
|
||||||
req, err := newTestSignedRequest("PUT", getPutPolicyURL("", testPolicy.bucketName),
|
reqV4, err := newTestSignedRequestV4("PUT", getPutPolicyURL("", testPolicy.bucketName),
|
||||||
int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testPolicy.accessKey, testPolicy.secretKey)
|
int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testPolicy.accessKey, testPolicy.secretKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %d: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, err)
|
t.Fatalf("Test %d: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, err)
|
||||||
}
|
}
|
||||||
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
|
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
|
||||||
// Call the ServeHTTP to execute the handler.
|
// Call the ServeHTTP to execute the handler.
|
||||||
apiRouter.ServeHTTP(rec, req)
|
apiRouter.ServeHTTP(recV4, reqV4)
|
||||||
if rec.Code != testPolicy.expectedRespStatus {
|
if recV4.Code != testPolicy.expectedRespStatus {
|
||||||
t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testPolicy.expectedRespStatus, rec.Code)
|
t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testPolicy.expectedRespStatus, recV4.Code)
|
||||||
|
}
|
||||||
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
|
recV2 := httptest.NewRecorder()
|
||||||
|
// construct HTTP request for PUT bucket policy endpoint.
|
||||||
|
reqV2, err := newTestSignedRequestV2("PUT", getPutPolicyURL("", testPolicy.bucketName),
|
||||||
|
int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testPolicy.accessKey, testPolicy.secretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Test %d: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, err)
|
||||||
|
}
|
||||||
|
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic ofthe handler.
|
||||||
|
// Call the ServeHTTP to execute the handler.
|
||||||
|
apiRouter.ServeHTTP(recV2, reqV2)
|
||||||
|
if recV2.Code != testPolicy.expectedRespStatus {
|
||||||
|
t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testPolicy.expectedRespStatus, recV2.Code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,9 +416,9 @@ func testGetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestErrH
|
|||||||
// expected bucket policy json string.
|
// expected bucket policy json string.
|
||||||
expectedBucketPolicyStr := fmt.Sprintf(testCase.expectedBucketPolicy, testCase.bucketName, testCase.bucketName)
|
expectedBucketPolicyStr := fmt.Sprintf(testCase.expectedBucketPolicy, testCase.bucketName, testCase.bucketName)
|
||||||
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
rec := httptest.NewRecorder()
|
recV4 := httptest.NewRecorder()
|
||||||
// construct HTTP request for PUT bucket policy endpoint.
|
// construct HTTP request for PUT bucket policy endpoint.
|
||||||
req, err := newTestSignedRequest("GET", getGetPolicyURL("", testCase.bucketName),
|
reqV4, err := newTestSignedRequestV4("GET", getGetPolicyURL("", testCase.bucketName),
|
||||||
0, nil, testCase.accessKey, testCase.secretKey)
|
0, nil, testCase.accessKey, testCase.secretKey)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -398,13 +426,37 @@ func testGetBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestErrH
|
|||||||
}
|
}
|
||||||
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
|
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
|
||||||
// Call the ServeHTTP to execute the handler, GetBucketPolicyHandler handles the request.
|
// Call the ServeHTTP to execute the handler, GetBucketPolicyHandler handles the request.
|
||||||
apiRouter.ServeHTTP(rec, req)
|
apiRouter.ServeHTTP(recV4, reqV4)
|
||||||
// Assert the response code with the expected status.
|
// Assert the response code with the expected status.
|
||||||
if rec.Code != testCase.expectedRespStatus {
|
if recV4.Code != testCase.expectedRespStatus {
|
||||||
t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testCase.expectedRespStatus, rec.Code)
|
t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testCase.expectedRespStatus, recV4.Code)
|
||||||
}
|
}
|
||||||
// read the response body.
|
// read the response body.
|
||||||
bucketPolicyReadBuf, err := ioutil.ReadAll(rec.Body)
|
bucketPolicyReadBuf, err := ioutil.ReadAll(recV4.Body)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Test %d: %s: Failed parsing response body: <ERROR> %v", i+1, instanceType, err)
|
||||||
|
}
|
||||||
|
// Verify whether the bucket policy fetched is same as the one inserted.
|
||||||
|
if expectedBucketPolicyStr != string(bucketPolicyReadBuf) {
|
||||||
|
t.Errorf("Test %d: %s: Bucket policy differs from expected value.", i+1, instanceType)
|
||||||
|
}
|
||||||
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
|
recV2 := httptest.NewRecorder()
|
||||||
|
// construct HTTP request for PUT bucket policy endpoint.
|
||||||
|
reqV2, err := newTestSignedRequestV2("GET", getGetPolicyURL("", testCase.bucketName),
|
||||||
|
0, nil, testCase.accessKey, testCase.secretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Test %d: Failed to create HTTP request for GetBucketPolicyHandler: <ERROR> %v", i+1, err)
|
||||||
|
}
|
||||||
|
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
|
||||||
|
// Call the ServeHTTP to execute the handler, GetBucketPolicyHandler handles the request.
|
||||||
|
apiRouter.ServeHTTP(recV2, reqV2)
|
||||||
|
// Assert the response code with the expected status.
|
||||||
|
if recV2.Code != testCase.expectedRespStatus {
|
||||||
|
t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testCase.expectedRespStatus, recV2.Code)
|
||||||
|
}
|
||||||
|
// read the response body.
|
||||||
|
bucketPolicyReadBuf, err = ioutil.ReadAll(recV2.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %d: %s: Failed parsing response body: <ERROR> %v", i+1, instanceType, err)
|
t.Fatalf("Test %d: %s: Failed parsing response body: <ERROR> %v", i+1, instanceType, err)
|
||||||
}
|
}
|
||||||
@ -502,20 +554,21 @@ func testDeleteBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestE
|
|||||||
// obtain the put bucket policy request body.
|
// obtain the put bucket policy request body.
|
||||||
bucketPolicyStr := fmt.Sprintf(bucketPolicyTemplate, testPolicy.bucketName, testPolicy.bucketName)
|
bucketPolicyStr := fmt.Sprintf(bucketPolicyTemplate, testPolicy.bucketName, testPolicy.bucketName)
|
||||||
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
rec := httptest.NewRecorder()
|
recV4 := httptest.NewRecorder()
|
||||||
// construct HTTP request for PUT bucket policy endpoint.
|
// construct HTTP request for PUT bucket policy endpoint.
|
||||||
req, err := newTestSignedRequest("PUT", getPutPolicyURL("", testPolicy.bucketName),
|
reqV4, err := newTestSignedRequestV4("PUT", getPutPolicyURL("", testPolicy.bucketName),
|
||||||
int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testPolicy.accessKey, testPolicy.secretKey)
|
int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testPolicy.accessKey, testPolicy.secretKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %d: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, err)
|
t.Fatalf("Test %d: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, err)
|
||||||
}
|
}
|
||||||
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
|
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
|
||||||
// Call the ServeHTTP to execute the handler.
|
// Call the ServeHTTP to execute the handler.
|
||||||
apiRouter.ServeHTTP(rec, req)
|
apiRouter.ServeHTTP(recV4, reqV4)
|
||||||
if rec.Code != testPolicy.expectedRespStatus {
|
if recV4.Code != testPolicy.expectedRespStatus {
|
||||||
t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testPolicy.expectedRespStatus, rec.Code)
|
t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testPolicy.expectedRespStatus, recV4.Code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// testcases with input and expected output for DeleteBucketPolicyHandler.
|
// testcases with input and expected output for DeleteBucketPolicyHandler.
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
bucketName string
|
bucketName string
|
||||||
@ -529,21 +582,58 @@ func testDeleteBucketPolicyHandler(obj ObjectLayer, instanceType string, t TestE
|
|||||||
// Iterating over the cases and deleting the bucket policy and then asserting response.
|
// Iterating over the cases and deleting the bucket policy and then asserting response.
|
||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
rec := httptest.NewRecorder()
|
recV4 := httptest.NewRecorder()
|
||||||
// construct HTTP request for Delete bucket policy endpoint.
|
// construct HTTP request for Delete bucket policy endpoint.
|
||||||
req, err := newTestSignedRequest("DELETE", getDeletePolicyURL("", testCase.bucketName),
|
reqV4, err := newTestSignedRequestV4("DELETE", getDeletePolicyURL("", testCase.bucketName),
|
||||||
0, nil, testCase.accessKey, testCase.secretKey)
|
0, nil, testCase.accessKey, testCase.secretKey)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %d: Failed to create HTTP request for GetBucketPolicyHandler: <ERROR> %v", i+1, err)
|
t.Fatalf("Test %d: Failed to create HTTP request for GetBucketPolicyHandler: <ERROR> %v", i+1, err)
|
||||||
}
|
}
|
||||||
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
|
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
|
||||||
// Call the ServeHTTP to execute the handler, DeleteBucketPolicyHandler handles the request.
|
// Call the ServeHTTP to execute the handler, DeleteBucketPolicyHandler handles the request.
|
||||||
apiRouter.ServeHTTP(rec, req)
|
apiRouter.ServeHTTP(recV4, reqV4)
|
||||||
// Assert the response code with the expected status.
|
// Assert the response code with the expected status.
|
||||||
if rec.Code != testCase.expectedRespStatus {
|
if recV4.Code != testCase.expectedRespStatus {
|
||||||
t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testCase.expectedRespStatus, rec.Code)
|
t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testCase.expectedRespStatus, recV4.Code)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Iterating over the cases and writing the bucket policy.
|
||||||
|
// its required to write the policies first before running tests on GetBucketPolicy.
|
||||||
|
for i, testPolicy := range putTestPolicies {
|
||||||
|
// obtain the put bucket policy request body.
|
||||||
|
bucketPolicyStr := fmt.Sprintf(bucketPolicyTemplate, testPolicy.bucketName, testPolicy.bucketName)
|
||||||
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
|
recV2 := httptest.NewRecorder()
|
||||||
|
// construct HTTP request for PUT bucket policy endpoint.
|
||||||
|
reqV2, err := newTestSignedRequestV2("PUT", getPutPolicyURL("", testPolicy.bucketName),
|
||||||
|
int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testPolicy.accessKey, testPolicy.secretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Test %d: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, err)
|
||||||
|
}
|
||||||
|
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
|
||||||
|
// Call the ServeHTTP to execute the handler.
|
||||||
|
apiRouter.ServeHTTP(recV2, reqV2)
|
||||||
|
if recV2.Code != testPolicy.expectedRespStatus {
|
||||||
|
t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testPolicy.expectedRespStatus, recV2.Code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, testCase := range testCases {
|
||||||
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
|
recV2 := httptest.NewRecorder()
|
||||||
|
// construct HTTP request for Delete bucket policy endpoint.
|
||||||
|
reqV2, err := newTestSignedRequestV2("DELETE", getDeletePolicyURL("", testCase.bucketName),
|
||||||
|
0, nil, testCase.accessKey, testCase.secretKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Test %d: Failed to create HTTP request for GetBucketPolicyHandler: <ERROR> %v", i+1, err)
|
||||||
|
}
|
||||||
|
// Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler.
|
||||||
|
// Call the ServeHTTP to execute the handler, DeleteBucketPolicyHandler handles the request.
|
||||||
|
apiRouter.ServeHTTP(recV2, reqV2)
|
||||||
|
// Assert the response code with the expected status.
|
||||||
|
if recV2.Code != testCase.expectedRespStatus {
|
||||||
|
t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testCase.expectedRespStatus, recV2.Code)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +61,7 @@ func generateSystemLockResponse() (SystemLockState, error) {
|
|||||||
defer nsMutex.lockMapMutex.Unlock()
|
defer nsMutex.lockMapMutex.Unlock()
|
||||||
|
|
||||||
if nsMutex.debugLockMap == nil {
|
if nsMutex.debugLockMap == nil {
|
||||||
return SystemLockState{}, LockInfoNil{}
|
return SystemLockState{}, errLockNotInitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
lockState := SystemLockState{}
|
lockState := SystemLockState{}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@ -58,14 +59,6 @@ func newDebugLockInfoPerVolumePath() *debugLockInfoPerVolumePath {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// LockInfoNil - Returned if the lock info map is not initialized.
|
|
||||||
type LockInfoNil struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (l LockInfoNil) Error() string {
|
|
||||||
return fmt.Sprintf("Debug Lock Map not initialized:\n1. Enable Lock Debugging using right ENV settings \n2. Make sure initNSLock() is called.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// LockInfoOriginNotFound - While changing the state of the lock info its important that the entry for
|
// LockInfoOriginNotFound - While changing the state of the lock info its important that the entry for
|
||||||
// lock at a given origin exists, if not `LockInfoOriginNotFound` is returned.
|
// lock at a given origin exists, if not `LockInfoOriginNotFound` is returned.
|
||||||
type LockInfoOriginNotFound struct {
|
type LockInfoOriginNotFound struct {
|
||||||
@ -114,13 +107,15 @@ func (l LockInfoStateNotBlocked) Error() string {
|
|||||||
return fmt.Sprintf("Lock state should be \"Blocked\" for <volume> %s, <path> %s, <operationID> %s.", l.volume, l.path, l.operationID)
|
return fmt.Sprintf("Lock state should be \"Blocked\" for <volume> %s, <path> %s, <operationID> %s.", l.volume, l.path, l.operationID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var errLockNotInitialized = errors.New("Debug Lock Map not initialized:\n1. Enable Lock Debugging using right ENV settings \n2. Make sure initNSLock() is called.")
|
||||||
|
|
||||||
// change the state of the lock from Blocked to Running.
|
// change the state of the lock from Blocked to Running.
|
||||||
func (n *nsLockMap) statusBlockedToRunning(param nsParam, lockOrigin, operationID string, readLock bool) error {
|
func (n *nsLockMap) statusBlockedToRunning(param nsParam, lockOrigin, operationID string, readLock bool) error {
|
||||||
// This operation is not executed under the scope nsLockMap.mutex.Lock(), lock has to be explicitly held here.
|
// This operation is not executed under the scope nsLockMap.mutex.Lock(), lock has to be explicitly held here.
|
||||||
n.lockMapMutex.Lock()
|
n.lockMapMutex.Lock()
|
||||||
defer n.lockMapMutex.Unlock()
|
defer n.lockMapMutex.Unlock()
|
||||||
if n.debugLockMap == nil {
|
if n.debugLockMap == nil {
|
||||||
return LockInfoNil{}
|
return errLockNotInitialized
|
||||||
}
|
}
|
||||||
// new state info to be set for the lock.
|
// new state info to be set for the lock.
|
||||||
newLockInfo := debugLockInfo{
|
newLockInfo := debugLockInfo{
|
||||||
@ -140,7 +135,7 @@ func (n *nsLockMap) statusBlockedToRunning(param nsParam, lockOrigin, operationI
|
|||||||
if debugLockMap, ok := n.debugLockMap[param]; ok {
|
if debugLockMap, ok := n.debugLockMap[param]; ok {
|
||||||
// ``*debugLockInfoPerVolumePath` entry containing lock info for `param <volume, path>` is `nil`.
|
// ``*debugLockInfoPerVolumePath` entry containing lock info for `param <volume, path>` is `nil`.
|
||||||
if debugLockMap == nil {
|
if debugLockMap == nil {
|
||||||
return LockInfoNil{}
|
return errLockNotInitialized
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The lock state info foe given <volume, path> pair should already exist.
|
// The lock state info foe given <volume, path> pair should already exist.
|
||||||
@ -186,7 +181,7 @@ func (n *nsLockMap) statusBlockedToRunning(param nsParam, lockOrigin, operationI
|
|||||||
// change the state of the lock from Ready to Blocked.
|
// change the state of the lock from Ready to Blocked.
|
||||||
func (n *nsLockMap) statusNoneToBlocked(param nsParam, lockOrigin, operationID string, readLock bool) error {
|
func (n *nsLockMap) statusNoneToBlocked(param nsParam, lockOrigin, operationID string, readLock bool) error {
|
||||||
if n.debugLockMap == nil {
|
if n.debugLockMap == nil {
|
||||||
return LockInfoNil{}
|
return errLockNotInitialized
|
||||||
}
|
}
|
||||||
|
|
||||||
newLockInfo := debugLockInfo{
|
newLockInfo := debugLockInfo{
|
||||||
@ -230,7 +225,7 @@ func (n *nsLockMap) statusNoneToBlocked(param nsParam, lockOrigin, operationID s
|
|||||||
// deleteLockInfoEntry - Deletes the lock state information for given <volume, path> pair. Called when nsLk.ref count is 0.
|
// deleteLockInfoEntry - Deletes the lock state information for given <volume, path> pair. Called when nsLk.ref count is 0.
|
||||||
func (n *nsLockMap) deleteLockInfoEntryForVolumePath(param nsParam) error {
|
func (n *nsLockMap) deleteLockInfoEntryForVolumePath(param nsParam) error {
|
||||||
if n.debugLockMap == nil {
|
if n.debugLockMap == nil {
|
||||||
return LockInfoNil{}
|
return errLockNotInitialized
|
||||||
}
|
}
|
||||||
// delete the lock info for the given operation.
|
// delete the lock info for the given operation.
|
||||||
if _, found := n.debugLockMap[param]; found {
|
if _, found := n.debugLockMap[param]; found {
|
||||||
@ -246,7 +241,7 @@ func (n *nsLockMap) deleteLockInfoEntryForVolumePath(param nsParam) error {
|
|||||||
// called when the nsLk ref count for the given <volume, path> pair is not 0.
|
// called when the nsLk ref count for the given <volume, path> pair is not 0.
|
||||||
func (n *nsLockMap) deleteLockInfoEntryForOps(param nsParam, operationID string) error {
|
func (n *nsLockMap) deleteLockInfoEntryForOps(param nsParam, operationID string) error {
|
||||||
if n.debugLockMap == nil {
|
if n.debugLockMap == nil {
|
||||||
return LockInfoNil{}
|
return errLockNotInitialized
|
||||||
}
|
}
|
||||||
// delete the lock info for the given operation.
|
// delete the lock info for the given operation.
|
||||||
if infoMap, found := n.debugLockMap[param]; found {
|
if infoMap, found := n.debugLockMap[param]; found {
|
||||||
|
@ -327,7 +327,7 @@ func TestNsLockMapStatusBlockedToRunning(t *testing.T) {
|
|||||||
actualErr := nsMutex.statusBlockedToRunning(param, testCases[0].lockOrigin,
|
actualErr := nsMutex.statusBlockedToRunning(param, testCases[0].lockOrigin,
|
||||||
testCases[0].opsID, testCases[0].readLock)
|
testCases[0].opsID, testCases[0].readLock)
|
||||||
|
|
||||||
expectedNilErr := LockInfoNil{}
|
expectedNilErr := errLockNotInitialized
|
||||||
if actualErr != expectedNilErr {
|
if actualErr != expectedNilErr {
|
||||||
t.Fatalf("Errors mismatch: Expected \"%s\", got \"%s\"", expectedNilErr, actualErr)
|
t.Fatalf("Errors mismatch: Expected \"%s\", got \"%s\"", expectedNilErr, actualErr)
|
||||||
}
|
}
|
||||||
@ -338,12 +338,12 @@ func TestNsLockMapStatusBlockedToRunning(t *testing.T) {
|
|||||||
lockMap: make(map[nsParam]*nsLock),
|
lockMap: make(map[nsParam]*nsLock),
|
||||||
}
|
}
|
||||||
// Entry for <volume, path> pair is set to nil.
|
// Entry for <volume, path> pair is set to nil.
|
||||||
// Should fail with `LockInfoNil{}`.
|
// Should fail with `errLockNotInitialized`.
|
||||||
nsMutex.debugLockMap[param] = nil
|
nsMutex.debugLockMap[param] = nil
|
||||||
actualErr = nsMutex.statusBlockedToRunning(param, testCases[0].lockOrigin,
|
actualErr = nsMutex.statusBlockedToRunning(param, testCases[0].lockOrigin,
|
||||||
testCases[0].opsID, testCases[0].readLock)
|
testCases[0].opsID, testCases[0].readLock)
|
||||||
|
|
||||||
expectedNilErr = LockInfoNil{}
|
expectedNilErr = errLockNotInitialized
|
||||||
if actualErr != expectedNilErr {
|
if actualErr != expectedNilErr {
|
||||||
t.Fatalf("Errors mismatch: Expected \"%s\", got \"%s\"", expectedNilErr, actualErr)
|
t.Fatalf("Errors mismatch: Expected \"%s\", got \"%s\"", expectedNilErr, actualErr)
|
||||||
}
|
}
|
||||||
@ -524,7 +524,7 @@ func TestNsLockMapStatusNoneToBlocked(t *testing.T) {
|
|||||||
actualErr := nsMutex.statusBlockedToRunning(param, testCases[0].lockOrigin,
|
actualErr := nsMutex.statusBlockedToRunning(param, testCases[0].lockOrigin,
|
||||||
testCases[0].opsID, testCases[0].readLock)
|
testCases[0].opsID, testCases[0].readLock)
|
||||||
|
|
||||||
expectedNilErr := LockInfoNil{}
|
expectedNilErr := errLockNotInitialized
|
||||||
if actualErr != expectedNilErr {
|
if actualErr != expectedNilErr {
|
||||||
t.Fatalf("Errors mismatch: Expected \"%s\", got \"%s\"", expectedNilErr, actualErr)
|
t.Fatalf("Errors mismatch: Expected \"%s\", got \"%s\"", expectedNilErr, actualErr)
|
||||||
}
|
}
|
||||||
@ -569,7 +569,7 @@ func TestNsLockMapDeleteLockInfoEntryForOps(t *testing.T) {
|
|||||||
|
|
||||||
actualErr := nsMutex.deleteLockInfoEntryForOps(param, testCases[0].opsID)
|
actualErr := nsMutex.deleteLockInfoEntryForOps(param, testCases[0].opsID)
|
||||||
|
|
||||||
expectedNilErr := LockInfoNil{}
|
expectedNilErr := errLockNotInitialized
|
||||||
if actualErr != expectedNilErr {
|
if actualErr != expectedNilErr {
|
||||||
t.Fatalf("Errors mismatch: Expected \"%s\", got \"%s\"", expectedNilErr, actualErr)
|
t.Fatalf("Errors mismatch: Expected \"%s\", got \"%s\"", expectedNilErr, actualErr)
|
||||||
}
|
}
|
||||||
@ -667,7 +667,7 @@ func TestNsLockMapDeleteLockInfoEntryForVolumePath(t *testing.T) {
|
|||||||
|
|
||||||
actualErr := nsMutex.deleteLockInfoEntryForVolumePath(param)
|
actualErr := nsMutex.deleteLockInfoEntryForVolumePath(param)
|
||||||
|
|
||||||
expectedNilErr := LockInfoNil{}
|
expectedNilErr := errLockNotInitialized
|
||||||
if actualErr != expectedNilErr {
|
if actualErr != expectedNilErr {
|
||||||
t.Fatalf("Errors mismatch: Expected \"%s\", got \"%s\"", expectedNilErr, actualErr)
|
t.Fatalf("Errors mismatch: Expected \"%s\", got \"%s\"", expectedNilErr, actualErr)
|
||||||
}
|
}
|
||||||
|
@ -110,6 +110,13 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
@ -222,6 +229,13 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
@ -279,6 +293,13 @@ func (api objectAPIHandlers) CopyObjectHandler(w http.ResponseWriter, r *http.Re
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
@ -399,12 +420,12 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the matching failed, it means that the X-Amz-Copy-Source was
|
// X-Amz-Copy-Source shouldn't be set for this call.
|
||||||
// wrong, fail right here.
|
|
||||||
if _, ok := r.Header["X-Amz-Copy-Source"]; ok {
|
if _, ok := r.Header["X-Amz-Copy-Source"]; ok {
|
||||||
writeErrorResponse(w, r, ErrInvalidCopySource, r.URL.Path)
|
writeErrorResponse(w, r, ErrInvalidCopySource, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
bucket := vars["bucket"]
|
bucket := vars["bucket"]
|
||||||
object := vars["object"]
|
object := vars["object"]
|
||||||
@ -416,6 +437,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
writeErrorResponse(w, r, ErrInvalidDigest, r.URL.Path)
|
writeErrorResponse(w, r, ErrInvalidDigest, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/// if Content-Length is unknown/missing, deny the request
|
/// if Content-Length is unknown/missing, deny the request
|
||||||
size := r.ContentLength
|
size := r.ContentLength
|
||||||
rAuthType := getRequestAuthType(r)
|
rAuthType := getRequestAuthType(r)
|
||||||
@ -432,6 +454,7 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
writeErrorResponse(w, r, ErrMissingContentLength, r.URL.Path)
|
writeErrorResponse(w, r, ErrMissingContentLength, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
/// maximum Upload size for objects in a single operation
|
/// maximum Upload size for objects in a single operation
|
||||||
if isMaxObjectSize(size) {
|
if isMaxObjectSize(size) {
|
||||||
writeErrorResponse(w, r, ErrEntityTooLarge, r.URL.Path)
|
writeErrorResponse(w, r, ErrEntityTooLarge, r.URL.Path)
|
||||||
@ -466,6 +489,14 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
objInfo, err = objectAPI.PutObject(bucket, object, size, reader, metadata)
|
objInfo, err = objectAPI.PutObject(bucket, object, size, reader, metadata)
|
||||||
|
case authTypeSignedV2, authTypePresignedV2:
|
||||||
|
s3Error := isReqAuthenticatedV2(r)
|
||||||
|
if s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
objInfo, err = objectAPI.PutObject(bucket, object, size, r.Body, metadata)
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
// Initialize signature verifier.
|
// Initialize signature verifier.
|
||||||
reader := newSignVerify(r)
|
reader := newSignVerify(r)
|
||||||
@ -517,6 +548,13 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
@ -626,6 +664,14 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
partMD5, err = objectAPI.PutObjectPart(bucket, object, uploadID, partID, size, reader, incomingMD5)
|
partMD5, err = objectAPI.PutObjectPart(bucket, object, uploadID, partID, size, reader, incomingMD5)
|
||||||
|
case authTypeSignedV2, authTypePresignedV2:
|
||||||
|
s3Error := isReqAuthenticatedV2(r)
|
||||||
|
if s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
partMD5, err = objectAPI.PutObjectPart(bucket, object, uploadID, partID, size, r.Body, incomingMD5)
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
// Initialize signature verifier.
|
// Initialize signature verifier.
|
||||||
reader := newSignVerify(r)
|
reader := newSignVerify(r)
|
||||||
@ -666,6 +712,13 @@ func (api objectAPIHandlers) AbortMultipartUploadHandler(w http.ResponseWriter,
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
@ -706,6 +759,13 @@ func (api objectAPIHandlers) ListObjectPartsHandler(w http.ResponseWriter, r *ht
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
@ -764,6 +824,13 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypePresigned, authTypeSigned:
|
case authTypePresigned, authTypeSigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
@ -873,6 +940,13 @@ func (api objectAPIHandlers) DeleteObjectHandler(w http.ResponseWriter, r *http.
|
|||||||
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case authTypePresignedV2, authTypeSignedV2:
|
||||||
|
// Signature V2 validation.
|
||||||
|
if s3Error := isReqAuthenticatedV2(r); s3Error != ErrNone {
|
||||||
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
writeErrorResponse(w, r, s3Error, r.URL.Path)
|
||||||
|
return
|
||||||
|
}
|
||||||
case authTypeSigned, authTypePresigned:
|
case authTypeSigned, authTypePresigned:
|
||||||
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
if s3Error := isReqAuthenticated(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||||
errorIf(errSignatureMismatch, dumpRequest(r))
|
errorIf(errSignatureMismatch, dumpRequest(r))
|
||||||
|
@ -128,7 +128,7 @@ func testAPIGetOjectHandler(obj ObjectLayer, instanceType, bucketName string, ap
|
|||||||
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
// construct HTTP request for Get Object end point.
|
// construct HTTP request for Get Object end point.
|
||||||
req, err := newTestSignedRequest("GET", getGetObjectURL("", testCase.bucketName, testCase.objectName),
|
req, err := newTestSignedRequestV4("GET", getGetObjectURL("", testCase.bucketName, testCase.objectName),
|
||||||
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -342,7 +342,7 @@ func testAPIPutObjectHandler(obj ObjectLayer, instanceType, bucketName string, a
|
|||||||
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
// construct HTTP request for Get Object end point.
|
// construct HTTP request for Get Object end point.
|
||||||
req, err := newTestSignedRequest("PUT", getPutObjectURL("", testCase.bucketName, testCase.objectName),
|
req, err := newTestSignedRequestV4("PUT", getPutObjectURL("", testCase.bucketName, testCase.objectName),
|
||||||
int64(testCase.dataLen), bytes.NewReader(testCase.data), credentials.AccessKeyID, credentials.SecretAccessKey)
|
int64(testCase.dataLen), bytes.NewReader(testCase.data), credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Test %d: Failed to create HTTP request for Put Object: <ERROR> %v", i+1, err)
|
t.Fatalf("Test %d: Failed to create HTTP request for Put Object: <ERROR> %v", i+1, err)
|
||||||
@ -484,7 +484,7 @@ func testAPICopyObjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
|||||||
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
// construct HTTP request for copy object.
|
// construct HTTP request for copy object.
|
||||||
req, err := newTestSignedRequest("PUT", getCopyObjectURL("", testCase.bucketName, testCase.newObjectName),
|
req, err := newTestSignedRequestV4("PUT", getCopyObjectURL("", testCase.bucketName, testCase.newObjectName),
|
||||||
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -529,7 +529,7 @@ func testAPINewMultipartHandler(obj ObjectLayer, instanceType, bucketName string
|
|||||||
objectName := "test-object-new-multipart"
|
objectName := "test-object-new-multipart"
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
// construct HTTP request for copy object.
|
// construct HTTP request for copy object.
|
||||||
req, err := newTestSignedRequest("POST", getNewMultipartURL("", bucketName, objectName), 0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
req, err := newTestSignedRequestV4("POST", getNewMultipartURL("", bucketName, objectName), 0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create HTTP request for copy Object: <ERROR> %v", err)
|
t.Fatalf("Failed to create HTTP request for copy Object: <ERROR> %v", err)
|
||||||
@ -581,7 +581,7 @@ func testAPINewMultipartHandlerParallel(obj ObjectLayer, instanceType, bucketNam
|
|||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
// construct HTTP request for copy object.
|
// construct HTTP request for copy object.
|
||||||
req, err := newTestSignedRequest("POST", getNewMultipartURL("", bucketName, objectName), 0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
req, err := newTestSignedRequestV4("POST", getNewMultipartURL("", bucketName, objectName), 0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create HTTP request for copy Object: <ERROR> %v", err)
|
t.Fatalf("Failed to create HTTP request for copy Object: <ERROR> %v", err)
|
||||||
@ -828,7 +828,7 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s
|
|||||||
t.Fatalf("Error XML encoding of parts: <ERROR> %s.", err)
|
t.Fatalf("Error XML encoding of parts: <ERROR> %s.", err)
|
||||||
}
|
}
|
||||||
// Indicating that all parts are uploaded and initiating completeMultipartUpload.
|
// Indicating that all parts are uploaded and initiating completeMultipartUpload.
|
||||||
req, err = newTestSignedRequest("POST", getCompleteMultipartUploadURL("", bucketName, objectName, testCase.uploadID),
|
req, err = newTestSignedRequestV4("POST", getCompleteMultipartUploadURL("", bucketName, objectName, testCase.uploadID),
|
||||||
int64(len(completeBytes)), bytes.NewReader(completeBytes), credentials.AccessKeyID, credentials.SecretAccessKey)
|
int64(len(completeBytes)), bytes.NewReader(completeBytes), credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Failed to create HTTP request for copy Object: <ERROR> %v", err)
|
t.Fatalf("Failed to create HTTP request for copy Object: <ERROR> %v", err)
|
||||||
@ -930,7 +930,7 @@ func testAPIDeleteOjectHandler(obj ObjectLayer, instanceType, bucketName string,
|
|||||||
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
// initialize HTTP NewRecorder, this records any mutations to response writer inside the handler.
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
// construct HTTP request for Get Object end point.
|
// construct HTTP request for Get Object end point.
|
||||||
req, err := newTestSignedRequest("DELETE", getDeleteObjectURL("", testCase.bucketName, testCase.objectName),
|
req, err := newTestSignedRequestV4("DELETE", getDeleteObjectURL("", testCase.bucketName, testCase.objectName),
|
||||||
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
File diff suppressed because it is too large
Load Diff
2453
cmd/server_v2_test.go
Normal file
2453
cmd/server_v2_test.go
Normal file
File diff suppressed because it is too large
Load Diff
56
cmd/signature-v2-utils.go
Normal file
56
cmd/signature-v2-utils.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2016 Minio, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Replaces any occurring '/' in string, into its encoded representation.
|
||||||
|
func percentEncodeSlash(s string) string {
|
||||||
|
return strings.Replace(s, "/", "%2F", -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryEncode - encodes query values in their URL encoded form. In
|
||||||
|
// addition to the percent encoding performed by getURLEncodedName() used
|
||||||
|
// here, it also percent encodes '/' (forward slash)
|
||||||
|
func queryEncode(v url.Values) string {
|
||||||
|
if v == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
keys := make([]string, 0, len(v))
|
||||||
|
for k := range v {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(keys)
|
||||||
|
for _, k := range keys {
|
||||||
|
vs := v[k]
|
||||||
|
prefix := percentEncodeSlash(getURLEncodedName(k)) + "="
|
||||||
|
for _, v := range vs {
|
||||||
|
if buf.Len() > 0 {
|
||||||
|
buf.WriteByte('&')
|
||||||
|
}
|
||||||
|
buf.WriteString(prefix)
|
||||||
|
buf.WriteString(percentEncodeSlash(getURLEncodedName(v)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
303
cmd/signature-v2.go
Normal file
303
cmd/signature-v2.go
Normal file
@ -0,0 +1,303 @@
|
|||||||
|
/*
|
||||||
|
* Minio Cloud Storage, (C) 2016 Minio, Inc.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha1"
|
||||||
|
"encoding/base64"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Signature and API related constants.
|
||||||
|
const (
|
||||||
|
signV2Algorithm = "AWS"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO add post policy signature.
|
||||||
|
|
||||||
|
// doesPresignV2SignatureMatch - Verify query headers with presigned signature
|
||||||
|
// - http://docs.aws.amazon.com/AmazonS3/latest/dev/RESTAuthentication.html#RESTAuthenticationQueryStringAuth
|
||||||
|
// returns ErrNone if matches. S3 errors otherwise.
|
||||||
|
func doesPresignV2SignatureMatch(r *http.Request) APIErrorCode {
|
||||||
|
// Access credentials.
|
||||||
|
cred := serverConfig.GetCredential()
|
||||||
|
|
||||||
|
// Copy request
|
||||||
|
req := *r
|
||||||
|
|
||||||
|
// Validate if we do have query params.
|
||||||
|
if req.URL.Query().Encode() == "" {
|
||||||
|
return ErrInvalidQueryParams
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate if access key id same.
|
||||||
|
if req.URL.Query().Get("AWSAccessKeyId") != cred.AccessKeyID {
|
||||||
|
return ErrInvalidAccessKeyID
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse expires param into its native form.
|
||||||
|
expired, err := strconv.ParseInt(req.URL.Query().Get("Expires"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
errorIf(err, "Unable to parse expires query param")
|
||||||
|
return ErrMalformedExpires
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate if the request has already expired.
|
||||||
|
if expired < time.Now().UTC().Unix() {
|
||||||
|
return ErrExpiredPresignRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get presigned string to sign.
|
||||||
|
stringToSign := preStringifyHTTPReq(req)
|
||||||
|
hm := hmac.New(sha1.New, []byte(cred.SecretAccessKey))
|
||||||
|
hm.Write([]byte(stringToSign))
|
||||||
|
|
||||||
|
// Calculate signature and validate.
|
||||||
|
signature := base64.StdEncoding.EncodeToString(hm.Sum(nil))
|
||||||
|
if req.URL.Query().Get("Signature") != signature {
|
||||||
|
return ErrSignatureDoesNotMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success.
|
||||||
|
return ErrNone
|
||||||
|
}
|
||||||
|
|
||||||
|
// Authorization = "AWS" + " " + AWSAccessKeyId + ":" + Signature;
|
||||||
|
// Signature = Base64( HMAC-SHA1( YourSecretAccessKeyID, UTF-8-Encoding-Of( StringToSign ) ) );
|
||||||
|
//
|
||||||
|
// StringToSign = HTTP-Verb + "\n" +
|
||||||
|
// Content-Md5 + "\n" +
|
||||||
|
// Content-Type + "\n" +
|
||||||
|
// Date + "\n" +
|
||||||
|
// CanonicalizedProtocolHeaders +
|
||||||
|
// CanonicalizedResource;
|
||||||
|
//
|
||||||
|
// CanonicalizedResource = [ "/" + Bucket ] +
|
||||||
|
// <HTTP-Request-URI, from the protocol name up to the query string> +
|
||||||
|
// [ subresource, if present. For example "?acl", "?location", "?logging", or "?torrent"];
|
||||||
|
//
|
||||||
|
// CanonicalizedProtocolHeaders = <described below>
|
||||||
|
|
||||||
|
// doesSignV2Match - Verify authorization header with calculated header in accordance with
|
||||||
|
// - http://docs.aws.amazon.com/AmazonS3/latest/dev/auth-request-sig-v2.html
|
||||||
|
// returns true if matches, false otherwise. if error is not nil then it is always false
|
||||||
|
func doesSignV2Match(r *http.Request) APIErrorCode {
|
||||||
|
// Access credentials.
|
||||||
|
cred := serverConfig.GetCredential()
|
||||||
|
|
||||||
|
// Copy request.
|
||||||
|
req := *r
|
||||||
|
|
||||||
|
// Save authorization header.
|
||||||
|
v2Auth := req.Header.Get("Authorization")
|
||||||
|
if v2Auth == "" {
|
||||||
|
return ErrAuthHeaderEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add date if not present.
|
||||||
|
if date := req.Header.Get("Date"); date == "" {
|
||||||
|
if date = req.Header.Get("X-Amz-Date"); date == "" {
|
||||||
|
return ErrMissingDateHeader
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate HMAC for secretAccessKey.
|
||||||
|
stringToSign := stringifyHTTPReq(req)
|
||||||
|
hm := hmac.New(sha1.New, []byte(cred.SecretAccessKey))
|
||||||
|
hm.Write([]byte(stringToSign))
|
||||||
|
|
||||||
|
// Prepare auth header.
|
||||||
|
authHeader := new(bytes.Buffer)
|
||||||
|
authHeader.WriteString(fmt.Sprintf("%s %s:", signV2Algorithm, cred.AccessKeyID))
|
||||||
|
encoder := base64.NewEncoder(base64.StdEncoding, authHeader)
|
||||||
|
encoder.Write(hm.Sum(nil))
|
||||||
|
encoder.Close()
|
||||||
|
|
||||||
|
// Verify if signature match.
|
||||||
|
if authHeader.String() != v2Auth {
|
||||||
|
return ErrSignatureDoesNotMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrNone
|
||||||
|
}
|
||||||
|
|
||||||
|
// From the Amazon docs:
|
||||||
|
//
|
||||||
|
// StringToSign = HTTP-Verb + "\n" +
|
||||||
|
// Content-Md5 + "\n" +
|
||||||
|
// Content-Type + "\n" +
|
||||||
|
// Expires + "\n" +
|
||||||
|
// CanonicalizedProtocolHeaders +
|
||||||
|
// CanonicalizedResource;
|
||||||
|
func preStringifyHTTPReq(req http.Request) string {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
// Write standard headers.
|
||||||
|
writePreSignV2Headers(buf, req)
|
||||||
|
// Write canonicalized protocol headers if any.
|
||||||
|
writeCanonicalizedHeaders(buf, req)
|
||||||
|
// Write canonicalized Query resources if any.
|
||||||
|
isPreSign := true
|
||||||
|
writeCanonicalizedResource(buf, req, isPreSign)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// writePreSignV2Headers - write preSign v2 required headers.
|
||||||
|
func writePreSignV2Headers(buf *bytes.Buffer, req http.Request) {
|
||||||
|
buf.WriteString(req.Method + "\n")
|
||||||
|
buf.WriteString(req.Header.Get("Content-Md5") + "\n")
|
||||||
|
buf.WriteString(req.Header.Get("Content-Type") + "\n")
|
||||||
|
buf.WriteString(req.Header.Get("Expires") + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// From the Amazon docs:
|
||||||
|
//
|
||||||
|
// StringToSign = HTTP-Verb + "\n" +
|
||||||
|
// Content-Md5 + "\n" +
|
||||||
|
// Content-Type + "\n" +
|
||||||
|
// Date + "\n" +
|
||||||
|
// CanonicalizedProtocolHeaders +
|
||||||
|
// CanonicalizedResource;
|
||||||
|
func stringifyHTTPReq(req http.Request) string {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
// Write standard headers.
|
||||||
|
writeSignV2Headers(buf, req)
|
||||||
|
// Write canonicalized protocol headers if any.
|
||||||
|
writeCanonicalizedHeaders(buf, req)
|
||||||
|
// Write canonicalized Query resources if any.
|
||||||
|
isPreSign := false
|
||||||
|
writeCanonicalizedResource(buf, req, isPreSign)
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeSignV2Headers - write signV2 required headers.
|
||||||
|
func writeSignV2Headers(buf *bytes.Buffer, req http.Request) {
|
||||||
|
buf.WriteString(req.Method + "\n")
|
||||||
|
buf.WriteString(req.Header.Get("Content-Md5") + "\n")
|
||||||
|
buf.WriteString(req.Header.Get("Content-Type") + "\n")
|
||||||
|
buf.WriteString(req.Header.Get("Date") + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeCanonicalizedHeaders - write canonicalized headers.
|
||||||
|
func writeCanonicalizedHeaders(buf *bytes.Buffer, req http.Request) {
|
||||||
|
var protoHeaders []string
|
||||||
|
vals := make(map[string][]string)
|
||||||
|
for k, vv := range req.Header {
|
||||||
|
// All the AMZ headers should be lowercase
|
||||||
|
lk := strings.ToLower(k)
|
||||||
|
if strings.HasPrefix(lk, "x-amz") {
|
||||||
|
protoHeaders = append(protoHeaders, lk)
|
||||||
|
vals[lk] = vv
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(protoHeaders)
|
||||||
|
for _, k := range protoHeaders {
|
||||||
|
buf.WriteString(k)
|
||||||
|
buf.WriteByte(':')
|
||||||
|
for idx, v := range vals[k] {
|
||||||
|
if idx > 0 {
|
||||||
|
buf.WriteByte(',')
|
||||||
|
}
|
||||||
|
if strings.Contains(v, "\n") {
|
||||||
|
// TODO: "Unfold" long headers that
|
||||||
|
// span multiple lines (as allowed by
|
||||||
|
// RFC 2616, section 4.2) by replacing
|
||||||
|
// the folding white-space (including
|
||||||
|
// new-line) by a single space.
|
||||||
|
buf.WriteString(v)
|
||||||
|
} else {
|
||||||
|
buf.WriteString(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf.WriteByte('\n')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following list is already sorted and should always be, otherwise we could
|
||||||
|
// have signature-related issues
|
||||||
|
var resourceList = []string{
|
||||||
|
"acl",
|
||||||
|
"delete",
|
||||||
|
"location",
|
||||||
|
"logging",
|
||||||
|
"notification",
|
||||||
|
"partNumber",
|
||||||
|
"policy",
|
||||||
|
"requestPayment",
|
||||||
|
"torrent",
|
||||||
|
"uploadId",
|
||||||
|
"uploads",
|
||||||
|
"versionId",
|
||||||
|
"versioning",
|
||||||
|
"versions",
|
||||||
|
"website",
|
||||||
|
}
|
||||||
|
|
||||||
|
// From the Amazon docs:
|
||||||
|
//
|
||||||
|
// CanonicalizedResource = [ "/" + Bucket ] +
|
||||||
|
// <HTTP-Request-URI, from the protocol name up to the query string> +
|
||||||
|
// [ sub-resource, if present. For example "?acl", "?location", "?logging", or "?torrent"];
|
||||||
|
func writeCanonicalizedResource(buf *bytes.Buffer, req http.Request, isPreSign bool) {
|
||||||
|
// Save request URL.
|
||||||
|
requestURL := req.URL
|
||||||
|
// Get encoded URL path.
|
||||||
|
path := getURLEncodedName(requestURL.Path)
|
||||||
|
if isPreSign {
|
||||||
|
// Get encoded URL path.
|
||||||
|
if len(requestURL.Query()) > 0 {
|
||||||
|
// Keep the usual queries unescaped for string to sign.
|
||||||
|
query, _ := url.QueryUnescape(queryEncode(requestURL.Query()))
|
||||||
|
path = path + "?" + query
|
||||||
|
}
|
||||||
|
buf.WriteString(path)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
buf.WriteString(path)
|
||||||
|
if requestURL.RawQuery != "" {
|
||||||
|
var n int
|
||||||
|
vals, _ := url.ParseQuery(requestURL.RawQuery)
|
||||||
|
// Verify if any sub resource queries are present, if yes
|
||||||
|
// canonicallize them.
|
||||||
|
for _, resource := range resourceList {
|
||||||
|
if vv, ok := vals[resource]; ok && len(vv) > 0 {
|
||||||
|
n++
|
||||||
|
// First element
|
||||||
|
switch n {
|
||||||
|
case 1:
|
||||||
|
buf.WriteByte('?')
|
||||||
|
// The rest
|
||||||
|
default:
|
||||||
|
buf.WriteByte('&')
|
||||||
|
}
|
||||||
|
buf.WriteString(resource)
|
||||||
|
// Request parameters
|
||||||
|
if len(vv[0]) > 0 {
|
||||||
|
buf.WriteByte('=')
|
||||||
|
buf.WriteString(strings.Replace(url.QueryEscape(vv[0]), "+", "%20", -1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
159
cmd/signature-v2_test.go
Normal file
159
cmd/signature-v2_test.go
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"sort"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tests for 'func TestResourceListSorting(t *testing.T)'.
|
||||||
|
func TestResourceListSorting(t *testing.T) {
|
||||||
|
sortedResourceList := make([]string, len(resourceList))
|
||||||
|
copy(sortedResourceList, resourceList)
|
||||||
|
sort.Strings(sortedResourceList)
|
||||||
|
for i := 0; i < len(resourceList); i++ {
|
||||||
|
if resourceList[i] != sortedResourceList[i] {
|
||||||
|
t.Errorf("Expected resourceList[%d] = \"%s\", resourceList is not correctly sorted.", i, sortedResourceList[i])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests validate the query encoding.
|
||||||
|
func TestQueryEncode(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
// Input.
|
||||||
|
input url.Values
|
||||||
|
// Expected result.
|
||||||
|
result string
|
||||||
|
}{
|
||||||
|
// % should be encoded as %25
|
||||||
|
{url.Values{
|
||||||
|
"key": []string{"thisisthe%url"},
|
||||||
|
}, "key=thisisthe%25url"},
|
||||||
|
// UTF-8 encoding.
|
||||||
|
{url.Values{
|
||||||
|
"key": []string{"本語"},
|
||||||
|
}, "key=%E6%9C%AC%E8%AA%9E"},
|
||||||
|
// UTF-8 encoding with ASCII.
|
||||||
|
{url.Values{
|
||||||
|
"key": []string{"本語.1"},
|
||||||
|
}, "key=%E6%9C%AC%E8%AA%9E.1"},
|
||||||
|
// Unusual ASCII characters.
|
||||||
|
{url.Values{
|
||||||
|
"key": []string{">123"},
|
||||||
|
}, "key=%3E123"},
|
||||||
|
// Fragment path characters.
|
||||||
|
{url.Values{
|
||||||
|
"key": []string{"myurl#link"},
|
||||||
|
}, "key=myurl%23link"},
|
||||||
|
// Space should be set to %20 not '+'.
|
||||||
|
{url.Values{
|
||||||
|
"key": []string{"space in url"},
|
||||||
|
}, "key=space%20in%20url"},
|
||||||
|
// '+' shouldn't be treated as space.
|
||||||
|
{url.Values{
|
||||||
|
"key": []string{"url+path"},
|
||||||
|
}, "key=url%2Bpath"},
|
||||||
|
// '/' shouldn't be treated as '/' should be percent coded.
|
||||||
|
{url.Values{
|
||||||
|
"key": []string{"url/+path"},
|
||||||
|
}, "key=url%2F%2Bpath"},
|
||||||
|
// Values is empty and empty string.
|
||||||
|
{nil, ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests generated values from url encoded name.
|
||||||
|
for i, testCase := range testCases {
|
||||||
|
result := queryEncode(testCase.input)
|
||||||
|
if testCase.result != result {
|
||||||
|
t.Errorf("Test %d: Expected queryEncoded result to be \"%s\", but found it to be \"%s\" instead", i+1, testCase.result, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDoesPresignedV2SignatureMatch(t *testing.T) {
|
||||||
|
root, err := newTestConfig("us-east-1")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("Unable to initialize test config.")
|
||||||
|
}
|
||||||
|
defer removeAll(root)
|
||||||
|
|
||||||
|
now := time.Now().UTC()
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
queryParams map[string]string
|
||||||
|
headers map[string]string
|
||||||
|
expected APIErrorCode
|
||||||
|
}{
|
||||||
|
// (0) Should error without a set URL query.
|
||||||
|
{
|
||||||
|
expected: ErrInvalidQueryParams,
|
||||||
|
},
|
||||||
|
// (1) Should error on an invalid access key.
|
||||||
|
{
|
||||||
|
queryParams: map[string]string{
|
||||||
|
"Expires": "60",
|
||||||
|
"Signature": "badsignature",
|
||||||
|
"AWSAccessKeyId": "Z7IXGOO6BZ0REAN1Q26I",
|
||||||
|
},
|
||||||
|
expected: ErrInvalidAccessKeyID,
|
||||||
|
},
|
||||||
|
// (2) Should error with malformed expires.
|
||||||
|
{
|
||||||
|
queryParams: map[string]string{
|
||||||
|
"Expires": "60s",
|
||||||
|
"Signature": "badsignature",
|
||||||
|
"AWSAccessKeyId": serverConfig.GetCredential().AccessKeyID,
|
||||||
|
},
|
||||||
|
expected: ErrMalformedExpires,
|
||||||
|
},
|
||||||
|
// (3) Should give an expired request if it has expired.
|
||||||
|
{
|
||||||
|
queryParams: map[string]string{
|
||||||
|
"Expires": "60",
|
||||||
|
"Signature": "badsignature",
|
||||||
|
"AWSAccessKeyId": serverConfig.GetCredential().AccessKeyID,
|
||||||
|
},
|
||||||
|
expected: ErrExpiredPresignRequest,
|
||||||
|
},
|
||||||
|
// (4) Should error when the signature does not match.
|
||||||
|
{
|
||||||
|
queryParams: map[string]string{
|
||||||
|
"Expires": fmt.Sprintf("%d", now.Unix()+60),
|
||||||
|
"Signature": "badsignature",
|
||||||
|
"AWSAccessKeyId": serverConfig.GetCredential().AccessKeyID,
|
||||||
|
},
|
||||||
|
expected: ErrSignatureDoesNotMatch,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run each test case individually.
|
||||||
|
for i, testCase := range testCases {
|
||||||
|
// Turn the map[string]string into map[string][]string, because Go.
|
||||||
|
query := url.Values{}
|
||||||
|
for key, value := range testCase.queryParams {
|
||||||
|
query.Set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a request to use.
|
||||||
|
req, e := http.NewRequest(http.MethodGet, "http://host/a/b?"+query.Encode(), nil)
|
||||||
|
if e != nil {
|
||||||
|
t.Errorf("(%d) failed to create http.Request, got %v", i, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the same for the headers.
|
||||||
|
for key, value := range testCase.headers {
|
||||||
|
req.Header.Set(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it matches!
|
||||||
|
err := doesPresignV2SignatureMatch(req)
|
||||||
|
if err != testCase.expected {
|
||||||
|
t.Errorf("(%d) expected to get %s, instead got %s", i, niceError(testCase.expected), niceError(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestSkipContentSha256Cksum - Test validate the logic which decides whether to skip checksum validation based on the request header.
|
// TestSkipContentSha256Cksum - Test validate the logic which decides whether
|
||||||
|
// to skip checksum validation based on the request header.
|
||||||
func TestSkipContentSha256Cksum(t *testing.T) {
|
func TestSkipContentSha256Cksum(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
inputHeaderKey string
|
inputHeaderKey string
|
||||||
@ -124,7 +125,7 @@ func TestGetURLEncodedName(t *testing.T) {
|
|||||||
for i, testCase := range testCases {
|
for i, testCase := range testCases {
|
||||||
result := getURLEncodedName(testCase.inputStr)
|
result := getURLEncodedName(testCase.inputStr)
|
||||||
if testCase.result != result {
|
if testCase.result != result {
|
||||||
t.Errorf("Test %d: Expected queryEncode result to be \"%s\", but found it to be \"%s\" instead", i+1, testCase.result, result)
|
t.Errorf("Test %d: Expected URLEncoded result to be \"%s\", but found it to be \"%s\" instead", i+1, testCase.result, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ package cmd
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/hmac"
|
||||||
|
"crypto/sha1"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
@ -31,14 +33,12 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
"unicode/utf8"
|
|
||||||
|
|
||||||
router "github.com/gorilla/mux"
|
router "github.com/gorilla/mux"
|
||||||
)
|
)
|
||||||
@ -476,8 +476,76 @@ func newTestStreamingSignedRequest(method, urlStr string, contentLength, chunkSi
|
|||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// preSignV2 - presign the request in following style.
|
||||||
|
// https://${S3_BUCKET}.s3.amazonaws.com/${S3_OBJECT}?AWSAccessKeyId=${S3_ACCESS_KEY}&Expires=${TIMESTAMP}&Signature=${SIGNATURE}.
|
||||||
|
func preSignV2(req *http.Request, accessKeyID, secretAccessKey string, expires int64) error {
|
||||||
|
// Presign is not needed for anonymous credentials.
|
||||||
|
if accessKeyID == "" || secretAccessKey == "" {
|
||||||
|
return errors.New("Presign cannot be generated without access and secret keys")
|
||||||
|
}
|
||||||
|
|
||||||
|
d := time.Now().UTC()
|
||||||
|
// Find epoch expires when the request will expire.
|
||||||
|
epochExpires := d.Unix() + expires
|
||||||
|
|
||||||
|
// Add expires header if not present.
|
||||||
|
if expiresStr := req.Header.Get("Expires"); expiresStr == "" {
|
||||||
|
req.Header.Set("Expires", strconv.FormatInt(epochExpires, 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get presigned string to sign.
|
||||||
|
stringToSign := preStringifyHTTPReq(*req)
|
||||||
|
hm := hmac.New(sha1.New, []byte(secretAccessKey))
|
||||||
|
hm.Write([]byte(stringToSign))
|
||||||
|
|
||||||
|
// Calculate signature.
|
||||||
|
signature := base64.StdEncoding.EncodeToString(hm.Sum(nil))
|
||||||
|
|
||||||
|
query := req.URL.Query()
|
||||||
|
// Handle specially for Google Cloud Storage.
|
||||||
|
query.Set("AWSAccessKeyId", accessKeyID)
|
||||||
|
// Fill in Expires for presigned query.
|
||||||
|
query.Set("Expires", strconv.FormatInt(epochExpires, 10))
|
||||||
|
|
||||||
|
// Encode query and save.
|
||||||
|
req.URL.RawQuery = queryEncode(query)
|
||||||
|
|
||||||
|
// Save signature finally.
|
||||||
|
req.URL.RawQuery += "&Signature=" + getURLEncodedName(signature)
|
||||||
|
|
||||||
|
// Success.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign given request using Signature V2.
|
||||||
|
func signRequestV2(req *http.Request, accessKey, secretKey string) error {
|
||||||
|
// Initial time.
|
||||||
|
d := time.Now().UTC()
|
||||||
|
|
||||||
|
// Add date if not present.
|
||||||
|
if date := req.Header.Get("Date"); date == "" {
|
||||||
|
req.Header.Set("Date", d.Format(http.TimeFormat))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate HMAC for secretAccessKey.
|
||||||
|
stringToSign := stringifyHTTPReq(*req)
|
||||||
|
hm := hmac.New(sha1.New, []byte(secretKey))
|
||||||
|
hm.Write([]byte(stringToSign))
|
||||||
|
|
||||||
|
// Prepare auth header.
|
||||||
|
authHeader := new(bytes.Buffer)
|
||||||
|
authHeader.WriteString(fmt.Sprintf("%s %s:", signV2Algorithm, accessKey))
|
||||||
|
encoder := base64.NewEncoder(base64.StdEncoding, authHeader)
|
||||||
|
encoder.Write(hm.Sum(nil))
|
||||||
|
encoder.Close()
|
||||||
|
|
||||||
|
// Set Authorization header.
|
||||||
|
req.Header.Set("Authorization", authHeader.String())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// Sign given request using Signature V4.
|
// Sign given request using Signature V4.
|
||||||
func signRequest(req *http.Request, accessKey, secretKey string) error {
|
func signRequestV4(req *http.Request, accessKey, secretKey string) error {
|
||||||
// Get hashed payload.
|
// Get hashed payload.
|
||||||
hashedPayload := req.Header.Get("x-amz-content-sha256")
|
hashedPayload := req.Header.Get("x-amz-content-sha256")
|
||||||
if hashedPayload == "" {
|
if hashedPayload == "" {
|
||||||
@ -611,9 +679,9 @@ func newTestRequest(method, urlStr string, contentLength int64, body io.ReadSeek
|
|||||||
case body == nil:
|
case body == nil:
|
||||||
hashedPayload = hex.EncodeToString(sum256([]byte{}))
|
hashedPayload = hex.EncodeToString(sum256([]byte{}))
|
||||||
default:
|
default:
|
||||||
payloadBytes, e := ioutil.ReadAll(body)
|
payloadBytes, err := ioutil.ReadAll(body)
|
||||||
if e != nil {
|
if err != nil {
|
||||||
return nil, e
|
return nil, err
|
||||||
}
|
}
|
||||||
hashedPayload = hex.EncodeToString(sum256(payloadBytes))
|
hashedPayload = hex.EncodeToString(sum256(payloadBytes))
|
||||||
md5Base64 := base64.StdEncoding.EncodeToString(sumMD5(payloadBytes))
|
md5Base64 := base64.StdEncoding.EncodeToString(sumMD5(payloadBytes))
|
||||||
@ -635,8 +703,50 @@ func newTestRequest(method, urlStr string, contentLength int64, body io.ReadSeek
|
|||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newTestSignedRequestV2ContentType(method, urlStr string, contentLength int64, body io.ReadSeeker, accessKey, secretKey, contentType string) (*http.Request, error) {
|
||||||
|
req, err := newTestRequest(method, urlStr, contentLength, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Del("x-amz-content-sha256")
|
||||||
|
req.Header.Set("Content-Type", contentType)
|
||||||
|
|
||||||
|
// Anonymous request return quickly.
|
||||||
|
if accessKey == "" || secretKey == "" {
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = signRequestV2(req, accessKey, secretKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns new HTTP request object signed with signature v2.
|
||||||
|
func newTestSignedRequestV2(method, urlStr string, contentLength int64, body io.ReadSeeker, accessKey, secretKey string) (*http.Request, error) {
|
||||||
|
req, err := newTestRequest(method, urlStr, contentLength, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
req.Header.Del("x-amz-content-sha256")
|
||||||
|
|
||||||
|
// Anonymous request return quickly.
|
||||||
|
if accessKey == "" || secretKey == "" {
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = signRequestV2(req, accessKey, secretKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Returns new HTTP request object signed with signature v4.
|
// Returns new HTTP request object signed with signature v4.
|
||||||
func newTestSignedRequest(method, urlStr string, contentLength int64, body io.ReadSeeker, accessKey, secretKey string) (*http.Request, error) {
|
func newTestSignedRequestV4(method, urlStr string, contentLength int64, body io.ReadSeeker, accessKey, secretKey string) (*http.Request, error) {
|
||||||
req, err := newTestRequest(method, urlStr, contentLength, body)
|
req, err := newTestRequest(method, urlStr, contentLength, body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -647,7 +757,7 @@ func newTestSignedRequest(method, urlStr string, contentLength int64, body io.Re
|
|||||||
return req, nil
|
return req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
err = signRequest(req, accessKey, secretKey)
|
err = signRequestV4(req, accessKey, secretKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -842,71 +952,6 @@ func (t *EOFWriter) Write(p []byte) (n int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// queryEncode - encodes query values in their URL encoded form.
|
|
||||||
func queryEncode(v url.Values) string {
|
|
||||||
if v == nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
var buf bytes.Buffer
|
|
||||||
keys := make([]string, 0, len(v))
|
|
||||||
for k := range v {
|
|
||||||
keys = append(keys, k)
|
|
||||||
}
|
|
||||||
sort.Strings(keys)
|
|
||||||
for _, k := range keys {
|
|
||||||
vs := v[k]
|
|
||||||
prefix := urlEncodePath(k) + "="
|
|
||||||
for _, v := range vs {
|
|
||||||
if buf.Len() > 0 {
|
|
||||||
buf.WriteByte('&')
|
|
||||||
}
|
|
||||||
buf.WriteString(prefix)
|
|
||||||
buf.WriteString(urlEncodePath(v))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// urlEncodePath encode the strings from UTF-8 byte representations to HTML hex escape sequences
|
|
||||||
//
|
|
||||||
// This is necessary since regular url.Parse() and url.Encode() functions do not support UTF-8
|
|
||||||
// non english characters cannot be parsed due to the nature in which url.Encode() is written
|
|
||||||
//
|
|
||||||
// This function on the other hand is a direct replacement for url.Encode() technique to support
|
|
||||||
// pretty much every UTF-8 character.
|
|
||||||
func urlEncodePath(pathName string) string {
|
|
||||||
// if object matches reserved string, no need to encode them
|
|
||||||
reservedNames := regexp.MustCompile("^[a-zA-Z0-9-_.~/]+$")
|
|
||||||
if reservedNames.MatchString(pathName) {
|
|
||||||
return pathName
|
|
||||||
}
|
|
||||||
var encodedPathname string
|
|
||||||
for _, s := range pathName {
|
|
||||||
if 'A' <= s && s <= 'Z' || 'a' <= s && s <= 'z' || '0' <= s && s <= '9' { // §2.3 Unreserved characters (mark)
|
|
||||||
encodedPathname = encodedPathname + string(s)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch s {
|
|
||||||
case '-', '_', '.', '~', '/': // §2.3 Unreserved characters (mark)
|
|
||||||
encodedPathname = encodedPathname + string(s)
|
|
||||||
continue
|
|
||||||
default:
|
|
||||||
len := utf8.RuneLen(s)
|
|
||||||
if len < 0 {
|
|
||||||
// if utf8 cannot convert return the same string as is
|
|
||||||
return pathName
|
|
||||||
}
|
|
||||||
u := make([]byte, len)
|
|
||||||
utf8.EncodeRune(u, s)
|
|
||||||
for _, r := range u {
|
|
||||||
hex := hex.EncodeToString([]byte{r})
|
|
||||||
encodedPathname = encodedPathname + "%" + strings.ToUpper(hex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return encodedPathname
|
|
||||||
}
|
|
||||||
|
|
||||||
// construct URL for http requests for bucket operations.
|
// construct URL for http requests for bucket operations.
|
||||||
func makeTestTargetURL(endPoint, bucketName, objectName string, queryValues url.Values) string {
|
func makeTestTargetURL(endPoint, bucketName, objectName string, queryValues url.Values) string {
|
||||||
urlStr := endPoint + "/"
|
urlStr := endPoint + "/"
|
||||||
@ -914,7 +959,7 @@ func makeTestTargetURL(endPoint, bucketName, objectName string, queryValues url.
|
|||||||
urlStr = urlStr + bucketName + "/"
|
urlStr = urlStr + bucketName + "/"
|
||||||
}
|
}
|
||||||
if objectName != "" {
|
if objectName != "" {
|
||||||
urlStr = urlStr + urlEncodePath(objectName)
|
urlStr = urlStr + getURLEncodedName(objectName)
|
||||||
}
|
}
|
||||||
if len(queryValues) > 0 {
|
if len(queryValues) > 0 {
|
||||||
urlStr = urlStr + "?" + queryEncode(queryValues)
|
urlStr = urlStr + "?" + queryEncode(queryValues)
|
||||||
|
Loading…
Reference in New Issue
Block a user