| 
									
										
										
										
											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/>.
 | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | package cmd | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 	"bytes" | 
					
						
							|  |  |  | 	"encoding/base64" | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 	"encoding/xml" | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	"io" | 
					
						
							|  |  |  | 	"net/http" | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 17:02:32 +08:00
										 |  |  | 	jsoniter "github.com/json-iterator/go" | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 	"github.com/klauspost/compress/zip" | 
					
						
							|  |  |  | 	"github.com/minio/kes" | 
					
						
							| 
									
										
										
										
											2022-12-07 05:46:50 +08:00
										 |  |  | 	"github.com/minio/madmin-go/v2" | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 	"github.com/minio/minio-go/v7/pkg/tags" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/bucket/lifecycle" | 
					
						
							|  |  |  | 	objectlock "github.com/minio/minio/internal/bucket/object/lock" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/bucket/versioning" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/event" | 
					
						
							|  |  |  | 	"github.com/minio/minio/internal/kms" | 
					
						
							| 
									
										
										
										
											2021-06-02 05:59:40 +08:00
										 |  |  | 	"github.com/minio/minio/internal/logger" | 
					
						
							| 
									
										
										
										
											2023-01-23 19:12:47 +08:00
										 |  |  | 	"github.com/minio/mux" | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 	"github.com/minio/pkg/bucket/policy" | 
					
						
							| 
									
										
										
										
											2021-05-30 12:16:42 +08:00
										 |  |  | 	iampolicy "github.com/minio/pkg/iam/policy" | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2020-07-29 02:50:47 +08:00
										 |  |  | 	bucketQuotaConfigFile = "quota.json" | 
					
						
							|  |  |  | 	bucketTargetsFile     = "bucket-targets.json" | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // PutBucketQuotaConfigHandler - PUT Bucket quota configuration.
 | 
					
						
							|  |  |  | // ----------
 | 
					
						
							|  |  |  | // Places a quota configuration on the specified bucket. The quota
 | 
					
						
							|  |  |  | // specified in the quota configuration will be applied by default
 | 
					
						
							|  |  |  | // to enforce total quota for the specified bucket.
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) PutBucketQuotaConfigHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "PutBucketQuotaConfig") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SetBucketQuotaAdminAction) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							| 
									
										
										
										
											2021-04-16 07:32:13 +08:00
										 |  |  | 	bucket := pathClean(vars["bucket"]) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 	if _, err := objectAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-20 02:05:16 +08:00
										 |  |  | 	data, err := io.ReadAll(r.Body) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-20 12:02:24 +08:00
										 |  |  | 	quotaConfig, err := parseBucketQuota(bucket, data) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-18 04:49:46 +08:00
										 |  |  | 	if quotaConfig.Type == "fifo" { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 	updatedAt, err := globalBucketMetadataSys.Update(ctx, bucket, bucketQuotaConfigFile, data) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-20 12:02:24 +08:00
										 |  |  | 	bucketMeta := madmin.SRBucketMeta{ | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 		Type:      madmin.SRBucketMetaTypeQuotaConfig, | 
					
						
							|  |  |  | 		Bucket:    bucket, | 
					
						
							|  |  |  | 		Quota:     data, | 
					
						
							|  |  |  | 		UpdatedAt: updatedAt, | 
					
						
							| 
									
										
										
										
											2022-01-20 12:02:24 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if quotaConfig.Quota == 0 { | 
					
						
							|  |  |  | 		bucketMeta.Quota = nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Call site replication hook.
 | 
					
						
							| 
									
										
										
										
											2023-01-27 03:11:54 +08:00
										 |  |  | 	logger.LogIf(ctx, globalSiteReplicationSys.BucketMetaHook(ctx, bucketMeta)) | 
					
						
							| 
									
										
										
										
											2022-01-20 12:02:24 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	// Write success response.
 | 
					
						
							|  |  |  | 	writeSuccessResponseHeadersOnly(w) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetBucketQuotaConfigHandler - gets bucket quota configuration
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) GetBucketQuotaConfigHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "GetBucketQuotaConfig") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.GetBucketQuotaAdminAction) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							| 
									
										
										
										
											2021-04-16 07:32:13 +08:00
										 |  |  | 	bucket := pathClean(vars["bucket"]) | 
					
						
							| 
									
										
										
										
											2021-04-04 00:03:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 	if _, err := objectAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-04-24 17:36:31 +08:00
										 |  |  | 	config, _, err := globalBucketMetadataSys.GetQuotaConfig(ctx, bucket) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	configData, err := json.Marshal(config) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Write success response.
 | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, configData) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | // SetRemoteTargetHandler - sets a remote target for bucket
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) SetRemoteTargetHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2020-07-29 02:50:47 +08:00
										 |  |  | 	ctx := newContext(r, w, "SetBucketTarget") | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	vars := mux.Vars(r) | 
					
						
							| 
									
										
										
										
											2021-04-16 07:32:13 +08:00
										 |  |  | 	bucket := pathClean(vars["bucket"]) | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 	update := r.Form.Get("update") == "true" | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Get current object layer instance.
 | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SetBucketTargetAction) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check if bucket exists.
 | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 	if _, err := objectAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-02-07 01:27:29 +08:00
										 |  |  | 	cred, _, s3Err := validateAdminSignature(ctx, r, "") | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if s3Err != ErrNone { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(s3Err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	password := cred.SecretKey | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	reqBytes, err := madmin.DecryptData(password, io.LimitReader(r.Body, r.ContentLength)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-29 02:50:47 +08:00
										 |  |  | 	var target madmin.BucketTarget | 
					
						
							| 
									
										
										
										
											2022-01-03 01:15:06 +08:00
										 |  |  | 	json := jsoniter.ConfigCompatibleWithStandardLibrary | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if err = json.Unmarshal(reqBytes, &target); err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-26 03:24:50 +08:00
										 |  |  | 	sameTarget, _ := isLocalHost(target.URL().Hostname(), target.URL().Port(), globalMinioPort) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	if sameTarget && bucket == target.TargetBucket { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrBucketRemoteIdenticalToSource), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-25 11:09:05 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	target.SourceBucket = bucket | 
					
						
							| 
									
										
										
										
											2021-04-29 06:26:20 +08:00
										 |  |  | 	var ops []madmin.TargetUpdateType | 
					
						
							|  |  |  | 	if update { | 
					
						
							| 
									
										
										
										
											2021-08-08 13:43:01 +08:00
										 |  |  | 		ops = madmin.GetTargetUpdateOps(r.Form) | 
					
						
							| 
									
										
										
										
											2021-04-29 06:26:20 +08:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2022-12-14 19:24:06 +08:00
										 |  |  | 		var exists bool // true if arn exists
 | 
					
						
							|  |  |  | 		target.Arn, exists = globalBucketTargetSys.getRemoteARN(bucket, &target) | 
					
						
							|  |  |  | 		if exists && target.Arn != "" { // return pre-existing ARN
 | 
					
						
							|  |  |  | 			data, err := json.Marshal(target.Arn) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			// Write success response.
 | 
					
						
							|  |  |  | 			writeSuccessResponseJSON(w, data) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-11-25 11:09:05 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	if target.Arn == "" { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-29 06:26:20 +08:00
										 |  |  | 	if update { | 
					
						
							|  |  |  | 		// overlay the updates on existing target
 | 
					
						
							|  |  |  | 		tgt := globalBucketTargetSys.GetRemoteBucketTargetByArn(ctx, bucket, target.Arn) | 
					
						
							|  |  |  | 		if tgt.Empty() { | 
					
						
							|  |  |  | 			writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrRemoteTargetNotFoundError, err), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for _, op := range ops { | 
					
						
							|  |  |  | 			switch op { | 
					
						
							|  |  |  | 			case madmin.CredentialsUpdateType: | 
					
						
							| 
									
										
										
										
											2022-12-24 07:44:48 +08:00
										 |  |  | 				if !globalSiteReplicationSys.isEnabled() { | 
					
						
							|  |  |  | 					tgt.Credentials = target.Credentials | 
					
						
							|  |  |  | 					tgt.TargetBucket = target.TargetBucket | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2021-04-29 06:26:20 +08:00
										 |  |  | 				tgt.Secure = target.Secure | 
					
						
							|  |  |  | 				tgt.Endpoint = target.Endpoint | 
					
						
							|  |  |  | 			case madmin.SyncUpdateType: | 
					
						
							|  |  |  | 				tgt.ReplicationSync = target.ReplicationSync | 
					
						
							|  |  |  | 			case madmin.ProxyUpdateType: | 
					
						
							|  |  |  | 				tgt.DisableProxy = target.DisableProxy | 
					
						
							|  |  |  | 			case madmin.PathUpdateType: | 
					
						
							|  |  |  | 				tgt.Path = target.Path | 
					
						
							|  |  |  | 			case madmin.BandwidthLimitUpdateType: | 
					
						
							|  |  |  | 				tgt.BandwidthLimit = target.BandwidthLimit | 
					
						
							|  |  |  | 			case madmin.HealthCheckDurationUpdateType: | 
					
						
							|  |  |  | 				tgt.HealthCheckDuration = target.HealthCheckDuration | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		target = tgt | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-25 09:29:30 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// enforce minimum bandwidth limit as 100MBps
 | 
					
						
							| 
									
										
										
										
											2021-06-28 01:11:13 +08:00
										 |  |  | 	if target.BandwidthLimit > 0 && target.BandwidthLimit < 100*1000*1000 { | 
					
						
							| 
									
										
										
										
											2021-06-25 09:29:30 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrReplicationBandwidthLimitError, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-11-25 11:09:05 +08:00
										 |  |  | 	if err = globalBucketTargetSys.SetTarget(ctx, bucket, &target, update); err != nil { | 
					
						
							| 
									
										
										
										
											2021-03-27 09:58:13 +08:00
										 |  |  | 		switch err.(type) { | 
					
						
							| 
									
										
										
										
											2022-08-17 08:46:22 +08:00
										 |  |  | 		case RemoteTargetConnectionErr: | 
					
						
							| 
									
										
										
										
											2021-03-27 09:58:13 +08:00
										 |  |  | 			writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrReplicationRemoteConnectionError, err), r.URL) | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	targets, err := globalBucketTargetSys.ListBucketTargets(ctx, bucket) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tgtBytes, err := json.Marshal(&targets) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 	if _, err = globalBucketMetadataSys.Update(ctx, bucket, bucketTargetsFile, tgtBytes); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	data, err := json.Marshal(target.Arn) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	// Write success response.
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	writeSuccessResponseJSON(w, data) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | // ListRemoteTargetsHandler - lists remote target(s) for a bucket or gets a target
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | // for a particular ARN type
 | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | func (a adminAPIHandlers) ListRemoteTargetsHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	ctx := newContext(r, w, "ListBucketTargets") | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	vars := mux.Vars(r) | 
					
						
							| 
									
										
										
										
											2021-04-16 07:32:13 +08:00
										 |  |  | 	bucket := pathClean(vars["bucket"]) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	arnType := vars["type"] | 
					
						
							| 
									
										
										
										
											2022-05-31 01:58:37 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	// Get current object layer instance.
 | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.GetBucketTargetAction) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	if bucket != "" { | 
					
						
							| 
									
										
										
										
											2020-09-24 01:37:54 +08:00
										 |  |  | 		// Check if bucket exists.
 | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 		if _, err := objectAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { | 
					
						
							| 
									
										
										
										
											2020-09-24 01:37:54 +08:00
										 |  |  | 			writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 		if _, err := globalBucketMetadataSys.GetBucketTargetsConfig(bucket); err != nil { | 
					
						
							|  |  |  | 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	targets := globalBucketTargetSys.ListTargets(ctx, bucket, arnType) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	data, err := json.Marshal(targets) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// Write success response.
 | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, data) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | // RemoveRemoteTargetHandler - removes a remote target for bucket with specified ARN
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) RemoveRemoteTargetHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	ctx := newContext(r, w, "RemoveBucketTarget") | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-27 05:21:51 +08:00
										 |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	vars := mux.Vars(r) | 
					
						
							| 
									
										
										
										
											2021-04-16 07:32:13 +08:00
										 |  |  | 	bucket := pathClean(vars["bucket"]) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	arn := vars["arn"] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	// Get current object layer instance.
 | 
					
						
							| 
									
										
										
										
											2021-08-10 09:14:38 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.SetBucketTargetAction) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Check if bucket exists.
 | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 	if _, err := objectAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 		writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	if err := globalBucketTargetSys.RemoveTarget(ctx, bucket, arn); err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-08-07 08:10:21 +08:00
										 |  |  | 	targets, err := globalBucketTargetSys.ListBucketTargets(ctx, bucket) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tgtBytes, err := json.Marshal(&targets) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrAdminConfigBadJSON, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 	if _, err = globalBucketMetadataSys.Update(ctx, bucket, bucketTargetsFile, tgtBytes); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-18 11:27:04 +08:00
										 |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | 	// Write success response.
 | 
					
						
							| 
									
										
										
										
											2020-07-31 10:55:22 +08:00
										 |  |  | 	writeSuccessNoContent(w) | 
					
						
							| 
									
										
										
										
											2020-07-22 08:49:56 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // ExportBucketMetadataHandler - exports all bucket metadata as a zipped file
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) ExportBucketMetadataHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "ExportBucketMetadata") | 
					
						
							|  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bucket := pathClean(r.Form.Get("bucket")) | 
					
						
							|  |  |  | 	// Get current object layer instance.
 | 
					
						
							|  |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.ExportBucketMetadataAction) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-07-22 02:05:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		buckets []BucketInfo | 
					
						
							|  |  |  | 		err     error | 
					
						
							|  |  |  | 	) | 
					
						
							|  |  |  | 	if bucket != "" { | 
					
						
							|  |  |  | 		// Check if bucket exists.
 | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 		if _, err := objectAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			writeErrorResponseJSON(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		buckets = append(buckets, BucketInfo{Name: bucket}) | 
					
						
							|  |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 		buckets, err = objectAPI.ListBuckets(ctx, BucketOptions{}) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Initialize a zip writer which will provide a zipped content
 | 
					
						
							|  |  |  | 	// of bucket metadata
 | 
					
						
							|  |  |  | 	zipWriter := zip.NewWriter(w) | 
					
						
							|  |  |  | 	defer zipWriter.Close() | 
					
						
							|  |  |  | 	rawDataFn := func(r io.Reader, filename string, sz int) error { | 
					
						
							|  |  |  | 		header, zerr := zip.FileInfoHeader(dummyFileInfo{ | 
					
						
							|  |  |  | 			name:    filename, | 
					
						
							|  |  |  | 			size:    int64(sz), | 
					
						
							|  |  |  | 			mode:    0o600, | 
					
						
							|  |  |  | 			modTime: time.Now(), | 
					
						
							|  |  |  | 			isDir:   false, | 
					
						
							|  |  |  | 			sys:     nil, | 
					
						
							|  |  |  | 		}) | 
					
						
							|  |  |  | 		if zerr != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, zerr) | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		header.Method = zip.Deflate | 
					
						
							|  |  |  | 		zwriter, zerr := zipWriter.CreateHeader(header) | 
					
						
							|  |  |  | 		if zerr != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, zerr) | 
					
						
							|  |  |  | 			return nil | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if _, err := io.Copy(zwriter, r); err != nil { | 
					
						
							|  |  |  | 			logger.LogIf(ctx, err) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	cfgFiles := []string{ | 
					
						
							|  |  |  | 		bucketPolicyConfig, | 
					
						
							|  |  |  | 		bucketNotificationConfig, | 
					
						
							|  |  |  | 		bucketLifecycleConfig, | 
					
						
							|  |  |  | 		bucketSSEConfig, | 
					
						
							|  |  |  | 		bucketTaggingConfig, | 
					
						
							|  |  |  | 		bucketQuotaConfigFile, | 
					
						
							|  |  |  | 		objectLockConfig, | 
					
						
							|  |  |  | 		bucketVersioningConfig, | 
					
						
							|  |  |  | 		bucketReplicationConfig, | 
					
						
							|  |  |  | 		bucketTargetsFile, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, bi := range buckets { | 
					
						
							|  |  |  | 		for _, cfgFile := range cfgFiles { | 
					
						
							|  |  |  | 			cfgPath := pathJoin(bi.Name, cfgFile) | 
					
						
							|  |  |  | 			bucket := bi.Name | 
					
						
							|  |  |  | 			switch cfgFile { | 
					
						
							|  |  |  | 			case bucketNotificationConfig: | 
					
						
							|  |  |  | 				config, err := globalBucketMetadataSys.GetNotificationConfig(bucket) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				configData, err := xml.Marshal(config) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if err = rawDataFn(bytes.NewReader(configData), cfgPath, len(configData)); err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			case bucketLifecycleConfig: | 
					
						
							|  |  |  | 				config, err := globalBucketMetadataSys.GetLifecycleConfig(bucket) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					if errors.Is(err, BucketLifecycleNotFound{Bucket: bucket}) { | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					logger.LogIf(ctx, err) | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				configData, err := xml.Marshal(config) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if err = rawDataFn(bytes.NewReader(configData), cfgPath, len(configData)); err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			case bucketQuotaConfigFile: | 
					
						
							|  |  |  | 				config, _, err := globalBucketMetadataSys.GetQuotaConfig(ctx, bucket) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					if errors.Is(err, BucketQuotaConfigNotFound{Bucket: bucket}) { | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				configData, err := json.Marshal(config) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if err = rawDataFn(bytes.NewReader(configData), cfgPath, len(configData)); err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			case bucketSSEConfig: | 
					
						
							|  |  |  | 				config, _, err := globalBucketMetadataSys.GetSSEConfig(bucket) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					if errors.Is(err, BucketSSEConfigNotFound{Bucket: bucket}) { | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				configData, err := xml.Marshal(config) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if err = rawDataFn(bytes.NewReader(configData), cfgPath, len(configData)); err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			case bucketTaggingConfig: | 
					
						
							|  |  |  | 				config, _, err := globalBucketMetadataSys.GetTaggingConfig(bucket) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					if errors.Is(err, BucketTaggingNotFound{Bucket: bucket}) { | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				configData, err := xml.Marshal(config) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if err = rawDataFn(bytes.NewReader(configData), cfgPath, len(configData)); err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			case objectLockConfig: | 
					
						
							|  |  |  | 				config, _, err := globalBucketMetadataSys.GetObjectLockConfig(bucket) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					if errors.Is(err, BucketObjectLockConfigNotFound{Bucket: bucket}) { | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				configData, err := xml.Marshal(config) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if err = rawDataFn(bytes.NewReader(configData), cfgPath, len(configData)); err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			case bucketVersioningConfig: | 
					
						
							|  |  |  | 				config, _, err := globalBucketMetadataSys.GetVersioningConfig(bucket) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				// ignore empty versioning configs
 | 
					
						
							|  |  |  | 				if config.Status != versioning.Enabled && config.Status != versioning.Suspended { | 
					
						
							|  |  |  | 					continue | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				configData, err := xml.Marshal(config) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if err = rawDataFn(bytes.NewReader(configData), cfgPath, len(configData)); err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			case bucketReplicationConfig: | 
					
						
							|  |  |  | 				config, _, err := globalBucketMetadataSys.GetReplicationConfig(ctx, bucket) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					if errors.Is(err, BucketReplicationConfigNotFound{Bucket: bucket}) { | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				configData, err := xml.Marshal(config) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if err = rawDataFn(bytes.NewReader(configData), cfgPath, len(configData)); err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			case bucketTargetsFile: | 
					
						
							|  |  |  | 				config, err := globalBucketMetadataSys.GetBucketTargetsConfig(bucket) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					if errors.Is(err, BucketRemoteTargetNotFound{Bucket: bucket}) { | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					writeErrorResponseJSON(ctx, w, toAdminAPIErr(ctx, err), r.URL) | 
					
						
							|  |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				configData, err := xml.Marshal(config) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if err = rawDataFn(bytes.NewReader(configData), cfgPath, len(configData)); err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-21 07:13:45 +08:00
										 |  |  | 					writeErrorResponse(ctx, w, exportError(ctx, err, cfgFile, bucket), r.URL) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					return | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | type importMetaReport struct { | 
					
						
							|  |  |  | 	madmin.BucketMetaImportErrs | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (i *importMetaReport) SetStatus(bucket, fname string, err error) { | 
					
						
							|  |  |  | 	st := i.Buckets[bucket] | 
					
						
							|  |  |  | 	var errMsg string | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		errMsg = err.Error() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	switch fname { | 
					
						
							|  |  |  | 	case bucketPolicyConfig: | 
					
						
							|  |  |  | 		st.Policy = madmin.MetaStatus{IsSet: true, Err: errMsg} | 
					
						
							|  |  |  | 	case bucketNotificationConfig: | 
					
						
							|  |  |  | 		st.Notification = madmin.MetaStatus{IsSet: true, Err: errMsg} | 
					
						
							|  |  |  | 	case bucketLifecycleConfig: | 
					
						
							|  |  |  | 		st.Lifecycle = madmin.MetaStatus{IsSet: true, Err: errMsg} | 
					
						
							|  |  |  | 	case bucketSSEConfig: | 
					
						
							|  |  |  | 		st.SSEConfig = madmin.MetaStatus{IsSet: true, Err: errMsg} | 
					
						
							|  |  |  | 	case bucketTaggingConfig: | 
					
						
							|  |  |  | 		st.Tagging = madmin.MetaStatus{IsSet: true, Err: errMsg} | 
					
						
							|  |  |  | 	case bucketQuotaConfigFile: | 
					
						
							|  |  |  | 		st.Quota = madmin.MetaStatus{IsSet: true, Err: errMsg} | 
					
						
							|  |  |  | 	case objectLockConfig: | 
					
						
							|  |  |  | 		st.ObjectLock = madmin.MetaStatus{IsSet: true, Err: errMsg} | 
					
						
							|  |  |  | 	case bucketVersioningConfig: | 
					
						
							|  |  |  | 		st.Versioning = madmin.MetaStatus{IsSet: true, Err: errMsg} | 
					
						
							|  |  |  | 	default: | 
					
						
							|  |  |  | 		st.Err = errMsg | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	i.Buckets[bucket] = st | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | // ImportBucketMetadataHandler - imports all bucket metadata from a zipped file and overwrite bucket metadata config
 | 
					
						
							|  |  |  | // There are some caveats regarding the following:
 | 
					
						
							|  |  |  | // 1. object lock config - object lock should have been specified at time of bucket creation. Only default retention settings are imported here.
 | 
					
						
							|  |  |  | // 2. Replication config - is omitted from import as remote target credentials are not available from exported data for security reasons.
 | 
					
						
							|  |  |  | // 3. lifecycle config - if transition rules are present, tier name needs to have been defined.
 | 
					
						
							|  |  |  | func (a adminAPIHandlers) ImportBucketMetadataHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "ImportBucketMetadata") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Get current object layer instance.
 | 
					
						
							|  |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.ImportBucketMetadataAction) | 
					
						
							|  |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-09-20 02:05:16 +08:00
										 |  |  | 	data, err := io.ReadAll(r.Body) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	reader := bytes.NewReader(data) | 
					
						
							|  |  |  | 	zr, err := zip.NewReader(reader, int64(len(data))) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErr(ErrInvalidRequest), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	bucketMap := make(map[string]struct{}, 1) | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 	rpt := importMetaReport{ | 
					
						
							|  |  |  | 		madmin.BucketMetaImportErrs{ | 
					
						
							|  |  |  | 			Buckets: make(map[string]madmin.BucketStatus, len(zr.File)), | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 	// import object lock config if any - order of import matters here.
 | 
					
						
							|  |  |  | 	for _, file := range zr.File { | 
					
						
							|  |  |  | 		slc := strings.Split(file.Name, slashSeparator) | 
					
						
							|  |  |  | 		if len(slc) != 2 { // expecting bucket/configfile in the zipfile
 | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 			rpt.SetStatus(file.Name, "", fmt.Errorf("malformed zip - expecting format bucket/<config.json>")) | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 		bucket, fileName := slc[0], slc[1] | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 		switch fileName { | 
					
						
							|  |  |  | 		case objectLockConfig: | 
					
						
							|  |  |  | 			reader, err := file.Open() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			config, err := objectlock.ParseObjectLockConfig(reader) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, fmt.Errorf("%s (%s)", errorCodes[ErrMalformedXML].Description, err)) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			configData, err := xml.Marshal(config) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			if _, ok := bucketMap[bucket]; !ok { | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 				opts := MakeBucketOptions{ | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					LockEnabled: config.ObjectLockEnabled == "Enabled", | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2022-12-23 23:46:00 +08:00
										 |  |  | 				err = objectAPI.MakeBucket(ctx, bucket, opts) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					if _, ok := err.(BucketExists); !ok { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 						rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 						continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				bucketMap[bucket] = struct{}{} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Deny object locking configuration settings on existing buckets without object lock enabled.
 | 
					
						
							|  |  |  | 			if _, _, err = globalBucketMetadataSys.GetObjectLockConfig(bucket); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 			updatedAt, err := globalBucketMetadataSys.Update(ctx, bucket, objectLockConfig, configData) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 			rpt.SetStatus(bucket, fileName, nil) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Call site replication hook.
 | 
					
						
							|  |  |  | 			//
 | 
					
						
							|  |  |  | 			// We encode the xml bytes as base64 to ensure there are no encoding
 | 
					
						
							|  |  |  | 			// errors.
 | 
					
						
							|  |  |  | 			cfgStr := base64.StdEncoding.EncodeToString(configData) | 
					
						
							|  |  |  | 			if err = globalSiteReplicationSys.BucketMetaHook(ctx, madmin.SRBucketMeta{ | 
					
						
							|  |  |  | 				Type:             madmin.SRBucketMetaTypeObjectLockConfig, | 
					
						
							|  |  |  | 				Bucket:           bucket, | 
					
						
							|  |  |  | 				ObjectLockConfig: &cfgStr, | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 				UpdatedAt:        updatedAt, | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			}); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// import versioning metadata
 | 
					
						
							|  |  |  | 	for _, file := range zr.File { | 
					
						
							|  |  |  | 		slc := strings.Split(file.Name, slashSeparator) | 
					
						
							|  |  |  | 		if len(slc) != 2 { // expecting bucket/configfile in the zipfile
 | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 			rpt.SetStatus(file.Name, "", fmt.Errorf("malformed zip - expecting format bucket/<config.json>")) | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 		bucket, fileName := slc[0], slc[1] | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 		switch fileName { | 
					
						
							|  |  |  | 		case bucketVersioningConfig: | 
					
						
							|  |  |  | 			reader, err := file.Open() | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			v, err := versioning.ParseConfig(io.LimitReader(reader, maxBucketVersioningConfigSize)) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			if _, ok := bucketMap[bucket]; !ok { | 
					
						
							| 
									
										
										
										
											2022-12-23 23:46:00 +08:00
										 |  |  | 				if err = objectAPI.MakeBucket(ctx, bucket, MakeBucketOptions{}); err != nil { | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					if _, ok := err.(BucketExists); !ok { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 						rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 						continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				bucketMap[bucket] = struct{}{} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if globalSiteReplicationSys.isEnabled() && v.Suspended() { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, fmt.Errorf("Cluster replication is enabled for this site, so the versioning state cannot be suspended.")) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if rcfg, _ := globalBucketObjectLockSys.Get(bucket); rcfg.LockEnabled && v.Suspended() { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, fmt.Errorf("An Object Lock configuration is present on this bucket, so the versioning state cannot be suspended.")) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			if _, err := getReplicationConfig(ctx, bucket); err == nil && v.Suspended() { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, fmt.Errorf("A replication configuration is present on this bucket, so the versioning state cannot be suspended.")) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			configData, err := xml.Marshal(v) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, fmt.Errorf("%s (%s)", errorCodes[ErrMalformedXML].Description, err)) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 			if _, err = globalBucketMetadataSys.Update(ctx, bucket, bucketVersioningConfig, configData); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 			rpt.SetStatus(bucket, fileName, nil) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for _, file := range zr.File { | 
					
						
							|  |  |  | 		reader, err := file.Open() | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 			rpt.SetStatus(file.Name, "", err) | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		sz := file.FileInfo().Size() | 
					
						
							|  |  |  | 		slc := strings.Split(file.Name, slashSeparator) | 
					
						
							|  |  |  | 		if len(slc) != 2 { // expecting bucket/configfile in the zipfile
 | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 			rpt.SetStatus(file.Name, "", fmt.Errorf("malformed zip - expecting format bucket/<config.json>")) | 
					
						
							|  |  |  | 			continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 		bucket, fileName := slc[0], slc[1] | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 		// create bucket if it does not exist yet.
 | 
					
						
							|  |  |  | 		if _, ok := bucketMap[bucket]; !ok { | 
					
						
							| 
									
										
										
										
											2022-12-23 23:46:00 +08:00
										 |  |  | 			err = objectAPI.MakeBucket(ctx, bucket, MakeBucketOptions{}) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				if _, ok := err.(BucketExists); !ok { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 					rpt.SetStatus(bucket, "", err) | 
					
						
							|  |  |  | 					continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			bucketMap[bucket] = struct{}{} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 		if _, ok := bucketMap[bucket]; !ok { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 		switch fileName { | 
					
						
							|  |  |  | 		case bucketNotificationConfig: | 
					
						
							| 
									
										
										
										
											2022-08-24 21:42:36 +08:00
										 |  |  | 			config, err := event.ParseConfig(io.LimitReader(reader, sz), globalSite.Region, globalEventNotifier.targetList) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, fmt.Errorf("%s (%s)", errorCodes[ErrMalformedXML].Description, err)) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			configData, err := xml.Marshal(config) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 			if _, err = globalBucketMetadataSys.Update(ctx, bucket, bucketNotificationConfig, configData); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			rulesMap := config.ToRulesMap() | 
					
						
							| 
									
										
										
										
											2022-08-24 21:42:36 +08:00
										 |  |  | 			globalEventNotifier.AddRulesMap(bucket, rulesMap) | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 			rpt.SetStatus(bucket, fileName, nil) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 		case bucketPolicyConfig: | 
					
						
							|  |  |  | 			// Error out if Content-Length is beyond allowed size.
 | 
					
						
							|  |  |  | 			if sz > maxBucketPolicySize { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, fmt.Errorf(ErrPolicyTooLarge.String())) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-20 02:05:16 +08:00
										 |  |  | 			bucketPolicyBytes, err := io.ReadAll(io.LimitReader(reader, sz)) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			bucketPolicy, err := policy.ParseConfig(bytes.NewReader(bucketPolicyBytes), bucket) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Version in policy must not be empty
 | 
					
						
							|  |  |  | 			if bucketPolicy.Version == "" { | 
					
						
							| 
									
										
										
										
											2022-12-07 00:07:24 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, fmt.Errorf(ErrPolicyInvalidVersion.String())) | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			configData, err := json.Marshal(bucketPolicy) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 			updatedAt, err := globalBucketMetadataSys.Update(ctx, bucket, bucketPolicyConfig, configData) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 			rpt.SetStatus(bucket, fileName, nil) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			// Call site replication hook.
 | 
					
						
							|  |  |  | 			if err = globalSiteReplicationSys.BucketMetaHook(ctx, madmin.SRBucketMeta{ | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 				Type:      madmin.SRBucketMetaTypePolicy, | 
					
						
							|  |  |  | 				Bucket:    bucket, | 
					
						
							|  |  |  | 				Policy:    bucketPolicyBytes, | 
					
						
							|  |  |  | 				UpdatedAt: updatedAt, | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			}); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		case bucketLifecycleConfig: | 
					
						
							|  |  |  | 			bucketLifecycle, err := lifecycle.ParseLifecycleConfig(io.LimitReader(reader, sz)) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Validate the received bucket policy document
 | 
					
						
							|  |  |  | 			if err = bucketLifecycle.Validate(); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Validate the transition storage ARNs
 | 
					
						
							|  |  |  | 			if err = validateTransitionTier(bucketLifecycle); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			configData, err := xml.Marshal(bucketLifecycle) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 			if _, err = globalBucketMetadataSys.Update(ctx, bucket, bucketLifecycleConfig, configData); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 			rpt.SetStatus(bucket, fileName, nil) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 		case bucketSSEConfig: | 
					
						
							|  |  |  | 			// Parse bucket encryption xml
 | 
					
						
							|  |  |  | 			encConfig, err := validateBucketSSEConfig(io.LimitReader(reader, maxBucketSSEConfigSize)) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, fmt.Errorf("%s (%s)", errorCodes[ErrMalformedXML].Description, err)) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Return error if KMS is not initialized
 | 
					
						
							|  |  |  | 			if GlobalKMS == nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, fmt.Errorf("%s", errorCodes[ErrKMSNotConfigured].Description)) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			kmsKey := encConfig.KeyID() | 
					
						
							|  |  |  | 			if kmsKey != "" { | 
					
						
							|  |  |  | 				kmsContext := kms.Context{"MinIO admin API": "ServerInfoHandler"} // Context for a test key operation
 | 
					
						
							| 
									
										
										
										
											2022-07-19 09:54:27 +08:00
										 |  |  | 				_, err := GlobalKMS.GenerateKey(ctx, kmsKey, kmsContext) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					if errors.Is(err, kes.ErrKeyNotFound) { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 						rpt.SetStatus(bucket, fileName, errKMSKeyNotFound) | 
					
						
							|  |  |  | 						continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 					rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 					continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			configData, err := xml.Marshal(encConfig) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Store the bucket encryption configuration in the object layer
 | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 			updatedAt, err := globalBucketMetadataSys.Update(ctx, bucket, bucketSSEConfig, configData) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 			rpt.SetStatus(bucket, fileName, nil) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Call site replication hook.
 | 
					
						
							|  |  |  | 			//
 | 
					
						
							|  |  |  | 			// We encode the xml bytes as base64 to ensure there are no encoding
 | 
					
						
							|  |  |  | 			// errors.
 | 
					
						
							|  |  |  | 			cfgStr := base64.StdEncoding.EncodeToString(configData) | 
					
						
							|  |  |  | 			if err = globalSiteReplicationSys.BucketMetaHook(ctx, madmin.SRBucketMeta{ | 
					
						
							|  |  |  | 				Type:      madmin.SRBucketMetaTypeSSEConfig, | 
					
						
							|  |  |  | 				Bucket:    bucket, | 
					
						
							|  |  |  | 				SSEConfig: &cfgStr, | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 				UpdatedAt: updatedAt, | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			}); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case bucketTaggingConfig: | 
					
						
							|  |  |  | 			tags, err := tags.ParseBucketXML(io.LimitReader(reader, sz)) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, fmt.Errorf("%s (%s)", errorCodes[ErrMalformedXML].Description, err)) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			configData, err := xml.Marshal(tags) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 			updatedAt, err := globalBucketMetadataSys.Update(ctx, bucket, bucketTaggingConfig, configData) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 			rpt.SetStatus(bucket, fileName, nil) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			// Call site replication hook.
 | 
					
						
							|  |  |  | 			//
 | 
					
						
							|  |  |  | 			// We encode the xml bytes as base64 to ensure there are no encoding
 | 
					
						
							|  |  |  | 			// errors.
 | 
					
						
							|  |  |  | 			cfgStr := base64.StdEncoding.EncodeToString(configData) | 
					
						
							|  |  |  | 			if err = globalSiteReplicationSys.BucketMetaHook(ctx, madmin.SRBucketMeta{ | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 				Type:      madmin.SRBucketMetaTypeTags, | 
					
						
							|  |  |  | 				Bucket:    bucket, | 
					
						
							|  |  |  | 				Tags:      &cfgStr, | 
					
						
							|  |  |  | 				UpdatedAt: updatedAt, | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			}); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		case bucketQuotaConfigFile: | 
					
						
							| 
									
										
										
										
											2022-09-20 02:05:16 +08:00
										 |  |  | 			data, err := io.ReadAll(reader) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			quotaConfig, err := parseBucketQuota(bucket, data) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if quotaConfig.Type == "fifo" { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, fmt.Errorf("Detected older 'fifo' quota config, 'fifo' feature is removed and not supported anymore")) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 			updatedAt, err := globalBucketMetadataSys.Update(ctx, bucket, bucketQuotaConfigFile, data) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 			rpt.SetStatus(bucket, fileName, nil) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			bucketMeta := madmin.SRBucketMeta{ | 
					
						
							| 
									
										
										
										
											2022-06-29 09:09:20 +08:00
										 |  |  | 				Type:      madmin.SRBucketMetaTypeQuotaConfig, | 
					
						
							|  |  |  | 				Bucket:    bucket, | 
					
						
							|  |  |  | 				Quota:     data, | 
					
						
							|  |  |  | 				UpdatedAt: updatedAt, | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			if quotaConfig.Quota == 0 { | 
					
						
							|  |  |  | 				bucketMeta.Quota = nil | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Call site replication hook.
 | 
					
						
							|  |  |  | 			if err = globalSiteReplicationSys.BucketMetaHook(ctx, bucketMeta); err != nil { | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 				rpt.SetStatus(bucket, fileName, err) | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-08-05 16:52:50 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	rptData, err := json.Marshal(rpt.BucketMetaImportErrs) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	writeSuccessResponseJSON(w, rptData) | 
					
						
							| 
									
										
										
										
											2022-06-18 21:55:39 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2022-07-22 02:05:44 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // ReplicationDiffHandler - POST returns info on unreplicated versions for a remote target ARN
 | 
					
						
							| 
									
										
										
										
											2022-10-05 08:47:31 +08:00
										 |  |  | // to the connected HTTP client.
 | 
					
						
							| 
									
										
										
										
											2022-07-22 02:05:44 +08:00
										 |  |  | func (a adminAPIHandlers) ReplicationDiffHandler(w http.ResponseWriter, r *http.Request) { | 
					
						
							|  |  |  | 	ctx := newContext(r, w, "ReplicationDiff") | 
					
						
							|  |  |  | 	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vars := mux.Vars(r) | 
					
						
							|  |  |  | 	bucket := vars["bucket"] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-05 08:47:31 +08:00
										 |  |  | 	objectAPI, _ := validateAdminReq(ctx, w, r, iampolicy.ReplicationDiff) | 
					
						
							| 
									
										
										
										
											2022-07-22 02:05:44 +08:00
										 |  |  | 	if objectAPI == nil { | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Check if bucket exists.
 | 
					
						
							| 
									
										
										
										
											2022-07-26 08:51:32 +08:00
										 |  |  | 	if _, err := objectAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { | 
					
						
							| 
									
										
										
										
											2022-07-22 02:05:44 +08:00
										 |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	opts := extractReplicateDiffOpts(r.Form) | 
					
						
							|  |  |  | 	if opts.ARN != "" { | 
					
						
							|  |  |  | 		tgt := globalBucketTargetSys.GetRemoteBucketTargetByArn(ctx, bucket, opts.ARN) | 
					
						
							|  |  |  | 		if tgt.Empty() { | 
					
						
							|  |  |  | 			writeErrorResponseJSON(ctx, w, errorCodes.ToAPIErrWithErr(ErrInvalidRequest, fmt.Errorf("invalid arn : '%s'", opts.ARN)), r.URL) | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	keepAliveTicker := time.NewTicker(500 * time.Millisecond) | 
					
						
							|  |  |  | 	defer keepAliveTicker.Stop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	diffCh, err := getReplicationDiff(ctx, objectAPI, bucket, opts) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) | 
					
						
							|  |  |  | 		return | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	enc := json.NewEncoder(w) | 
					
						
							|  |  |  | 	for { | 
					
						
							|  |  |  | 		select { | 
					
						
							|  |  |  | 		case entry, ok := <-diffCh: | 
					
						
							|  |  |  | 			if !ok { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if err := enc.Encode(entry); err != nil { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if len(diffCh) == 0 { | 
					
						
							|  |  |  | 				// Flush if nothing is queued
 | 
					
						
							|  |  |  | 				w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		case <-keepAliveTicker.C: | 
					
						
							|  |  |  | 			if len(diffCh) > 0 { | 
					
						
							|  |  |  | 				continue | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if _, err := w.Write([]byte(" ")); err != nil { | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			w.(http.Flusher).Flush() | 
					
						
							|  |  |  | 		case <-ctx.Done(): | 
					
						
							|  |  |  | 			return | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |