storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/admin-bucket-handlers.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 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 "encoding/json" 21 "io" 22 "io/ioutil" 23 "net/http" 24 25 "github.com/gorilla/mux" 26 27 "storj.io/minio/cmd/logger" 28 iampolicy "storj.io/minio/pkg/iam/policy" 29 "storj.io/minio/pkg/madmin" 30 ) 31 32 const ( 33 bucketQuotaConfigFile = "quota.json" 34 bucketTargetsFile = "bucket-targets.json" 35 ) 36 37 // PutBucketQuotaConfigHandler - PUT Bucket quota configuration. 38 // ---------- 39 // Places a quota configuration on the specified bucket. The quota 40 // specified in the quota configuration will be applied by default 41 // to enforce total quota for the specified bucket. 42 func (a adminAPIHandlers) PutBucketQuotaConfigHandler(w http.ResponseWriter, r *http.Request) { 43 ctx := NewContext(r, w, "PutBucketQuotaConfig") 44 45 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 46 47 objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SetBucketQuotaAdminAction) 48 if objectAPI == nil { 49 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 50 return 51 } 52 53 vars := mux.Vars(r) 54 bucket := pathClean(vars["bucket"]) 55 56 if _, err := objectAPI.GetBucketInfo(ctx, bucket); err != nil { 57 writeErrorResponseJSON(ctx, w, ToAPIError(ctx, err), r.URL) 58 return 59 } 60 61 data, err := ioutil.ReadAll(r.Body) 62 if err != nil { 63 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) 64 return 65 } 66 67 if _, err = parseBucketQuota(bucket, data); err != nil { 68 WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) 69 return 70 } 71 72 if err = globalBucketMetadataSys.Update(bucket, bucketQuotaConfigFile, data); err != nil { 73 WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) 74 return 75 } 76 77 // Write success response. 78 writeSuccessResponseHeadersOnly(w) 79 } 80 81 // GetBucketQuotaConfigHandler - gets bucket quota configuration 82 func (a adminAPIHandlers) GetBucketQuotaConfigHandler(w http.ResponseWriter, r *http.Request) { 83 ctx := NewContext(r, w, "GetBucketQuotaConfig") 84 85 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 86 87 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.GetBucketQuotaAdminAction) 88 if objectAPI == nil { 89 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 90 return 91 } 92 93 vars := mux.Vars(r) 94 bucket := pathClean(vars["bucket"]) 95 96 if _, err := objectAPI.GetBucketInfo(ctx, bucket); err != nil { 97 writeErrorResponseJSON(ctx, w, ToAPIError(ctx, err), r.URL) 98 return 99 } 100 101 config, err := globalBucketMetadataSys.GetQuotaConfig(bucket) 102 if err != nil { 103 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 104 return 105 } 106 107 configData, err := json.Marshal(config) 108 if err != nil { 109 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 110 return 111 } 112 113 // Write success response. 114 writeSuccessResponseJSON(w, configData) 115 } 116 117 // SetRemoteTargetHandler - sets a remote target for bucket 118 func (a adminAPIHandlers) SetRemoteTargetHandler(w http.ResponseWriter, r *http.Request) { 119 ctx := NewContext(r, w, "SetBucketTarget") 120 121 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 122 vars := mux.Vars(r) 123 bucket := pathClean(vars["bucket"]) 124 update := r.URL.Query().Get("update") == "true" 125 126 if !globalIsErasure { 127 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL) 128 return 129 } 130 131 // Get current object layer instance. 132 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.SetBucketTargetAction) 133 if objectAPI == nil { 134 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 135 return 136 } 137 138 // Check if bucket exists. 139 if _, err := objectAPI.GetBucketInfo(ctx, bucket); err != nil { 140 writeErrorResponseJSON(ctx, w, ToAPIError(ctx, err), r.URL) 141 return 142 } 143 144 cred, _, _, s3Err := validateAdminSignature(ctx, r, "") 145 if s3Err != ErrNone { 146 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) 147 return 148 } 149 password := cred.SecretKey 150 151 reqBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength)) 152 if err != nil { 153 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) 154 return 155 } 156 var target madmin.BucketTarget 157 if err = json.Unmarshal(reqBytes, &target); err != nil { 158 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) 159 return 160 } 161 sameTarget, _ := isLocalHost(target.URL().Hostname(), target.URL().Port(), globalMinioPort) 162 if sameTarget && bucket == target.TargetBucket { 163 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrBucketRemoteIdenticalToSource), r.URL) 164 return 165 } 166 167 target.SourceBucket = bucket 168 if !update { 169 target.Arn = globalBucketTargetSys.getRemoteARN(bucket, &target) 170 } 171 if target.Arn == "" { 172 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) 173 return 174 } 175 176 if err = globalBucketTargetSys.SetTarget(ctx, bucket, &target, update); err != nil { 177 switch err.(type) { 178 case BucketRemoteConnectionErr: 179 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrReplicationRemoteConnectionError, err), r.URL) 180 default: 181 writeErrorResponseJSON(ctx, w, ToAPIError(ctx, err), r.URL) 182 } 183 return 184 } 185 targets, err := globalBucketTargetSys.ListBucketTargets(ctx, bucket) 186 if err != nil { 187 WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) 188 return 189 } 190 tgtBytes, err := json.Marshal(&targets) 191 if err != nil { 192 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) 193 return 194 } 195 if err = globalBucketMetadataSys.Update(bucket, bucketTargetsFile, tgtBytes); err != nil { 196 WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) 197 return 198 } 199 200 data, err := json.Marshal(target.Arn) 201 if err != nil { 202 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 203 return 204 } 205 // Write success response. 206 writeSuccessResponseJSON(w, data) 207 } 208 209 // ListRemoteTargetsHandler - lists remote target(s) for a bucket or gets a target 210 // for a particular ARN type 211 func (a adminAPIHandlers) ListRemoteTargetsHandler(w http.ResponseWriter, r *http.Request) { 212 ctx := NewContext(r, w, "ListBucketTargets") 213 214 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 215 vars := mux.Vars(r) 216 bucket := pathClean(vars["bucket"]) 217 arnType := vars["type"] 218 if !globalIsErasure { 219 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL) 220 return 221 } 222 // Get current object layer instance. 223 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.GetBucketTargetAction) 224 if objectAPI == nil { 225 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 226 return 227 } 228 if bucket != "" { 229 // Check if bucket exists. 230 if _, err := objectAPI.GetBucketInfo(ctx, bucket); err != nil { 231 writeErrorResponseJSON(ctx, w, ToAPIError(ctx, err), r.URL) 232 return 233 } 234 if _, err := globalBucketMetadataSys.GetBucketTargetsConfig(bucket); err != nil { 235 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 236 return 237 } 238 } 239 targets := globalBucketTargetSys.ListTargets(ctx, bucket, arnType) 240 data, err := json.Marshal(targets) 241 if err != nil { 242 writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) 243 return 244 } 245 // Write success response. 246 writeSuccessResponseJSON(w, data) 247 } 248 249 // RemoveRemoteTargetHandler - removes a remote target for bucket with specified ARN 250 func (a adminAPIHandlers) RemoveRemoteTargetHandler(w http.ResponseWriter, r *http.Request) { 251 ctx := NewContext(r, w, "RemoveBucketTarget") 252 253 defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) 254 vars := mux.Vars(r) 255 bucket := pathClean(vars["bucket"]) 256 arn := vars["arn"] 257 258 if !globalIsErasure { 259 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL) 260 return 261 } 262 // Get current object layer instance. 263 objectAPI, _ := validateAdminUsersReq(ctx, w, r, iampolicy.SetBucketTargetAction) 264 if objectAPI == nil { 265 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) 266 return 267 } 268 269 // Check if bucket exists. 270 if _, err := objectAPI.GetBucketInfo(ctx, bucket); err != nil { 271 writeErrorResponseJSON(ctx, w, ToAPIError(ctx, err), r.URL) 272 return 273 } 274 275 if err := globalBucketTargetSys.RemoveTarget(ctx, bucket, arn); err != nil { 276 writeErrorResponseJSON(ctx, w, ToAPIError(ctx, err), r.URL) 277 return 278 } 279 targets, err := globalBucketTargetSys.ListBucketTargets(ctx, bucket) 280 if err != nil { 281 WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) 282 return 283 } 284 tgtBytes, err := json.Marshal(&targets) 285 if err != nil { 286 writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) 287 return 288 } 289 if err = globalBucketMetadataSys.Update(bucket, bucketTargetsFile, tgtBytes); err != nil { 290 WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r)) 291 return 292 } 293 294 // Write success response. 295 writeSuccessNoContent(w) 296 }