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

     1  /*
     2   * MinIO Cloud Storage, (C) 2016, 2017, 2018 MinIO, Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package cmd
    18  
    19  import (
    20  	"encoding/xml"
    21  	"io"
    22  	"net/http"
    23  	"reflect"
    24  
    25  	"github.com/gorilla/mux"
    26  
    27  	"storj.io/minio/cmd/logger"
    28  	"storj.io/minio/pkg/bucket/policy"
    29  	"storj.io/minio/pkg/event"
    30  )
    31  
    32  const (
    33  	bucketConfigPrefix       = "buckets"
    34  	bucketNotificationConfig = "notification.xml"
    35  )
    36  
    37  // GetBucketNotificationHandler - This HTTP handler returns event notification configuration
    38  // as per http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html.
    39  // It returns empty configuration if its not set.
    40  func (api ObjectAPIHandlers) GetBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
    41  	ctx := NewContext(r, w, "GetBucketNotification")
    42  
    43  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
    44  
    45  	vars := mux.Vars(r)
    46  	bucketName := vars["bucket"]
    47  
    48  	objAPI := api.ObjectAPI()
    49  	if objAPI == nil {
    50  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
    51  		return
    52  	}
    53  
    54  	if !objAPI.IsNotificationSupported() {
    55  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
    56  		return
    57  	}
    58  
    59  	if s3Error := checkRequestAuthType(ctx, r, policy.GetBucketNotificationAction, bucketName, ""); s3Error != ErrNone {
    60  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
    61  		return
    62  	}
    63  
    64  	_, err := objAPI.GetBucketInfo(ctx, bucketName)
    65  	if err != nil {
    66  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
    67  		return
    68  	}
    69  
    70  	config, err := globalBucketMetadataSys.GetNotificationConfig(bucketName)
    71  	if err != nil {
    72  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
    73  		return
    74  	}
    75  	config.SetRegion(globalServerRegion)
    76  	if err = config.Validate(globalServerRegion, GlobalNotificationSys.targetList); err != nil {
    77  		arnErr, ok := err.(*event.ErrARNNotFound)
    78  		if ok {
    79  			for i, queue := range config.QueueList {
    80  				// Remove ARN not found queues, because we previously allowed
    81  				// adding unexpected entries into the config.
    82  				//
    83  				// With newer config disallowing changing / turning off
    84  				// notification targets without removing ARN in notification
    85  				// configuration we won't see this problem anymore.
    86  				if reflect.DeepEqual(queue.ARN, arnErr.ARN) && i < len(config.QueueList) {
    87  					config.QueueList = append(config.QueueList[:i],
    88  						config.QueueList[i+1:]...)
    89  				}
    90  				// This is a one time activity we shall do this
    91  				// here and allow stale ARN to be removed. We shall
    92  				// never reach a stage where we will have stale
    93  				// notification configs.
    94  			}
    95  		} else {
    96  			WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
    97  			return
    98  		}
    99  	}
   100  
   101  	configData, err := xml.Marshal(config)
   102  	if err != nil {
   103  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   104  		return
   105  	}
   106  
   107  	WriteSuccessResponseXML(w, configData)
   108  }
   109  
   110  // PutBucketNotificationHandler - This HTTP handler stores given notification configuration as per
   111  // http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html.
   112  func (api ObjectAPIHandlers) PutBucketNotificationHandler(w http.ResponseWriter, r *http.Request) {
   113  	ctx := NewContext(r, w, "PutBucketNotification")
   114  
   115  	defer logger.AuditLog(ctx, w, r, mustGetClaimsFromToken(r))
   116  
   117  	objectAPI := api.ObjectAPI()
   118  	if objectAPI == nil {
   119  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrServerNotInitialized), r.URL, guessIsBrowserReq(r))
   120  		return
   121  	}
   122  
   123  	if !objectAPI.IsNotificationSupported() {
   124  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrNotImplemented), r.URL, guessIsBrowserReq(r))
   125  		return
   126  	}
   127  
   128  	vars := mux.Vars(r)
   129  	bucketName := vars["bucket"]
   130  
   131  	if s3Error := checkRequestAuthType(ctx, r, policy.PutBucketNotificationAction, bucketName, ""); s3Error != ErrNone {
   132  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(s3Error), r.URL, guessIsBrowserReq(r))
   133  		return
   134  	}
   135  
   136  	_, err := objectAPI.GetBucketInfo(ctx, bucketName)
   137  	if err != nil {
   138  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   139  		return
   140  	}
   141  
   142  	// PutBucketNotification always needs a Content-Length.
   143  	if r.ContentLength <= 0 {
   144  		WriteErrorResponse(ctx, w, errorCodes.ToAPIErr(ErrMissingContentLength), r.URL, guessIsBrowserReq(r))
   145  		return
   146  	}
   147  
   148  	config, err := event.ParseConfig(io.LimitReader(r.Body, r.ContentLength), globalServerRegion, GlobalNotificationSys.targetList)
   149  	if err != nil {
   150  		apiErr := errorCodes.ToAPIErr(ErrMalformedXML)
   151  		if event.IsEventError(err) {
   152  			apiErr = ToAPIError(ctx, err)
   153  		}
   154  		WriteErrorResponse(ctx, w, apiErr, r.URL, guessIsBrowserReq(r))
   155  		return
   156  	}
   157  
   158  	configData, err := xml.Marshal(config)
   159  	if err != nil {
   160  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   161  		return
   162  	}
   163  
   164  	if err = globalBucketMetadataSys.Update(bucketName, bucketNotificationConfig, configData); err != nil {
   165  		WriteErrorResponse(ctx, w, ToAPIError(ctx, err), r.URL, guessIsBrowserReq(r))
   166  		return
   167  	}
   168  
   169  	rulesMap := config.ToRulesMap()
   170  	GlobalNotificationSys.AddRulesMap(bucketName, rulesMap)
   171  
   172  	writeSuccessResponseHeadersOnly(w)
   173  }