diff --git a/api/auth/center.go b/api/auth/center.go index 6ff416c85..8ffbf3833 100644 --- a/api/auth/center.go +++ b/api/auth/center.go @@ -14,6 +14,7 @@ import ( v4 "github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neofs-api-go/pkg/object" + apiErrors "github.com/nspcc-dev/neofs-s3-gw/api/errors" "github.com/nspcc-dev/neofs-s3-gw/creds/accessbox" "github.com/nspcc-dev/neofs-s3-gw/creds/tokens" "github.com/nspcc-dev/neofs-sdk-go/pkg/pool" @@ -77,7 +78,7 @@ func (c *center) Authenticate(r *http.Request) (*accessbox.Box, error) { sms1 := c.reg.getSubmatches(authHeaderField[0]) if len(sms1) != 7 { - return nil, errors.New("bad Authorization header field") + return nil, apiErrors.GetAPIError(apiErrors.ErrAuthorizationHeaderMalformed) } signedHeaderFieldsNames := strings.Split(sms1["signed_header_fields"], ";") @@ -125,7 +126,7 @@ func (c *center) Authenticate(r *http.Request) (*accessbox.Box, error) { sms2 := c.reg.getSubmatches(otherRequest.Header.Get("Authorization")) if sms1["v4_signature"] != sms2["v4_signature"] { - return nil, errors.New("failed to pass authentication procedure") + return nil, apiErrors.GetAPIError(apiErrors.ErrSignatureDoesNotMatch) } return box, nil diff --git a/api/auth/regexp-utils_test.go b/api/auth/regexp-utils_test.go new file mode 100644 index 000000000..fe853873e --- /dev/null +++ b/api/auth/regexp-utils_test.go @@ -0,0 +1,16 @@ +package auth + +import ( + "fmt" + "testing" +) + +func TestName(t *testing.T) { + //target:= "AWS4-HMAC-SHA256 Credential=vWqF8cMDRbJcvnPLALoQGnABPPhw8NyYMcGsfDPfZJM0HrgjonN8CgFvCZ3kh9BUXw4W2tJ5E7EAGhueSF122HB/20210809/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=2811ccb9e242f41426738fb1fa6a456ef37c63505da1a160f3d76a4f51b17581" + target := "AWS4-HMAC-SHA256 Credential=badauth/20210809/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-content-sha256;x-amz-date, Signature=2811ccb9e242f41426738fb1fa6a456ef37c63505da1a160f3d76a4f51b17581" + + subMatcher := ®expSubmatcher{re: authorizationFieldRegexp} + + submatches := subMatcher.getSubmatches(target) + fmt.Println(submatches) +} diff --git a/api/errors.go b/api/errors/errors.go similarity index 98% rename from api/errors.go rename to api/errors/errors.go index f02ba6192..7abbf83e0 100644 --- a/api/errors.go +++ b/api/errors/errors.go @@ -1,4 +1,4 @@ -package api +package errors import ( "fmt" @@ -10,6 +10,14 @@ type ( ErrorCode int errorCodeMap map[ErrorCode]Error + + // Error structure represents API error. + Error struct { + ErrCode ErrorCode + Code string + Description string + HTTPStatusCode int + } ) const maxEConfigJSONSize = 262272 @@ -1875,7 +1883,7 @@ func IsS3Error(err error, code ErrorCode) bool { return ok && e.ErrCode == code } -func (e errorCodeMap) ToAPIErrWithErr(errCode ErrorCode, err error) Error { +func (e errorCodeMap) toAPIErrWithErr(errCode ErrorCode, err error) Error { apiErr, ok := e[errCode] if !ok { apiErr = e[ErrInternalError] @@ -1886,8 +1894,8 @@ func (e errorCodeMap) ToAPIErrWithErr(errCode ErrorCode, err error) Error { return apiErr } -func (e errorCodeMap) ToAPIErr(errCode ErrorCode) Error { - return e.ToAPIErrWithErr(errCode, nil) +func (e errorCodeMap) toAPIErr(errCode ErrorCode) Error { + return e.toAPIErrWithErr(errCode, nil) } func (e Error) Error() string { @@ -1899,29 +1907,7 @@ func GetAPIError(code ErrorCode) Error { if apiErr, ok := errorCodes[code]; ok { return apiErr } - return errorCodes.ToAPIErr(ErrInternalError) -} - -// getErrorResponse gets in standard error and resource value and -// provides a encodable populated response values. -func getAPIErrorResponse(info *ReqInfo, err error) ErrorResponse { - code := "InternalError" - desc := err.Error() - - if e, ok := err.(Error); ok { - code = e.Code - desc = e.Description - } - - return ErrorResponse{ - Code: code, - Message: desc, - BucketName: info.BucketName, - Key: info.ObjectName, - Resource: info.URL.Path, - RequestID: info.RequestID, - HostID: info.DeploymentID, - } + return errorCodes.toAPIErr(ErrInternalError) } // GenericError - generic object layer error. diff --git a/api/errors_test.go b/api/errors/errors_test.go similarity index 96% rename from api/errors_test.go rename to api/errors/errors_test.go index 17e272e8e..71fa4c82a 100644 --- a/api/errors_test.go +++ b/api/errors/errors_test.go @@ -1,4 +1,4 @@ -package api +package errors import ( "errors" diff --git a/api/handler/copy.go b/api/handler/copy.go index 7709654fc..fea9ccdd1 100644 --- a/api/handler/copy.go +++ b/api/handler/copy.go @@ -7,6 +7,7 @@ import ( "time" "github.com/nspcc-dev/neofs-s3-gw/api" + "github.com/nspcc-dev/neofs-s3-gw/api/errors" "github.com/nspcc-dev/neofs-s3-gw/api/layer" "go.uber.org/zap" ) @@ -48,7 +49,7 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { // its non "null" value, we should error out since we do not support // any versions other than "null". if vid := u.Query().Get("versionId"); vid != "" && vid != "null" { - h.logAndSendError(w, "no such version", reqInfo, api.GetAPIError(api.ErrNoSuchVersion)) + h.logAndSendError(w, "no such version", reqInfo, errors.GetAPIError(errors.ErrNoSuchVersion)) return } @@ -66,7 +67,7 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { if args.MetadataDirective == replaceMetadataDirective { metadata = parseMetadata(r) } else if srcBucket == reqInfo.BucketName && srcObject == reqInfo.ObjectName { - h.logAndSendError(w, "could not copy to itself", reqInfo, api.GetAPIError(api.ErrInvalidRequest)) + h.logAndSendError(w, "could not copy to itself", reqInfo, errors.GetAPIError(errors.ErrInvalidRequest)) return } @@ -76,7 +77,7 @@ func (h *handler) CopyObjectHandler(w http.ResponseWriter, r *http.Request) { } if err = checkPreconditions(inf, args.Conditional); err != nil { - h.logAndSendError(w, "precondition failed", reqInfo, api.GetAPIError(api.ErrPreconditionFailed)) + h.logAndSendError(w, "precondition failed", reqInfo, errors.GetAPIError(errors.ErrPreconditionFailed)) return } diff --git a/api/handler/delete.go b/api/handler/delete.go index 66cd8d0ad..71c61a0bd 100644 --- a/api/handler/delete.go +++ b/api/handler/delete.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/nspcc-dev/neofs-s3-gw/api" + "github.com/nspcc-dev/neofs-s3-gw/api/errors" "github.com/nspcc-dev/neofs-s3-gw/api/layer" "go.uber.org/zap" ) @@ -69,14 +70,14 @@ func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Re // Content-Md5 is requied should be set // http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html if _, ok := r.Header[api.ContentMD5]; !ok { - h.logAndSendError(w, "missing Content-MD5", reqInfo, api.GetAPIError(api.ErrMissingContentMD5)) + h.logAndSendError(w, "missing Content-MD5", reqInfo, errors.GetAPIError(errors.ErrMissingContentMD5)) return } // Content-Length is required and should be non-zero // http://docs.aws.amazon.com/AmazonS3/latest/API/multiobjectdeleteapi.html if r.ContentLength <= 0 { - h.logAndSendError(w, "missing Content-Length", reqInfo, api.GetAPIError(api.ErrMissingContentLength)) + h.logAndSendError(w, "missing Content-Length", reqInfo, errors.GetAPIError(errors.ErrMissingContentLength)) return } @@ -107,7 +108,7 @@ func (h *handler) DeleteMultipleObjectsHandler(w http.ResponseWriter, r *http.Re h.logAndSendError(w, "could not delete objects", reqInfo, nil, additional...) for _, e := range errs { - if err, ok := e.(*api.DeleteError); ok { + if err, ok := e.(*errors.DeleteError); ok { code := "BadRequest" desc := err.Error() diff --git a/api/handler/get.go b/api/handler/get.go index 6ac1df6c0..40a3ed04d 100644 --- a/api/handler/get.go +++ b/api/handler/get.go @@ -8,6 +8,7 @@ import ( "time" "github.com/nspcc-dev/neofs-s3-gw/api" + "github.com/nspcc-dev/neofs-s3-gw/api/errors" "github.com/nspcc-dev/neofs-s3-gw/api/layer" ) @@ -29,7 +30,7 @@ func fetchRangeHeader(headers http.Header, fullSize uint64) (*layer.RangeParams, return nil, nil } if fullSize == 0 { - return nil, api.GetAPIError(api.ErrInvalidRange) + return nil, errors.GetAPIError(errors.ErrInvalidRange) } if !strings.HasPrefix(rangeHeader, prefix) { return nil, fmt.Errorf("unknown unit in range header") @@ -59,7 +60,7 @@ func fetchRangeHeader(headers http.Header, fullSize uint64) (*layer.RangeParams, } if err0 != nil || err1 != nil || start > end || start > fullSize { - return nil, api.GetAPIError(api.ErrInvalidRange) + return nil, errors.GetAPIError(errors.ErrInvalidRange) } return &layer.RangeParams{Start: start, End: end}, nil } @@ -124,17 +125,17 @@ func (h *handler) GetObjectHandler(w http.ResponseWriter, r *http.Request) { func checkPreconditions(inf *layer.ObjectInfo, args *conditionalArgs) error { if len(args.IfMatch) > 0 && args.IfMatch != inf.HashSum { - return api.GetAPIError(api.ErrPreconditionFailed) + return errors.GetAPIError(errors.ErrPreconditionFailed) } if len(args.IfNoneMatch) > 0 && args.IfNoneMatch == inf.HashSum { - return api.GetAPIError(api.ErrNotModified) + return errors.GetAPIError(errors.ErrNotModified) } if args.IfModifiedSince != nil && inf.Created.Before(*args.IfModifiedSince) { - return api.GetAPIError(api.ErrNotModified) + return errors.GetAPIError(errors.ErrNotModified) } if args.IfUnmodifiedSince != nil && inf.Created.After(*args.IfUnmodifiedSince) { if len(args.IfMatch) == 0 { - return api.GetAPIError(api.ErrPreconditionFailed) + return errors.GetAPIError(errors.ErrPreconditionFailed) } } diff --git a/api/handler/get_test.go b/api/handler/get_test.go index c303fa802..82dd16e06 100644 --- a/api/handler/get_test.go +++ b/api/handler/get_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/nspcc-dev/neofs-s3-gw/api" + "github.com/nspcc-dev/neofs-s3-gw/api/errors" "github.com/nspcc-dev/neofs-s3-gw/api/layer" "github.com/stretchr/testify/require" ) @@ -80,7 +80,7 @@ func TestPreconditions(t *testing.T) { name: "IfMatch false", info: newInfo(etag, today), args: &conditionalArgs{IfMatch: etag2}, - expected: api.GetAPIError(api.ErrPreconditionFailed)}, + expected: errors.GetAPIError(errors.ErrPreconditionFailed)}, { name: "IfNoneMatch true", info: newInfo(etag, today), @@ -90,7 +90,7 @@ func TestPreconditions(t *testing.T) { name: "IfNoneMatch false", info: newInfo(etag, today), args: &conditionalArgs{IfNoneMatch: etag}, - expected: api.GetAPIError(api.ErrNotModified)}, + expected: errors.GetAPIError(errors.ErrNotModified)}, { name: "IfModifiedSince true", info: newInfo(etag, today), @@ -100,7 +100,7 @@ func TestPreconditions(t *testing.T) { name: "IfModifiedSince false", info: newInfo(etag, yesterday), args: &conditionalArgs{IfModifiedSince: &today}, - expected: api.GetAPIError(api.ErrNotModified)}, + expected: errors.GetAPIError(errors.ErrNotModified)}, { name: "IfUnmodifiedSince true", info: newInfo(etag, yesterday), @@ -110,7 +110,7 @@ func TestPreconditions(t *testing.T) { name: "IfUnmodifiedSince false", info: newInfo(etag, today), args: &conditionalArgs{IfUnmodifiedSince: &yesterday}, - expected: api.GetAPIError(api.ErrPreconditionFailed)}, + expected: errors.GetAPIError(errors.ErrPreconditionFailed)}, { name: "IfMatch true, IfUnmodifiedSince false", @@ -122,19 +122,19 @@ func TestPreconditions(t *testing.T) { name: "IfMatch false, IfUnmodifiedSince true", info: newInfo(etag, yesterday), args: &conditionalArgs{IfMatch: etag2, IfUnmodifiedSince: &today}, - expected: api.GetAPIError(api.ErrPreconditionFailed), + expected: errors.GetAPIError(errors.ErrPreconditionFailed), }, { name: "IfNoneMatch false, IfModifiedSince true", info: newInfo(etag, today), args: &conditionalArgs{IfNoneMatch: etag, IfModifiedSince: &yesterday}, - expected: api.GetAPIError(api.ErrNotModified), + expected: errors.GetAPIError(errors.ErrNotModified), }, { name: "IfNoneMatch true, IfModifiedSince false", info: newInfo(etag, yesterday), args: &conditionalArgs{IfNoneMatch: etag2, IfModifiedSince: &today}, - expected: api.GetAPIError(api.ErrNotModified), + expected: errors.GetAPIError(errors.ErrNotModified), }, } { t.Run(tc.name, func(t *testing.T) { diff --git a/api/handler/not-support.go b/api/handler/not-support.go index 532ac5dbb..6556eaf62 100644 --- a/api/handler/not-support.go +++ b/api/handler/not-support.go @@ -4,28 +4,29 @@ import ( "net/http" "github.com/nspcc-dev/neofs-s3-gw/api" + "github.com/nspcc-dev/neofs-s3-gw/api/errors" ) func (h *handler) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotSupported)) + h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotSupported)) } func (h *handler) DeleteBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotSupported)) + h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotSupported)) } func (h *handler) DeleteBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotSupported)) + h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotSupported)) } func (h *handler) PutBucketTaggingHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotSupported)) + h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotSupported)) } func (h *handler) PutBucketVersioningHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotSupported)) + h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotSupported)) } func (h *handler) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotSupported)) + h.logAndSendError(w, "not supported", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotSupported)) } diff --git a/api/handler/object_list.go b/api/handler/object_list.go index 1a97c3ddd..796a83e2a 100644 --- a/api/handler/object_list.go +++ b/api/handler/object_list.go @@ -8,6 +8,7 @@ import ( "github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-s3-gw/api" + "github.com/nspcc-dev/neofs-s3-gw/api/errors" "github.com/nspcc-dev/neofs-s3-gw/api/layer" ) @@ -144,7 +145,7 @@ func parseListObjectArgs(reqInfo *api.ReqInfo) (*layer.ListObjectsParamsCommon, if queryValues.Get("max-keys") == "" { res.MaxKeys = maxObjectList } else if res.MaxKeys, err = strconv.Atoi(queryValues.Get("max-keys")); err != nil || res.MaxKeys < 0 { - return nil, api.GetAPIError(api.ErrInvalidMaxKeys) + return nil, errors.GetAPIError(errors.ErrInvalidMaxKeys) } res.Prefix = queryValues.Get("prefix") @@ -155,7 +156,7 @@ func parseListObjectArgs(reqInfo *api.ReqInfo) (*layer.ListObjectsParamsCommon, func parseContinuationToken(queryValues url.Values) (string, error) { if val, ok := queryValues["continuation-token"]; ok { if err := object.NewID().Parse(val[0]); err != nil { - return "", api.GetAPIError(api.ErrIncorrectContinuationToken) + return "", errors.GetAPIError(errors.ErrIncorrectContinuationToken) } return val[0], nil } @@ -228,7 +229,7 @@ func parseListObjectVersionsRequest(reqInfo *api.ReqInfo) (*layer.ListObjectVers if queryValues.Get("max-keys") == "" { res.MaxKeys = maxObjectList } else if res.MaxKeys, err = strconv.Atoi(queryValues.Get("max-keys")); err != nil || res.MaxKeys <= 0 { - return nil, api.GetAPIError(api.ErrInvalidMaxKeys) + return nil, errors.GetAPIError(errors.ErrInvalidMaxKeys) } res.Prefix = queryValues.Get("prefix") diff --git a/api/handler/put.go b/api/handler/put.go index 0c402dd2e..205a96c32 100644 --- a/api/handler/put.go +++ b/api/handler/put.go @@ -8,6 +8,8 @@ import ( "strconv" "strings" + "github.com/nspcc-dev/neofs-s3-gw/api/errors" + "github.com/nspcc-dev/neofs-api-go/pkg/acl" "github.com/nspcc-dev/neofs-node/pkg/policy" "github.com/nspcc-dev/neofs-s3-gw/api" @@ -135,27 +137,27 @@ func (h *handler) CreateBucketHandler(w http.ResponseWriter, r *http.Request) { func checkBucketName(bucketName string) error { if len(bucketName) < 3 || len(bucketName) > 63 { - return api.GetAPIError(api.ErrInvalidBucketName) + return errors.GetAPIError(errors.ErrInvalidBucketName) } if strings.HasPrefix(bucketName, "xn--") || strings.HasSuffix(bucketName, "-s3alias") { - return api.GetAPIError(api.ErrInvalidBucketName) + return errors.GetAPIError(errors.ErrInvalidBucketName) } if net.ParseIP(bucketName) != nil { - return api.GetAPIError(api.ErrInvalidBucketName) + return errors.GetAPIError(errors.ErrInvalidBucketName) } labels := strings.Split(bucketName, ".") for _, label := range labels { if len(label) == 0 { - return api.GetAPIError(api.ErrInvalidBucketName) + return errors.GetAPIError(errors.ErrInvalidBucketName) } for i, r := range label { if !isAlphaNum(r) && r != '-' { - return api.GetAPIError(api.ErrInvalidBucketName) + return errors.GetAPIError(errors.ErrInvalidBucketName) } if (i == 0 || i == len(label)-1) && r == '-' { - return api.GetAPIError(api.ErrInvalidBucketName) + return errors.GetAPIError(errors.ErrInvalidBucketName) } } } diff --git a/api/handler/unimplemented.go b/api/handler/unimplemented.go index 69c8e6232..b8009551a 100644 --- a/api/handler/unimplemented.go +++ b/api/handler/unimplemented.go @@ -4,160 +4,161 @@ import ( "net/http" "github.com/nspcc-dev/neofs-s3-gw/api" + "github.com/nspcc-dev/neofs-s3-gw/api/errors" ) func (h *handler) CopyObjectPartHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) PutObjectPartHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) ListObjectPartsHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) CompleteMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) NewMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) AbortMultipartUploadHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetObjectACLHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) PutObjectACLHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) PutObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) DeleteObjectTaggingHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) SelectObjectContentHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetObjectRetentionHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) PutObjectRetentionHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) PutObjectLegalHoldHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketACLHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) PutBucketACLHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketCorsHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketWebsiteHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketAccelerateHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketRequestPaymentHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketLoggingHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketReplicationHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketTaggingHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) DeleteBucketWebsiteHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) DeleteBucketTaggingHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) ListenBucketNotificationHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) ListObjectsV2MHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) PutBucketLifecycleHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) PutBucketEncryptionHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) PutBucketObjectLockConfigHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } func (h *handler) PostPolicyBucketHandler(w http.ResponseWriter, r *http.Request) { - h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), api.GetAPIError(api.ErrNotImplemented)) + h.logAndSendError(w, "not implemented", api.GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrNotImplemented)) } diff --git a/api/layer/container.go b/api/layer/container.go index 7d2d6a124..381b6af6a 100644 --- a/api/layer/container.go +++ b/api/layer/container.go @@ -15,6 +15,7 @@ import ( cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id" "github.com/nspcc-dev/neofs-api-go/pkg/owner" "github.com/nspcc-dev/neofs-s3-gw/api" + "github.com/nspcc-dev/neofs-s3-gw/api/errors" "github.com/nspcc-dev/neofs-sdk-go/pkg/pool" "go.uber.org/zap" ) @@ -49,7 +50,7 @@ func (n *layer) containerInfo(ctx context.Context, cid *cid.ID) (*BucketInfo, er zap.Error(err)) if strings.Contains(err.Error(), "container not found") { - return nil, api.GetAPIError(api.ErrNoSuchBucket) + return nil, errors.GetAPIError(errors.ErrNoSuchBucket) } return nil, err } diff --git a/api/layer/layer.go b/api/layer/layer.go index fdea8fb0e..e5d1291c7 100644 --- a/api/layer/layer.go +++ b/api/layer/layer.go @@ -15,6 +15,7 @@ import ( "github.com/nspcc-dev/neofs-api-go/pkg/object" "github.com/nspcc-dev/neofs-api-go/pkg/owner" "github.com/nspcc-dev/neofs-s3-gw/api" + "github.com/nspcc-dev/neofs-s3-gw/api/errors" "github.com/nspcc-dev/neofs-s3-gw/creds/accessbox" "github.com/nspcc-dev/neofs-sdk-go/pkg/pool" "go.uber.org/zap" @@ -187,7 +188,7 @@ func (n *layer) GetBucketInfo(ctx context.Context, name string) (*BucketInfo, er } } - return nil, api.GetAPIError(api.ErrNoSuchBucket) + return nil, errors.GetAPIError(errors.ErrNoSuchBucket) } return n.containerInfo(ctx, containerID) @@ -246,7 +247,7 @@ func (n *layer) checkObject(ctx context.Context, cid *cid.ID, filename string) e var err error if _, err = n.objectFindID(ctx, &findParams{cid: cid, val: filename}); err == nil { - return new(api.ObjectAlreadyExists) + return new(errors.ObjectAlreadyExists) } return err @@ -320,12 +321,12 @@ func (n *layer) DeleteObject(ctx context.Context, bucket, filename string) error ) if bkt, err = n.GetBucketInfo(ctx, bucket); err != nil { - return &api.DeleteError{ + return &errors.DeleteError{ Err: err, Object: filename, } } else if ids, err = n.objectSearch(ctx, &findParams{cid: bkt.CID, val: filename}); err != nil { - return &api.DeleteError{ + return &errors.DeleteError{ Err: err, Object: filename, } @@ -337,7 +338,7 @@ func (n *layer) DeleteObject(ctx context.Context, bucket, filename string) error addr.SetContainerID(bkt.CID) if err = n.objectDelete(ctx, addr); err != nil { - return &api.DeleteError{ + return &errors.DeleteError{ Err: err, Object: filename, } @@ -363,13 +364,13 @@ func (n *layer) DeleteObjects(ctx context.Context, bucket string, objects []stri func (n *layer) CreateBucket(ctx context.Context, p *CreateBucketParams) (*cid.ID, error) { _, err := n.GetBucketInfo(ctx, p.Name) if err != nil { - if api.IsS3Error(err, api.ErrNoSuchBucket) { + if errors.IsS3Error(err, errors.ErrNoSuchBucket) { return n.createContainer(ctx, p) } return nil, err } - return nil, api.GetAPIError(api.ErrBucketAlreadyExists) + return nil, errors.GetAPIError(errors.ErrBucketAlreadyExists) } func (n *layer) DeleteBucket(ctx context.Context, p *DeleteBucketParams) error { diff --git a/api/layer/object.go b/api/layer/object.go index 2ce970431..59700c094 100644 --- a/api/layer/object.go +++ b/api/layer/object.go @@ -12,7 +12,7 @@ import ( "github.com/nspcc-dev/neofs-api-go/pkg/client" cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id" "github.com/nspcc-dev/neofs-api-go/pkg/object" - "github.com/nspcc-dev/neofs-s3-gw/api" + apiErrors "github.com/nspcc-dev/neofs-s3-gw/api/errors" "go.uber.org/zap" ) @@ -81,7 +81,7 @@ func (n *layer) objectFindID(ctx context.Context, p *findParams) (*object.ID, er if result, err := n.objectSearch(ctx, p); err != nil { return nil, err } else if ln := len(result); ln == 0 { - return nil, api.GetAPIError(api.ErrNoSuchKey) + return nil, apiErrors.GetAPIError(apiErrors.ErrNoSuchKey) } else if ln == 1 { return result[0], nil } @@ -124,14 +124,14 @@ func (n *layer) objectPut(ctx context.Context, p *PutObjectParams) (*ObjectInfo, } else if bkt, err = n.GetBucketInfo(ctx, p.Bucket); err != nil { return nil, err } else if err = n.checkObject(ctx, bkt.CID, p.Object); err != nil { - var errExist *api.ObjectAlreadyExists + var errExist *apiErrors.ObjectAlreadyExists if ok := errors.As(err, &errExist); ok { errExist.Bucket = p.Bucket errExist.Object = p.Object return nil, errExist } - if !api.IsS3Error(err, api.ErrNoSuchKey) { + if !apiErrors.IsS3Error(err, apiErrors.ErrNoSuchKey) { return nil, err } } diff --git a/api/max-clients.go b/api/max-clients.go index d578ef47c..bc75a92f3 100644 --- a/api/max-clients.go +++ b/api/max-clients.go @@ -3,6 +3,8 @@ package api import ( "net/http" "time" + + "github.com/nspcc-dev/neofs-s3-gw/api/errors" ) type ( @@ -49,7 +51,7 @@ func (m *maxClients) Handle(f http.HandlerFunc) http.HandlerFunc { f.ServeHTTP(w, r) case <-deadline.C: // Send a http timeout message - WriteErrorResponse(w, GetReqInfo(r.Context()), errorCodes.ToAPIErr(ErrOperationMaxedOut)) + WriteErrorResponse(w, GetReqInfo(r.Context()), errors.GetAPIError(errors.ErrOperationTimedOut)) return case <-r.Context().Done(): return diff --git a/api/response.go b/api/response.go index 6e823468b..eaf2b8f07 100644 --- a/api/response.go +++ b/api/response.go @@ -7,6 +7,8 @@ import ( "net/http" "strconv" + "github.com/nspcc-dev/neofs-s3-gw/api/errors" + "github.com/google/uuid" "github.com/nspcc-dev/neofs-s3-gw/internal/version" ) @@ -33,14 +35,6 @@ type ( // Underlying HTTP status code for the returned error. StatusCode int `xml:"-" json:"-"` } - - // Error structure represents API error. - Error struct { - ErrCode ErrorCode - Code string - Description string - HTTPStatusCode int - } ) const ( @@ -120,7 +114,7 @@ var s3ErrorResponseMap = map[string]string{ func WriteErrorResponse(w http.ResponseWriter, reqInfo *ReqInfo, err error) { code := http.StatusInternalServerError - if e, ok := err.(Error); ok { + if e, ok := err.(errors.Error); ok { code = e.HTTPStatusCode switch e.Code { @@ -142,7 +136,7 @@ func WriteErrorResponse(w http.ResponseWriter, reqInfo *ReqInfo, err error) { // If none of the http routes match respond with appropriate errors. func errorResponseHandler(w http.ResponseWriter, r *http.Request) { desc := fmt.Sprintf("Unknown API request at %s", r.URL.Path) - WriteErrorResponse(w, GetReqInfo(r.Context()), Error{ + WriteErrorResponse(w, GetReqInfo(r.Context()), errors.Error{ Code: "XMinioUnknownAPIRequest", Description: desc, HTTPStatusCode: http.StatusBadRequest, @@ -230,3 +224,25 @@ func (e ErrorResponse) Error() string { } return e.Message } + +// getErrorResponse gets in standard error and resource value and +// provides a encodable populated response values. +func getAPIErrorResponse(info *ReqInfo, err error) ErrorResponse { + code := "InternalError" + desc := err.Error() + + if e, ok := err.(errors.Error); ok { + code = e.Code + desc = e.Description + } + + return ErrorResponse{ + Code: code, + Message: desc, + BucketName: info.BucketName, + Key: info.ObjectName, + Resource: info.URL.Path, + RequestID: info.RequestID, + HostID: info.DeploymentID, + } +} diff --git a/api/user-auth.go b/api/user-auth.go index 6762ea9b8..a3967abf6 100644 --- a/api/user-auth.go +++ b/api/user-auth.go @@ -6,6 +6,7 @@ import ( "github.com/gorilla/mux" "github.com/nspcc-dev/neofs-s3-gw/api/auth" + "github.com/nspcc-dev/neofs-s3-gw/api/errors" "go.uber.org/zap" ) @@ -27,7 +28,10 @@ func AttachUserAuth(router *mux.Router, center auth.Center, log *zap.Logger) { ctx = r.Context() } else { log.Error("failed to pass authentication", zap.Error(err)) - WriteErrorResponse(w, GetReqInfo(r.Context()), GetAPIError(ErrAccessDenied)) + if _, ok := err.(errors.Error); !ok { + err = errors.GetAPIError(errors.ErrAccessDenied) + } + WriteErrorResponse(w, GetReqInfo(r.Context()), err) return } } else {