mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
tests: Add helper function for API handler anonymous request tests. (#2876)
- Add helper function for API handler anonymous request tests. - Add PutObject Part Anonymous request case using the new helper function to validate its functionality.
This commit is contained in:
parent
f1bc9343a1
commit
d1df5e0ae1
@ -1276,92 +1276,6 @@ func testAPIPutObjectPartHandlerStreaming(obj ObjectLayer, instanceType, bucketN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAPIPutObjectPartHandlerAnon - Tests validate the response of PutObjectPart HTTP handler
|
|
||||||
// when the request type is anonymous/unsigned.
|
|
||||||
func TestAPIPutObjectPartHandlerAnon(t *testing.T) {
|
|
||||||
ExecObjectLayerAPITest(t, testAPIPutObjectPartHandlerAnon, []string{"PutObjectPart", "NewMultipart"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAPIPutObjectPartHandlerAnon(obj ObjectLayer, instanceType, bucketName string, apiRouter http.Handler,
|
|
||||||
credentials credential, t *testing.T) {
|
|
||||||
// Initialize bucket policies for anonymous request test
|
|
||||||
err := initBucketPolicies(obj)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to initialize bucket policies: <ERROR> %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
testObject := "testobject"
|
|
||||||
rec := httptest.NewRecorder()
|
|
||||||
req, err := newTestSignedRequestV4("POST", getNewMultipartURL("", bucketName, "testobject"),
|
|
||||||
0, nil, credentials.AccessKeyID, credentials.SecretAccessKey)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] - Failed to create a signed request to initiate multipart upload for %s/%s: <ERROR> %v",
|
|
||||||
instanceType, bucketName, testObject, err)
|
|
||||||
}
|
|
||||||
apiRouter.ServeHTTP(rec, req)
|
|
||||||
|
|
||||||
// Get uploadID of the mulitpart upload initiated.
|
|
||||||
var mpartResp InitiateMultipartUploadResponse
|
|
||||||
mpartRespBytes, err := ioutil.ReadAll(rec.Result().Body)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Failed to read NewMultipartUpload response <ERROR> %v", instanceType, err)
|
|
||||||
}
|
|
||||||
err = xml.Unmarshal(mpartRespBytes, &mpartResp)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("[%s] Failed to unmarshal NewMultipartUpload response <ERROR> %v", instanceType, err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
accessDeniedErr := getAPIError(ErrAccessDenied)
|
|
||||||
anonRec := httptest.NewRecorder()
|
|
||||||
anonReq, aErr := newTestRequest("PUT",
|
|
||||||
getPutObjectPartURL("", bucketName, testObject, mpartResp.UploadID, "1"),
|
|
||||||
int64(len("hello")), bytes.NewReader([]byte("hello")))
|
|
||||||
if aErr != nil {
|
|
||||||
t.Fatalf("Test %d %s Failed to create an anonymous request to upload part for %s/%s: <ERROR> %v",
|
|
||||||
1, instanceType, bucketName, testObject, aErr)
|
|
||||||
}
|
|
||||||
apiRouter.ServeHTTP(anonRec, anonReq)
|
|
||||||
|
|
||||||
anonErrBytes, err := ioutil.ReadAll(anonRec.Result().Body)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Test %d %s Failed to read error response from upload part request %s/%s: <ERROR> %v",
|
|
||||||
1, instanceType, bucketName, testObject, err)
|
|
||||||
}
|
|
||||||
var anonErrXML APIErrorResponse
|
|
||||||
err = xml.Unmarshal(anonErrBytes, &anonErrXML)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Test %d %s Failed to unmarshal error response from upload part request %s/%s: <ERROR> %v",
|
|
||||||
1, instanceType, bucketName, testObject, err)
|
|
||||||
}
|
|
||||||
if accessDeniedErr.Code != anonErrXML.Code {
|
|
||||||
t.Errorf("Test %d %s expected to fail with error %s, but received %s", 1, instanceType,
|
|
||||||
accessDeniedErr.Code, anonErrXML.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set write only policy on bucket to allow anonymous PutObjectPart API
|
|
||||||
// request to go through.
|
|
||||||
writeOnlyPolicy := bucketPolicy{
|
|
||||||
Version: "1.0",
|
|
||||||
Statements: []policyStatement{getWriteOnlyObjectStatement(bucketName, "")},
|
|
||||||
}
|
|
||||||
globalBucketPolicies.SetBucketPolicy(bucketName, &writeOnlyPolicy)
|
|
||||||
|
|
||||||
anonRec = httptest.NewRecorder()
|
|
||||||
anonReq, aErr = newTestRequest("PUT",
|
|
||||||
getPutObjectPartURL("", bucketName, testObject, mpartResp.UploadID, "1"),
|
|
||||||
int64(len("hello")), bytes.NewReader([]byte("hello")))
|
|
||||||
if aErr != nil {
|
|
||||||
t.Fatalf("Test %d %s Failed to create an anonymous request to upload part for %s/%s: <ERROR> %v",
|
|
||||||
1, instanceType, bucketName, testObject, aErr)
|
|
||||||
}
|
|
||||||
apiRouter.ServeHTTP(anonRec, anonReq)
|
|
||||||
if anonRec.Code != http.StatusOK {
|
|
||||||
t.Errorf("Test %d %s expected PutObject Part with authAnonymous type to succeed but failed with "+
|
|
||||||
"HTTP status code %d", 1, instanceType, anonRec.Code)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestAPIPutObjectPartHandler - Tests validate the response of PutObjectPart HTTP handler
|
// TestAPIPutObjectPartHandler - Tests validate the response of PutObjectPart HTTP handler
|
||||||
// for variety of inputs.
|
// for variety of inputs.
|
||||||
func TestAPIPutObjectPartHandler(t *testing.T) {
|
func TestAPIPutObjectPartHandler(t *testing.T) {
|
||||||
@ -1383,6 +1297,8 @@ func testAPIPutObjectPartHandler(obj ObjectLayer, instanceType, bucketName strin
|
|||||||
t.Fatalf("Minio %s : <ERROR> %s", instanceType, err)
|
t.Fatalf("Minio %s : <ERROR> %s", instanceType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uploadIDCopy := uploadID
|
||||||
|
|
||||||
// expected error types for invalid inputs to PutObjectPartHandler.
|
// expected error types for invalid inputs to PutObjectPartHandler.
|
||||||
noAPIErr := APIError{}
|
noAPIErr := APIError{}
|
||||||
// expected error when content length is missing in the HTTP request.
|
// expected error when content length is missing in the HTTP request.
|
||||||
@ -1554,6 +1470,19 @@ func testAPIPutObjectPartHandler(obj ObjectLayer, instanceType, bucketName strin
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test for Anonymous/unsigned http request.
|
||||||
|
anonReq, err := newTestRequest("PUT", getPutObjectPartURL("", bucketName, testObject, uploadIDCopy, "1"),
|
||||||
|
int64(len("hello")), bytes.NewReader([]byte("hello")))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Minio %s: Failed to create an anonymous request to upload part for %s/%s: <ERROR> %v",
|
||||||
|
instanceType, bucketName, testObject, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExecObjectLayerAPIAnonTest - Calls the HTTP API handler using the anonymous request, validates the ErrAccessDeniedResponse,
|
||||||
|
// sets the bucket policy using the policy statement generated from `getWriteOnlyObjectStatement` so that the
|
||||||
|
// unsigned request goes through and its validated again.
|
||||||
|
ExecObjectLayerAPIAnonTest(t, "TestAPIPutObjectPartHandler", bucketName, testObject, instanceType, apiRouter, anonReq, getWriteOnlyObjectStatement)
|
||||||
|
|
||||||
// HTTP request for testing when `ObjectLayer` is set to `nil`.
|
// HTTP request for testing when `ObjectLayer` is set to `nil`.
|
||||||
// There is no need to use an existing bucket and valid input for creating the request
|
// There is no need to use an existing bucket and valid input for creating the request
|
||||||
// since the `objectLayer==nil` check is performed before any other checks inside the handlers.
|
// since the `objectLayer==nil` check is performed before any other checks inside the handlers.
|
||||||
@ -1561,8 +1490,7 @@ func testAPIPutObjectPartHandler(obj ObjectLayer, instanceType, bucketName strin
|
|||||||
nilBucket := "dummy-bucket"
|
nilBucket := "dummy-bucket"
|
||||||
nilObject := "dummy-object"
|
nilObject := "dummy-object"
|
||||||
|
|
||||||
nilReq, err := newTestSignedRequestV4("PUT",
|
nilReq, err := newTestSignedRequestV4("PUT", getPutObjectPartURL("", nilBucket, nilObject, "0", "0"),
|
||||||
getPutObjectPartURL("", nilBucket, nilObject, "0", "0"),
|
|
||||||
0, bytes.NewReader([]byte("testNilObjLayer")), "", "")
|
0, bytes.NewReader([]byte("testNilObjLayer")), "", "")
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1571,6 +1499,7 @@ func testAPIPutObjectPartHandler(obj ObjectLayer, instanceType, bucketName strin
|
|||||||
// execute the object layer set to `nil` test.
|
// execute the object layer set to `nil` test.
|
||||||
// `ExecObjectLayerAPINilTest` manages the operation.
|
// `ExecObjectLayerAPINilTest` manages the operation.
|
||||||
ExecObjectLayerAPINilTest(t, nilBucket, nilObject, instanceType, apiRouter, nilReq)
|
ExecObjectLayerAPINilTest(t, nilBucket, nilObject, instanceType, apiRouter, nilReq)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestAPIListObjectPartsHandlerPreSign - Tests validate the response of ListObjectParts HTTP handler
|
// TestAPIListObjectPartsHandlerPreSign - Tests validate the response of ListObjectParts HTTP handler
|
||||||
@ -1949,6 +1878,7 @@ func testAPIListObjectPartsHandlerAnon(obj ObjectLayer, instanceType, bucketName
|
|||||||
t.Fatalf("Test %d %s Failed to create a signed request to upload part for %s/%s: <ERROR> %v", 1, instanceType,
|
t.Fatalf("Test %d %s Failed to create a signed request to upload part for %s/%s: <ERROR> %v", 1, instanceType,
|
||||||
bucketName, testObject, err)
|
bucketName, testObject, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attempt an anonymous ListObjectParts API request to trigger AccessDenied error.
|
// Attempt an anonymous ListObjectParts API request to trigger AccessDenied error.
|
||||||
accessDeniedErr := getAPIError(ErrAccessDenied)
|
accessDeniedErr := getAPIError(ErrAccessDenied)
|
||||||
anonRec := httptest.NewRecorder()
|
anonRec := httptest.NewRecorder()
|
||||||
@ -1959,6 +1889,7 @@ func testAPIListObjectPartsHandlerAnon(obj ObjectLayer, instanceType, bucketName
|
|||||||
t.Fatalf("Test %d %s Failed to create an anonymous request to list multipart of an upload for %s/%s: <ERROR> %v",
|
t.Fatalf("Test %d %s Failed to create an anonymous request to list multipart of an upload for %s/%s: <ERROR> %v",
|
||||||
1, instanceType, bucketName, testObject, aErr)
|
1, instanceType, bucketName, testObject, aErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
apiRouter.ServeHTTP(anonRec, anonReq)
|
apiRouter.ServeHTTP(anonRec, anonReq)
|
||||||
anonErrBytes, err := ioutil.ReadAll(anonRec.Result().Body)
|
anonErrBytes, err := ioutil.ReadAll(anonRec.Result().Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -1387,6 +1387,76 @@ func initAPIHandlerTest(obj ObjectLayer, endPoints []string) (bucketName, rootPa
|
|||||||
return bucketName, rootPath, apiRouter, nil
|
return bucketName, rootPath, apiRouter, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExecObjectLayerAPIAnonTest - Helper function to validate object Layer API handler response for anonymous/unsigned HTTP request.
|
||||||
|
// Here is the brief description of some of the arguments to the function below.
|
||||||
|
// apiRouter - http.Handler with the relevant API endPoint (API endPoint under test) registered.
|
||||||
|
// anonReq - unsigned *http.Request to invoke the handler's response for anonymous requests.
|
||||||
|
// policyFunc - function to return bucketPolicy statement which would permit the anonymous request to be served.
|
||||||
|
// The test works in 2 steps, here is the description of the steps.
|
||||||
|
// STEP 1: Call the handler with the unsigned HTTP request (anonReq), assert for the `ErrAccessDenied` error response.
|
||||||
|
// STEP 2: Set the policy to allow the unsigned request, use the policyFunc to obtain the relevant statement and call the handler again to verify its success.
|
||||||
|
func ExecObjectLayerAPIAnonTest(t *testing.T, testName, bucketName, objectName, instanceType string, apiRouter http.Handler,
|
||||||
|
anonReq *http.Request, policyFunc func(string, string) policyStatement) {
|
||||||
|
// simple function which ends the test by printing the common message which gives the context of the test
|
||||||
|
// and then followed by the the actual error message.
|
||||||
|
failTest := func(failMsg string) {
|
||||||
|
t.Fatalf("Minio %s: Anonymous HTTP request test Fail for \"%s\": \n<Error> %s.", instanceType, testName, failMsg)
|
||||||
|
}
|
||||||
|
// httptest Recorder to capture all the response by the http handler.
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
// reading the body to preserve it so that it can be used again for second attempt of sending unsigned HTTP request.
|
||||||
|
// If the body is read in the handler the same request cannot be made use of.
|
||||||
|
buf, err := ioutil.ReadAll(anonReq.Body)
|
||||||
|
if err != nil {
|
||||||
|
failTest(err.Error())
|
||||||
|
}
|
||||||
|
// creating 2 read closer (to set as request body) from the body content.
|
||||||
|
readerOne := ioutil.NopCloser(bytes.NewBuffer(buf))
|
||||||
|
readerTwo := ioutil.NopCloser(bytes.NewBuffer(buf))
|
||||||
|
|
||||||
|
anonReq.Body = readerOne
|
||||||
|
|
||||||
|
// call the HTTP handler.
|
||||||
|
apiRouter.ServeHTTP(rec, anonReq)
|
||||||
|
|
||||||
|
// expected error response when the unsigned HTTP request is not permitted.
|
||||||
|
accesDeniedHTTPStatus := getAPIError(ErrAccessDenied).HTTPStatusCode
|
||||||
|
if rec.Code != accesDeniedHTTPStatus {
|
||||||
|
failTest(fmt.Sprintf("Object API Nil Test expected to fail with %d, but failed with %d.", accesDeniedHTTPStatus, rec.Code))
|
||||||
|
}
|
||||||
|
|
||||||
|
// expected error response in bytes when objectLayer is not initialized, or set to `nil`.
|
||||||
|
expectedErrResponse := encodeResponse(getAPIErrorResponse(getAPIError(ErrAccessDenied), getGetObjectURL("", bucketName, objectName)))
|
||||||
|
|
||||||
|
// read the response body.
|
||||||
|
actualContent, err := ioutil.ReadAll(rec.Body)
|
||||||
|
if err != nil {
|
||||||
|
failTest(fmt.Sprintf("Failed parsing response body: <ERROR> %v.", err))
|
||||||
|
}
|
||||||
|
// verify whether actual error response (from the response body), matches the expected error response.
|
||||||
|
if !bytes.Equal(expectedErrResponse, actualContent) {
|
||||||
|
failTest("Object content differs from expected value.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set write only policy on bucket to allow anonymous HTTP request for the operation under test.
|
||||||
|
// request to go through.
|
||||||
|
policy := bucketPolicy{
|
||||||
|
Version: "1.0",
|
||||||
|
Statements: []policyStatement{policyFunc(bucketName, "")},
|
||||||
|
}
|
||||||
|
globalBucketPolicies.SetBucketPolicy(bucketName, &policy)
|
||||||
|
// now call the handler again with the unsigned/anonymous request, it should be accepted.
|
||||||
|
rec = httptest.NewRecorder()
|
||||||
|
|
||||||
|
anonReq.Body = readerTwo
|
||||||
|
|
||||||
|
apiRouter.ServeHTTP(rec, anonReq)
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
failTest(fmt.Sprintf("Expected the anonymous HTTP request to be served after the policy changes\n,Expected response HTTP status code to be %d, got %d.",
|
||||||
|
http.StatusOK, rec.Code))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ExecObjectLayerAPINilTest - Sets the object layer to `nil`, and calls rhe registered object layer API endpoint, and assert the error response.
|
// ExecObjectLayerAPINilTest - Sets the object layer to `nil`, and calls rhe registered object layer API endpoint, and assert the error response.
|
||||||
// The purpose is to validate the API handlers response when the object layer is uninitialized.
|
// The purpose is to validate the API handlers response when the object layer is uninitialized.
|
||||||
// Usage hint: Should be used at the end of the API end points tests (ex: check the last few lines of `testAPIListObjectPartsHandler`), need a sample HTTP request
|
// Usage hint: Should be used at the end of the API end points tests (ex: check the last few lines of `testAPIListObjectPartsHandler`), need a sample HTTP request
|
||||||
@ -1606,19 +1676,6 @@ func registerAPIFunctions(muxRouter *router.Router, objLayer ObjectLayer, apiFun
|
|||||||
registerBucketLevelFunc(bucketRouter, api, apiFunctions...)
|
registerBucketLevelFunc(bucketRouter, api, apiFunctions...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a http.Handler capable of routing API requests to handlers corresponding to apiFunctions,
|
|
||||||
// with ObjectAPI set to nil.
|
|
||||||
func initTestNilObjAPIEndPoints(apiFunctions []string) http.Handler {
|
|
||||||
muxRouter := router.NewRouter()
|
|
||||||
if len(apiFunctions) > 0 {
|
|
||||||
// Iterate the list of API functions requested for and register them in mux HTTP handler.
|
|
||||||
registerAPIFunctions(muxRouter, nil, apiFunctions...)
|
|
||||||
return muxRouter
|
|
||||||
}
|
|
||||||
registerAPIRouter(muxRouter)
|
|
||||||
return muxRouter
|
|
||||||
}
|
|
||||||
|
|
||||||
// Takes in XL/FS object layer, and the list of API end points to be tested/required, registers the API end points and returns the HTTP handler.
|
// Takes in XL/FS object layer, and the list of API end points to be tested/required, registers the API end points and returns the HTTP handler.
|
||||||
// Need isolated registration of API end points while writing unit tests for end points.
|
// Need isolated registration of API end points while writing unit tests for end points.
|
||||||
// All the API end points are registered only for the default case.
|
// All the API end points are registered only for the default case.
|
||||||
|
Loading…
Reference in New Issue
Block a user