* Add documentation for issue #7941 fix * ensure auth * rm FIX_ISSUE_7941.md * Integrate STS session token validation into V4 signature verification - Check for X-Amz-Security-Token header in verifyV4Signature - Call validateSTSSessionToken for STS requests - Skip regular access key lookup and expiration check for STS sessions * Fix variable scoping in verifyV4Signature for STS session token validation * Add ErrExpiredToken error for better AWS S3 compatibility with STS session tokens * Support STS session token in query parameters for presigned URLs * Fix nil pointer dereference in validateSTSSessionToken * Enhance STS token validation with detailed error diagnostics and logging * Fix missing credentials in STSSessionClaims.ToSessionInfo() * test: Add comprehensive STS session claims validation tests - TestSTSSessionClaimsToSessionInfo: Validates basic claims conversion - TestSTSSessionClaimsToSessionInfoCredentialGeneration: Verifies credential generation - TestSTSSessionClaimsToSessionInfoPreservesAllFields: Ensures all fields are preserved - TestSTSSessionClaimsToSessionInfoEmptyFields: Tests handling of empty/nil fields - TestSTSSessionClaimsToSessionInfoCredentialExpiration: Validates expiration handling All tests pass with proper timing tolerance for credential generation. * perf: Reuse CredentialGenerator instance for STS session claims Optimize ToSessionInfo() to reuse a package-level defaultCredentialGenerator instead of allocating a new CredentialGenerator on every call. This reduces allocation overhead since this method is called frequently during signature verification (potentially once per request). The CredentialGenerator is stateless and deterministic, making it safe to reuse across concurrent calls without synchronization. * refactor: Surface credential generation errors and remove sensitive logging Two improvements to error handling and security: 1. weed/iam/sts/session_claims.go: - Add logging for credential generation failures in ToSessionInfo() - Wrap errors with context (session ID) to aid debugging - Use glog.Warningf() to surface errors instead of silently swallowing them - Add fmt import for error wrapping 2. weed/s3api/auth_signature_v4.go: - Remove debug logging of actual access key IDs (glog.V(2) call) - Security improvement: avoid exposing sensitive access keys even at debug level - Keep warning-level logging that shows only count of available keys This ensures credential generation failures are observable while protecting sensitive authentication material from logs. * test: Verify deterministic credential generation in session claims tests Update TestSTSSessionClaimsToSessionInfoCredentialGeneration to properly verify deterministic credential generation: - Remove misleading comment about 'randomness' - parts of credentials ARE deterministic - Add assertions that AccessKeyId is identical for same SessionId (hash-based, deterministic) - Add assertions that SessionToken is identical for same SessionId (hash-based, deterministic) - Verify Expiration matches when SessionId is identical - Document that SecretAccessKey is NOT deterministic (uses random.Read) - Truncate expiresAt to second precision to avoid timing issues This test now properly verifies that the deterministic components of credential generation work correctly while acknowledging the cryptographic randomness of the secret access key. * test(sts): Assert credentials expiration relative to now in credential expiration tests Replace wallclock assertions comparing tc.expiresAt to time.Now() (which only verified test setup) with assertions that check sessionInfo.Credentials.Expiration relative to time.Now(), thus exercising the code under test. Include clarifying comment for intent. * feat(sts): Add IsExpired helpers and use them in expiration tests - Add Credentials.IsExpired() and SessionInfo.IsExpired() in new file session_helpers.go. - Update TestSTSSessionClaimsToSessionInfoCredentialExpiration to use helpers for clearer intent. * test: revert test-only IsExpired helpers; restore direct expiration assertions Remove session_helpers.go and update TestSTSSessionClaimsToSessionInfoCredentialExpiration to assert against sessionInfo.Credentials.Expiration directly as requested by reviewer., * fix(s3api): restore error return when access key not found Critical fix: The previous cleanup of sensitive logging inadvertently removed the error return statement when access key lookup fails. This caused the code to continue and call isCredentialExpired() on nil pointer, crashing the server. This explains EOF errors in CORS tests - server was panicking on requests with invalid keys. * fix(sts): make secret access key deterministic based on sessionId CRITICAL FIX: The secret access key was being randomly generated, causing signature verification failures when the same session token was used twice: 1. AssumeRoleWithWebIdentity generates random secret key X 2. Client signs request using secret key X 3. Server validates token, regenerates credentials via ToSessionInfo() 4. ToSessionInfo() calls generateSecretAccessKey(), which generates random key Y 5. Server tries to verify signature using key Y, but signature was made with X 6. Signature verification fails (SignatureDoesNotMatch) Solution: Make generateSecretAccessKey() deterministic by using SHA256 hash of 'secret-key:' + sessionId, just like generateAccessKeyId() already does. This ensures: - AssumeRoleWithWebIdentity generates deterministic secret key from sessionId - ToSessionInfo() regenerates the same secret key from the same sessionId - Client signature verification succeeds because keys match Fixes: AWS SDK v2 CORS tests failing with 'ExpiredToken' errors Affected files: - weed/iam/sts/token_utils.go: Updated generateSecretAccessKey() signature and implementation to be deterministic - Updated GenerateTemporaryCredentials() to pass sessionId parameter Tests: All 54 STS tests pass with this fix * test(sts): add comprehensive secret key determinism test coverage Updated tests to verify that secret access keys are now deterministic: 1. Updated TestSTSSessionClaimsToSessionInfoCredentialGeneration: - Changed comment from 'NOT deterministic' to 'NOW deterministic' - Added assertion that same sessionId produces identical secret key - Explains why this is critical for signature verification 2. Added TestSecretAccessKeyDeterminism (new dedicated test): - Verifies secret key is identical across multiple calls with same sessionId - Verifies access key ID and session token are also identical - Verifies different sessionIds produce different credentials - Includes detailed comments explaining why determinism is critical These tests ensure that the STS implementation correctly regenerates deterministic credentials during signature verification. Without determinism, signature verification would always fail because the server would use different secret keys than the client used to sign. * refactor(sts): add explicit zero-time expiration handling Improved defensive programming in IsExpired() methods: 1. Credentials.IsExpired(): - Added explicit check for zero-time expiration (time.Time{}) - Treats uninitialized credentials as expired - Prevents accidentally treating uninitialized creds as valid 2. SessionInfo.IsExpired(): - Added same explicit zero-time check - Treats uninitialized sessions as expired - Protects against bugs where sessions might not be properly initialized This is important because time.Now().After(time.Time{}) returns true, but explicitly checking for zero time makes the intent clear and helps catch initialization bugs during code review and debugging. * refactor(sts): remove unused IsExpired() helper functions The session_helpers.go file contained two unused IsExpired() methods: - Credentials.IsExpired() - SessionInfo.IsExpired() These were never called anywhere in the codebase. The actual expiration checks use: - isCredentialExpired() in weed/s3api/auth_credentials.go (S3 auth) - Direct time.Now().After() checks Removing unused code improves code clarity and reduces maintenance burden. * fix(auth): pass STS session token to IAM authorization for V4 signature auth CRITICAL FIX: Session tokens were not being passed to the authorization check when using AWS Signature V4 authentication with STS credentials. The bug: 1. AWS SDK sends request with X-Amz-Security-Token header (V4 signature) 2. validateSTSSessionToken validates the token, creates Identity with PrincipalArn 3. authorizeWithIAM only checked X-SeaweedFS-Session-Token (JWT auth header) 4. Since it was empty, fell into 'static V4' branch which set SessionToken = '' 5. AuthorizeAction returned ErrAccessDenied because SessionToken was empty The fix (in authorizeWithIAM): - Check X-SeaweedFS-Session-Token first (JWT auth) - If empty, fallback to X-Amz-Security-Token header (V4 STS auth) - If still empty, check X-Amz-Security-Token query param (presigned URLs) - When session token is found with PrincipalArn, use 'STS V4 signature' path - Only use 'static V4' path when there's no session token This ensures: - JWT Bearer auth with session tokens works (existing path) - STS V4 signature auth with session tokens works (new path) - Static V4 signature auth without session tokens works (existing path) Logging updated to distinguish: - 'JWT-based IAM authorization' - 'STS V4 signature IAM authorization' (new) - 'static V4 signature IAM authorization' (clarified) * test(s3api): add comprehensive STS session token authorization test coverage Added new test file auth_sts_v4_test.go with comprehensive tests for the STS session token authorization fix: 1. TestAuthorizeWithIAMSessionTokenExtraction: - Verifies X-SeaweedFS-Session-Token is extracted from JWT auth headers - Verifies X-Amz-Security-Token is extracted from V4 STS auth headers - Verifies X-Amz-Security-Token is extracted from query parameters (presigned URLs) - Verifies JWT tokens take precedence when both are present - Regression test for the bug where V4 STS tokens were not being passed to authorization 2. TestSTSSessionTokenIntoCredentials: - Verifies STS credentials have all required fields (AccessKeyId, SecretAccessKey, SessionToken) - Verifies deterministic generation from sessionId (same sessionId = same credentials) - Verifies different sessionIds produce different credentials - Critical for signature verification: same session must regenerate same secret key 3. TestActionConstantsForV4Auth: - Verifies S3 action constants are available for authorization checks - Ensures ACTION_READ, ACTION_WRITE, etc. are properly defined These tests ensure that: - V4 Signature auth with STS tokens properly extracts and uses session tokens - Session tokens are prioritized correctly (JWT > X-Amz-Security-Token header > query param) - STS credentials are deterministically generated for signature verification - The fix for passing STS session tokens to authorization is properly covered All 3 test functions pass (6 test cases total). * refactor(s3api): improve code quality and performance - Rename authorization path constants to avoid conflict with existing authType enum - Replace nested if/else with clean switch statement in authorizeWithIAM() - Add determineIAMAuthPath() helper for clearer intent and testability - Optimize key counting in auth_signature_v4.go: remove unnecessary slice allocation - Fix timing assertion in session_claims_test.go: use WithinDuration for symmetric tolerance These changes improve code readability, maintainability, and performance while maintaining full backward compatibility and test coverage. * refactor(s3api): use typed iamAuthPath for authorization path constants - Define iamAuthPath as a named string type (similar to existing authType enum) - Update constants to use explicit type: iamAuthPathJWT, iamAuthPathSTS_V4, etc. - Update determineIAMAuthPath() to return typed iamAuthPath - Improves type safety and prevents accidental string value misuse
597 lines
20 KiB
Go
597 lines
20 KiB
Go
package s3err
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/seaweedfs/seaweedfs/weed/util/constants"
|
|
)
|
|
|
|
// APIError structure
|
|
type APIError struct {
|
|
Code string
|
|
Description string
|
|
HTTPStatusCode int
|
|
}
|
|
|
|
// RESTErrorResponse - error response format
|
|
type RESTErrorResponse struct {
|
|
XMLName xml.Name `xml:"Error" json:"-"`
|
|
Code string `xml:"Code" json:"Code"`
|
|
Message string `xml:"Message" json:"Message"`
|
|
Resource string `xml:"Resource" json:"Resource"`
|
|
RequestID string `xml:"RequestId" json:"RequestId"`
|
|
Key string `xml:"Key,omitempty" json:"Key,omitempty"`
|
|
BucketName string `xml:"BucketName,omitempty" json:"BucketName,omitempty"`
|
|
|
|
// Underlying HTTP status code for the returned error
|
|
StatusCode int `xml:"-" json:"-"`
|
|
}
|
|
|
|
// Error - Returns S3 error string.
|
|
func (e RESTErrorResponse) Error() string {
|
|
if e.Message == "" {
|
|
msg, ok := s3ErrorResponseMap[e.Code]
|
|
if !ok {
|
|
msg = fmt.Sprintf("Error response code %s.", e.Code)
|
|
}
|
|
return msg
|
|
}
|
|
return e.Message
|
|
}
|
|
|
|
// ErrorCode type of error status.
|
|
type ErrorCode int
|
|
|
|
// Error codes, see full list at http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
|
|
const (
|
|
ErrNone ErrorCode = iota
|
|
ErrAccessDenied
|
|
ErrMethodNotAllowed
|
|
ErrBucketNotEmpty
|
|
ErrBucketAlreadyExists
|
|
ErrBucketAlreadyOwnedByYou
|
|
ErrNoSuchBucket
|
|
ErrNoSuchBucketPolicy
|
|
ErrNoSuchCORSConfiguration
|
|
ErrNoSuchLifecycleConfiguration
|
|
ErrNoSuchKey
|
|
ErrNoSuchUpload
|
|
ErrInvalidBucketName
|
|
ErrInvalidBucketState
|
|
ErrInvalidDigest
|
|
ErrBadDigest
|
|
ErrInvalidMaxKeys
|
|
ErrInvalidMaxUploads
|
|
ErrInvalidMaxParts
|
|
ErrInvalidMaxDeleteObjects
|
|
ErrInvalidPartNumberMarker
|
|
ErrInvalidPart
|
|
ErrInvalidRange
|
|
ErrInternalError
|
|
ErrInvalidCopyDest
|
|
ErrInvalidCopySource
|
|
ErrInvalidTag
|
|
ErrAuthHeaderEmpty
|
|
ErrSignatureVersionNotSupported
|
|
ErrMalformedPOSTRequest
|
|
ErrPOSTFileRequired
|
|
ErrPostPolicyConditionInvalidFormat
|
|
ErrEntityTooSmall
|
|
ErrEntityTooLarge
|
|
ErrMissingFields
|
|
ErrMissingCredTag
|
|
ErrCredMalformed
|
|
ErrMalformedXML
|
|
ErrMalformedDate
|
|
ErrMalformedPresignedDate
|
|
ErrMalformedCredentialDate
|
|
ErrMalformedPolicy
|
|
ErrInvalidPolicyDocument
|
|
ErrMissingSignHeadersTag
|
|
ErrMissingSignTag
|
|
ErrUnsignedHeaders
|
|
ErrInvalidQueryParams
|
|
ErrInvalidQuerySignatureAlgo
|
|
ErrExpiredPresignRequest
|
|
ErrExpiredToken
|
|
ErrMalformedExpires
|
|
ErrNegativeExpires
|
|
ErrMaximumExpires
|
|
ErrSignatureDoesNotMatch
|
|
ErrContentSHA256Mismatch
|
|
ErrInvalidAccessKeyID
|
|
ErrRequestNotReadyYet
|
|
ErrRequestTimeTooSkewed
|
|
ErrMissingDateHeader
|
|
ErrInvalidRequest
|
|
ErrAuthNotSetup
|
|
ErrNotImplemented
|
|
ErrPreconditionFailed
|
|
ErrNotModified
|
|
|
|
ErrExistingObjectIsDirectory
|
|
ErrExistingObjectIsFile
|
|
|
|
ErrTooManyRequest
|
|
ErrRequestBytesExceed
|
|
|
|
OwnershipControlsNotFoundError
|
|
ErrNoSuchTagSet
|
|
ErrNoSuchObjectLockConfiguration
|
|
ErrNoSuchObjectLegalHold
|
|
ErrInvalidRetentionPeriod
|
|
ErrObjectLockConfigurationNotFoundError
|
|
ErrInvalidUnorderedWithDelimiter
|
|
|
|
// SSE-C related errors
|
|
ErrInvalidEncryptionAlgorithm
|
|
ErrInvalidEncryptionKey
|
|
ErrSSECustomerKeyMD5Mismatch
|
|
ErrSSECustomerKeyMissing
|
|
ErrSSECustomerKeyNotNeeded
|
|
ErrSSEEncryptionTypeMismatch
|
|
|
|
// SSE-KMS related errors
|
|
ErrKMSKeyNotFound
|
|
ErrKMSAccessDenied
|
|
ErrKMSDisabled
|
|
ErrKMSInvalidCiphertext
|
|
|
|
// Bucket encryption errors
|
|
ErrNoSuchBucketEncryptionConfiguration
|
|
)
|
|
|
|
// Error message constants for checksum validation
|
|
const (
|
|
ErrMsgPayloadChecksumMismatch = "payload checksum does not match"
|
|
ErrMsgChunkSignatureMismatch = "chunk signature does not match"
|
|
ErrMsgChecksumAlgorithmMismatch = "checksum algorithm mismatch"
|
|
)
|
|
|
|
// error code to APIError structure, these fields carry respective
|
|
// descriptions for all the error responses.
|
|
var errorCodeResponse = map[ErrorCode]APIError{
|
|
ErrAccessDenied: {
|
|
Code: "AccessDenied",
|
|
Description: "Access Denied.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrMethodNotAllowed: {
|
|
Code: "MethodNotAllowed",
|
|
Description: "The specified method is not allowed against this resource.",
|
|
HTTPStatusCode: http.StatusMethodNotAllowed,
|
|
},
|
|
ErrBucketNotEmpty: {
|
|
Code: "BucketNotEmpty",
|
|
Description: "The bucket you tried to delete is not empty",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrBucketAlreadyExists: {
|
|
Code: "BucketAlreadyExists",
|
|
Description: "The requested bucket name is not available. The bucket name can not be an existing collection, and the bucket namespace is shared by all users of the system. Please select a different name and try again.",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrBucketAlreadyOwnedByYou: {
|
|
Code: "BucketAlreadyOwnedByYou",
|
|
Description: "Your previous request to create the named bucket succeeded and you already own it.",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrInvalidBucketName: {
|
|
Code: "InvalidBucketName",
|
|
Description: "The specified bucket is not valid.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidBucketState: {
|
|
Code: "InvalidBucketState",
|
|
Description: "The bucket is not in a valid state for the requested operation",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrInvalidDigest: {
|
|
Code: "InvalidDigest",
|
|
Description: "The Content-Md5 you specified is not valid.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrBadDigest: {
|
|
Code: "BadDigest",
|
|
Description: constants.ErrMsgBadDigest,
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidMaxUploads: {
|
|
Code: "InvalidArgument",
|
|
Description: "Argument max-uploads 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 max-parts must be an integer between 0 and 2147483647",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidMaxDeleteObjects: {
|
|
Code: "InvalidArgument",
|
|
Description: "Argument objects can contain a list of up to 1000 keys",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidPartNumberMarker: {
|
|
Code: "InvalidArgument",
|
|
Description: "Argument partNumberMarker must be an integer.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrNoSuchBucket: {
|
|
Code: "NoSuchBucket",
|
|
Description: "The specified bucket does not exist",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrNoSuchBucketPolicy: {
|
|
Code: "NoSuchBucketPolicy",
|
|
Description: "The bucket policy does not exist",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrNoSuchTagSet: {
|
|
Code: "NoSuchTagSet",
|
|
Description: "The TagSet does not exist",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrNoSuchObjectLockConfiguration: {
|
|
Code: "NoSuchObjectLockConfiguration",
|
|
Description: "The specified object does not have an ObjectLock configuration",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrNoSuchObjectLegalHold: {
|
|
Code: "NoSuchObjectLegalHold",
|
|
Description: "The specified object does not have a legal hold configuration",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrInvalidRetentionPeriod: {
|
|
Code: "InvalidRetentionPeriod",
|
|
Description: "The retention period specified is invalid",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrNoSuchCORSConfiguration: {
|
|
Code: "NoSuchCORSConfiguration",
|
|
Description: "The CORS configuration does not exist",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrNoSuchLifecycleConfiguration: {
|
|
Code: "NoSuchLifecycleConfiguration",
|
|
Description: "The lifecycle configuration does not exist",
|
|
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. The upload ID may be invalid, or the upload may have been aborted or completed.",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrInternalError: {
|
|
Code: "InternalError",
|
|
Description: "We encountered an internal error, please try again.",
|
|
HTTPStatusCode: http.StatusInternalServerError,
|
|
},
|
|
|
|
ErrInvalidPart: {
|
|
Code: "InvalidPart",
|
|
Description: "One or more of the specified parts could not be found. The part may not have been uploaded, or the specified entity tag may not match the part's entity tag.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
|
|
ErrInvalidCopyDest: {
|
|
Code: "InvalidRequest",
|
|
Description: "This copy request is illegal because it is trying to copy an object to itself without changing the object's metadata, storage class, website redirect location or encryption attributes.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidCopySource: {
|
|
Code: "InvalidArgument",
|
|
Description: "Copy Source must mention the source bucket and key: sourcebucket/sourcekey.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidTag: {
|
|
Code: "InvalidTag",
|
|
Description: "The Tag value you have provided is invalid",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedXML: {
|
|
Code: "MalformedXML",
|
|
Description: "The XML you provided was not well-formed or did not validate against our published schema.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedPolicy: {
|
|
Code: "MalformedPolicy",
|
|
Description: "Policy has invalid resource.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidPolicyDocument: {
|
|
Code: "InvalidPolicyDocument",
|
|
Description: "The content of the policy document is invalid.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrAuthHeaderEmpty: {
|
|
Code: "InvalidArgument",
|
|
Description: "Authorization header is invalid -- one and only one ' ' (space) required.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrSignatureVersionNotSupported: {
|
|
Code: "InvalidRequest",
|
|
Description: "The authorization mechanism you have provided is not supported. Please use AWS4-HMAC-SHA256.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedPOSTRequest: {
|
|
Code: "MalformedPOSTRequest",
|
|
Description: "The body of your POST request is not well-formed multipart/form-data.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrPOSTFileRequired: {
|
|
Code: "InvalidArgument",
|
|
Description: "POST requires exactly one file upload per request.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrPostPolicyConditionInvalidFormat: {
|
|
Code: "PostPolicyInvalidKeyName",
|
|
Description: "Invalid according to Policy: Policy Condition failed",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
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,
|
|
},
|
|
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: "AuthorizationQueryParametersError",
|
|
Description: "Error parsing the X-Amz-Credential parameter; the Credential is mal-formed; expecting \"<YOUR-AKID>/YYYYMMDD/REGION/SERVICE/aws4_request\".",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedDate: {
|
|
Code: "MalformedDate",
|
|
Description: "Invalid date format header, expected to be in ISO8601, RFC1123 or RFC1123Z time format.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedPresignedDate: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "X-Amz-Date must be in the ISO8601 Long Format \"yyyyMMdd'T'HHmmss'Z'\"",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingSignHeadersTag: {
|
|
Code: "InvalidArgument",
|
|
Description: "Signature header missing SignedHeaders field.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingSignTag: {
|
|
Code: "AccessDenied",
|
|
Description: "Signature header missing Signature field.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
|
|
ErrUnsignedHeaders: {
|
|
Code: "AccessDenied",
|
|
Description: "There were headers present in the request which were not signed",
|
|
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,
|
|
},
|
|
ErrInvalidQuerySignatureAlgo: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "X-Amz-Algorithm only supports \"AWS4-HMAC-SHA256\".",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrExpiredPresignRequest: {
|
|
Code: "AccessDenied",
|
|
Description: "Request has expired",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrExpiredToken: {
|
|
Code: "ExpiredToken",
|
|
Description: "The provided token has expired.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMalformedExpires: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "X-Amz-Expires should be a number",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrNegativeExpires: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "X-Amz-Expires must be non-negative",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMaximumExpires: {
|
|
Code: "AuthorizationQueryParametersError",
|
|
Description: "X-Amz-Expires must be less than a week (in seconds); that is, the given X-Amz-Expires must be less than 604800 seconds",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
|
|
ErrInvalidAccessKeyID: {
|
|
Code: "InvalidAccessKeyId",
|
|
Description: "The access key ID you provided does not exist in our records.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
|
|
ErrRequestNotReadyYet: {
|
|
Code: "AccessDenied",
|
|
Description: "Request is not valid yet",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
|
|
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. Check your key and signing method.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
|
|
ErrContentSHA256Mismatch: {
|
|
Code: "XAmzContentSHA256Mismatch",
|
|
Description: "The provided 'x-amz-content-sha256' header does not match what was computed.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrMissingDateHeader: {
|
|
Code: "AccessDenied",
|
|
Description: "AWS authentication requires a valid Date or x-amz-date header",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidRequest: {
|
|
Code: "InvalidRequest",
|
|
Description: "Invalid Request",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidRange: {
|
|
Code: "InvalidRange",
|
|
Description: "The requested range is not satisfiable",
|
|
HTTPStatusCode: http.StatusRequestedRangeNotSatisfiable,
|
|
},
|
|
ErrAuthNotSetup: {
|
|
Code: "InvalidRequest",
|
|
Description: "Signed request requires setting up SeaweedFS S3 authentication",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrNotImplemented: {
|
|
Code: "NotImplemented",
|
|
Description: "A header you provided implies functionality that is not implemented",
|
|
HTTPStatusCode: http.StatusNotImplemented,
|
|
},
|
|
ErrPreconditionFailed: {
|
|
Code: "PreconditionFailed",
|
|
Description: "At least one of the pre-conditions you specified did not hold",
|
|
HTTPStatusCode: http.StatusPreconditionFailed,
|
|
},
|
|
ErrNotModified: {
|
|
Code: "NotModified",
|
|
Description: "The object was not modified since the specified time",
|
|
HTTPStatusCode: http.StatusNotModified,
|
|
},
|
|
ErrExistingObjectIsDirectory: {
|
|
Code: "ExistingObjectIsDirectory",
|
|
Description: "Existing Object is a directory.",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrExistingObjectIsFile: {
|
|
Code: "ExistingObjectIsFile",
|
|
Description: "Existing Object is a file.",
|
|
HTTPStatusCode: http.StatusConflict,
|
|
},
|
|
ErrTooManyRequest: {
|
|
Code: "ErrTooManyRequest",
|
|
Description: "Too many simultaneous request count",
|
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
|
},
|
|
ErrRequestBytesExceed: {
|
|
Code: "ErrRequestBytesExceed",
|
|
Description: "Simultaneous request bytes exceed limitations",
|
|
HTTPStatusCode: http.StatusServiceUnavailable,
|
|
},
|
|
|
|
OwnershipControlsNotFoundError: {
|
|
Code: "OwnershipControlsNotFoundError",
|
|
Description: "The bucket ownership controls were not found",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrObjectLockConfigurationNotFoundError: {
|
|
Code: "ObjectLockConfigurationNotFoundError",
|
|
Description: "Object Lock configuration does not exist for this bucket",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
ErrInvalidUnorderedWithDelimiter: {
|
|
Code: "InvalidArgument",
|
|
Description: "Unordered listing cannot be used with delimiter",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
|
|
// SSE-C related error mappings
|
|
ErrInvalidEncryptionAlgorithm: {
|
|
Code: "InvalidEncryptionAlgorithmError",
|
|
Description: "The encryption algorithm specified is not valid.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrInvalidEncryptionKey: {
|
|
Code: "InvalidArgument",
|
|
Description: "Invalid encryption key. Encryption key must be 256-bit AES256.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrSSECustomerKeyMD5Mismatch: {
|
|
Code: "InvalidArgument",
|
|
Description: "The provided customer encryption key MD5 does not match the key.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrSSECustomerKeyMissing: {
|
|
Code: "InvalidArgument",
|
|
Description: "Requests specifying Server Side Encryption with Customer provided keys must provide the customer key.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrSSECustomerKeyNotNeeded: {
|
|
Code: "InvalidArgument",
|
|
Description: "The object was not encrypted with customer provided keys.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrSSEEncryptionTypeMismatch: {
|
|
Code: "InvalidRequest",
|
|
Description: "The encryption method specified in the request does not match the encryption method used to encrypt the object.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
|
|
// SSE-KMS error responses
|
|
ErrKMSKeyNotFound: {
|
|
Code: "KMSKeyNotFoundException",
|
|
Description: "The specified KMS key does not exist.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrKMSAccessDenied: {
|
|
Code: "KMSAccessDeniedException",
|
|
Description: "Access denied to the specified KMS key.",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
ErrKMSDisabled: {
|
|
Code: "KMSKeyDisabledException",
|
|
Description: "The specified KMS key is disabled.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
ErrKMSInvalidCiphertext: {
|
|
Code: "InvalidCiphertext",
|
|
Description: "The provided ciphertext is invalid or corrupted.",
|
|
HTTPStatusCode: http.StatusBadRequest,
|
|
},
|
|
|
|
// Bucket encryption error responses
|
|
ErrNoSuchBucketEncryptionConfiguration: {
|
|
Code: "ServerSideEncryptionConfigurationNotFoundError",
|
|
Description: "The server side encryption configuration was not found.",
|
|
HTTPStatusCode: http.StatusNotFound,
|
|
},
|
|
}
|
|
|
|
// GetAPIError provides API Error for input API error code.
|
|
func GetAPIError(code ErrorCode) APIError {
|
|
return errorCodeResponse[code]
|
|
}
|