diff --git a/s3api/utils/utils.go b/s3api/utils/utils.go index 3e14f65a..a60730f5 100644 --- a/s3api/utils/utils.go +++ b/s3api/utils/utils.go @@ -355,7 +355,7 @@ func ParsObjectLockHdrs(ctx *fiber.Ctx) (*objLockCfg, error) { rDate, err := time.Parse(time.RFC3339, objLockDate) if err != nil { debuglogger.Logf("failed to parse retain until date: %v\n", err) - return nil, s3err.GetAPIError(s3err.ErrInvalidRequest) + return nil, s3err.GetAPIError(s3err.ErrInvalidRetainUntilDate) } if rDate.Before(time.Now()) { debuglogger.Logf("expired retain until date: %v\n", rDate.Format(time.RFC3339)) diff --git a/s3err/s3err.go b/s3err/s3err.go index 6abf1892..eec7f5f3 100644 --- a/s3err/s3err.go +++ b/s3err/s3err.go @@ -141,6 +141,7 @@ const ( ErrInvalidBucketObjectLockConfiguration ErrObjectLockConfigurationNotAllowed ErrObjectLocked + ErrInvalidRetainUntilDate ErrPastObjectLockRetainDate ErrObjectLockInvalidRetentionPeriod ErrInvalidLegalHoldStatus @@ -613,9 +614,14 @@ var errorCodeResponse = map[ErrorCode]APIError{ Description: "Access Denied because object protected by object lock.", HTTPStatusCode: http.StatusForbidden, }, + ErrInvalidRetainUntilDate: { + Code: "InvalidArgument", + Description: "The retain until date must be provided in ISO 8601 format", + HTTPStatusCode: http.StatusBadRequest, + }, ErrPastObjectLockRetainDate: { - Code: "InvalidRequest", - Description: "the retain until date must be in the future.", + Code: "InvalidArgument", + Description: "The retain until date must be in the future!", HTTPStatusCode: http.StatusBadRequest, }, ErrObjectLockInvalidRetentionPeriod: { diff --git a/tests/integration/PutObject.go b/tests/integration/PutObject.go index 0026b5cd..7fa44b1c 100644 --- a/tests/integration/PutObject.go +++ b/tests/integration/PutObject.go @@ -18,6 +18,7 @@ import ( "bytes" "context" "fmt" + "net/http" "strings" "time" @@ -299,6 +300,52 @@ func PutObject_invalid_object_lock_mode(s *S3Conf) error { }, withLock()) } +func PutObject_past_retain_until_date(s *S3Conf) error { + testName := "PutObject_past_retain_until_date" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + rDate := time.Now().AddDate(-1, 0, 0) + _, err := putObjectWithData(10, &s3.PutObjectInput{ + Bucket: &bucket, + Key: getPtr("my-object"), + ObjectLockMode: types.ObjectLockModeGovernance, + ObjectLockRetainUntilDate: &rDate, + }, s3client) + + return checkApiErr(err, s3err.GetAPIError(s3err.ErrPastObjectLockRetainDate)) + }, withLock()) +} + +func PutObject_invalid_retain_until_date(s *S3Conf) error { + testName := "PutObject_invalid_retain_until_date" + return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { + req, err := createSignedReq( + http.MethodPut, + s.endpoint, + fmt.Sprintf("%s/my-object", bucket), + s.awsID, + s.awsSecret, + "s3", + s.awsRegion, + nil, + time.Now(), + map[string]string{ + "x-amz-object-lock-retain-until-date": "invalid_date", + "x-amz-object-lock-mode": "GOVERNANCE", + }, + ) + if err != nil { + return err + } + + resp, err := s.httpClient.Do(req) + if err != nil { + return err + } + + return checkHTTPResponseApiErr(resp, s3err.GetAPIError(s3err.ErrInvalidRetainUntilDate)) + }, withLock()) +} + func PutObject_conditional_writes(s *S3Conf) error { testName := "PutObject_conditional_writes" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { diff --git a/tests/integration/group-tests.go b/tests/integration/group-tests.go index 39be64c9..d6b2798a 100644 --- a/tests/integration/group-tests.go +++ b/tests/integration/group-tests.go @@ -165,6 +165,8 @@ func TestPutObject(ts *TestState) { ts.Run(PutObject_with_object_lock) ts.Run(PutObject_invalid_legal_hold) ts.Run(PutObject_invalid_object_lock_mode) + ts.Run(PutObject_past_retain_until_date) + ts.Run(PutObject_invalid_retain_until_date) ts.Run(PutObject_conditional_writes) //TODO: remove the condition after implementing checksums in azure if !ts.conf.azureTests { @@ -1194,6 +1196,8 @@ func GetIntTests() IntTests { "PutObject_with_object_lock": PutObject_with_object_lock, "PutObject_invalid_legal_hold": PutObject_invalid_legal_hold, "PutObject_invalid_object_lock_mode": PutObject_invalid_object_lock_mode, + "PutObject_past_retain_until_date": PutObject_past_retain_until_date, + "PutObject_invalid_retain_until_date": PutObject_invalid_retain_until_date, "PutObject_conditional_writes": PutObject_conditional_writes, "PutObject_with_metadata": PutObject_with_metadata, "PutObject_invalid_credentials": PutObject_invalid_credentials,