storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/bucket-object-lock.go (about)

     1  /*
     2   * MinIO Cloud Storage, (C) 2019-2020 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"context"
    21  	"math"
    22  	"net/http"
    23  
    24  	xhttp "storj.io/minio/cmd/http"
    25  	"storj.io/minio/cmd/logger"
    26  	"storj.io/minio/pkg/auth"
    27  	objectlock "storj.io/minio/pkg/bucket/object/lock"
    28  	"storj.io/minio/pkg/bucket/policy"
    29  	"storj.io/minio/pkg/bucket/replication"
    30  )
    31  
    32  // BucketObjectLockSys - map of bucket and retention configuration.
    33  type BucketObjectLockSys struct{}
    34  
    35  // Get - Get retention configuration.
    36  func (sys *BucketObjectLockSys) Get(bucketName string) (r objectlock.Retention, err error) {
    37  	if GlobalIsGateway {
    38  		objAPI := newObjectLayerFn()
    39  		if objAPI == nil {
    40  			return r, errServerNotInitialized
    41  		}
    42  
    43  		return r, nil
    44  	}
    45  
    46  	config, err := globalBucketMetadataSys.GetObjectLockConfig(bucketName)
    47  	if err != nil {
    48  		if _, ok := err.(BucketObjectLockConfigNotFound); ok {
    49  			return r, nil
    50  		}
    51  		return r, err
    52  
    53  	}
    54  	return config.ToRetention(), nil
    55  }
    56  
    57  // enforceRetentionForDeletion checks if it is appropriate to remove an
    58  // object according to locking configuration when this is lifecycle/ bucket quota asking.
    59  func enforceRetentionForDeletion(ctx context.Context, objInfo ObjectInfo) (locked bool) {
    60  	lhold := objectlock.GetObjectLegalHoldMeta(objInfo.UserDefined)
    61  	if lhold.Status.Valid() && lhold.Status == objectlock.LegalHoldOn {
    62  		return true
    63  	}
    64  
    65  	ret := objectlock.GetObjectRetentionMeta(objInfo.UserDefined)
    66  	if ret.Mode.Valid() && (ret.Mode == objectlock.RetCompliance || ret.Mode == objectlock.RetGovernance) {
    67  		t, err := objectlock.UTCNowNTP()
    68  		if err != nil {
    69  			logger.LogIf(ctx, err)
    70  			return true
    71  		}
    72  		if ret.RetainUntilDate.After(t) {
    73  			return true
    74  		}
    75  	}
    76  	return false
    77  }
    78  
    79  // enforceRetentionBypassForDelete enforces whether an existing object under governance can be deleted
    80  // with governance bypass headers set in the request.
    81  // Objects under site wide WORM can never be overwritten.
    82  // For objects in "Governance" mode, overwrite is allowed if a) object retention date is past OR
    83  // governance bypass headers are set and user has governance bypass permissions.
    84  // Objects in "Compliance" mode can be overwritten only if retention date is past.
    85  func enforceRetentionBypassForDelete(ctx context.Context, r *http.Request, bucket string, object ObjectToDelete, oi ObjectInfo, gerr error) APIErrorCode {
    86  	opts, err := getOpts(ctx, r, bucket, object.ObjectName)
    87  	if err != nil {
    88  		return toAPIErrorCode(ctx, err)
    89  	}
    90  
    91  	opts.VersionID = object.VersionID
    92  	if gerr != nil { // error from GetObjectInfo
    93  		switch gerr.(type) {
    94  		case MethodNotAllowed: // This happens usually for a delete marker
    95  			if oi.DeleteMarker {
    96  				// Delete marker should be present and valid.
    97  				return ErrNone
    98  			}
    99  		}
   100  		if isErrObjectNotFound(gerr) || isErrVersionNotFound(gerr) {
   101  			return ErrNone
   102  		}
   103  		return toAPIErrorCode(ctx, gerr)
   104  	}
   105  
   106  	lhold := objectlock.GetObjectLegalHoldMeta(oi.UserDefined)
   107  	if lhold.Status.Valid() && lhold.Status == objectlock.LegalHoldOn {
   108  		return ErrObjectLocked
   109  	}
   110  
   111  	ret := objectlock.GetObjectRetentionMeta(oi.UserDefined)
   112  	if ret.Mode.Valid() {
   113  		switch ret.Mode {
   114  		case objectlock.RetCompliance:
   115  			// In compliance mode, a protected object version can't be overwritten
   116  			// or deleted by any user, including the root user in your AWS account.
   117  			// When an object is locked in compliance mode, its retention mode can't
   118  			// be changed, and its retention period can't be shortened. Compliance mode
   119  			// ensures that an object version can't be overwritten or deleted for the
   120  			// duration of the retention period.
   121  			t, err := objectlock.UTCNowNTP()
   122  			if err != nil {
   123  				logger.LogIf(ctx, err)
   124  				return ErrObjectLocked
   125  			}
   126  
   127  			if !ret.RetainUntilDate.Before(t) {
   128  				return ErrObjectLocked
   129  			}
   130  			return ErrNone
   131  		case objectlock.RetGovernance:
   132  			// In governance mode, users can't overwrite or delete an object
   133  			// version or alter its lock settings unless they have special
   134  			// permissions. With governance mode, you protect objects against
   135  			// being deleted by most users, but you can still grant some users
   136  			// permission to alter the retention settings or delete the object
   137  			// if necessary. You can also use governance mode to test retention-period
   138  			// settings before creating a compliance-mode retention period.
   139  			// To override or remove governance-mode retention settings, a
   140  			// user must have the s3:BypassGovernanceRetention permission
   141  			// and must explicitly include x-amz-bypass-governance-retention:true
   142  			// as a request header with any request that requires overriding
   143  			// governance mode.
   144  			//
   145  			byPassSet := objectlock.IsObjectLockGovernanceBypassSet(r.Header)
   146  			if !byPassSet {
   147  				t, err := objectlock.UTCNowNTP()
   148  				if err != nil {
   149  					logger.LogIf(ctx, err)
   150  					return ErrObjectLocked
   151  				}
   152  
   153  				if !ret.RetainUntilDate.Before(t) {
   154  					return ErrObjectLocked
   155  				}
   156  				return ErrNone
   157  			}
   158  			// https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-overview.html#object-lock-retention-modes
   159  			// If you try to delete objects protected by governance mode and have s3:BypassGovernanceRetention
   160  			// or s3:GetBucketObjectLockConfiguration permissions, the operation will succeed.
   161  			govBypassPerms1 := checkRequestAuthType(ctx, r, policy.BypassGovernanceRetentionAction, bucket, object.ObjectName)
   162  			govBypassPerms2 := checkRequestAuthType(ctx, r, policy.GetBucketObjectLockConfigurationAction, bucket, object.ObjectName)
   163  			if govBypassPerms1 != ErrNone && govBypassPerms2 != ErrNone {
   164  				return ErrAccessDenied
   165  			}
   166  		}
   167  	}
   168  	return ErrNone
   169  }
   170  
   171  // enforceRetentionBypassForPut enforces whether an existing object under governance can be overwritten
   172  // with governance bypass headers set in the request.
   173  // Objects under site wide WORM cannot be overwritten.
   174  // For objects in "Governance" mode, overwrite is allowed if a) object retention date is past OR
   175  // governance bypass headers are set and user has governance bypass permissions.
   176  // Objects in compliance mode can be overwritten only if retention date is being extended. No mode change is permitted.
   177  func enforceRetentionBypassForPut(ctx context.Context, r *http.Request, bucket, object string, getObjectInfoFn GetObjectInfoFn, objRetention *objectlock.ObjectRetention, cred auth.Credentials, owner bool, claims map[string]interface{}) (ObjectInfo, APIErrorCode) {
   178  	byPassSet := objectlock.IsObjectLockGovernanceBypassSet(r.Header)
   179  	opts, err := getOpts(ctx, r, bucket, object)
   180  	if err != nil {
   181  		return ObjectInfo{}, toAPIErrorCode(ctx, err)
   182  	}
   183  
   184  	oi, err := getObjectInfoFn(ctx, bucket, object, opts)
   185  	if err != nil {
   186  		return oi, toAPIErrorCode(ctx, err)
   187  	}
   188  
   189  	t, err := objectlock.UTCNowNTP()
   190  	if err != nil {
   191  		logger.LogIf(ctx, err)
   192  		return oi, ErrObjectLocked
   193  	}
   194  
   195  	// Pass in relative days from current time, to additionally to verify "object-lock-remaining-retention-days" policy if any.
   196  	days := int(math.Ceil(math.Abs(objRetention.RetainUntilDate.Sub(t).Hours()) / 24))
   197  
   198  	ret := objectlock.GetObjectRetentionMeta(oi.UserDefined)
   199  	if ret.Mode.Valid() {
   200  		// Retention has expired you may change whatever you like.
   201  		if ret.RetainUntilDate.Before(t) {
   202  			perm := isPutRetentionAllowed(bucket, object,
   203  				days, objRetention.RetainUntilDate.Time,
   204  				objRetention.Mode, byPassSet, r, cred,
   205  				owner, claims)
   206  			return oi, perm
   207  		}
   208  
   209  		switch ret.Mode {
   210  		case objectlock.RetGovernance:
   211  			govPerm := isPutRetentionAllowed(bucket, object, days,
   212  				objRetention.RetainUntilDate.Time, objRetention.Mode,
   213  				byPassSet, r, cred, owner, claims)
   214  			// Governance mode retention period cannot be shortened, if x-amz-bypass-governance is not set.
   215  			if !byPassSet {
   216  				if objRetention.Mode != objectlock.RetGovernance || objRetention.RetainUntilDate.Before((ret.RetainUntilDate.Time)) {
   217  					return oi, ErrObjectLocked
   218  				}
   219  			}
   220  			return oi, govPerm
   221  		case objectlock.RetCompliance:
   222  			// Compliance retention mode cannot be changed or shortened.
   223  			// https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-overview.html#object-lock-retention-modes
   224  			if objRetention.Mode != objectlock.RetCompliance || objRetention.RetainUntilDate.Before((ret.RetainUntilDate.Time)) {
   225  				return oi, ErrObjectLocked
   226  			}
   227  			compliancePerm := isPutRetentionAllowed(bucket, object,
   228  				days, objRetention.RetainUntilDate.Time, objRetention.Mode,
   229  				false, r, cred, owner, claims)
   230  			return oi, compliancePerm
   231  		}
   232  		return oi, ErrNone
   233  	} // No pre-existing retention metadata present.
   234  
   235  	perm := isPutRetentionAllowed(bucket, object,
   236  		days, objRetention.RetainUntilDate.Time,
   237  		objRetention.Mode, byPassSet, r, cred, owner, claims)
   238  	return oi, perm
   239  }
   240  
   241  // checkPutObjectLockAllowed enforces object retention policy and legal hold policy
   242  // for requests with WORM headers
   243  // See https://docs.aws.amazon.com/AmazonS3/latest/dev/object-lock-managing.html for the spec.
   244  // For non-existing objects with object retention headers set, this method returns ErrNone if bucket has
   245  // locking enabled and user has requisite permissions (s3:PutObjectRetention)
   246  // If object exists on object store and site wide WORM enabled - this method
   247  // returns an error. For objects in "Governance" mode, overwrite is allowed if the retention date has expired.
   248  // For objects in "Compliance" mode, retention date cannot be shortened, and mode cannot be altered.
   249  // For objects with legal hold header set, the s3:PutObjectLegalHold permission is expected to be set
   250  // Both legal hold and retention can be applied independently on an object
   251  func checkPutObjectLockAllowed(ctx context.Context, rq *http.Request, bucket, object string, getObjectInfoFn GetObjectInfoFn, retentionPermErr, legalHoldPermErr APIErrorCode) (objectlock.RetMode, objectlock.RetentionDate, objectlock.ObjectLegalHold, APIErrorCode) {
   252  	var mode objectlock.RetMode
   253  	var retainDate objectlock.RetentionDate
   254  	var legalHold objectlock.ObjectLegalHold
   255  
   256  	retentionRequested := objectlock.IsObjectLockRetentionRequested(rq.Header)
   257  	legalHoldRequested := objectlock.IsObjectLockLegalHoldRequested(rq.Header)
   258  
   259  	retentionCfg, err := globalBucketObjectLockSys.Get(bucket)
   260  	if err != nil {
   261  		return mode, retainDate, legalHold, ErrInvalidBucketObjectLockConfiguration
   262  	}
   263  
   264  	if !retentionCfg.LockEnabled {
   265  		if legalHoldRequested || retentionRequested {
   266  			return mode, retainDate, legalHold, ErrInvalidBucketObjectLockConfiguration
   267  		}
   268  
   269  		// If this not a WORM enabled bucket, we should return right here.
   270  		return mode, retainDate, legalHold, ErrNone
   271  	}
   272  
   273  	opts, err := getOpts(ctx, rq, bucket, object)
   274  	if err != nil {
   275  		return mode, retainDate, legalHold, toAPIErrorCode(ctx, err)
   276  	}
   277  
   278  	replica := rq.Header.Get(xhttp.AmzBucketReplicationStatus) == replication.Replica.String()
   279  
   280  	if opts.VersionID != "" && !replica {
   281  		if objInfo, err := getObjectInfoFn(ctx, bucket, object, opts); err == nil {
   282  			r := objectlock.GetObjectRetentionMeta(objInfo.UserDefined)
   283  			t, err := objectlock.UTCNowNTP()
   284  			if err != nil {
   285  				logger.LogIf(ctx, err)
   286  				return mode, retainDate, legalHold, ErrObjectLocked
   287  			}
   288  			if r.Mode == objectlock.RetCompliance && r.RetainUntilDate.After(t) {
   289  				return mode, retainDate, legalHold, ErrObjectLocked
   290  			}
   291  			mode = r.Mode
   292  			retainDate = r.RetainUntilDate
   293  			legalHold = objectlock.GetObjectLegalHoldMeta(objInfo.UserDefined)
   294  			// Disallow overwriting an object on legal hold
   295  			if legalHold.Status == objectlock.LegalHoldOn {
   296  				return mode, retainDate, legalHold, ErrObjectLocked
   297  			}
   298  		}
   299  	}
   300  
   301  	if legalHoldRequested {
   302  		var lerr error
   303  		if legalHold, lerr = objectlock.ParseObjectLockLegalHoldHeaders(rq.Header); lerr != nil {
   304  			return mode, retainDate, legalHold, toAPIErrorCode(ctx, err)
   305  		}
   306  	}
   307  
   308  	if retentionRequested {
   309  		legalHold, err := objectlock.ParseObjectLockLegalHoldHeaders(rq.Header)
   310  		if err != nil {
   311  			return mode, retainDate, legalHold, toAPIErrorCode(ctx, err)
   312  		}
   313  		rMode, rDate, err := objectlock.ParseObjectLockRetentionHeaders(rq.Header)
   314  		if err != nil {
   315  			return mode, retainDate, legalHold, toAPIErrorCode(ctx, err)
   316  		}
   317  		if retentionPermErr != ErrNone {
   318  			return mode, retainDate, legalHold, retentionPermErr
   319  		}
   320  		return rMode, rDate, legalHold, ErrNone
   321  	}
   322  	if replica { // replica inherits retention metadata only from source
   323  		return "", objectlock.RetentionDate{}, legalHold, ErrNone
   324  	}
   325  	if !retentionRequested && retentionCfg.Validity > 0 {
   326  		if retentionPermErr != ErrNone {
   327  			return mode, retainDate, legalHold, retentionPermErr
   328  		}
   329  
   330  		t, err := objectlock.UTCNowNTP()
   331  		if err != nil {
   332  			logger.LogIf(ctx, err)
   333  			return mode, retainDate, legalHold, ErrObjectLocked
   334  		}
   335  
   336  		if !legalHoldRequested && retentionCfg.LockEnabled {
   337  			// inherit retention from bucket configuration
   338  			return retentionCfg.Mode, objectlock.RetentionDate{Time: t.Add(retentionCfg.Validity)}, legalHold, ErrNone
   339  		}
   340  		return "", objectlock.RetentionDate{}, legalHold, ErrNone
   341  	}
   342  	return mode, retainDate, legalHold, ErrNone
   343  }
   344  
   345  // NewBucketObjectLockSys returns initialized BucketObjectLockSys
   346  func NewBucketObjectLockSys() *BucketObjectLockSys {
   347  	return &BucketObjectLockSys{}
   348  }