diff --git a/auth/object_lock.go b/auth/object_lock.go index 7a91e6aa..ba57ebda 100644 --- a/auth/object_lock.go +++ b/auth/object_lock.go @@ -35,6 +35,11 @@ type BucketLockConfig struct { CreatedAt *time.Time } +const ( + maxObjectLockRetentionDays int32 = 36500 + maxObjectLockRetentionYears int32 = 100 +) + func ParseBucketLockConfigurationInput(input []byte) ([]byte, error) { var lockConfig types.ObjectLockConfiguration if err := xml.Unmarshal(input, &lockConfig); err != nil { @@ -62,9 +67,15 @@ func ParseBucketLockConfigurationInput(input []byte) ([]byte, error) { if retention.Days != nil && *retention.Days <= 0 { return nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionDays, fmt.Sprint(*retention.Days)) } + if retention.Days != nil && *retention.Days > maxObjectLockRetentionDays { + return nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionDaysTooLarge, fmt.Sprint(*retention.Days)) + } if retention.Years != nil && *retention.Years <= 0 { return nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionYears, fmt.Sprint(*retention.Years)) } + if retention.Years != nil && *retention.Years > maxObjectLockRetentionYears { + return nil, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionYearsTooLarge, fmt.Sprint(*retention.Years)) + } config.DefaultRetention = retention now := time.Now() diff --git a/s3err/invalid-argument.go b/s3err/invalid-argument.go index 5e8d65b5..3171514e 100644 --- a/s3err/invalid-argument.go +++ b/s3err/invalid-argument.go @@ -43,6 +43,8 @@ const ( InvalidArgPastObjectLockRetainDate InvalidArgObjectLockRetentionDays InvalidArgObjectLockRetentionYears + InvalidArgObjectLockRetentionDaysTooLarge + InvalidArgObjectLockRetentionYearsTooLarge InvalidArgMissingObjectLockRetainDate InvalidArgMissingObjectLockMode InvalidArgLegalHoldStatus @@ -145,6 +147,14 @@ var invalidArgErrResponses = map[InvalidArgErrorCode]InvalidArgumentError{ Description: "Default retention period must be a positive integer value.", ArgumentName: "Years", }, + InvalidArgObjectLockRetentionDaysTooLarge: { + Description: "Default retention period too large.", + ArgumentName: "Days", + }, + InvalidArgObjectLockRetentionYearsTooLarge: { + Description: "Default retention period too large.", + ArgumentName: "Years", + }, InvalidArgLegalHoldStatus: { Description: "Legal Hold must be either of 'ON' or 'OFF'", ArgumentName: "x-amz-object-lock-legal-hold", diff --git a/tests/integration/PutObjectLockConfiguration.go b/tests/integration/PutObjectLockConfiguration.go index 6ce6778c..38c09e0b 100644 --- a/tests/integration/PutObjectLockConfiguration.go +++ b/tests/integration/PutObjectLockConfiguration.go @@ -211,42 +211,67 @@ func PutObjectLockConfiguration_both_years_and_days(s *S3Conf) error { } func PutObjectLockConfiguration_invalid_years_days(s *S3Conf) error { - testName := "PutObjectLockConfiguration_invalid_years" + testName := "PutObjectLockConfiguration_invalid_years_days" return actionHandler(s, testName, func(s3client *s3.Client, bucket string) error { - var days, years int32 = -3, -5 - ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) - _, err := s3client.PutObjectLockConfiguration(ctx, &s3.PutObjectLockConfigurationInput{ - Bucket: &bucket, - ObjectLockConfiguration: &types.ObjectLockConfiguration{ - ObjectLockEnabled: types.ObjectLockEnabledEnabled, - Rule: &types.ObjectLockRule{ - DefaultRetention: &types.DefaultRetention{ - Days: &days, - Mode: types.ObjectLockRetentionModeCompliance, + putObjectLockConfiguration := func(retention *types.DefaultRetention) error { + ctx, cancel := context.WithTimeout(context.Background(), shortTimeout) + _, err := s3client.PutObjectLockConfiguration(ctx, &s3.PutObjectLockConfigurationInput{ + Bucket: &bucket, + ObjectLockConfiguration: &types.ObjectLockConfiguration{ + ObjectLockEnabled: types.ObjectLockEnabledEnabled, + Rule: &types.ObjectLockRule{ + DefaultRetention: retention, }, }, - }, - }) - cancel() - if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionDays, fmt.Sprint(days))); err != nil { + }) + cancel() return err } - ctx, cancel = context.WithTimeout(context.Background(), shortTimeout) - _, err = s3client.PutObjectLockConfiguration(ctx, &s3.PutObjectLockConfigurationInput{ - Bucket: &bucket, - ObjectLockConfiguration: &types.ObjectLockConfiguration{ - ObjectLockEnabled: types.ObjectLockEnabledEnabled, - Rule: &types.ObjectLockRule{ - DefaultRetention: &types.DefaultRetention{ - Years: &years, - Mode: types.ObjectLockRetentionModeCompliance, - }, + + tests := []struct { + name string + retention *types.DefaultRetention + err s3err.InvalidArgumentError + }{ + { + name: "negative days", + retention: &types.DefaultRetention{ + Days: getPtr[int32](-3), + Mode: types.ObjectLockRetentionModeCompliance, }, + err: s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionDays, "-3"), }, - }) - cancel() - if err := checkApiErr(err, s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionYears, fmt.Sprint(years))); err != nil { - return err + { + name: "too many days", + retention: &types.DefaultRetention{ + Days: getPtr[int32](36501), + Mode: types.ObjectLockRetentionModeCompliance, + }, + err: s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionDaysTooLarge, "36501"), + }, + { + name: "negative years", + retention: &types.DefaultRetention{ + Years: getPtr[int32](-5), + Mode: types.ObjectLockRetentionModeCompliance, + }, + err: s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionYears, "-5"), + }, + { + name: "too many years", + retention: &types.DefaultRetention{ + Years: getPtr[int32](101), + Mode: types.ObjectLockRetentionModeCompliance, + }, + err: s3err.GetInvalidArgumentErr(s3err.InvalidArgObjectLockRetentionYearsTooLarge, "101"), + }, + } + + for _, test := range tests { + err := putObjectLockConfiguration(test.retention) + if err := checkApiErr(err, test.err); err != nil { + return fmt.Errorf("%s: %w", test.name, err) + } } return nil