github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/bucket-policy-handlers_test.go (about) 1 // Copyright (c) 2015-2021 MinIO, Inc. 2 // 3 // This file is part of MinIO Object Storage stack 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package cmd 19 20 import ( 21 "bytes" 22 "fmt" 23 "io" 24 "net/http" 25 "net/http/httptest" 26 "reflect" 27 "strings" 28 "sync" 29 "testing" 30 31 "github.com/minio/minio/internal/auth" 32 "github.com/minio/pkg/v2/policy" 33 "github.com/minio/pkg/v2/policy/condition" 34 ) 35 36 func getAnonReadOnlyBucketPolicy(bucketName string) *policy.BucketPolicy { 37 return &policy.BucketPolicy{ 38 Version: policy.DefaultVersion, 39 Statements: []policy.BPStatement{ 40 policy.NewBPStatement( 41 "", 42 policy.Allow, 43 policy.NewPrincipal("*"), 44 policy.NewActionSet(policy.GetBucketLocationAction, policy.ListBucketAction), 45 policy.NewResourceSet(policy.NewResource(bucketName)), 46 condition.NewFunctions(), 47 ), 48 }, 49 } 50 } 51 52 func getAnonWriteOnlyBucketPolicy(bucketName string) *policy.BucketPolicy { 53 return &policy.BucketPolicy{ 54 Version: policy.DefaultVersion, 55 Statements: []policy.BPStatement{ 56 policy.NewBPStatement( 57 "", 58 policy.Allow, 59 policy.NewPrincipal("*"), 60 policy.NewActionSet( 61 policy.GetBucketLocationAction, 62 policy.ListBucketMultipartUploadsAction, 63 ), 64 policy.NewResourceSet(policy.NewResource(bucketName)), 65 condition.NewFunctions(), 66 ), 67 }, 68 } 69 } 70 71 func getAnonReadOnlyObjectPolicy(bucketName, prefix string) *policy.BucketPolicy { 72 return &policy.BucketPolicy{ 73 Version: policy.DefaultVersion, 74 Statements: []policy.BPStatement{ 75 policy.NewBPStatement( 76 "", 77 policy.Allow, 78 policy.NewPrincipal("*"), 79 policy.NewActionSet(policy.GetObjectAction), 80 policy.NewResourceSet(policy.NewResource(bucketName+"/"+prefix)), 81 condition.NewFunctions(), 82 ), 83 }, 84 } 85 } 86 87 func getAnonWriteOnlyObjectPolicy(bucketName, prefix string) *policy.BucketPolicy { 88 return &policy.BucketPolicy{ 89 Version: policy.DefaultVersion, 90 Statements: []policy.BPStatement{ 91 policy.NewBPStatement( 92 "", 93 policy.Allow, 94 policy.NewPrincipal("*"), 95 policy.NewActionSet( 96 policy.AbortMultipartUploadAction, 97 policy.DeleteObjectAction, 98 policy.ListMultipartUploadPartsAction, 99 policy.PutObjectAction, 100 ), 101 policy.NewResourceSet(policy.NewResource(bucketName+"/"+prefix)), 102 condition.NewFunctions(), 103 ), 104 }, 105 } 106 } 107 108 // Wrapper for calling Create Bucket and ensure we get one and only one success. 109 func TestCreateBucket(t *testing.T) { 110 ExecObjectLayerAPITest(t, testCreateBucket, []string{"MakeBucket"}) 111 } 112 113 // testCreateBucket - Test for calling Create Bucket and ensure we get one and only one success. 114 func testCreateBucket(obj ObjectLayer, instanceType, bucketName string, apiRouter http.Handler, 115 credentials auth.Credentials, t *testing.T, 116 ) { 117 bucketName1 := fmt.Sprintf("%s-1", bucketName) 118 119 const n = 100 120 start := make(chan struct{}) 121 var ok, errs int 122 var wg sync.WaitGroup 123 var mu sync.Mutex 124 wg.Add(n) 125 for i := 0; i < n; i++ { 126 go func() { 127 defer wg.Done() 128 // Sync start. 129 <-start 130 if err := obj.MakeBucket(GlobalContext, bucketName1, MakeBucketOptions{}); err != nil { 131 if _, ok := err.(BucketExists); !ok { 132 t.Logf("unexpected error: %T: %v", err, err) 133 return 134 } 135 mu.Lock() 136 errs++ 137 mu.Unlock() 138 return 139 } 140 mu.Lock() 141 ok++ 142 mu.Unlock() 143 }() 144 } 145 close(start) 146 wg.Wait() 147 if ok != 1 { 148 t.Fatalf("want 1 ok, got %d", ok) 149 } 150 if errs != n-1 { 151 t.Fatalf("want %d errors, got %d", n-1, errs) 152 } 153 } 154 155 // Wrapper for calling Put Bucket Policy HTTP handler tests for both Erasure multiple disks and single node setup. 156 func TestPutBucketPolicyHandler(t *testing.T) { 157 ExecObjectLayerAPITest(t, testPutBucketPolicyHandler, []string{"PutBucketPolicy"}) 158 } 159 160 // testPutBucketPolicyHandler - Test for Bucket policy end point. 161 func testPutBucketPolicyHandler(obj ObjectLayer, instanceType, bucketName string, apiRouter http.Handler, 162 credentials auth.Credentials, t *testing.T, 163 ) { 164 bucketName1 := fmt.Sprintf("%s-1", bucketName) 165 if err := obj.MakeBucket(GlobalContext, bucketName1, MakeBucketOptions{}); err != nil { 166 t.Fatal(err) 167 } 168 169 // template for constructing HTTP request body for PUT bucket policy. 170 bucketPolicyTemplate := `{"Version":"2012-10-17","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucket"],"Resource":["arn:aws:s3:::%s"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::%s/this*"]}]}` 171 172 bucketPolicyTemplateWithoutVersion := `{"Version":"","Statement":[{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation","s3:ListBucket"],"Resource":["arn:aws:s3:::%s"]},{"Sid":"","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::%s/this*"]}]}` 173 174 // test cases with sample input and expected output. 175 testCases := []struct { 176 bucketName string 177 // bucket policy to be set, 178 // set as request body. 179 bucketPolicyReader io.ReadSeeker 180 // length in bytes of the bucket policy being set. 181 policyLen int 182 accessKey string 183 secretKey string 184 // expected Response. 185 expectedRespStatus int 186 }{ 187 // Test case - 1. 188 { 189 bucketName: bucketName, 190 bucketPolicyReader: bytes.NewReader([]byte(fmt.Sprintf(bucketPolicyTemplate, bucketName, bucketName))), 191 192 policyLen: len(fmt.Sprintf(bucketPolicyTemplate, bucketName, bucketName)), 193 accessKey: credentials.AccessKey, 194 secretKey: credentials.SecretKey, 195 expectedRespStatus: http.StatusNoContent, 196 }, 197 // Test case - 2. 198 // Setting the content length to be more than max allowed size. 199 // Expecting StatusBadRequest (400). 200 { 201 bucketName: bucketName, 202 bucketPolicyReader: bytes.NewReader([]byte(fmt.Sprintf(bucketPolicyTemplate, bucketName, bucketName))), 203 204 policyLen: maxBucketPolicySize + 1, 205 accessKey: credentials.AccessKey, 206 secretKey: credentials.SecretKey, 207 expectedRespStatus: http.StatusBadRequest, 208 }, 209 // Test case - 3. 210 // Case with content-length of the HTTP request set to 0. 211 // Expecting the HTTP response status to be StatusLengthRequired (411). 212 { 213 bucketName: bucketName, 214 bucketPolicyReader: bytes.NewReader([]byte(fmt.Sprintf(bucketPolicyTemplate, bucketName, bucketName))), 215 216 policyLen: 0, 217 accessKey: credentials.AccessKey, 218 secretKey: credentials.SecretKey, 219 expectedRespStatus: http.StatusLengthRequired, 220 }, 221 // Test case - 4. 222 // setting the readSeeker to `nil`, bucket policy parser will fail. 223 { 224 bucketName: bucketName, 225 bucketPolicyReader: nil, 226 227 policyLen: 10, 228 accessKey: credentials.AccessKey, 229 secretKey: credentials.SecretKey, 230 expectedRespStatus: http.StatusBadRequest, 231 }, 232 // Test case - 5. 233 // setting the keys to be empty. 234 // Expecting statusForbidden. 235 { 236 bucketName: bucketName, 237 bucketPolicyReader: nil, 238 239 policyLen: 10, 240 accessKey: "", 241 secretKey: "", 242 expectedRespStatus: http.StatusForbidden, 243 }, 244 // Test case - 6. 245 // setting an invalid bucket policy. 246 // the bucket policy parser will fail. 247 { 248 bucketName: bucketName, 249 bucketPolicyReader: bytes.NewReader([]byte("dummy-policy")), 250 251 policyLen: len([]byte("dummy-policy")), 252 accessKey: credentials.AccessKey, 253 secretKey: credentials.SecretKey, 254 expectedRespStatus: http.StatusBadRequest, 255 }, 256 // Test case - 7. 257 // Different bucket name used in the HTTP request and the policy string. 258 // checkBucketPolicyResources should fail. 259 { 260 bucketName: bucketName1, 261 bucketPolicyReader: bytes.NewReader([]byte(fmt.Sprintf(bucketPolicyTemplate, bucketName, bucketName))), 262 263 policyLen: len(fmt.Sprintf(bucketPolicyTemplate, bucketName, bucketName)), 264 accessKey: credentials.AccessKey, 265 secretKey: credentials.SecretKey, 266 expectedRespStatus: http.StatusBadRequest, 267 }, 268 // Test case - 8. 269 // non-existent bucket is used. 270 // writing BucketPolicy should fail. 271 // should result in 404 StatusNotFound 272 { 273 bucketName: "non-existent-bucket", 274 bucketPolicyReader: bytes.NewReader([]byte(fmt.Sprintf(bucketPolicyTemplate, "non-existent-bucket", "non-existent-bucket"))), 275 276 policyLen: len(fmt.Sprintf(bucketPolicyTemplate, bucketName, bucketName)), 277 accessKey: credentials.AccessKey, 278 secretKey: credentials.SecretKey, 279 expectedRespStatus: http.StatusNotFound, 280 }, 281 // Test case - 9. 282 // non-existent bucket is used (with invalid bucket name) 283 // writing BucketPolicy should fail. 284 // should result in 404 StatusNotFound 285 { 286 bucketName: ".invalid-bucket", 287 bucketPolicyReader: bytes.NewReader([]byte(fmt.Sprintf(bucketPolicyTemplate, ".invalid-bucket", ".invalid-bucket"))), 288 289 policyLen: len(fmt.Sprintf(bucketPolicyTemplate, bucketName, bucketName)), 290 accessKey: credentials.AccessKey, 291 secretKey: credentials.SecretKey, 292 expectedRespStatus: http.StatusBadRequest, 293 }, 294 // Test case - 10. 295 // Existent bucket with policy with Version field empty. 296 // writing BucketPolicy should fail. 297 // should result in 400 StatusBadRequest. 298 { 299 bucketName: bucketName, 300 bucketPolicyReader: bytes.NewReader([]byte(fmt.Sprintf(bucketPolicyTemplateWithoutVersion, bucketName, bucketName))), 301 302 policyLen: len(fmt.Sprintf(bucketPolicyTemplateWithoutVersion, bucketName, bucketName)), 303 accessKey: credentials.AccessKey, 304 secretKey: credentials.SecretKey, 305 expectedRespStatus: http.StatusBadRequest, 306 }, 307 } 308 309 // Iterating over the test cases, calling the function under test and asserting the response. 310 for i, testCase := range testCases { 311 // obtain the put bucket policy request body. 312 // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. 313 recV4 := httptest.NewRecorder() 314 // construct HTTP request for PUT bucket policy endpoint. 315 reqV4, err := newTestSignedRequestV4(http.MethodPut, getPutPolicyURL("", testCase.bucketName), 316 int64(testCase.policyLen), testCase.bucketPolicyReader, testCase.accessKey, testCase.secretKey, nil) 317 if err != nil { 318 t.Fatalf("Test %d: %s: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, instanceType, err) 319 } 320 // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. 321 // Call the ServeHTTP to execute the handler. 322 apiRouter.ServeHTTP(recV4, reqV4) 323 if recV4.Code != testCase.expectedRespStatus { 324 t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, recV4.Code) 325 } 326 // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. 327 recV2 := httptest.NewRecorder() 328 // construct HTTP request for PUT bucket policy endpoint. 329 reqV2, err := newTestSignedRequestV2(http.MethodPut, getPutPolicyURL("", testCase.bucketName), 330 int64(testCase.policyLen), testCase.bucketPolicyReader, testCase.accessKey, testCase.secretKey, nil) 331 if err != nil { 332 t.Fatalf("Test %d: %s: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, instanceType, err) 333 } 334 // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. 335 // Call the ServeHTTP to execute the handler. 336 apiRouter.ServeHTTP(recV2, reqV2) 337 if recV2.Code != testCase.expectedRespStatus { 338 t.Errorf("Test %d: %s: Expected the response status to be `%d`, but instead found `%d`", i+1, instanceType, testCase.expectedRespStatus, recV2.Code) 339 } 340 } 341 342 // Test for Anonymous/unsigned http request. 343 // Bucket policy related functions doesn't support anonymous requests, setting policies shouldn't make a difference. 344 bucketPolicyStr := fmt.Sprintf(bucketPolicyTemplate, bucketName, bucketName) 345 // create unsigned HTTP request for PutBucketPolicyHandler. 346 anonReq, err := newTestRequest(http.MethodPut, getPutPolicyURL("", bucketName), 347 int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr))) 348 if err != nil { 349 t.Fatalf("MinIO %s: Failed to create an anonymous request for bucket \"%s\": <ERROR> %v", 350 instanceType, bucketName, err) 351 } 352 353 // ExecObjectLayerAPIAnonTest - Calls the HTTP API handler using the anonymous request, validates the ErrAccessDeniedResponse, 354 // sets the bucket policy using the policy statement generated from `getWriteOnlyObjectStatement` so that the 355 // unsigned request goes through and its validated again. 356 ExecObjectLayerAPIAnonTest(t, obj, "PutBucketPolicyHandler", bucketName, "", instanceType, apiRouter, anonReq, getAnonWriteOnlyBucketPolicy(bucketName)) 357 358 // HTTP request for testing when `objectLayer` is set to `nil`. 359 // There is no need to use an existing bucket and valid input for creating the request 360 // since the `objectLayer==nil` check is performed before any other checks inside the handlers. 361 // The only aim is to generate an HTTP request in a way that the relevant/registered end point is evoked/called. 362 nilBucket := "dummy-bucket" 363 364 nilReq, err := newTestSignedRequestV4(http.MethodPut, getPutPolicyURL("", nilBucket), 365 0, nil, "", "", nil) 366 if err != nil { 367 t.Errorf("MinIO %s: Failed to create HTTP request for testing the response when object Layer is set to `nil`.", instanceType) 368 } 369 // execute the object layer set to `nil` test. 370 // `ExecObjectLayerAPINilTest` manages the operation. 371 ExecObjectLayerAPINilTest(t, nilBucket, "", instanceType, apiRouter, nilReq) 372 } 373 374 // Wrapper for calling Get Bucket Policy HTTP handler tests for both Erasure multiple disks and single node setup. 375 func TestGetBucketPolicyHandler(t *testing.T) { 376 ExecObjectLayerAPITest(t, testGetBucketPolicyHandler, []string{"PutBucketPolicy", "GetBucketPolicy"}) 377 } 378 379 // testGetBucketPolicyHandler - Test for end point which fetches the access policy json of the given bucket. 380 func testGetBucketPolicyHandler(obj ObjectLayer, instanceType, bucketName string, apiRouter http.Handler, 381 credentials auth.Credentials, t *testing.T, 382 ) { 383 // template for constructing HTTP request body for PUT bucket policy. 384 bucketPolicyTemplate := `{"Version":"2012-10-17","Statement":[{"Action":["s3:GetBucketLocation","s3:ListBucket"],"Effect":"Allow","Principal":{"AWS":["*"]},"Resource":["arn:aws:s3:::%s"]},{"Action":["s3:GetObject"],"Effect":"Allow","Principal":{"AWS":["*"]},"Resource":["arn:aws:s3:::%s/this*"]}]}` 385 386 // Writing bucket policy before running test on GetBucketPolicy. 387 putTestPolicies := []struct { 388 bucketName string 389 accessKey string 390 secretKey string 391 // expected Response. 392 expectedRespStatus int 393 }{ 394 {bucketName, credentials.AccessKey, credentials.SecretKey, http.StatusNoContent}, 395 } 396 397 // Iterating over the cases and writing the bucket policy. 398 // its required to write the policies first before running tests on GetBucketPolicy. 399 for i, testPolicy := range putTestPolicies { 400 // obtain the put bucket policy request body. 401 bucketPolicyStr := fmt.Sprintf(bucketPolicyTemplate, testPolicy.bucketName, testPolicy.bucketName) 402 // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. 403 recV4 := httptest.NewRecorder() 404 // construct HTTP request for PUT bucket policy endpoint. 405 reqV4, err := newTestSignedRequestV4(http.MethodPut, getPutPolicyURL("", testPolicy.bucketName), 406 int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testPolicy.accessKey, testPolicy.secretKey, nil) 407 if err != nil { 408 t.Fatalf("Test %d: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, err) 409 } 410 // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. 411 // Call the ServeHTTP to execute the handler. 412 apiRouter.ServeHTTP(recV4, reqV4) 413 if recV4.Code != testPolicy.expectedRespStatus { 414 t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testPolicy.expectedRespStatus, recV4.Code) 415 } 416 // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. 417 recV2 := httptest.NewRecorder() 418 // construct HTTP request for PUT bucket policy endpoint. 419 reqV2, err := newTestSignedRequestV2(http.MethodPut, getPutPolicyURL("", testPolicy.bucketName), 420 int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testPolicy.accessKey, testPolicy.secretKey, nil) 421 if err != nil { 422 t.Fatalf("Test %d: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, err) 423 } 424 // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. 425 // Call the ServeHTTP to execute the handler. 426 apiRouter.ServeHTTP(recV2, reqV2) 427 if recV2.Code != testPolicy.expectedRespStatus { 428 t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testPolicy.expectedRespStatus, recV2.Code) 429 } 430 } 431 432 // test cases with inputs and expected result for GetBucketPolicyHandler. 433 testCases := []struct { 434 bucketName string 435 accessKey string 436 secretKey string 437 // expected output. 438 expectedBucketPolicy string 439 expectedRespStatus int 440 }{ 441 // Test case - 1. 442 // Case which valid inputs, expected to return success status of 200OK. 443 { 444 bucketName: bucketName, 445 accessKey: credentials.AccessKey, 446 secretKey: credentials.SecretKey, 447 expectedBucketPolicy: bucketPolicyTemplate, 448 expectedRespStatus: http.StatusOK, 449 }, 450 // Test case - 2. 451 // Case with non-existent bucket name. 452 { 453 bucketName: "non-existent-bucket", 454 accessKey: credentials.AccessKey, 455 secretKey: credentials.SecretKey, 456 expectedBucketPolicy: bucketPolicyTemplate, 457 expectedRespStatus: http.StatusNotFound, 458 }, 459 // Test case - 3. 460 // Case with non-existent bucket name. 461 { 462 bucketName: ".invalid-bucket-name", 463 accessKey: credentials.AccessKey, 464 secretKey: credentials.SecretKey, 465 expectedBucketPolicy: "", 466 expectedRespStatus: http.StatusBadRequest, 467 }, 468 } 469 // Iterating over the cases, fetching the policy and validating the response. 470 for i, testCase := range testCases { 471 // expected bucket policy json string. 472 expectedBucketPolicyStr := fmt.Sprintf(testCase.expectedBucketPolicy, testCase.bucketName, testCase.bucketName) 473 // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. 474 recV4 := httptest.NewRecorder() 475 // construct HTTP request for PUT bucket policy endpoint. 476 reqV4, err := newTestSignedRequestV4(http.MethodGet, getGetPolicyURL("", testCase.bucketName), 477 0, nil, testCase.accessKey, testCase.secretKey, nil) 478 if err != nil { 479 t.Fatalf("Test %d: Failed to create HTTP request for GetBucketPolicyHandler: <ERROR> %v", i+1, err) 480 } 481 // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. 482 // Call the ServeHTTP to execute the handler, GetBucketPolicyHandler handles the request. 483 apiRouter.ServeHTTP(recV4, reqV4) 484 // Assert the response code with the expected status. 485 if recV4.Code != testCase.expectedRespStatus { 486 t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testCase.expectedRespStatus, recV4.Code) 487 } 488 // read the response body. 489 bucketPolicyReadBuf, err := io.ReadAll(recV4.Body) 490 if err != nil { 491 t.Fatalf("Test %d: %s: Failed parsing response body: <ERROR> %v", i+1, instanceType, err) 492 } 493 494 if recV4.Code != testCase.expectedRespStatus { 495 // Verify whether the bucket policy fetched is same as the one inserted. 496 var expectedPolicy *policy.BucketPolicy 497 expectedPolicy, err = policy.ParseBucketPolicyConfig(strings.NewReader(expectedBucketPolicyStr), testCase.bucketName) 498 if err != nil { 499 t.Fatalf("unexpected error. %v", err) 500 } 501 var gotPolicy *policy.BucketPolicy 502 gotPolicy, err = policy.ParseBucketPolicyConfig(bytes.NewReader(bucketPolicyReadBuf), testCase.bucketName) 503 if err != nil { 504 t.Fatalf("unexpected error. %v", err) 505 } 506 507 if !reflect.DeepEqual(expectedPolicy, gotPolicy) { 508 t.Errorf("Test %d: %s: Bucket policy differs from expected value.", i+1, instanceType) 509 } 510 } 511 // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. 512 recV2 := httptest.NewRecorder() 513 // construct HTTP request for PUT bucket policy endpoint. 514 reqV2, err := newTestSignedRequestV2(http.MethodGet, getGetPolicyURL("", testCase.bucketName), 515 0, nil, testCase.accessKey, testCase.secretKey, nil) 516 if err != nil { 517 t.Fatalf("Test %d: Failed to create HTTP request for GetBucketPolicyHandler: <ERROR> %v", i+1, err) 518 } 519 // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. 520 // Call the ServeHTTP to execute the handler, GetBucketPolicyHandler handles the request. 521 apiRouter.ServeHTTP(recV2, reqV2) 522 // Assert the response code with the expected status. 523 if recV2.Code != testCase.expectedRespStatus { 524 t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testCase.expectedRespStatus, recV2.Code) 525 } 526 // read the response body. 527 bucketPolicyReadBuf, err = io.ReadAll(recV2.Body) 528 if err != nil { 529 t.Fatalf("Test %d: %s: Failed parsing response body: <ERROR> %v", i+1, instanceType, err) 530 } 531 if recV2.Code == http.StatusOK { 532 // Verify whether the bucket policy fetched is same as the one inserted. 533 expectedPolicy, err := policy.ParseBucketPolicyConfig(strings.NewReader(expectedBucketPolicyStr), testCase.bucketName) 534 if err != nil { 535 t.Fatalf("unexpected error. %v", err) 536 } 537 gotPolicy, err := policy.ParseBucketPolicyConfig(bytes.NewReader(bucketPolicyReadBuf), testCase.bucketName) 538 if err != nil { 539 t.Fatalf("unexpected error. %v", err) 540 } 541 542 if !reflect.DeepEqual(expectedPolicy, gotPolicy) { 543 t.Errorf("Test %d: %s: Bucket policy differs from expected value.", i+1, instanceType) 544 } 545 } 546 } 547 548 // Test for Anonymous/unsigned http request. 549 // Bucket policy related functions doesn't support anonymous requests, setting policies shouldn't make a difference. 550 // create unsigned HTTP request for PutBucketPolicyHandler. 551 anonReq, err := newTestRequest(http.MethodGet, getPutPolicyURL("", bucketName), 0, nil) 552 if err != nil { 553 t.Fatalf("MinIO %s: Failed to create an anonymous request for bucket \"%s\": <ERROR> %v", 554 instanceType, bucketName, err) 555 } 556 557 // ExecObjectLayerAPIAnonTest - Calls the HTTP API handler using the anonymous request, validates the ErrAccessDeniedResponse, 558 // sets the bucket policy using the policy statement generated from `getWriteOnlyObjectStatement` so that the 559 // unsigned request goes through and its validated again. 560 ExecObjectLayerAPIAnonTest(t, obj, "GetBucketPolicyHandler", bucketName, "", instanceType, apiRouter, anonReq, getAnonReadOnlyBucketPolicy(bucketName)) 561 562 // HTTP request for testing when `objectLayer` is set to `nil`. 563 // There is no need to use an existing bucket and valid input for creating the request 564 // since the `objectLayer==nil` check is performed before any other checks inside the handlers. 565 // The only aim is to generate an HTTP request in a way that the relevant/registered end point is evoked/called. 566 nilBucket := "dummy-bucket" 567 568 nilReq, err := newTestSignedRequestV4(http.MethodGet, getGetPolicyURL("", nilBucket), 569 0, nil, "", "", nil) 570 if err != nil { 571 t.Errorf("MinIO %s: Failed to create HTTP request for testing the response when object Layer is set to `nil`.", instanceType) 572 } 573 // execute the object layer set to `nil` test. 574 // `ExecObjectLayerAPINilTest` manages the operation. 575 ExecObjectLayerAPINilTest(t, nilBucket, "", instanceType, apiRouter, nilReq) 576 } 577 578 // Wrapper for calling Delete Bucket Policy HTTP handler tests for both Erasure multiple disks and single node setup. 579 func TestDeleteBucketPolicyHandler(t *testing.T) { 580 ExecObjectLayerAPITest(t, testDeleteBucketPolicyHandler, []string{"PutBucketPolicy", "DeleteBucketPolicy"}) 581 } 582 583 // testDeleteBucketPolicyHandler - Test for Delete bucket policy end point. 584 func testDeleteBucketPolicyHandler(obj ObjectLayer, instanceType, bucketName string, apiRouter http.Handler, 585 credentials auth.Credentials, t *testing.T, 586 ) { 587 // template for constructing HTTP request body for PUT bucket policy. 588 bucketPolicyTemplate := `{ 589 "Version": "2012-10-17", 590 "Statement": [ 591 { 592 "Action": [ 593 "s3:GetBucketLocation", 594 "s3:ListBucket" 595 ], 596 "Effect": "Allow", 597 "Principal": { 598 "AWS": [ 599 "*" 600 ] 601 }, 602 "Resource": [ 603 "arn:aws:s3:::%s" 604 ] 605 }, 606 { 607 "Action": [ 608 "s3:GetObject" 609 ], 610 "Effect": "Allow", 611 "Principal": { 612 "AWS": [ 613 "*" 614 ] 615 }, 616 "Resource": [ 617 "arn:aws:s3:::%s/this*" 618 ] 619 } 620 ] 621 }` 622 623 // Writing bucket policy before running test on DeleteBucketPolicy. 624 putTestPolicies := []struct { 625 bucketName string 626 accessKey string 627 secretKey string 628 // expected Response. 629 expectedRespStatus int 630 }{ 631 { 632 bucketName: bucketName, 633 accessKey: credentials.AccessKey, 634 secretKey: credentials.SecretKey, 635 expectedRespStatus: http.StatusNoContent, 636 }, 637 } 638 639 // Iterating over the cases and writing the bucket policy. 640 // its required to write the policies first before running tests on GetBucketPolicy. 641 for i, testPolicy := range putTestPolicies { 642 // obtain the put bucket policy request body. 643 bucketPolicyStr := fmt.Sprintf(bucketPolicyTemplate, testPolicy.bucketName, testPolicy.bucketName) 644 // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. 645 recV4 := httptest.NewRecorder() 646 // construct HTTP request for PUT bucket policy endpoint. 647 reqV4, err := newTestSignedRequestV4(http.MethodPut, getPutPolicyURL("", testPolicy.bucketName), 648 int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testPolicy.accessKey, testPolicy.secretKey, nil) 649 if err != nil { 650 t.Fatalf("Test %d: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, err) 651 } 652 // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. 653 // Call the ServeHTTP to execute the handler. 654 apiRouter.ServeHTTP(recV4, reqV4) 655 if recV4.Code != testPolicy.expectedRespStatus { 656 t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testPolicy.expectedRespStatus, recV4.Code) 657 } 658 } 659 660 // testcases with input and expected output for DeleteBucketPolicyHandler. 661 testCases := []struct { 662 bucketName string 663 accessKey string 664 secretKey string 665 // expected response. 666 expectedRespStatus int 667 }{ 668 // Test case - 1. 669 { 670 bucketName: bucketName, 671 accessKey: credentials.AccessKey, 672 secretKey: credentials.SecretKey, 673 expectedRespStatus: http.StatusNoContent, 674 }, 675 // Test case - 2. 676 // Case with non-existent-bucket. 677 { 678 bucketName: "non-existent-bucket", 679 accessKey: credentials.AccessKey, 680 secretKey: credentials.SecretKey, 681 expectedRespStatus: http.StatusNotFound, 682 }, 683 // Test case - 3. 684 // Case with non-existent-bucket. 685 { 686 bucketName: ".invalid-bucket-name", 687 accessKey: credentials.AccessKey, 688 secretKey: credentials.SecretKey, 689 expectedRespStatus: http.StatusBadRequest, 690 }, 691 } 692 // Iterating over the cases and deleting the bucket policy and then asserting response. 693 for i, testCase := range testCases { 694 // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. 695 recV4 := httptest.NewRecorder() 696 // construct HTTP request for Delete bucket policy endpoint. 697 reqV4, err := newTestSignedRequestV4(http.MethodDelete, getDeletePolicyURL("", testCase.bucketName), 698 0, nil, testCase.accessKey, testCase.secretKey, nil) 699 if err != nil { 700 t.Fatalf("Test %d: Failed to create HTTP request for GetBucketPolicyHandler: <ERROR> %v", i+1, err) 701 } 702 // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. 703 // Call the ServeHTTP to execute the handler, DeleteBucketPolicyHandler handles the request. 704 apiRouter.ServeHTTP(recV4, reqV4) 705 // Assert the response code with the expected status. 706 if recV4.Code != testCase.expectedRespStatus { 707 t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testCase.expectedRespStatus, recV4.Code) 708 } 709 } 710 711 // Iterating over the cases and writing the bucket policy. 712 // its required to write the policies first before running tests on GetBucketPolicy. 713 for i, testPolicy := range putTestPolicies { 714 // obtain the put bucket policy request body. 715 bucketPolicyStr := fmt.Sprintf(bucketPolicyTemplate, testPolicy.bucketName, testPolicy.bucketName) 716 // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. 717 recV2 := httptest.NewRecorder() 718 // construct HTTP request for PUT bucket policy endpoint. 719 reqV2, err := newTestSignedRequestV2(http.MethodPut, getPutPolicyURL("", testPolicy.bucketName), 720 int64(len(bucketPolicyStr)), bytes.NewReader([]byte(bucketPolicyStr)), testPolicy.accessKey, testPolicy.secretKey, nil) 721 if err != nil { 722 t.Fatalf("Test %d: Failed to create HTTP request for PutBucketPolicyHandler: <ERROR> %v", i+1, err) 723 } 724 // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. 725 // Call the ServeHTTP to execute the handler. 726 apiRouter.ServeHTTP(recV2, reqV2) 727 if recV2.Code != testPolicy.expectedRespStatus { 728 t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testPolicy.expectedRespStatus, recV2.Code) 729 } 730 } 731 732 for i, testCase := range testCases { 733 // initialize HTTP NewRecorder, this records any mutations to response writer inside the handler. 734 recV2 := httptest.NewRecorder() 735 // construct HTTP request for Delete bucket policy endpoint. 736 reqV2, err := newTestSignedRequestV2(http.MethodDelete, getDeletePolicyURL("", testCase.bucketName), 737 0, nil, testCase.accessKey, testCase.secretKey, nil) 738 if err != nil { 739 t.Fatalf("Test %d: Failed to create HTTP request for GetBucketPolicyHandler: <ERROR> %v", i+1, err) 740 } 741 // Since `apiRouter` satisfies `http.Handler` it has a ServeHTTP to execute the logic of the handler. 742 // Call the ServeHTTP to execute the handler, DeleteBucketPolicyHandler handles the request. 743 apiRouter.ServeHTTP(recV2, reqV2) 744 // Assert the response code with the expected status. 745 if recV2.Code != testCase.expectedRespStatus { 746 t.Fatalf("Case %d: Expected the response status to be `%d`, but instead found `%d`", i+1, testCase.expectedRespStatus, recV2.Code) 747 } 748 } 749 // Test for Anonymous/unsigned http request. 750 // Bucket policy related functions doesn't support anonymous requests, setting policies shouldn't make a difference. 751 // create unsigned HTTP request for PutBucketPolicyHandler. 752 anonReq, err := newTestRequest(http.MethodDelete, getPutPolicyURL("", bucketName), 0, nil) 753 if err != nil { 754 t.Fatalf("MinIO %s: Failed to create an anonymous request for bucket \"%s\": <ERROR> %v", 755 instanceType, bucketName, err) 756 } 757 758 // ExecObjectLayerAPIAnonTest - Calls the HTTP API handler using the anonymous request, validates the ErrAccessDeniedResponse, 759 // sets the bucket policy using the policy statement generated from `getWriteOnlyObjectStatement` so that the 760 // unsigned request goes through and its validated again. 761 ExecObjectLayerAPIAnonTest(t, obj, "DeleteBucketPolicyHandler", bucketName, "", instanceType, apiRouter, anonReq, getAnonWriteOnlyBucketPolicy(bucketName)) 762 763 // HTTP request for testing when `objectLayer` is set to `nil`. 764 // There is no need to use an existing bucket and valid input for creating the request 765 // since the `objectLayer==nil` check is performed before any other checks inside the handlers. 766 // The only aim is to generate an HTTP request in a way that the relevant/registered end point is evoked/called. 767 nilBucket := "dummy-bucket" 768 769 nilReq, err := newTestSignedRequestV4(http.MethodDelete, getDeletePolicyURL("", nilBucket), 770 0, nil, "", "", nil) 771 if err != nil { 772 t.Errorf("MinIO %s: Failed to create HTTP request for testing the response when object Layer is set to `nil`.", instanceType) 773 } 774 // execute the object layer set to `nil` test. 775 // `ExecObjectLayerAPINilTest` manages the operation. 776 ExecObjectLayerAPINilTest(t, nilBucket, "", instanceType, apiRouter, nilReq) 777 }