mirror of
https://github.com/minio/minio.git
synced 2025-01-22 20:23:14 -05:00
api: BucketNotification should disallow duplicate notification. (#2539)
Added checks to look for duplicated notification configs. Fixes #2472
This commit is contained in:
parent
c39d3db7a0
commit
ec4260d260
@ -119,6 +119,7 @@ const (
|
|||||||
ErrFilterNamePrefix
|
ErrFilterNamePrefix
|
||||||
ErrFilterNameSuffix
|
ErrFilterNameSuffix
|
||||||
ErrFilterValueInvalid
|
ErrFilterValueInvalid
|
||||||
|
ErrOverlappingConfigs
|
||||||
|
|
||||||
// S3 extended errors.
|
// S3 extended errors.
|
||||||
ErrContentSHA256Mismatch
|
ErrContentSHA256Mismatch
|
||||||
@ -505,6 +506,11 @@ var errorCodeResponse = map[APIErrorCode]APIError{
|
|||||||
Description: "Size of filter rule value cannot exceed 1024 bytes in UTF-8 representation",
|
Description: "Size of filter rule value cannot exceed 1024 bytes in UTF-8 representation",
|
||||||
HTTPStatusCode: http.StatusBadRequest,
|
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.
|
/// S3 extensions.
|
||||||
ErrContentSHA256Mismatch: {
|
ErrContentSHA256Mismatch: {
|
||||||
|
@ -32,33 +32,30 @@ type keyFilter struct {
|
|||||||
FilterRules []filterRule `xml:"FilterRule,omitempty"`
|
FilterRules []filterRule `xml:"FilterRule,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Queue SQS configuration.
|
// Common elements of service notification.
|
||||||
type queueConfig struct {
|
type serviceConfig struct {
|
||||||
Events []string `xml:"Event"`
|
Events []string `xml:"Event"`
|
||||||
Filter struct {
|
Filter struct {
|
||||||
Key keyFilter `xml:"S3Key,omitempty"`
|
Key keyFilter `xml:"S3Key,omitempty"`
|
||||||
}
|
}
|
||||||
ID string `xml:"Id"`
|
ID string `xml:"Id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue SQS configuration.
|
||||||
|
type queueConfig struct {
|
||||||
|
serviceConfig
|
||||||
QueueARN string `xml:"Queue"`
|
QueueARN string `xml:"Queue"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Topic SNS configuration, this is a compliance field not used by minio yet.
|
// Topic SNS configuration, this is a compliance field not used by minio yet.
|
||||||
type topicConfig struct {
|
type topicConfig struct {
|
||||||
Events []string `xml:"Event"`
|
serviceConfig
|
||||||
Filter struct {
|
|
||||||
Key keyFilter `xml:"S3Key"`
|
|
||||||
}
|
|
||||||
ID string `xml:"Id"`
|
|
||||||
TopicARN string `xml:"Topic"`
|
TopicARN string `xml:"Topic"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lambda function configuration, this is a compliance field not used by minio yet.
|
// Lambda function configuration, this is a compliance field not used by minio yet.
|
||||||
type lambdaConfig struct {
|
type lambdaConfig struct {
|
||||||
Events []string `xml:"Event"`
|
serviceConfig
|
||||||
Filter struct {
|
|
||||||
Key keyFilter `xml:"S3Key,omitempty"`
|
|
||||||
}
|
|
||||||
ID string `xml:"Id"`
|
|
||||||
LambdaARN string `xml:"CloudFunction"`
|
LambdaARN string `xml:"CloudFunction"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,17 +266,68 @@ func validateTopicConfigs(topicConfigs []topicConfig) APIErrorCode {
|
|||||||
return ErrNone
|
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,
|
// Validates all the bucket notification configuration for their validity,
|
||||||
// if one of the config is malformed or has invalid data it is rejected.
|
// if one of the config is malformed or has invalid data it is rejected.
|
||||||
// Configuration is never applied partially.
|
// Configuration is never applied partially.
|
||||||
func validateNotificationConfig(nConfig notificationConfig) APIErrorCode {
|
func validateNotificationConfig(nConfig notificationConfig) APIErrorCode {
|
||||||
|
// Validate all queue configs.
|
||||||
if s3Error := validateQueueConfigs(nConfig.QueueConfigs); s3Error != ErrNone {
|
if s3Error := validateQueueConfigs(nConfig.QueueConfigs); s3Error != ErrNone {
|
||||||
return s3Error
|
return s3Error
|
||||||
}
|
}
|
||||||
|
// Validate all topic configs.
|
||||||
if s3Error := validateTopicConfigs(nConfig.TopicConfigs); s3Error != ErrNone {
|
if s3Error := validateTopicConfigs(nConfig.TopicConfigs); s3Error != ErrNone {
|
||||||
return s3Error
|
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.
|
// Add validation for other configurations.
|
||||||
return ErrNone
|
return ErrNone
|
||||||
}
|
}
|
||||||
|
@ -75,10 +75,9 @@ func (s *TestSuiteCommon) TestAuth(c *C) {
|
|||||||
c.Assert(len(accessID), Equals, minioAccessID)
|
c.Assert(len(accessID), Equals, minioAccessID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestBucketNotification - Inserts the bucket notification and
|
// TestBucketNotification - Inserts the bucket notification and verifies it by fetching the notification back.
|
||||||
// verifies it by fetching the notification back.
|
|
||||||
func (s *TestSuiteCommon) TestBucketNotification(c *C) {
|
func (s *TestSuiteCommon) TestBucketNotification(c *C) {
|
||||||
// Sample bucket notification
|
// Sample bucket notification.
|
||||||
bucketNotificationBuf := `<NotificationConfiguration><TopicConfiguration><Event>s3:ObjectCreated:Put</Event><Filter><S3Key><FilterRule><Name>prefix</Name><Value>images/</Value></FilterRule></S3Key></Filter><Id>1</Id><Topic>arn:minio:sns:us-east-1:444455556666:listen</Topic></TopicConfiguration></NotificationConfiguration>`
|
bucketNotificationBuf := `<NotificationConfiguration><TopicConfiguration><Event>s3:ObjectCreated:Put</Event><Filter><S3Key><FilterRule><Name>prefix</Name><Value>images/</Value></FilterRule></S3Key></Filter><Id>1</Id><Topic>arn:minio:sns:us-east-1:444455556666:listen</Topic></TopicConfiguration></NotificationConfiguration>`
|
||||||
|
|
||||||
// generate a random bucket Name.
|
// generate a random bucket Name.
|
||||||
@ -100,7 +99,7 @@ func (s *TestSuiteCommon) TestBucketNotification(c *C) {
|
|||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
client = http.Client{}
|
client = http.Client{}
|
||||||
// execute the HTTP request to create bucket.
|
// execute the HTTP request.
|
||||||
response, err = client.Do(request)
|
response, err = client.Do(request)
|
||||||
|
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
@ -128,7 +127,7 @@ func (s *TestSuiteCommon) TestBucketNotification(c *C) {
|
|||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
client = http.Client{}
|
client = http.Client{}
|
||||||
// execute the HTTP request to create bucket.
|
// execute the HTTP request.
|
||||||
response, err = client.Do(request)
|
response, err = client.Do(request)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
@ -140,7 +139,7 @@ func (s *TestSuiteCommon) TestBucketNotification(c *C) {
|
|||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
client = http.Client{}
|
client = http.Client{}
|
||||||
// execute the HTTP request to create bucket.
|
// execute the HTTP request.
|
||||||
response, err = client.Do(request)
|
response, err = client.Do(request)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
@ -152,10 +151,21 @@ func (s *TestSuiteCommon) TestBucketNotification(c *C) {
|
|||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
|
|
||||||
client = http.Client{}
|
client = http.Client{}
|
||||||
// execute the HTTP request to create bucket.
|
// execute the HTTP request.
|
||||||
response, err = client.Do(request)
|
response, err = client.Do(request)
|
||||||
c.Assert(err, IsNil)
|
c.Assert(err, IsNil)
|
||||||
verifyError(c, response, "InvalidArgument", "A specified event is not supported for notifications.", http.StatusBadRequest)
|
verifyError(c, response, "InvalidArgument", "A specified event is not supported for notifications.", http.StatusBadRequest)
|
||||||
|
|
||||||
|
bucketNotificationDuplicates := `<NotificationConfiguration><TopicConfiguration><Event>s3:ObjectCreated:Put</Event><Filter><S3Key><FilterRule><Name>prefix</Name><Value>images/</Value></FilterRule></S3Key></Filter><Id>1</Id><Topic>arn:minio:sns:us-east-1:444455556666:listen</Topic></TopicConfiguration><TopicConfiguration><Event>s3:ObjectCreated:Put</Event><Filter><S3Key><FilterRule><Name>prefix</Name><Value>images/</Value></FilterRule></S3Key></Filter><Id>1</Id><Topic>arn:minio:sns:us-east-1:444455556666:listen</Topic></TopicConfiguration></NotificationConfiguration>`
|
||||||
|
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.
|
// TestBucketPolicy - Inserts the bucket policy and verifies it by fetching the policy back.
|
||||||
|
@ -1840,10 +1840,6 @@ var DB = map[string]struct {
|
|||||||
ContentType: "image/vnd.ms-modi",
|
ContentType: "image/vnd.ms-modi",
|
||||||
Compressible: false,
|
Compressible: false,
|
||||||
},
|
},
|
||||||
"mdp": {
|
|
||||||
ContentType: "application/dash+xml",
|
|
||||||
Compressible: false,
|
|
||||||
},
|
|
||||||
"me": {
|
"me": {
|
||||||
ContentType: "text/troff",
|
ContentType: "text/troff",
|
||||||
Compressible: false,
|
Compressible: false,
|
||||||
@ -2008,6 +2004,10 @@ var DB = map[string]struct {
|
|||||||
ContentType: "application/vnd.mophun.certificate",
|
ContentType: "application/vnd.mophun.certificate",
|
||||||
Compressible: false,
|
Compressible: false,
|
||||||
},
|
},
|
||||||
|
"mpd": {
|
||||||
|
ContentType: "application/dash+xml",
|
||||||
|
Compressible: false,
|
||||||
|
},
|
||||||
"mpe": {
|
"mpe": {
|
||||||
ContentType: "video/mpeg",
|
ContentType: "video/mpeg",
|
||||||
Compressible: false,
|
Compressible: false,
|
||||||
@ -2824,6 +2824,10 @@ var DB = map[string]struct {
|
|||||||
ContentType: "application/relax-ng-compact-syntax",
|
ContentType: "application/relax-ng-compact-syntax",
|
||||||
Compressible: false,
|
Compressible: false,
|
||||||
},
|
},
|
||||||
|
"rng": {
|
||||||
|
ContentType: "application/xml",
|
||||||
|
Compressible: false,
|
||||||
|
},
|
||||||
"roa": {
|
"roa": {
|
||||||
ContentType: "application/rpki-roa",
|
ContentType: "application/rpki-roa",
|
||||||
Compressible: false,
|
Compressible: false,
|
||||||
@ -3088,6 +3092,14 @@ var DB = map[string]struct {
|
|||||||
ContentType: "application/vnd.openxmlformats-officedocument.presentationml.slide",
|
ContentType: "application/vnd.openxmlformats-officedocument.presentationml.slide",
|
||||||
Compressible: false,
|
Compressible: false,
|
||||||
},
|
},
|
||||||
|
"slim": {
|
||||||
|
ContentType: "text/slim",
|
||||||
|
Compressible: false,
|
||||||
|
},
|
||||||
|
"slm": {
|
||||||
|
ContentType: "text/slim",
|
||||||
|
Compressible: false,
|
||||||
|
},
|
||||||
"slt": {
|
"slt": {
|
||||||
ContentType: "application/vnd.epson.salt",
|
ContentType: "application/vnd.epson.salt",
|
||||||
Compressible: false,
|
Compressible: false,
|
||||||
|
@ -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");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@ -14,24 +14,18 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package mimedb_test
|
package mimedb
|
||||||
|
|
||||||
import (
|
import "testing"
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/mimedb"
|
func TestMimeLookup(t *testing.T) {
|
||||||
|
// Test mimeLookup.
|
||||||
. "gopkg.in/check.v1"
|
contentType := DB["txt"].ContentType
|
||||||
)
|
if contentType != "text/plain" {
|
||||||
|
t.Fatalf("Invalid content type are found expected \"application/x-msdownload\", got %s", contentType)
|
||||||
func Test(t *testing.T) { TestingT(t) }
|
}
|
||||||
|
compressible := DB["txt"].Compressible
|
||||||
type MySuite struct{}
|
if compressible != false {
|
||||||
|
t.Fatalf("Invalid content type are found expected \"false\", got %t", compressible)
|
||||||
var _ = Suite(&MySuite{})
|
}
|
||||||
|
|
||||||
func (s *MySuite) TestLookup(c *C) {
|
|
||||||
// Test MustLookup.
|
|
||||||
contentType := mimedb.DB["exe"].ContentType
|
|
||||||
c.Assert(contentType, Not(Equals), "")
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user