mirror of
https://github.com/minio/minio.git
synced 2024-12-25 22:55:54 -05:00
bba5468368
S3 API returns BucketAlreadyExists error when some another user has such bucket. If user that creates the bucket already has it, s3 returns BucketAlreadyOwnedByYou. As minio has only one user, it should behave accordingly. Otherwise it causes failures in the applications that ignore creation of already existing bucket in the account, but fail when bucket name is used by someone else.
441 lines
14 KiB
Go
441 lines
14 KiB
Go
/*
|
|
* Minio Cloud Storage, (C) 2015 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 main
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"net/http"
|
|
)
|
|
|
|
// APIError structure
|
|
type APIError struct {
|
|
Code string
|
|
Description string
|
|
HTTPStatusCode int
|
|
}
|
|
|
|
// APIErrorResponse - error response format
|
|
type APIErrorResponse struct {
|
|
XMLName xml.Name `xml:"Error" json:"-"`
|
|
Code string
|
|
Message string
|
|
Key string
|
|
BucketName string
|
|
Resource string
|
|
RequestID string `xml:"RequestId"`
|
|
HostID string `xml:"HostId"`
|
|
}
|
|
|
|
// APIErrorCode type of error status.
|
|
type APIErrorCode int
|
|
|
|
// Error codes, non exhaustive list - http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
|
|
const (
|
|
ErrNone APIErrorCode = iota
|
|
ErrAccessDenied
|
|
ErrBadDigest
|
|
ErrBucketAlreadyExists
|
|
ErrEntityTooSmall
|
|
ErrEntityTooLarge
|
|
ErrIncompleteBody
|
|
ErrInternalError
|
|
ErrInvalidAccessKeyID
|
|
ErrInvalidBucketName
|
|
ErrInvalidDigest
|
|
ErrInvalidRange
|
|
ErrInvalidMaxKeys
|
|
ErrInvalidMaxUploads
|
|
ErrInvalidMaxParts
|
|
ErrInvalidPartNumberMarker
|
|
ErrInvalidRequestBody
|
|
ErrInvalidCopySource
|
|
ErrInvalidCopyDest
|
|
ErrInvalidPolicyDocument
|
|
ErrMalformedXML
|
|
ErrMissingContentLength
|
|
ErrMissingContentMD5
|
|
ErrMissingRequestBodyError
|
|
ErrNoSuchBucket
|
|
ErrNoSuchBucketPolicy
|
|
ErrNoSuchKey
|
|
ErrNoSuchUpload
|
|
ErrNotImplemented
|
|
ErrRequestTimeTooSkewed
|
|
ErrSignatureDoesNotMatch
|
|
ErrMethodNotAllowed
|
|
ErrInvalidPart
|
|
ErrInvalidPartOrder
|
|
ErrAuthorizationHeaderMalformed
|
|
ErrMalformedPOSTRequest
|
|
ErrSignatureVersionNotSupported
|
|
ErrBucketNotEmpty
|
|
ErrStorageFull
|
|
ErrObjectExistsAsPrefix
|
|
ErrAllAccessDisabled
|
|
ErrMalformedPolicy
|
|
ErrMissingFields
|
|
ErrMissingCredTag
|
|
ErrCredMalformed
|
|
ErrInvalidRegion
|
|
ErrInvalidService
|
|
ErrInvalidRequestVersion
|
|
ErrMissingSignTag
|
|
ErrMissingSignHeadersTag
|
|
ErrPolicyAlreadyExpired
|
|
ErrMalformedDate
|
|
ErrMalformedExpires
|
|
ErrAuthHeaderEmpty
|
|
ErrDateHeaderMissing
|
|
ErrExpiredPresignRequest
|
|
ErrMissingDateHeader
|
|
ErrInvalidQuerySignatureAlgo
|
|
ErrInvalidQueryParams
|
|
ErrBucketAlreadyOwnedByYou
|
|
// Add new error codes here.
|
|
|
|
// Extended errors.
|
|
ErrInsufficientReadResources
|
|
ErrInsufficientWriteResources
|
|
)
|
|
|
|
// error code to APIError structure, these fields carry respective
|
|
// descriptions for all the error responses.
|
|
var errorCodeResponse = map[APIErrorCode]APIError{
|
|
ErrInvalidCopyDest: {
|
|
Code: "InvalidRequest",
|
|
Description: "This copy request is illegal because it is trying to copy an object to itself.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidCopySource: {
|
|
Code: "InvalidArgument",
|
|
Description: "Copy Source must mention the source bucket and key: sourcebucket/sourcekey.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidRequestBody: {
|
|
Code: "InvalidArgument",
|
|
Description: "Body shouldn't be set for this request.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidMaxUploads: {
|
|
Code: "InvalidArgument",
|
|
Description: "Argument maxUploads must be an integer between 0 and 2147483647.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidMaxKeys: {
|
|
Code: "InvalidArgument",
|
|
Description: "Argument maxKeys must be an integer between 0 and 2147483647.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidMaxParts: {
|
|
Code: "InvalidArgument",
|
|
Description: "Argument maxParts must be an integer between 1 and 10000.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidPartNumberMarker: {
|
|
Code: "InvalidArgument",
|
|
Description: "Argument partNumberMarker must be an integer.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidPolicyDocument: {
|
|
Code: "InvalidPolicyDocument",
|
|
Description: "The content of the form does not meet the conditions specified in the policy document.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrAccessDenied: {
|
|
Code: "AccessDenied",
|
|
Description: "Access Denied.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrBadDigest: {
|
|
Code: "BadDigest",
|
|
Description: "The Content-Md5 you specified did not match what we received.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrBucketAlreadyExists: {
|
|
Code: "BucketAlreadyExists",
|
|
Description: "The requested bucket name is not available.",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrEntityTooSmall: {
|
|
Code: "EntityTooSmall",
|
|
Description: "Your proposed upload is smaller than the minimum allowed object size.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrEntityTooLarge: {
|
|
Code: "EntityTooLarge",
|
|
Description: "Your proposed upload exceeds the maximum allowed object size.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrIncompleteBody: {
|
|
Code: "IncompleteBody",
|
|
Description: "You did not provide the number of bytes specified by the Content-Length HTTP header.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInternalError: {
|
|
Code: "InternalError",
|
|
Description: "We encountered an internal error, please try again.",
|
|
HTTPStatusCode: http.StatusInternalServerError,
|
|
},
|
|
ErrInsufficientReadResources: {
|
|
Code: "InternalError",
|
|
Description: "We cannot satisfy sufficient read resources requested at this moment, please try again.",
|
|
HTTPStatusCode: http.StatusInternalServerError,
|
|
},
|
|
ErrInsufficientWriteResources: {
|
|
Code: "InternalError",
|
|
Description: "We cannot satisfy sufficient write resources requested at this moment, please try again.",
|
|
HTTPStatusCode: http.StatusInternalServerError,
|
|
},
|
|
ErrInvalidAccessKeyID: {
|
|
Code: "InvalidAccessKeyID",
|
|
Description: "The access key ID you provided does not exist in our records.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrInvalidBucketName: {
|
|
Code: "InvalidBucketName",
|
|
Description: "The specified bucket is not valid.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidDigest: {
|
|
Code: "InvalidDigest",
|
|
Description: "The Content-Md5 you specified is not valid.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidRange: {
|
|
Code: "InvalidRange",
|
|
Description: "The requested range cannot be satisfied.",
|
|
HTTPStatusCode: http.StatusRequestedRangeNotSatisfiable,
|
|
},
|
|
ErrMalformedXML: {
|
|
Code: "MalformedXML",
|
|
Description: "The XML you provided was not well-formed or did not validate against our published schema.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingContentLength: {
|
|
Code: "MissingContentLength",
|
|
Description: "You must provide the Content-Length HTTP header.",
|
|
HTTPStatusCode: http.StatusLengthRequired,
|
|
},
|
|
ErrMissingContentMD5: {
|
|
Code: "MissingContentMD5",
|
|
Description: "Missing required header for this request: Content-Md5.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingRequestBodyError: {
|
|
Code: "MissingRequestBodyError",
|
|
Description: "Request body is empty.",
|
|
HTTPStatusCode: http.StatusLengthRequired,
|
|
},
|
|
ErrNoSuchBucket: {
|
|
Code: "NoSuchBucket",
|
|
Description: "The specified bucket does not exist.",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrNoSuchBucketPolicy: {
|
|
Code: "NoSuchBucketPolicy",
|
|
Description: "The specified bucket does not have a bucket policy.",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrNoSuchKey: {
|
|
Code: "NoSuchKey",
|
|
Description: "The specified key does not exist.",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrNoSuchUpload: {
|
|
Code: "NoSuchUpload",
|
|
Description: "The specified multipart upload does not exist.",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrNotImplemented: {
|
|
Code: "NotImplemented",
|
|
Description: "A header you provided implies functionality that is not implemented.",
|
|
HTTPStatusCode: http.StatusNotImplemented,
|
|
},
|
|
ErrRequestTimeTooSkewed: {
|
|
Code: "RequestTimeTooSkewed",
|
|
Description: "The difference between the request time and the server's time is too large.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrSignatureDoesNotMatch: {
|
|
Code: "SignatureDoesNotMatch",
|
|
Description: "The request signature we calculated does not match the signature you provided.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrMethodNotAllowed: {
|
|
Code: "MethodNotAllowed",
|
|
Description: "The specified method is not allowed against this resource.",
|
|
HTTPStatusCode: http.StatusMethodNotAllowed,
|
|
},
|
|
ErrInvalidPart: {
|
|
Code: "InvalidPart",
|
|
Description: "One or more of the specified parts could not be found.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidPartOrder: {
|
|
Code: "InvalidPartOrder",
|
|
Description: "The list of parts was not in ascending order. The parts list must be specified in order by part number.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrAuthorizationHeaderMalformed: {
|
|
Code: "AuthorizationHeaderMalformed",
|
|
Description: "The authorization header is malformed; the region is wrong; expecting 'us-east-1'.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedPOSTRequest: {
|
|
Code: "MalformedPOSTRequest",
|
|
Description: "The body of your POST request is not well-formed multipart/form-data.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrSignatureVersionNotSupported: {
|
|
Code: "InvalidRequest",
|
|
Description: "The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrBucketNotEmpty: {
|
|
Code: "BucketNotEmpty",
|
|
Description: "The bucket you tried to delete is not empty.",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrStorageFull: {
|
|
Code: "StorageFull",
|
|
Description: "Storage backend has reached its minimum free disk threshold. Please delete few objects to proceed.",
|
|
HTTPStatusCode: http.StatusInternalServerError,
|
|
},
|
|
ErrObjectExistsAsPrefix: {
|
|
Code: "ObjectExistsAsPrefix",
|
|
Description: "An object already exists as your prefix, choose a different prefix to proceed.",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrAllAccessDisabled: {
|
|
Code: "AllAccessDisabled",
|
|
Description: "All access to this bucket has been disabled.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrMalformedPolicy: {
|
|
Code: "MalformedPolicy",
|
|
Description: "Policy has invalid resource.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingFields: {
|
|
Code: "MissingFields",
|
|
Description: "Missing fields in request.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingCredTag: {
|
|
Code: "InvalidRequest",
|
|
Description: "Missing Credential field for this request.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrCredMalformed: {
|
|
Code: "CredentialMalformed",
|
|
Description: "Credential field malformed does not follow accessKeyID/credScope.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedDate: {
|
|
Code: "MalformedDate",
|
|
Description: "Invalid date format header, expected to be in ISO8601, RFC1123 or RFC1123Z time format.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidRegion: {
|
|
Code: "InvalidRegion",
|
|
Description: "Region does not match.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidService: {
|
|
Code: "AccessDenied",
|
|
Description: "Service scope should be of value 's3'.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidRequestVersion: {
|
|
Code: "AccessDenied",
|
|
Description: "Request scope should be of value 'aws4_request'.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingSignTag: {
|
|
Code: "AccessDenied",
|
|
Description: "Signature header missing Signature field.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingSignHeadersTag: {
|
|
Code: "InvalidArgument",
|
|
Description: "Signature header missing SignedHeaders field.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrPolicyAlreadyExpired: {
|
|
Code: "AccessDenied",
|
|
Description: "Invalid according to Policy: Policy expired.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedExpires: {
|
|
Code: "MalformedExpires",
|
|
Description: "Malformed expires header, expected non-zero number.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrAuthHeaderEmpty: {
|
|
Code: "InvalidArgument",
|
|
Description: "Authorization header is invalid -- one and only one ' ' (space) required.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingDateHeader: {
|
|
Code: "AccessDenied",
|
|
Description: "AWS authentication requires a valid Date or x-amz-date header",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidQuerySignatureAlgo: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "X-Amz-Algorithm only supports \"AWS4-HMAC-SHA256\".",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrExpiredPresignRequest: {
|
|
Code: "AccessDenied",
|
|
Description: "Request has expired.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidQueryParams: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "Query-string authentication version 4 requires the X-Amz-Algorithm, X-Amz-Credential, X-Amz-Signature, X-Amz-Date, X-Amz-SignedHeaders, and X-Amz-Expires parameters.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrBucketAlreadyOwnedByYou: {
|
|
Code: "BucketAlreadyOwnedByYou",
|
|
Description: "Your previous request to create the named bucket succeeded and you already own it.",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
// Add your error structure here.
|
|
}
|
|
|
|
// getAPIError provides API Error for input API error code.
|
|
func getAPIError(code APIErrorCode) APIError {
|
|
return errorCodeResponse[code]
|
|
}
|
|
|
|
// getErrorResponse gets in standard error and resource value and
|
|
// provides a encodable populated response values
|
|
func getAPIErrorResponse(err APIError, resource string) APIErrorResponse {
|
|
var data = APIErrorResponse{}
|
|
data.Code = err.Code
|
|
data.Message = err.Description
|
|
if resource != "" {
|
|
data.Resource = resource
|
|
}
|
|
// TODO implement this in future
|
|
data.RequestID = "3L137"
|
|
data.HostID = "3L137"
|
|
|
|
return data
|
|
}
|