github.com/minio/console@v1.4.1/api/user_buckets_events.go (about) 1 // This file is part of MinIO Console Server 2 // Copyright (c) 2021 MinIO, Inc. 3 // 4 // This program is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Affero General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // This program is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Affero General Public License for more details. 13 // 14 // You should have received a copy of the GNU Affero General Public License 15 // along with this program. If not, see <http://www.gnu.org/licenses/>. 16 17 package api 18 19 import ( 20 "context" 21 "strings" 22 23 "github.com/go-openapi/runtime/middleware" 24 "github.com/go-openapi/swag" 25 "github.com/minio/console/api/operations" 26 bucketApi "github.com/minio/console/api/operations/bucket" 27 "github.com/minio/console/models" 28 "github.com/minio/minio-go/v7/pkg/notification" 29 ) 30 31 func registerBucketEventsHandlers(api *operations.ConsoleAPI) { 32 // list bucket events 33 api.BucketListBucketEventsHandler = bucketApi.ListBucketEventsHandlerFunc(func(params bucketApi.ListBucketEventsParams, session *models.Principal) middleware.Responder { 34 listBucketEventsResponse, err := getListBucketEventsResponse(session, params) 35 if err != nil { 36 return bucketApi.NewListBucketEventsDefault(err.Code).WithPayload(err.APIError) 37 } 38 return bucketApi.NewListBucketEventsOK().WithPayload(listBucketEventsResponse) 39 }) 40 // create bucket event 41 api.BucketCreateBucketEventHandler = bucketApi.CreateBucketEventHandlerFunc(func(params bucketApi.CreateBucketEventParams, session *models.Principal) middleware.Responder { 42 if err := getCreateBucketEventsResponse(session, params); err != nil { 43 return bucketApi.NewCreateBucketEventDefault(err.Code).WithPayload(err.APIError) 44 } 45 return bucketApi.NewCreateBucketEventCreated() 46 }) 47 // delete bucket event 48 api.BucketDeleteBucketEventHandler = bucketApi.DeleteBucketEventHandlerFunc(func(params bucketApi.DeleteBucketEventParams, session *models.Principal) middleware.Responder { 49 if err := getDeleteBucketEventsResponse(session, params); err != nil { 50 return bucketApi.NewDeleteBucketEventDefault(err.Code).WithPayload(err.APIError) 51 } 52 return bucketApi.NewDeleteBucketEventNoContent() 53 }) 54 } 55 56 // listBucketEvents fetches a list of all events set for a bucket and serializes them for a proper output 57 func listBucketEvents(client MinioClient, bucketName string) ([]*models.NotificationConfig, error) { 58 var configs []*models.NotificationConfig 59 bn, err := client.getBucketNotification(context.Background(), bucketName) 60 if err != nil { 61 return nil, err 62 } 63 64 // Generate pretty event names from event types 65 prettyEventNames := func(eventsTypes []notification.EventType) []models.NotificationEventType { 66 var result []models.NotificationEventType 67 for _, eventType := range eventsTypes { 68 var eventTypePretty models.NotificationEventType 69 switch eventType { 70 case notification.ObjectAccessedAll: 71 eventTypePretty = models.NotificationEventTypeGet 72 case notification.ObjectCreatedAll: 73 eventTypePretty = models.NotificationEventTypePut 74 case notification.ObjectRemovedAll: 75 eventTypePretty = models.NotificationEventTypeDelete 76 case notification.ObjectReplicationAll: 77 eventTypePretty = models.NotificationEventTypeReplica 78 case notification.ObjectTransitionAll: 79 eventTypePretty = models.NotificationEventTypeIlm 80 case notification.ObjectScannerManyVersions, notification.ObjectScannerBigPrefix: 81 eventTypePretty = models.NotificationEventTypeScanner 82 default: 83 continue 84 } 85 86 result = append(result, eventTypePretty) 87 } 88 return result 89 } 90 // part of implementation taken from minio/mc 91 // s3Client.ListNotificationConfigs()... to serialize configurations 92 getFilters := func(config notification.Config) (prefix, suffix string) { 93 if config.Filter == nil { 94 return 95 } 96 for _, filter := range config.Filter.S3Key.FilterRules { 97 if strings.ToLower(filter.Name) == "prefix" { 98 prefix = filter.Value 99 } 100 if strings.ToLower(filter.Name) == "suffix" { 101 suffix = filter.Value 102 } 103 104 } 105 return prefix, suffix 106 } 107 for _, embed := range bn.TopicConfigs { 108 prefix, suffix := getFilters(embed.Config) 109 configs = append(configs, &models.NotificationConfig{ 110 ID: embed.ID, 111 Arn: swag.String(embed.Topic), 112 Events: prettyEventNames(embed.Events), 113 Prefix: prefix, 114 Suffix: suffix, 115 }) 116 } 117 for _, embed := range bn.QueueConfigs { 118 prefix, suffix := getFilters(embed.Config) 119 configs = append(configs, &models.NotificationConfig{ 120 ID: embed.ID, 121 Arn: swag.String(embed.Queue), 122 Events: prettyEventNames(embed.Events), 123 Prefix: prefix, 124 Suffix: suffix, 125 }) 126 } 127 for _, embed := range bn.LambdaConfigs { 128 prefix, suffix := getFilters(embed.Config) 129 configs = append(configs, &models.NotificationConfig{ 130 ID: embed.ID, 131 Arn: swag.String(embed.Lambda), 132 Events: prettyEventNames(embed.Events), 133 Prefix: prefix, 134 Suffix: suffix, 135 }) 136 } 137 return configs, nil 138 } 139 140 // getListBucketsResponse performs listBucketEvents() and serializes it to the handler's output 141 func getListBucketEventsResponse(session *models.Principal, params bucketApi.ListBucketEventsParams) (*models.ListBucketEventsResponse, *CodedAPIError) { 142 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 143 defer cancel() 144 mClient, err := newMinioClient(session, getClientIP(params.HTTPRequest)) 145 if err != nil { 146 return nil, ErrorWithContext(ctx, err) 147 } 148 // create a minioClient interface implementation 149 // defining the client to be used 150 minioClient := minioClient{client: mClient} 151 152 bucketEvents, err := listBucketEvents(minioClient, params.BucketName) 153 if err != nil { 154 return nil, ErrorWithContext(ctx, err) 155 } 156 // serialize output 157 listBucketsResponse := &models.ListBucketEventsResponse{ 158 Events: bucketEvents, 159 Total: int64(len(bucketEvents)), 160 } 161 return listBucketsResponse, nil 162 } 163 164 // createBucketEvent calls mc AddNotificationConfig() to create a bucket nofication 165 // 166 // If notificationEvents is empty, by default will set [get, put, delete], else the provided 167 // ones will be set. 168 // this function follows same behavior as minio/mc for adding a bucket event 169 func createBucketEvent(ctx context.Context, client MCClient, arn string, notificationEvents []models.NotificationEventType, prefix, suffix string, ignoreExisting bool) error { 170 var events []string 171 if len(notificationEvents) == 0 { 172 // default event values are [get, put, delete] 173 events = []string{ 174 string(models.NotificationEventTypeGet), 175 string(models.NotificationEventTypePut), 176 string(models.NotificationEventTypeDelete), 177 } 178 } else { 179 // else use defined events in request 180 // cast type models.NotificationEventType to string 181 for _, e := range notificationEvents { 182 events = append(events, string(e)) 183 } 184 } 185 186 perr := client.addNotificationConfig(ctx, arn, events, prefix, suffix, ignoreExisting) 187 if perr != nil { 188 return perr.Cause 189 } 190 return nil 191 } 192 193 // getCreateBucketEventsResponse calls createBucketEvent to add a bucket event notification 194 func getCreateBucketEventsResponse(session *models.Principal, params bucketApi.CreateBucketEventParams) *CodedAPIError { 195 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 196 defer cancel() 197 bucketName := params.BucketName 198 eventReq := params.Body 199 s3Client, err := newS3BucketClient(session, bucketName, "", getClientIP(params.HTTPRequest)) 200 if err != nil { 201 return ErrorWithContext(ctx, err) 202 } 203 // create a mc S3Client interface implementation 204 // defining the client to be used 205 mcClient := mcClient{client: s3Client} 206 err = createBucketEvent(ctx, mcClient, *eventReq.Configuration.Arn, eventReq.Configuration.Events, eventReq.Configuration.Prefix, eventReq.Configuration.Suffix, eventReq.IgnoreExisting) 207 if err != nil { 208 return ErrorWithContext(ctx, err) 209 } 210 return nil 211 } 212 213 // deleteBucketEventNotification calls S3Client.RemoveNotificationConfig to remove a bucket event notification 214 func deleteBucketEventNotification(ctx context.Context, client MCClient, arn string, events []models.NotificationEventType, prefix, suffix *string) error { 215 eventSingleString := joinNotificationEvents(events) 216 perr := client.removeNotificationConfig(ctx, arn, eventSingleString, *prefix, *suffix) 217 if perr != nil { 218 return perr.Cause 219 } 220 return nil 221 } 222 223 func joinNotificationEvents(events []models.NotificationEventType) string { 224 var eventsArn []string 225 for _, e := range events { 226 eventsArn = append(eventsArn, string(e)) 227 } 228 return strings.Join(eventsArn, ",") 229 } 230 231 // getDeleteBucketEventsResponse calls deleteBucketEventNotification() to delete a bucket event notification 232 func getDeleteBucketEventsResponse(session *models.Principal, params bucketApi.DeleteBucketEventParams) *CodedAPIError { 233 ctx, cancel := context.WithCancel(params.HTTPRequest.Context()) 234 defer cancel() 235 bucketName := params.BucketName 236 arn := params.Arn 237 events := params.Body.Events 238 prefix := params.Body.Prefix 239 suffix := params.Body.Suffix 240 s3Client, err := newS3BucketClient(session, bucketName, "", getClientIP(params.HTTPRequest)) 241 if err != nil { 242 return ErrorWithContext(ctx, err) 243 } 244 // create a mc S3Client interface implementation 245 // defining the client to be used 246 mcClient := mcClient{client: s3Client} 247 err = deleteBucketEventNotification(ctx, mcClient, arn, events, prefix, suffix) 248 if err != nil { 249 return ErrorWithContext(ctx, err) 250 } 251 return nil 252 }