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 }