| 
									
										
										
										
											2021-04-19 03:41:13 +08:00
										 |  |  | // Copyright (c) 2015-2021 MinIO, Inc.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This file is part of MinIO Object Storage stack
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // This program is distributed in the hope that it will be useful
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 	"math" | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	"net/http" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/auth" | 
					
						
							|  |  |  | 	objectlock "github.com/minio/minio/internal/bucket/object/lock" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/bucket/replication" | 
					
						
							|  |  |  | 	xhttp "github.com/minio/minio/internal/http" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2021-05-30 12:16:42 +08:00
										 |  |  | 	"github.com/minio/pkg/bucket/policy" | 
					
						
							| 
									
										
										
										
											2019-11-13 06:50:18 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | // BucketObjectLockSys - map of bucket and retention configuration.
 | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | type BucketObjectLockSys struct{} | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Get - Get retention configuration.
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | func (sys *BucketObjectLockSys) Get(bucketName string) (r objectlock.Retention, err error) { | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | 	if globalIsGateway { | 
					
						
							| 
									
										
										
										
											2020-10-10 00:59:52 +08:00
										 |  |  | 		objAPI := newObjectLayerFn() | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | 		if objAPI == nil { | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 			return r, errServerNotInitialized | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 		return r, nil | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 	config, err := globalBucketMetadataSys.GetObjectLockConfig(bucketName) | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 		if _, ok := err.(BucketObjectLockConfigNotFound); ok { | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 			return r, nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return r, err | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return config.ToRetention(), nil | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-01 06:55:54 +08:00
										 |  |  | // enforceRetentionForDeletion checks if it is appropriate to remove an
 | 
					
						
							|  |  |  | // object according to locking configuration when this is lifecycle/ bucket quota asking.
 | 
					
						
							|  |  |  | func enforceRetentionForDeletion(ctx context.Context, objInfo ObjectInfo) (locked bool) { | 
					
						
							| 
									
										
										
										
											2020-04-10 00:28:57 +08:00
										 |  |  | 	lhold := objectlock.GetObjectLegalHoldMeta(objInfo.UserDefined) | 
					
						
							|  |  |  | 	if lhold.Status.Valid() && lhold.Status == objectlock.LegalHoldOn { | 
					
						
							|  |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret := objectlock.GetObjectRetentionMeta(objInfo.UserDefined) | 
					
						
							|  |  |  | 	if ret.Mode.Valid() && (ret.Mode == objectlock.RetCompliance || ret.Mode == objectlock.RetGovernance) { | 
					
						
							|  |  |  | 		t, err := objectlock.UTCNowNTP() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if ret.RetainUntilDate.After(t) { | 
					
						
							|  |  |  | 			return true | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | // enforceRetentionBypassForDelete enforces whether an existing object under governance can be deleted
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | // with governance bypass headers set in the request.
 | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | // Objects under site wide WORM can never be overwritten.
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | // For objects in "Governance" mode, overwrite is allowed if a) object retention date is past OR
 | 
					
						
							|  |  |  | // governance bypass headers are set and user has governance bypass permissions.
 | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | // Objects in "Compliance" mode can be overwritten only if retention date is past.
 | 
					
						
							| 
									
										
										
										
											2020-11-26 03:24:50 +08:00
										 |  |  | func enforceRetentionBypassForDelete(ctx context.Context, r *http.Request, bucket string, object ObjectToDelete, oi ObjectInfo, gerr error) APIErrorCode { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	opts, err := getOpts(ctx, r, bucket, object.ObjectName) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 		return toAPIErrorCode(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 	opts.VersionID = object.VersionID | 
					
						
							| 
									
										
										
										
											2020-11-26 03:24:50 +08:00
										 |  |  | 	if gerr != nil { // error from GetObjectInfo
 | 
					
						
							| 
									
										
										
										
											2020-11-29 13:15:45 +08:00
										 |  |  | 		switch gerr.(type) { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		case MethodNotAllowed: // This happens usually for a delete marker
 | 
					
						
							| 
									
										
										
										
											2021-09-18 06:21:24 +08:00
										 |  |  | 			if oi.DeleteMarker || !oi.VersionPurgeStatus.Empty() { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 				// Delete marker should be present and valid.
 | 
					
						
							|  |  |  | 				return ErrNone | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-12-16 03:15:49 +08:00
										 |  |  | 		if isErrObjectNotFound(gerr) || isErrVersionNotFound(gerr) { | 
					
						
							|  |  |  | 			return ErrNone | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-29 13:15:45 +08:00
										 |  |  | 		return toAPIErrorCode(ctx, gerr) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	lhold := objectlock.GetObjectLegalHoldMeta(oi.UserDefined) | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 	if lhold.Status.Valid() && lhold.Status == objectlock.LegalHoldOn { | 
					
						
							|  |  |  | 		return ErrObjectLocked | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ret := objectlock.GetObjectRetentionMeta(oi.UserDefined) | 
					
						
							|  |  |  | 	if ret.Mode.Valid() { | 
					
						
							|  |  |  | 		switch ret.Mode { | 
					
						
							|  |  |  | 		case objectlock.RetCompliance: | 
					
						
							|  |  |  | 			// In compliance mode, a protected object version can't be overwritten
 | 
					
						
							|  |  |  | 			// or deleted by any user, including the root user in your AWS account.
 | 
					
						
							|  |  |  | 			// When an object is locked in compliance mode, its retention mode can't
 | 
					
						
							|  |  |  | 			// be changed, and its retention period can't be shortened. Compliance mode
 | 
					
						
							|  |  |  | 			// ensures that an object version can't be overwritten or deleted for the
 | 
					
						
							|  |  |  | 			// duration of the retention period.
 | 
					
						
							|  |  |  | 			t, err := objectlock.UTCNowNTP() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 				return ErrObjectLocked | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if !ret.RetainUntilDate.Before(t) { | 
					
						
							|  |  |  | 				return ErrObjectLocked | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return ErrNone | 
					
						
							|  |  |  | 		case objectlock.RetGovernance: | 
					
						
							|  |  |  | 			// In governance mode, users can't overwrite or delete an object
 | 
					
						
							|  |  |  | 			// version or alter its lock settings unless they have special
 | 
					
						
							|  |  |  | 			// permissions. With governance mode, you protect objects against
 | 
					
						
							|  |  |  | 			// being deleted by most users, but you can still grant some users
 | 
					
						
							|  |  |  | 			// permission to alter the retention settings or delete the object
 | 
					
						
							|  |  |  | 			// if necessary. You can also use governance mode to test retention-period
 | 
					
						
							|  |  |  | 			// settings before creating a compliance-mode retention period.
 | 
					
						
							|  |  |  | 			// To override or remove governance-mode retention settings, a
 | 
					
						
							|  |  |  | 			// user must have the s3:BypassGovernanceRetention permission
 | 
					
						
							|  |  |  | 			// and must explicitly include x-amz-bypass-governance-retention:true
 | 
					
						
							|  |  |  | 			// as a request header with any request that requires overriding
 | 
					
						
							|  |  |  | 			// governance mode.
 | 
					
						
							|  |  |  | 			//
 | 
					
						
							|  |  |  | 			byPassSet := objectlock.IsObjectLockGovernanceBypassSet(r.Header) | 
					
						
							|  |  |  | 			if !byPassSet { | 
					
						
							|  |  |  | 				t, err := objectlock.UTCNowNTP() | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 					return ErrObjectLocked | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if !ret.RetainUntilDate.Before(t) { | 
					
						
							|  |  |  | 					return ErrObjectLocked | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return ErrNone | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-overview.html#object-lock-retention-modes
 | 
					
						
							|  |  |  | 			// If you try to delete objects protected by governance mode and have s3:BypassGovernanceRetention
 | 
					
						
							|  |  |  | 			// or s3:GetBucketObjectLockConfiguration permissions, the operation will succeed.
 | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 			govBypassPerms1 := checkRequestAuthType(ctx, r, policy.BypassGovernanceRetentionAction, bucket, object.ObjectName) | 
					
						
							|  |  |  | 			govBypassPerms2 := checkRequestAuthType(ctx, r, policy.GetBucketObjectLockConfigurationAction, bucket, object.ObjectName) | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 			if govBypassPerms1 != ErrNone && govBypassPerms2 != ErrNone { | 
					
						
							|  |  |  | 				return ErrAccessDenied | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 	return ErrNone | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // enforceRetentionBypassForPut enforces whether an existing object under governance can be overwritten
 | 
					
						
							|  |  |  | // with governance bypass headers set in the request.
 | 
					
						
							|  |  |  | // Objects under site wide WORM cannot be overwritten.
 | 
					
						
							|  |  |  | // For objects in "Governance" mode, overwrite is allowed if a) object retention date is past OR
 | 
					
						
							|  |  |  | // governance bypass headers are set and user has governance bypass permissions.
 | 
					
						
							|  |  |  | // Objects in compliance mode can be overwritten only if retention date is being extended. No mode change is permitted.
 | 
					
						
							| 
									
										
										
										
											2021-08-13 09:07:08 +08:00
										 |  |  | func enforceRetentionBypassForPut(ctx context.Context, r *http.Request, bucket, object string, getObjectInfoFn GetObjectInfoFn, objRetention *objectlock.ObjectRetention, cred auth.Credentials, owner bool) (ObjectInfo, APIErrorCode) { | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 	byPassSet := objectlock.IsObjectLockGovernanceBypassSet(r.Header) | 
					
						
							|  |  |  | 	opts, err := getOpts(ctx, r, bucket, object) | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 		return ObjectInfo{}, toAPIErrorCode(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	oi, err := getObjectInfoFn(ctx, bucket, object, opts) | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return oi, toAPIErrorCode(ctx, err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	t, err := objectlock.UTCNowNTP() | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		return oi, ErrObjectLocked | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 	// Pass in relative days from current time, to additionally to verify "object-lock-remaining-retention-days" policy if any.
 | 
					
						
							|  |  |  | 	days := int(math.Ceil(math.Abs(objRetention.RetainUntilDate.Sub(t).Hours()) / 24)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret := objectlock.GetObjectRetentionMeta(oi.UserDefined) | 
					
						
							|  |  |  | 	if ret.Mode.Valid() { | 
					
						
							|  |  |  | 		// Retention has expired you may change whatever you like.
 | 
					
						
							|  |  |  | 		if ret.RetainUntilDate.Before(t) { | 
					
						
							|  |  |  | 			perm := isPutRetentionAllowed(bucket, object, | 
					
						
							|  |  |  | 				days, objRetention.RetainUntilDate.Time, | 
					
						
							|  |  |  | 				objRetention.Mode, byPassSet, r, cred, | 
					
						
							| 
									
										
										
										
											2021-08-13 09:07:08 +08:00
										 |  |  | 				owner) | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 			return oi, perm | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 		switch ret.Mode { | 
					
						
							|  |  |  | 		case objectlock.RetGovernance: | 
					
						
							|  |  |  | 			govPerm := isPutRetentionAllowed(bucket, object, days, | 
					
						
							|  |  |  | 				objRetention.RetainUntilDate.Time, objRetention.Mode, | 
					
						
							| 
									
										
										
										
											2021-08-13 09:07:08 +08:00
										 |  |  | 				byPassSet, r, cred, owner) | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 			// Governance mode retention period cannot be shortened, if x-amz-bypass-governance is not set.
 | 
					
						
							|  |  |  | 			if !byPassSet { | 
					
						
							|  |  |  | 				if objRetention.Mode != objectlock.RetGovernance || objRetention.RetainUntilDate.Before((ret.RetainUntilDate.Time)) { | 
					
						
							|  |  |  | 					return oi, ErrObjectLocked | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-11-21 20:52:35 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 			return oi, govPerm | 
					
						
							|  |  |  | 		case objectlock.RetCompliance: | 
					
						
							|  |  |  | 			// Compliance retention mode cannot be changed or shortened.
 | 
					
						
							|  |  |  | 			// https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-overview.html#object-lock-retention-modes
 | 
					
						
							|  |  |  | 			if objRetention.Mode != objectlock.RetCompliance || objRetention.RetainUntilDate.Before((ret.RetainUntilDate.Time)) { | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 				return oi, ErrObjectLocked | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 			compliancePerm := isPutRetentionAllowed(bucket, object, | 
					
						
							|  |  |  | 				days, objRetention.RetainUntilDate.Time, objRetention.Mode, | 
					
						
							| 
									
										
										
										
											2021-08-13 09:07:08 +08:00
										 |  |  | 				false, r, cred, owner) | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 			return oi, compliancePerm | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 		return oi, ErrNone | 
					
						
							|  |  |  | 	} // No pre-existing retention metadata present.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	perm := isPutRetentionAllowed(bucket, object, | 
					
						
							|  |  |  | 		days, objRetention.RetainUntilDate.Time, | 
					
						
							| 
									
										
										
										
											2021-08-13 09:07:08 +08:00
										 |  |  | 		objRetention.Mode, byPassSet, r, cred, owner) | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 	return oi, perm | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | // checkPutObjectLockAllowed enforces object retention policy and legal hold policy
 | 
					
						
							|  |  |  | // for requests with WORM headers
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | // See https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-managing.html for the spec.
 | 
					
						
							|  |  |  | // For non-existing objects with object retention headers set, this method returns ErrNone if bucket has
 | 
					
						
							|  |  |  | // locking enabled and user has requisite permissions (s3:PutObjectRetention)
 | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | // If object exists on object store and site wide WORM enabled - this method
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | // returns an error. For objects in "Governance" mode, overwrite is allowed if the retention date has expired.
 | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | // For objects in "Compliance" mode, retention date cannot be shortened, and mode cannot be altered.
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | // For objects with legal hold header set, the s3:PutObjectLegalHold permission is expected to be set
 | 
					
						
							|  |  |  | // Both legal hold and retention can be applied independently on an object
 | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  | func checkPutObjectLockAllowed(ctx context.Context, rq *http.Request, bucket, object string, getObjectInfoFn GetObjectInfoFn, retentionPermErr, legalHoldPermErr APIErrorCode) (objectlock.RetMode, objectlock.RetentionDate, objectlock.ObjectLegalHold, APIErrorCode) { | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 	var mode objectlock.RetMode | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	var retainDate objectlock.RetentionDate | 
					
						
							|  |  |  | 	var legalHold objectlock.ObjectLegalHold | 
					
						
							| 
									
										
										
										
											2019-11-26 02:58:39 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  | 	retentionRequested := objectlock.IsObjectLockRetentionRequested(rq.Header) | 
					
						
							|  |  |  | 	legalHoldRequested := objectlock.IsObjectLockLegalHoldRequested(rq.Header) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	retentionCfg, err := globalBucketObjectLockSys.Get(bucket) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return mode, retainDate, legalHold, ErrInvalidBucketObjectLockConfiguration | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !retentionCfg.LockEnabled { | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 		if legalHoldRequested || retentionRequested { | 
					
						
							|  |  |  | 			return mode, retainDate, legalHold, ErrInvalidBucketObjectLockConfiguration | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 		// If this not a WORM enabled bucket, we should return right here.
 | 
					
						
							|  |  |  | 		return mode, retainDate, legalHold, ErrNone | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  | 	opts, err := getOpts(ctx, rq, bucket, object) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		return mode, retainDate, legalHold, toAPIErrorCode(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-01 00:15:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  | 	replica := rq.Header.Get(xhttp.AmzBucketReplicationStatus) == replication.Replica.String() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if opts.VersionID != "" && !replica { | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 		if objInfo, err := getObjectInfoFn(ctx, bucket, object, opts); err == nil { | 
					
						
							|  |  |  | 			r := objectlock.GetObjectRetentionMeta(objInfo.UserDefined) | 
					
						
							|  |  |  | 			t, err := objectlock.UTCNowNTP() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 				return mode, retainDate, legalHold, ErrObjectLocked | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if r.Mode == objectlock.RetCompliance && r.RetainUntilDate.After(t) { | 
					
						
							|  |  |  | 				return mode, retainDate, legalHold, ErrObjectLocked | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			mode = r.Mode | 
					
						
							|  |  |  | 			retainDate = r.RetainUntilDate | 
					
						
							|  |  |  | 			legalHold = objectlock.GetObjectLegalHoldMeta(objInfo.UserDefined) | 
					
						
							|  |  |  | 			// Disallow overwriting an object on legal hold
 | 
					
						
							|  |  |  | 			if legalHold.Status == objectlock.LegalHoldOn { | 
					
						
							|  |  |  | 				return mode, retainDate, legalHold, ErrObjectLocked | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	if legalHoldRequested { | 
					
						
							|  |  |  | 		var lerr error | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  | 		if legalHold, lerr = objectlock.ParseObjectLockLegalHoldHeaders(rq.Header); lerr != nil { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, toAPIErrorCode(ctx, err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	if retentionRequested { | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  | 		legalHold, err := objectlock.ParseObjectLockLegalHoldHeaders(rq.Header) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, toAPIErrorCode(ctx, err) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  | 		rMode, rDate, err := objectlock.ParseObjectLockRetentionHeaders(rq.Header) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return mode, retainDate, legalHold, toAPIErrorCode(ctx, err) | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if retentionPermErr != ErrNone { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, retentionPermErr | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		return rMode, rDate, legalHold, ErrNone | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-20 03:50:22 +08:00
										 |  |  | 	if replica { // replica inherits retention metadata only from source
 | 
					
						
							|  |  |  | 		return "", objectlock.RetentionDate{}, legalHold, ErrNone | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-05-20 04:53:54 +08:00
										 |  |  | 	if !retentionRequested && retentionCfg.Validity > 0 { | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		if retentionPermErr != ErrNone { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, retentionPermErr | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 		t, err := objectlock.UTCNowNTP() | 
					
						
							| 
									
										
										
										
											2019-11-21 20:52:35 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			return mode, retainDate, legalHold, ErrObjectLocked | 
					
						
							| 
									
										
										
										
											2019-11-21 20:52:35 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-06-13 11:04:01 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if !legalHoldRequested && retentionCfg.LockEnabled { | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 			// inherit retention from bucket configuration
 | 
					
						
							| 
									
										
										
										
											2020-04-01 00:15:42 +08:00
										 |  |  | 			return retentionCfg.Mode, objectlock.RetentionDate{Time: t.Add(retentionCfg.Validity)}, legalHold, ErrNone | 
					
						
							| 
									
										
										
										
											2020-01-07 02:15:45 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-04-07 04:44:16 +08:00
										 |  |  | 		return "", objectlock.RetentionDate{}, legalHold, ErrNone | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-17 07:41:56 +08:00
										 |  |  | 	return mode, retainDate, legalHold, ErrNone | 
					
						
							| 
									
										
										
										
											2019-11-21 05:18:09 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-02-24 09:09:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-09 04:44:44 +08:00
										 |  |  | // NewBucketObjectLockSys returns initialized BucketObjectLockSys
 | 
					
						
							|  |  |  | func NewBucketObjectLockSys() *BucketObjectLockSys { | 
					
						
							| 
									
										
										
										
											2020-05-21 01:18:15 +08:00
										 |  |  | 	return &BucketObjectLockSys{} | 
					
						
							| 
									
										
										
										
											2020-02-24 09:09:11 +08:00
										 |  |  | } |