From ec4260d260a51719e7acff463e4bae4ebc9c57cb Mon Sep 17 00:00:00 2001 From: Harshavardhana Date: Tue, 23 Aug 2016 18:42:30 -0700 Subject: [PATCH] api: BucketNotification should disallow duplicate notification. (#2539) Added checks to look for duplicated notification configs. Fixes #2472 --- cmd/api-errors.go | 6 ++++ cmd/bucket-notification-datatypes.go | 23 ++++++------- cmd/bucket-notification-utils.go | 51 ++++++++++++++++++++++++++++ cmd/server_test.go | 24 +++++++++---- pkg/mimedb/db.go | 20 ++++++++--- pkg/mimedb/db_test.go | 32 +++++++---------- 6 files changed, 113 insertions(+), 43 deletions(-) diff --git a/cmd/api-errors.go b/cmd/api-errors.go index 89b791935..e8a9cf46a 100644 --- a/cmd/api-errors.go +++ b/cmd/api-errors.go @@ -119,6 +119,7 @@ const ( ErrFilterNamePrefix ErrFilterNameSuffix ErrFilterValueInvalid + ErrOverlappingConfigs // S3 extended errors. ErrContentSHA256Mismatch @@ -505,6 +506,11 @@ var errorCodeResponse = map[APIErrorCode]APIError{ Description: "Size of filter rule value cannot exceed 1024 bytes in UTF-8 representation", HTTPStatusCode: http.StatusBadRequest, }, + ErrOverlappingConfigs: { + Code: "InvalidArgument", + Description: "Configurations overlap. Configurations on the same bucket cannot share a common event type.", + HTTPStatusCode: http.StatusBadRequest, + }, /// S3 extensions. ErrContentSHA256Mismatch: { diff --git a/cmd/bucket-notification-datatypes.go b/cmd/bucket-notification-datatypes.go index 070a4d34c..79532dd22 100644 --- a/cmd/bucket-notification-datatypes.go +++ b/cmd/bucket-notification-datatypes.go @@ -32,33 +32,30 @@ type keyFilter struct { FilterRules []filterRule `xml:"FilterRule,omitempty"` } -// Queue SQS configuration. -type queueConfig struct { +// Common elements of service notification. +type serviceConfig struct { Events []string `xml:"Event"` Filter struct { Key keyFilter `xml:"S3Key,omitempty"` } - ID string `xml:"Id"` + ID string `xml:"Id"` +} + +// Queue SQS configuration. +type queueConfig struct { + serviceConfig QueueARN string `xml:"Queue"` } // Topic SNS configuration, this is a compliance field not used by minio yet. type topicConfig struct { - Events []string `xml:"Event"` - Filter struct { - Key keyFilter `xml:"S3Key"` - } - ID string `xml:"Id"` + serviceConfig TopicARN string `xml:"Topic"` } // Lambda function configuration, this is a compliance field not used by minio yet. type lambdaConfig struct { - Events []string `xml:"Event"` - Filter struct { - Key keyFilter `xml:"S3Key,omitempty"` - } - ID string `xml:"Id"` + serviceConfig LambdaARN string `xml:"CloudFunction"` } diff --git a/cmd/bucket-notification-utils.go b/cmd/bucket-notification-utils.go index 3df2d0687..27930440c 100644 --- a/cmd/bucket-notification-utils.go +++ b/cmd/bucket-notification-utils.go @@ -266,17 +266,68 @@ func validateTopicConfigs(topicConfigs []topicConfig) APIErrorCode { return ErrNone } +// Check all the queue configs for any duplicates. +func checkDuplicateQueueConfigs(configs []queueConfig) APIErrorCode { + configMaps := make(map[string]int) + + // Navigate through each configs and count the entries. + for _, config := range configs { + configMaps[config.QueueARN]++ + } + + // Validate if there are any duplicate counts. + for _, count := range configMaps { + if count != 1 { + return ErrOverlappingConfigs + } + } + + // Success. + return ErrNone +} + +// Check all the topic configs for any duplicates. +func checkDuplicateTopicConfigs(configs []topicConfig) APIErrorCode { + configMaps := make(map[string]int) + + // Navigate through each configs and count the entries. + for _, config := range configs { + configMaps[config.TopicARN]++ + } + + // Validate if there are any duplicate counts. + for _, count := range configMaps { + if count != 1 { + return ErrOverlappingConfigs + } + } + + // Success. + return ErrNone +} + // Validates all the bucket notification configuration for their validity, // if one of the config is malformed or has invalid data it is rejected. // Configuration is never applied partially. func validateNotificationConfig(nConfig notificationConfig) APIErrorCode { + // Validate all queue configs. if s3Error := validateQueueConfigs(nConfig.QueueConfigs); s3Error != ErrNone { return s3Error } + // Validate all topic configs. if s3Error := validateTopicConfigs(nConfig.TopicConfigs); s3Error != ErrNone { return s3Error } + // Check for duplicate queue configs. + if s3Error := checkDuplicateQueueConfigs(nConfig.QueueConfigs); s3Error != ErrNone { + return s3Error + } + // Check for duplicate topic configs. + if s3Error := checkDuplicateTopicConfigs(nConfig.TopicConfigs); s3Error != ErrNone { + return s3Error + } + // Add validation for other configurations. return ErrNone } diff --git a/cmd/server_test.go b/cmd/server_test.go index 6a3426bd1..78784ba54 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -75,10 +75,9 @@ func (s *TestSuiteCommon) TestAuth(c *C) { c.Assert(len(accessID), Equals, minioAccessID) } -// TestBucketNotification - Inserts the bucket notification and -// verifies it by fetching the notification back. +// TestBucketNotification - Inserts the bucket notification and verifies it by fetching the notification back. func (s *TestSuiteCommon) TestBucketNotification(c *C) { - // Sample bucket notification + // Sample bucket notification. bucketNotificationBuf := `s3:ObjectCreated:Putprefiximages/1arn:minio:sns:us-east-1:444455556666:listen` // generate a random bucket Name. @@ -100,7 +99,7 @@ func (s *TestSuiteCommon) TestBucketNotification(c *C) { c.Assert(err, IsNil) client = http.Client{} - // execute the HTTP request to create bucket. + // execute the HTTP request. response, err = client.Do(request) c.Assert(err, IsNil) @@ -128,7 +127,7 @@ func (s *TestSuiteCommon) TestBucketNotification(c *C) { c.Assert(err, IsNil) client = http.Client{} - // execute the HTTP request to create bucket. + // execute the HTTP request. response, err = client.Do(request) c.Assert(err, IsNil) @@ -140,7 +139,7 @@ func (s *TestSuiteCommon) TestBucketNotification(c *C) { c.Assert(err, IsNil) client = http.Client{} - // execute the HTTP request to create bucket. + // execute the HTTP request. response, err = client.Do(request) c.Assert(err, IsNil) @@ -152,10 +151,21 @@ func (s *TestSuiteCommon) TestBucketNotification(c *C) { c.Assert(err, IsNil) client = http.Client{} - // execute the HTTP request to create bucket. + // execute the HTTP request. response, err = client.Do(request) c.Assert(err, IsNil) verifyError(c, response, "InvalidArgument", "A specified event is not supported for notifications.", http.StatusBadRequest) + + bucketNotificationDuplicates := `s3:ObjectCreated:Putprefiximages/1arn:minio:sns:us-east-1:444455556666:listens3:ObjectCreated:Putprefiximages/1arn:minio:sns:us-east-1:444455556666:listen` + request, err = newTestSignedRequest("PUT", getPutNotificationURL(s.endPoint, bucketName), + int64(len(bucketNotificationDuplicates)), bytes.NewReader([]byte(bucketNotificationDuplicates)), s.accessKey, s.secretKey) + c.Assert(err, IsNil) + + client = http.Client{} + // execute the HTTP request. + response, err = client.Do(request) + c.Assert(err, IsNil) + verifyError(c, response, "InvalidArgument", "Configurations overlap. Configurations on the same bucket cannot share a common event type.", http.StatusBadRequest) } // TestBucketPolicy - Inserts the bucket policy and verifies it by fetching the policy back. diff --git a/pkg/mimedb/db.go b/pkg/mimedb/db.go index c73ee4f23..5a8b61743 100644 --- a/pkg/mimedb/db.go +++ b/pkg/mimedb/db.go @@ -1840,10 +1840,6 @@ var DB = map[string]struct { ContentType: "image/vnd.ms-modi", Compressible: false, }, - "mdp": { - ContentType: "application/dash+xml", - Compressible: false, - }, "me": { ContentType: "text/troff", Compressible: false, @@ -2008,6 +2004,10 @@ var DB = map[string]struct { ContentType: "application/vnd.mophun.certificate", Compressible: false, }, + "mpd": { + ContentType: "application/dash+xml", + Compressible: false, + }, "mpe": { ContentType: "video/mpeg", Compressible: false, @@ -2824,6 +2824,10 @@ var DB = map[string]struct { ContentType: "application/relax-ng-compact-syntax", Compressible: false, }, + "rng": { + ContentType: "application/xml", + Compressible: false, + }, "roa": { ContentType: "application/rpki-roa", Compressible: false, @@ -3088,6 +3092,14 @@ var DB = map[string]struct { ContentType: "application/vnd.openxmlformats-officedocument.presentationml.slide", Compressible: false, }, + "slim": { + ContentType: "text/slim", + Compressible: false, + }, + "slm": { + ContentType: "text/slim", + Compressible: false, + }, "slt": { ContentType: "application/vnd.epson.salt", Compressible: false, diff --git a/pkg/mimedb/db_test.go b/pkg/mimedb/db_test.go index f630bfdb3..7ef73b65c 100644 --- a/pkg/mimedb/db_test.go +++ b/pkg/mimedb/db_test.go @@ -1,5 +1,5 @@ /* - * mime-db: Mime Database, (C) 2015 Minio, Inc. + * mime-db: Mime Database, (C) 2015, 2016 Minio, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,24 +14,18 @@ * limitations under the License. */ -package mimedb_test +package mimedb -import ( - "testing" +import "testing" - "github.com/minio/minio/pkg/mimedb" - - . "gopkg.in/check.v1" -) - -func Test(t *testing.T) { TestingT(t) } - -type MySuite struct{} - -var _ = Suite(&MySuite{}) - -func (s *MySuite) TestLookup(c *C) { - // Test MustLookup. - contentType := mimedb.DB["exe"].ContentType - c.Assert(contentType, Not(Equals), "") +func TestMimeLookup(t *testing.T) { + // Test mimeLookup. + contentType := DB["txt"].ContentType + if contentType != "text/plain" { + t.Fatalf("Invalid content type are found expected \"application/x-msdownload\", got %s", contentType) + } + compressible := DB["txt"].Compressible + if compressible != false { + t.Fatalf("Invalid content type are found expected \"false\", got %t", compressible) + } }