github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/bucket-versioning-handler.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "encoding/base64" 22 "encoding/xml" 23 "io" 24 "net/http" 25 26 humanize "github.com/dustin/go-humanize" 27 "github.com/minio/madmin-go/v3" 28 "github.com/minio/minio/internal/bucket/versioning" 29 "github.com/minio/minio/internal/logger" 30 "github.com/minio/mux" 31 "github.com/minio/pkg/v2/policy" 32 ) 33 34 const ( 35 bucketVersioningConfig = "versioning.xml" 36 37 // Maximum size of bucket versioning configuration payload sent to the PutBucketVersioningHandler. 38 maxBucketVersioningConfigSize = 1 * humanize.MiByte 39 ) 40 41 // PutBucketVersioningHandler - PUT Bucket Versioning. 42 // ---------- 43 func (api objectAPIHandlers) PutBucketVersioningHandler(w http.ResponseWriter, r *http.Request) { 44 ctx := newContext(r, w, "PutBucketVersioning") 45 46 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 47 48 vars := mux.Vars(r) 49 bucket := vars["bucket"] 50 51 objectAPI := api.ObjectAPI() 52 if objectAPI == nil { 53 writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 54 return 55 } 56 57 if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketVersioningAction, bucket, ""); s3Error != ErrNone { 58 writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) 59 return 60 } 61 62 v, err := versioning.ParseConfig(io.LimitReader(r.Body, maxBucketVersioningConfigSize)) 63 if err != nil { 64 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) 65 return 66 } 67 68 if globalSiteReplicationSys.isEnabled() && !v.Enabled() { 69 writeErrorResponse(ctx, w, APIError{ 70 Code: "InvalidBucketState", 71 Description: "Cluster replication is enabled on this site, versioning cannot be suspended on bucket.", 72 HTTPStatusCode: http.StatusBadRequest, 73 }, r.URL) 74 return 75 } 76 77 if rcfg, _ := globalBucketObjectLockSys.Get(bucket); rcfg.LockEnabled && (v.Suspended() || v.PrefixesExcluded()) { 78 writeErrorResponse(ctx, w, APIError{ 79 Code: "InvalidBucketState", 80 Description: "An Object Lock configuration is present on this bucket, versioning cannot be suspended.", 81 HTTPStatusCode: http.StatusBadRequest, 82 }, r.URL) 83 return 84 } 85 if _, err := getReplicationConfig(ctx, bucket); err == nil && v.Suspended() { 86 writeErrorResponse(ctx, w, APIError{ 87 Code: "InvalidBucketState", 88 Description: "A replication configuration is present on this bucket, bucket wide versioning cannot be suspended.", 89 HTTPStatusCode: http.StatusBadRequest, 90 }, r.URL) 91 return 92 } 93 94 configData, err := xml.Marshal(v) 95 if err != nil { 96 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) 97 return 98 } 99 100 updatedAt, err := globalBucketMetadataSys.Update(ctx, bucket, bucketVersioningConfig, configData) 101 if err != nil { 102 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) 103 return 104 } 105 106 // Call site replication hook. 107 // 108 // We encode the xml bytes as base64 to ensure there are no encoding 109 // errors. 110 cfgStr := base64.StdEncoding.EncodeToString(configData) 111 logger.LogIf(ctx, globalSiteReplicationSys.BucketMetaHook(ctx, madmin.SRBucketMeta{ 112 Type: madmin.SRBucketMetaTypeVersionConfig, 113 Bucket: bucket, 114 Versioning: &cfgStr, 115 UpdatedAt: updatedAt, 116 })) 117 118 writeSuccessResponseHeadersOnly(w) 119 } 120 121 // GetBucketVersioningHandler - GET Bucket Versioning. 122 // ---------- 123 func (api objectAPIHandlers) GetBucketVersioningHandler(w http.ResponseWriter, r *http.Request) { 124 ctx := newContext(r, w, "GetBucketVersioning") 125 126 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 127 128 vars := mux.Vars(r) 129 bucket := vars["bucket"] 130 131 objectAPI := api.ObjectAPI() 132 if objectAPI == nil { 133 writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 134 return 135 } 136 137 if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketVersioningAction, bucket, ""); s3Error != ErrNone { 138 writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) 139 return 140 } 141 142 // Check if bucket exists. 143 if _, err := objectAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { 144 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) 145 return 146 } 147 148 config, err := globalBucketVersioningSys.Get(bucket) 149 if err != nil { 150 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) 151 return 152 } 153 154 configData, err := xml.Marshal(config) 155 if err != nil { 156 writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) 157 return 158 } 159 160 // Write bucket versioning configuration to client 161 writeSuccessResponseXML(w, configData) 162 }