// 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 . package cmd import ( "bytes" "encoding/json" "io" "net/http" humanize "github.com/dustin/go-humanize" "github.com/minio/madmin-go/v2" "github.com/minio/minio/internal/logger" "github.com/minio/mux" "github.com/minio/pkg/bucket/policy" ) const ( // As per AWS S3 specification, 20KiB policy JSON data is allowed. maxBucketPolicySize = 20 * humanize.KiByte // Policy configuration file. bucketPolicyConfig = "policy.json" ) // PutBucketPolicyHandler - This HTTP handler stores given bucket policy configuration as per // https://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html func (api objectAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { ctx := newContext(r, w, "PutBucketPolicy") defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) objAPI := api.ObjectAPI() if objAPI == nil { writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) return } vars := mux.Vars(r) bucket := vars["bucket"] if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketPolicyAction, bucket, ""); s3Error != ErrNone { writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) return } // Check if bucket exists. if _, err := objAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) return } // Error out if Content-Length is missing. // PutBucketPolicy always needs Content-Length. if r.ContentLength <= 0 { writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL) return } // Error out if Content-Length is beyond allowed size. if r.ContentLength > maxBucketPolicySize { writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrPolicyTooLarge), r.URL) return } bucketPolicyBytes, err := io.ReadAll(io.LimitReader(r.Body, r.ContentLength)) if err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) return } bucketPolicy, err := policy.ParseConfig(bytes.NewReader(bucketPolicyBytes), bucket) if err != nil { writeErrorResponse(ctx, w, APIError{ Code: "MalformedPolicy", HTTPStatusCode: http.StatusBadRequest, Description: err.Error(), }, r.URL) return } // Version in policy must not be empty if bucketPolicy.Version == "" { writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrPolicyInvalidVersion), r.URL) return } configData, err := json.Marshal(bucketPolicy) if err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) return } updatedAt, err := globalBucketMetadataSys.Update(ctx, bucket, bucketPolicyConfig, configData) if err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) return } // Call site replication hook. logger.LogIf(ctx, globalSiteReplicationSys.BucketMetaHook(ctx, madmin.SRBucketMeta{ Type: madmin.SRBucketMetaTypePolicy, Bucket: bucket, Policy: bucketPolicyBytes, UpdatedAt: updatedAt, })) // Success. writeSuccessNoContent(w) } // DeleteBucketPolicyHandler - This HTTP handler removes bucket policy configuration. func (api objectAPIHandlers) DeleteBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { ctx := newContext(r, w, "DeleteBucketPolicy") defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) objAPI := api.ObjectAPI() if objAPI == nil { writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) return } vars := mux.Vars(r) bucket := vars["bucket"] if s3Error := checkRequestAuthType(ctx, r, policy.DeleteBucketPolicyAction, bucket, ""); s3Error != ErrNone { writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) return } // Check if bucket exists. if _, err := objAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) return } updatedAt, err := globalBucketMetadataSys.Delete(ctx, bucket, bucketPolicyConfig) if err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) return } // Call site replication hook. logger.LogIf(ctx, globalSiteReplicationSys.BucketMetaHook(ctx, madmin.SRBucketMeta{ Type: madmin.SRBucketMetaTypePolicy, Bucket: bucket, UpdatedAt: updatedAt, })) // Success. writeSuccessNoContent(w) } // GetBucketPolicyHandler - This HTTP handler returns bucket policy configuration. func (api objectAPIHandlers) GetBucketPolicyHandler(w http.ResponseWriter, r *http.Request) { ctx := newContext(r, w, "GetBucketPolicy") defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r)) objAPI := api.ObjectAPI() if objAPI == nil { writeErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL) return } vars := mux.Vars(r) bucket := vars["bucket"] if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketPolicyAction, bucket, ""); s3Error != ErrNone { writeErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL) return } // Check if bucket exists. if _, err := objAPI.GetBucketInfo(ctx, bucket, BucketOptions{}); err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) return } // Read bucket access policy. config, err := globalPolicySys.Get(bucket) if err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) return } configData, err := json.Marshal(config) if err != nil { writeErrorResponse(ctx, w, toAPIError(ctx, err), r.URL) return } // Write to client. writeSuccessResponseJSON(w, configData) }