diff --git a/cmd/admin-handlers_test.go b/cmd/admin-handlers_test.go index 94d56988c..6473cba63 100644 --- a/cmd/admin-handlers_test.go +++ b/cmd/admin-handlers_test.go @@ -1346,6 +1346,11 @@ func TestToAdminAPIErr(t *testing.T) { } func TestWriteSetConfigResponse(t *testing.T) { + rootPath, err := newTestConfig(globalMinioDefaultRegion) + if err != nil { + t.Fatal(err) + } + defer removeAll(rootPath) testCases := []struct { status bool errs []error diff --git a/cmd/api-headers.go b/cmd/api-headers.go index 4b6c332b3..cbabee1c9 100644 --- a/cmd/api-headers.go +++ b/cmd/api-headers.go @@ -36,6 +36,7 @@ func setCommonHeaders(w http.ResponseWriter) { // Set unique request ID for each reply. w.Header().Set(responseRequestIDKey, mustGetRequestID(UTCNow())) w.Header().Set("Server", globalServerUserAgent) + w.Header().Set("X-Amz-Bucket-Region", serverConfig.GetRegion()) w.Header().Set("Accept-Ranges", "bytes") } diff --git a/cmd/bucket-handlers.go b/cmd/bucket-handlers.go index 37757c46a..91b67c4a9 100644 --- a/cmd/bucket-handlers.go +++ b/cmd/bucket-handlers.go @@ -370,13 +370,20 @@ func (api objectAPIHandlers) PutBucketHandler(w http.ResponseWriter, r *http.Req vars := mux.Vars(r) bucket := vars["bucket"] - // Validate if incoming location constraint is valid, reject - // requests which do not follow valid region requirements. - if s3Error := isValidLocationConstraint(r); s3Error != ErrNone { + // Parse incoming location constraint. + location, s3Error := parseLocationConstraint(r) + if s3Error != ErrNone { writeErrorResponse(w, s3Error, r.URL) return } + // Validate if location sent by the client is valid, reject + // requests which do not follow valid region requirements. + if !isValidLocation(location) { + writeErrorResponse(w, ErrInvalidRegion, r.URL) + return + } + bucketLock := globalNSMutex.NewNSLock(bucket, "") bucketLock.Lock() defer bucketLock.Unlock() diff --git a/cmd/handler-utils.go b/cmd/handler-utils.go index e8435b1bd..e6c9dc35b 100644 --- a/cmd/handler-utils.go +++ b/cmd/handler-utils.go @@ -24,36 +24,29 @@ import ( "strings" ) -// Validates location constraint in PutBucket request body. -// The location value in the request body should match the -// region configured at serverConfig, otherwise error is returned. -func isValidLocationConstraint(r *http.Request) (s3Error APIErrorCode) { - serverRegion := serverConfig.GetRegion() +// Parses location constraint from the incoming reader. +func parseLocationConstraint(r *http.Request) (location string, s3Error APIErrorCode) { // If the request has no body with content-length set to 0, // we do not have to validate location constraint. Bucket will // be created at default region. locationConstraint := createBucketLocationConfiguration{} err := xmlDecoder(r.Body, &locationConstraint, r.ContentLength) - if err == nil || err == io.EOF { - // Successfully decoded, proceed to verify the region. - // Once region has been obtained we proceed to verify it. - incomingRegion := locationConstraint.Location - if incomingRegion == "" { - // Location constraint is empty for region globalMinioDefaultRegion, - // in accordance with protocol. - incomingRegion = globalMinioDefaultRegion - } - // Return errInvalidRegion if location constraint does not match - // with configured region. - s3Error = ErrNone - if serverRegion != incomingRegion { - s3Error = ErrInvalidRegion - } - return s3Error + if err != nil && err != io.EOF { + errorIf(err, "Unable to xml decode location constraint") + // Treat all other failures as XML parsing errors. + return "", ErrMalformedXML + } // else for both err as nil or io.EOF + location = locationConstraint.Location + if location == "" { + location = globalMinioDefaultRegion } - errorIf(err, "Unable to xml decode location constraint") - // Treat all other failures as XML parsing errors. - return ErrMalformedXML + return location, ErrNone +} + +// Validates input location is same as configured region +// of Minio server. +func isValidLocation(location string) bool { + return serverConfig.GetRegion() == location } // Supported headers that needs to be extracted. diff --git a/cmd/handler-utils_test.go b/cmd/handler-utils_test.go index 3c0b4c2a8..51c45adf6 100644 --- a/cmd/handler-utils_test.go +++ b/cmd/handler-utils_test.go @@ -39,7 +39,7 @@ func TestIsValidLocationContraint(t *testing.T) { Body: ioutil.NopCloser(bytes.NewBuffer([]byte("<>"))), ContentLength: int64(len("<>")), } - if err := isValidLocationConstraint(malformedReq); err != ErrMalformedXML { + if _, err := parseLocationConstraint(malformedReq); err != ErrMalformedXML { t.Fatal("Unexpected error: ", err) } @@ -68,8 +68,6 @@ func TestIsValidLocationContraint(t *testing.T) { // Test case - 2. // In case of empty request body ErrNone is returned. {"", globalMinioDefaultRegion, ErrNone}, - // Test case - 3. - {"eu-central-1", globalMinioDefaultRegion, ErrInvalidRegion}, } for i, testCase := range testCases { inputRequest, e := createExpectedRequest(&http.Request{}, testCase.locationForInputRequest) @@ -77,7 +75,7 @@ func TestIsValidLocationContraint(t *testing.T) { t.Fatalf("Test %d: Failed to Marshal bucket configuration", i+1) } serverConfig.SetRegion(testCase.serverConfigRegion) - actualCode := isValidLocationConstraint(inputRequest) + _, actualCode := parseLocationConstraint(inputRequest) if testCase.expectedCode != actualCode { t.Errorf("Test %d: Expected the APIErrCode to be %d, but instead found %d", i+1, testCase.expectedCode, actualCode) }