mirror of
				https://github.com/minio/minio.git
				synced 2025-10-29 15:55:00 -04:00 
			
		
		
		
	Fix validation in PutBucketNotification handler (#4841)
Fixes #4813 If a TopicConfiguration element or CloudFunction element is found in configuration submitted to PutBucketNotification API, an BadRequest error is returned.
This commit is contained in:
		
							parent
							
								
									3a73c675a6
								
							
						
					
					
						commit
						77d2870f5b
					
				| @ -129,6 +129,7 @@ const ( | ||||
| 	ErrFilterNameSuffix | ||||
| 	ErrFilterValueInvalid | ||||
| 	ErrOverlappingConfigs | ||||
| 	ErrUnsupportedNotification | ||||
| 
 | ||||
| 	// S3 extended errors. | ||||
| 	ErrContentSHA256Mismatch | ||||
| @ -552,6 +553,11 @@ var errorCodeResponse = map[APIErrorCode]APIError{ | ||||
| 		Description:    "Configurations overlap. Configurations on the same bucket cannot share a common event type.", | ||||
| 		HTTPStatusCode: http.StatusBadRequest, | ||||
| 	}, | ||||
| 	ErrUnsupportedNotification: { | ||||
| 		Code:           "UnsupportedNotification", | ||||
| 		Description:    "Minio server does not support Topic or Cloud Function based notifications.", | ||||
| 		HTTPStatusCode: http.StatusBadRequest, | ||||
| 	}, | ||||
| 	ErrInvalidCopyPartRange: { | ||||
| 		Code:           "InvalidArgument", | ||||
| 		Description:    "The x-amz-copy-source-range value must be of the form bytes=first-last where first and last are the zero-based offsets of the first and last bytes to copy", | ||||
|  | ||||
| @ -67,6 +67,7 @@ type notificationConfig struct { | ||||
| 	XMLName       xml.Name       `xml:"NotificationConfiguration"` | ||||
| 	QueueConfigs  []queueConfig  `xml:"QueueConfiguration"` | ||||
| 	LambdaConfigs []lambdaConfig `xml:"CloudFunctionConfiguration"` | ||||
| 	TopicConfigs  []topicConfig  `xml:"TopicConfiguration"` | ||||
| } | ||||
| 
 | ||||
| // listenerConfig structure represents run-time notification | ||||
|  | ||||
| @ -247,6 +247,95 @@ func testGetBucketNotificationHandler(obj ObjectLayer, instanceType, bucketName | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestPutBucketNotificationHandler(t *testing.T) { | ||||
| 	ExecObjectLayerAPITest(t, testPutBucketNotificationHandler, []string{ | ||||
| 		"PutBucketNotification", | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func testPutBucketNotificationHandler(obj ObjectLayer, instanceType, | ||||
| 	bucketName string, apiRouter http.Handler, credentials credential, | ||||
| 	t *testing.T) { | ||||
| 
 | ||||
| 	// declare sample configs | ||||
| 	filterRules := []filterRule{ | ||||
| 		{ | ||||
| 			Name:  "prefix", | ||||
| 			Value: "minio", | ||||
| 		}, | ||||
| 		{ | ||||
| 			Name:  "suffix", | ||||
| 			Value: "*.jpg", | ||||
| 		}, | ||||
| 	} | ||||
| 	sampleSvcCfg := ServiceConfig{ | ||||
| 		[]string{"s3:ObjectRemoved:*", "s3:ObjectCreated:*"}, | ||||
| 		filterStruct{ | ||||
| 			keyFilter{filterRules}, | ||||
| 		}, | ||||
| 		"1", | ||||
| 	} | ||||
| 	sampleNotifCfg := notificationConfig{ | ||||
| 		QueueConfigs: []queueConfig{ | ||||
| 			{ | ||||
| 				ServiceConfig: sampleSvcCfg, | ||||
| 				QueueARN:      "testqARN", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
| 		sampleNotifCfg.LambdaConfigs = []lambdaConfig{ | ||||
| 			{ | ||||
| 				sampleSvcCfg, "testLARN", | ||||
| 			}, | ||||
| 		} | ||||
| 		xmlBytes, err := xml.Marshal(sampleNotifCfg) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("%s: Unexpected err: %#v", instanceType, err) | ||||
| 		} | ||||
| 		rec := httptest.NewRecorder() | ||||
| 		req, err := newTestSignedRequestV4("PUT", | ||||
| 			getPutBucketNotificationURL("", bucketName), | ||||
| 			int64(len(xmlBytes)), bytes.NewReader(xmlBytes), | ||||
| 			credentials.AccessKey, credentials.SecretKey) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("%s: Failed to create HTTP testRequest for PutBucketNotification: <ERROR> %v", | ||||
| 				instanceType, err) | ||||
| 		} | ||||
| 		apiRouter.ServeHTTP(rec, req) | ||||
| 		if rec.Code != http.StatusBadRequest { | ||||
| 			t.Fatalf("Unexpected http response %d", rec.Code) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
| 		sampleNotifCfg.LambdaConfigs = nil | ||||
| 		sampleNotifCfg.TopicConfigs = []topicConfig{ | ||||
| 			{ | ||||
| 				sampleSvcCfg, "testTARN", | ||||
| 			}, | ||||
| 		} | ||||
| 		xmlBytes, err := xml.Marshal(sampleNotifCfg) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("%s: Unexpected err: %#v", instanceType, err) | ||||
| 		} | ||||
| 		rec := httptest.NewRecorder() | ||||
| 		req, err := newTestSignedRequestV4("PUT", | ||||
| 			getPutBucketNotificationURL("", bucketName), | ||||
| 			int64(len(xmlBytes)), bytes.NewReader(xmlBytes), | ||||
| 			credentials.AccessKey, credentials.SecretKey) | ||||
| 		if err != nil { | ||||
| 			t.Fatalf("%s: Failed to create HTTP testRequest for PutBucketNotification: <ERROR> %v", | ||||
| 				instanceType, err) | ||||
| 		} | ||||
| 		apiRouter.ServeHTTP(rec, req) | ||||
| 		if rec.Code != http.StatusBadRequest { | ||||
| 			t.Fatalf("Unexpected http response %d", rec.Code) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestListenBucketNotificationNilHandler(t *testing.T) { | ||||
| 	ExecObjectLayerAPITest(t, testListenBucketNotificationNilHandler, []string{ | ||||
| 		"ListenBucketNotification", | ||||
| @ -281,26 +370,28 @@ func testListenBucketNotificationNilHandler(obj ObjectLayer, instanceType, bucke | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func testRemoveNotificationConfig(obj ObjectLayer, instanceType, bucketName string, apiRouter http.Handler, | ||||
| 	credentials credential, t *testing.T) { | ||||
| func testRemoveNotificationConfig(obj ObjectLayer, instanceType, | ||||
| 	bucketName string, apiRouter http.Handler, credentials credential, | ||||
| 	t *testing.T) { | ||||
| 
 | ||||
| 	invalidBucket := "Invalid\\Bucket" | ||||
| 	// get random bucket name. | ||||
| 	randBucket := bucketName | ||||
| 
 | ||||
| 	sampleNotificationBytes := []byte("<NotificationConfiguration><TopicConfiguration>" + | ||||
| 		"<Event>s3:ObjectCreated:*</Event><Event>s3:ObjectRemoved:*</Event><Filter>" + | ||||
| 		"<S3Key></S3Key></Filter><Id></Id><Topic>arn:minio:sns:us-east-1:1474332374:listen</Topic>" + | ||||
| 		"</TopicConfiguration></NotificationConfiguration>") | ||||
| 
 | ||||
| 	// Set sample bucket notification on randBucket. | ||||
| 	testRec := httptest.NewRecorder() | ||||
| 	testReq, tErr := newTestSignedRequestV4("PUT", getPutBucketNotificationURL("", randBucket), | ||||
| 		int64(len(sampleNotificationBytes)), bytes.NewReader(sampleNotificationBytes), | ||||
| 		credentials.AccessKey, credentials.SecretKey) | ||||
| 	if tErr != nil { | ||||
| 		t.Fatalf("%s: Failed to create HTTP testRequest for PutBucketNotification: <ERROR> %v", instanceType, tErr) | ||||
| 	nCfg := notificationConfig{ | ||||
| 		QueueConfigs: []queueConfig{ | ||||
| 			{ | ||||
| 				ServiceConfig: ServiceConfig{ | ||||
| 					Events: []string{"s3:ObjectRemoved:*", | ||||
| 						"s3:ObjectCreated:*"}, | ||||
| 				}, | ||||
| 				QueueARN: "testqARN", | ||||
| 			}, | ||||
| 		}, | ||||
| 	} | ||||
| 	if err := persistNotificationConfig(randBucket, &nCfg, obj); err != nil { | ||||
| 		t.Fatalf("Unexpected error: %#v", err) | ||||
| 	} | ||||
| 	apiRouter.ServeHTTP(testRec, testReq) | ||||
| 
 | ||||
| 	testCases := []struct { | ||||
| 		bucketName  string | ||||
|  | ||||
| @ -235,6 +235,12 @@ func checkDuplicateQueueConfigs(configs []queueConfig) APIErrorCode { | ||||
| // if one of the config is malformed or has invalid data it is rejected. | ||||
| // Configuration is never applied partially. | ||||
| func validateNotificationConfig(nConfig notificationConfig) APIErrorCode { | ||||
| 	// Minio server does not support lambda/topic configurations | ||||
| 	// currently. Such configuration is rejected. | ||||
| 	if len(nConfig.LambdaConfigs) > 0 || len(nConfig.TopicConfigs) > 0 { | ||||
| 		return ErrUnsupportedNotification | ||||
| 	} | ||||
| 
 | ||||
| 	// Validate all queue configs. | ||||
| 	if s3Error := validateQueueConfigs(nConfig.QueueConfigs); s3Error != ErrNone { | ||||
| 		return s3Error | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user