storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/bucket/policy/policy_test.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2018 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package policy 18 19 import ( 20 "encoding/json" 21 "net" 22 "reflect" 23 "testing" 24 25 "storj.io/minio/pkg/bucket/policy/condition" 26 ) 27 28 func TestPolicyIsAllowed(t *testing.T) { 29 case1Policy := Policy{ 30 Version: DefaultVersion, 31 Statements: []Statement{ 32 NewStatement( 33 Allow, 34 NewPrincipal("*"), 35 NewActionSet(GetBucketLocationAction, PutObjectAction), 36 NewResourceSet(NewResource("*", "")), 37 condition.NewFunctions(), 38 )}, 39 } 40 41 case2Policy := Policy{ 42 Version: DefaultVersion, 43 Statements: []Statement{ 44 NewStatement( 45 Allow, 46 NewPrincipal("*"), 47 NewActionSet(GetObjectAction, PutObjectAction), 48 NewResourceSet(NewResource("mybucket", "/myobject*")), 49 condition.NewFunctions(), 50 )}, 51 } 52 53 _, IPNet, err := net.ParseCIDR("192.168.1.0/24") 54 if err != nil { 55 t.Fatalf("unexpected error. %v\n", err) 56 } 57 func1, err := condition.NewIPAddressFunc( 58 condition.AWSSourceIP, 59 IPNet, 60 ) 61 if err != nil { 62 t.Fatalf("unexpected error. %v\n", err) 63 } 64 65 case3Policy := Policy{ 66 Version: DefaultVersion, 67 Statements: []Statement{ 68 NewStatement( 69 Allow, 70 NewPrincipal("*"), 71 NewActionSet(GetObjectAction, PutObjectAction), 72 NewResourceSet(NewResource("mybucket", "/myobject*")), 73 condition.NewFunctions(func1), 74 )}, 75 } 76 77 case4Policy := Policy{ 78 Version: DefaultVersion, 79 Statements: []Statement{ 80 NewStatement( 81 Deny, 82 NewPrincipal("*"), 83 NewActionSet(GetObjectAction, PutObjectAction), 84 NewResourceSet(NewResource("mybucket", "/myobject*")), 85 condition.NewFunctions(func1), 86 )}, 87 } 88 89 anonGetBucketLocationArgs := Args{ 90 AccountName: "Q3AM3UQ867SPQQA43P2F", 91 Action: GetBucketLocationAction, 92 BucketName: "mybucket", 93 ConditionValues: map[string][]string{}, 94 } 95 96 anonPutObjectActionArgs := Args{ 97 AccountName: "Q3AM3UQ867SPQQA43P2F", 98 Action: PutObjectAction, 99 BucketName: "mybucket", 100 ConditionValues: map[string][]string{ 101 "x-amz-copy-source": {"mybucket/myobject"}, 102 "SourceIp": {"192.168.1.10"}, 103 }, 104 ObjectName: "myobject", 105 } 106 107 anonGetObjectActionArgs := Args{ 108 AccountName: "Q3AM3UQ867SPQQA43P2F", 109 Action: GetObjectAction, 110 BucketName: "mybucket", 111 ConditionValues: map[string][]string{}, 112 ObjectName: "myobject", 113 } 114 115 getBucketLocationArgs := Args{ 116 AccountName: "Q3AM3UQ867SPQQA43P2F", 117 Action: GetBucketLocationAction, 118 BucketName: "mybucket", 119 ConditionValues: map[string][]string{}, 120 IsOwner: true, 121 } 122 123 putObjectActionArgs := Args{ 124 AccountName: "Q3AM3UQ867SPQQA43P2F", 125 Action: PutObjectAction, 126 BucketName: "mybucket", 127 ConditionValues: map[string][]string{ 128 "x-amz-copy-source": {"mybucket/myobject"}, 129 "SourceIp": {"192.168.1.10"}, 130 }, 131 IsOwner: true, 132 ObjectName: "myobject", 133 } 134 135 getObjectActionArgs := Args{ 136 AccountName: "Q3AM3UQ867SPQQA43P2F", 137 Action: GetObjectAction, 138 BucketName: "mybucket", 139 ConditionValues: map[string][]string{}, 140 IsOwner: true, 141 ObjectName: "myobject", 142 } 143 144 testCases := []struct { 145 policy Policy 146 args Args 147 expectedResult bool 148 }{ 149 {case1Policy, anonGetBucketLocationArgs, true}, 150 {case1Policy, anonPutObjectActionArgs, true}, 151 {case1Policy, anonGetObjectActionArgs, false}, 152 {case1Policy, getBucketLocationArgs, true}, 153 {case1Policy, putObjectActionArgs, true}, 154 {case1Policy, getObjectActionArgs, true}, 155 156 {case2Policy, anonGetBucketLocationArgs, false}, 157 {case2Policy, anonPutObjectActionArgs, true}, 158 {case2Policy, anonGetObjectActionArgs, true}, 159 {case2Policy, getBucketLocationArgs, true}, 160 {case2Policy, putObjectActionArgs, true}, 161 {case2Policy, getObjectActionArgs, true}, 162 163 {case3Policy, anonGetBucketLocationArgs, false}, 164 {case3Policy, anonPutObjectActionArgs, true}, 165 {case3Policy, anonGetObjectActionArgs, false}, 166 {case3Policy, getBucketLocationArgs, true}, 167 {case3Policy, putObjectActionArgs, true}, 168 {case3Policy, getObjectActionArgs, true}, 169 170 {case4Policy, anonGetBucketLocationArgs, false}, 171 {case4Policy, anonPutObjectActionArgs, false}, 172 {case4Policy, anonGetObjectActionArgs, false}, 173 {case4Policy, getBucketLocationArgs, true}, 174 {case4Policy, putObjectActionArgs, false}, 175 {case4Policy, getObjectActionArgs, true}, 176 } 177 178 for i, testCase := range testCases { 179 result := testCase.policy.IsAllowed(testCase.args) 180 181 if result != testCase.expectedResult { 182 t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result) 183 } 184 } 185 } 186 187 func TestPolicyIsEmpty(t *testing.T) { 188 case1Policy := Policy{ 189 Version: DefaultVersion, 190 Statements: []Statement{ 191 NewStatement( 192 Allow, 193 NewPrincipal("*"), 194 NewActionSet(PutObjectAction), 195 NewResourceSet(NewResource("mybucket", "/myobject*")), 196 condition.NewFunctions(), 197 ), 198 }, 199 } 200 201 case2Policy := Policy{ 202 ID: "MyPolicyForMyBucket", 203 Version: DefaultVersion, 204 } 205 206 testCases := []struct { 207 policy Policy 208 expectedResult bool 209 }{ 210 {case1Policy, false}, 211 {case2Policy, true}, 212 } 213 214 for i, testCase := range testCases { 215 result := testCase.policy.IsEmpty() 216 217 if result != testCase.expectedResult { 218 t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result) 219 } 220 } 221 } 222 223 func TestPolicyIsValid(t *testing.T) { 224 case1Policy := Policy{ 225 Version: DefaultVersion, 226 Statements: []Statement{ 227 NewStatement( 228 Allow, 229 NewPrincipal("*"), 230 NewActionSet(PutObjectAction), 231 NewResourceSet(NewResource("mybucket", "/myobject*")), 232 condition.NewFunctions(), 233 ), 234 }, 235 } 236 237 case2Policy := Policy{ 238 Version: DefaultVersion, 239 Statements: []Statement{ 240 NewStatement( 241 Allow, 242 NewPrincipal("*"), 243 NewActionSet(PutObjectAction), 244 NewResourceSet(NewResource("mybucket", "/myobject*")), 245 condition.NewFunctions(), 246 ), 247 NewStatement( 248 Deny, 249 NewPrincipal("*"), 250 NewActionSet(GetObjectAction), 251 NewResourceSet(NewResource("mybucket", "/myobject*")), 252 condition.NewFunctions(), 253 ), 254 }, 255 } 256 257 case3Policy := Policy{ 258 Version: DefaultVersion, 259 Statements: []Statement{ 260 NewStatement( 261 Allow, 262 NewPrincipal("*"), 263 NewActionSet(PutObjectAction), 264 NewResourceSet(NewResource("mybucket", "/myobject*")), 265 condition.NewFunctions(), 266 ), 267 NewStatement( 268 Deny, 269 NewPrincipal("*"), 270 NewActionSet(PutObjectAction), 271 NewResourceSet(NewResource("mybucket", "/yourobject*")), 272 condition.NewFunctions(), 273 ), 274 }, 275 } 276 277 func1, err := condition.NewNullFunc( 278 condition.S3XAmzCopySource, 279 true, 280 ) 281 if err != nil { 282 t.Fatalf("unexpected error. %v\n", err) 283 } 284 func2, err := condition.NewNullFunc( 285 condition.S3XAmzServerSideEncryption, 286 false, 287 ) 288 if err != nil { 289 t.Fatalf("unexpected error. %v\n", err) 290 } 291 292 case4Policy := Policy{ 293 Version: DefaultVersion, 294 Statements: []Statement{ 295 NewStatement( 296 Allow, 297 NewPrincipal("*"), 298 NewActionSet(PutObjectAction), 299 NewResourceSet(NewResource("mybucket", "/myobject*")), 300 condition.NewFunctions(func1), 301 ), 302 NewStatement( 303 Deny, 304 NewPrincipal("*"), 305 NewActionSet(PutObjectAction), 306 NewResourceSet(NewResource("mybucket", "/myobject*")), 307 condition.NewFunctions(func2), 308 ), 309 }, 310 } 311 312 case5Policy := Policy{ 313 Version: "17-10-2012", 314 Statements: []Statement{ 315 NewStatement( 316 Allow, 317 NewPrincipal("*"), 318 NewActionSet(PutObjectAction), 319 NewResourceSet(NewResource("mybucket", "/myobject*")), 320 condition.NewFunctions(), 321 ), 322 }, 323 } 324 325 case6Policy := Policy{ 326 ID: "MyPolicyForMyBucket1", 327 Version: DefaultVersion, 328 Statements: []Statement{ 329 NewStatement( 330 Allow, 331 NewPrincipal("*"), 332 NewActionSet(GetObjectAction, PutObjectAction), 333 NewResourceSet(NewResource("mybucket", "myobject*")), 334 condition.NewFunctions(func1, func2), 335 ), 336 }, 337 } 338 339 case7Policy := Policy{ 340 Version: DefaultVersion, 341 Statements: []Statement{ 342 NewStatement( 343 Allow, 344 NewPrincipal("*"), 345 NewActionSet(PutObjectAction), 346 NewResourceSet(NewResource("mybucket", "/myobject*")), 347 condition.NewFunctions(), 348 ), 349 NewStatement( 350 Deny, 351 NewPrincipal("*"), 352 NewActionSet(PutObjectAction), 353 NewResourceSet(NewResource("mybucket", "/myobject*")), 354 condition.NewFunctions(), 355 ), 356 }, 357 } 358 359 case8Policy := Policy{ 360 Version: DefaultVersion, 361 Statements: []Statement{ 362 NewStatement( 363 Allow, 364 NewPrincipal("*"), 365 NewActionSet(PutObjectAction), 366 NewResourceSet(NewResource("mybucket", "/myobject*")), 367 condition.NewFunctions(), 368 ), 369 NewStatement( 370 Allow, 371 NewPrincipal("*"), 372 NewActionSet(PutObjectAction), 373 NewResourceSet(NewResource("mybucket", "/myobject*")), 374 condition.NewFunctions(), 375 ), 376 }, 377 } 378 379 testCases := []struct { 380 policy Policy 381 expectErr bool 382 }{ 383 {case1Policy, false}, 384 // allowed duplicate principal. 385 {case2Policy, false}, 386 // allowed duplicate principal and action. 387 {case3Policy, false}, 388 // allowed duplicate principal, action and resource. 389 {case4Policy, false}, 390 // Invalid version error. 391 {case5Policy, true}, 392 // Invalid statement error. 393 {case6Policy, true}, 394 // Duplicate statement success different effects. 395 {case7Policy, false}, 396 // Duplicate statement success, duplicate statement dropped. 397 {case8Policy, false}, 398 } 399 400 for i, testCase := range testCases { 401 err := testCase.policy.isValid() 402 expectErr := (err != nil) 403 404 if expectErr != testCase.expectErr { 405 t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) 406 } 407 } 408 } 409 410 func TestPolicyMarshalJSON(t *testing.T) { 411 case1Policy := Policy{ 412 ID: "MyPolicyForMyBucket1", 413 Version: DefaultVersion, 414 Statements: []Statement{ 415 NewStatement( 416 Allow, 417 NewPrincipal("*"), 418 NewActionSet(PutObjectAction), 419 NewResourceSet(NewResource("mybucket", "/myobject*")), 420 condition.NewFunctions(), 421 ), 422 }, 423 } 424 case1Policy.Statements[0].SID = "SomeId1" 425 case1Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Sid":"SomeId1","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]}]}`) 426 427 _, IPNet1, err := net.ParseCIDR("192.168.1.0/24") 428 if err != nil { 429 t.Fatalf("unexpected error. %v\n", err) 430 } 431 func1, err := condition.NewIPAddressFunc( 432 condition.AWSSourceIP, 433 IPNet1, 434 ) 435 if err != nil { 436 t.Fatalf("unexpected error. %v\n", err) 437 } 438 439 case2Policy := Policy{ 440 Version: DefaultVersion, 441 Statements: []Statement{ 442 NewStatement( 443 Allow, 444 NewPrincipal("*"), 445 NewActionSet(PutObjectAction), 446 NewResourceSet(NewResource("mybucket", "/myobject*")), 447 condition.NewFunctions(), 448 ), 449 NewStatement( 450 Deny, 451 NewPrincipal("*"), 452 NewActionSet(GetObjectAction), 453 NewResourceSet(NewResource("mybucket", "/yourobject*")), 454 condition.NewFunctions(func1), 455 ), 456 }, 457 } 458 case2Data := []byte(`{"Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]},{"Effect":"Deny","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::mybucket/yourobject*"],"Condition":{"IpAddress":{"aws:SourceIp":["192.168.1.0/24"]}}}]}`) 459 460 case3Policy := Policy{ 461 ID: "MyPolicyForMyBucket1", 462 Version: DefaultVersion, 463 Statements: []Statement{ 464 NewStatement( 465 Allow, 466 NewPrincipal("Q3AM3UQ867SPQQA43P2F"), 467 NewActionSet(PutObjectAction), 468 NewResourceSet(NewResource("mybucket", "/myobject*")), 469 condition.NewFunctions(), 470 ), 471 NewStatement( 472 Allow, 473 NewPrincipal("*"), 474 NewActionSet(PutObjectAction), 475 NewResourceSet(NewResource("mybucket", "/myobject*")), 476 condition.NewFunctions(), 477 ), 478 }, 479 } 480 case3Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["Q3AM3UQ867SPQQA43P2F"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]}]}`) 481 482 case4Policy := Policy{ 483 ID: "MyPolicyForMyBucket1", 484 Version: DefaultVersion, 485 Statements: []Statement{ 486 NewStatement( 487 Allow, 488 NewPrincipal("*"), 489 NewActionSet(PutObjectAction), 490 NewResourceSet(NewResource("mybucket", "/myobject*")), 491 condition.NewFunctions(), 492 ), 493 NewStatement( 494 Allow, 495 NewPrincipal("*"), 496 NewActionSet(GetObjectAction), 497 NewResourceSet(NewResource("mybucket", "/myobject*")), 498 condition.NewFunctions(), 499 ), 500 }, 501 } 502 case4Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]}]}`) 503 504 case5Policy := Policy{ 505 ID: "MyPolicyForMyBucket1", 506 Version: DefaultVersion, 507 Statements: []Statement{ 508 NewStatement( 509 Allow, 510 NewPrincipal("*"), 511 NewActionSet(PutObjectAction), 512 NewResourceSet(NewResource("mybucket", "/myobject*")), 513 condition.NewFunctions(), 514 ), 515 NewStatement( 516 Allow, 517 NewPrincipal("*"), 518 NewActionSet(PutObjectAction), 519 NewResourceSet(NewResource("mybucket", "/yourobject*")), 520 condition.NewFunctions(), 521 ), 522 }, 523 } 524 case5Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/yourobject*"]}]}`) 525 526 _, IPNet2, err := net.ParseCIDR("192.168.2.0/24") 527 if err != nil { 528 t.Fatalf("unexpected error. %v\n", err) 529 } 530 func2, err := condition.NewIPAddressFunc( 531 condition.AWSSourceIP, 532 IPNet2, 533 ) 534 if err != nil { 535 t.Fatalf("unexpected error. %v\n", err) 536 } 537 538 case6Policy := Policy{ 539 ID: "MyPolicyForMyBucket1", 540 Version: DefaultVersion, 541 Statements: []Statement{ 542 NewStatement( 543 Allow, 544 NewPrincipal("*"), 545 NewActionSet(PutObjectAction), 546 NewResourceSet(NewResource("mybucket", "/myobject*")), 547 condition.NewFunctions(func1), 548 ), 549 NewStatement( 550 Allow, 551 NewPrincipal("*"), 552 NewActionSet(PutObjectAction), 553 NewResourceSet(NewResource("mybucket", "/myobject*")), 554 condition.NewFunctions(func2), 555 ), 556 }, 557 } 558 case6Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"IpAddress":{"aws:SourceIp":["192.168.1.0/24"]}}},{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"IpAddress":{"aws:SourceIp":["192.168.2.0/24"]}}}]}`) 559 560 case7Policy := Policy{ 561 ID: "MyPolicyForMyBucket1", 562 Version: DefaultVersion, 563 Statements: []Statement{ 564 NewStatement( 565 Allow, 566 NewPrincipal("*"), 567 NewActionSet(GetBucketLocationAction), 568 NewResourceSet(NewResource("mybucket", "")), 569 condition.NewFunctions(), 570 ), 571 }, 572 } 573 case7Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation"],"Resource":["arn:aws:s3:::mybucket"]}]}`) 574 575 case8Policy := Policy{ 576 ID: "MyPolicyForMyBucket1", 577 Version: DefaultVersion, 578 Statements: []Statement{ 579 NewStatement( 580 Allow, 581 NewPrincipal("*"), 582 NewActionSet(GetBucketLocationAction), 583 NewResourceSet(NewResource("*", "")), 584 condition.NewFunctions(), 585 ), 586 }, 587 } 588 case8Data := []byte(`{"ID":"MyPolicyForMyBucket1","Version":"2012-10-17","Statement":[{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:GetBucketLocation"],"Resource":["arn:aws:s3:::*"]}]}`) 589 590 func3, err := condition.NewNullFunc( 591 condition.S3XAmzCopySource, 592 true, 593 ) 594 if err != nil { 595 t.Fatalf("unexpected error. %v\n", err) 596 } 597 case9Policy := Policy{ 598 ID: "MyPolicyForMyBucket1", 599 Version: DefaultVersion, 600 Statements: []Statement{ 601 NewStatement( 602 Allow, 603 NewPrincipal("*"), 604 NewActionSet(GetObjectAction, PutObjectAction), 605 NewResourceSet(NewResource("mybucket", "myobject*")), 606 condition.NewFunctions(func1, func2, func3), 607 ), 608 }, 609 } 610 611 testCases := []struct { 612 policy Policy 613 expectedResult []byte 614 expectErr bool 615 }{ 616 {case1Policy, case1Data, false}, 617 {case2Policy, case2Data, false}, 618 {case3Policy, case3Data, false}, 619 {case4Policy, case4Data, false}, 620 {case5Policy, case5Data, false}, 621 {case6Policy, case6Data, false}, 622 {case7Policy, case7Data, false}, 623 {case8Policy, case8Data, false}, 624 {case9Policy, nil, true}, 625 } 626 627 for i, testCase := range testCases { 628 result, err := json.Marshal(testCase.policy) 629 expectErr := (err != nil) 630 631 if expectErr != testCase.expectErr { 632 t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) 633 } 634 635 if !testCase.expectErr { 636 if !reflect.DeepEqual(result, testCase.expectedResult) { 637 t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result)) 638 } 639 } 640 } 641 } 642 643 func TestPolicyUnmarshalJSON(t *testing.T) { 644 case1Data := []byte(`{ 645 "ID": "MyPolicyForMyBucket1", 646 "Version": "2012-10-17", 647 "Statement": [ 648 { 649 "Sid": "SomeId1", 650 "Effect": "Allow", 651 "Principal": "*", 652 "Action": "s3:PutObject", 653 "Resource": "arn:aws:s3:::mybucket/myobject*" 654 } 655 ] 656 }`) 657 case1Policy := Policy{ 658 ID: "MyPolicyForMyBucket1", 659 Version: DefaultVersion, 660 Statements: []Statement{ 661 NewStatement( 662 Allow, 663 NewPrincipal("*"), 664 NewActionSet(PutObjectAction), 665 NewResourceSet(NewResource("mybucket", "/myobject*")), 666 condition.NewFunctions(), 667 ), 668 }, 669 } 670 case1Policy.Statements[0].SID = "SomeId1" 671 672 case2Data := []byte(`{ 673 "Version": "2012-10-17", 674 "Statement": [ 675 { 676 "Effect": "Allow", 677 "Principal": "*", 678 "Action": "s3:PutObject", 679 "Resource": "arn:aws:s3:::mybucket/myobject*" 680 }, 681 { 682 "Effect": "Deny", 683 "Principal": "*", 684 "Action": "s3:GetObject", 685 "Resource": "arn:aws:s3:::mybucket/yourobject*", 686 "Condition": { 687 "IpAddress": { 688 "aws:SourceIp": "192.168.1.0/24" 689 } 690 } 691 } 692 ] 693 }`) 694 _, IPNet1, err := net.ParseCIDR("192.168.1.0/24") 695 if err != nil { 696 t.Fatalf("unexpected error. %v\n", err) 697 } 698 func1, err := condition.NewIPAddressFunc( 699 condition.AWSSourceIP, 700 IPNet1, 701 ) 702 if err != nil { 703 t.Fatalf("unexpected error. %v\n", err) 704 } 705 706 case2Policy := Policy{ 707 Version: DefaultVersion, 708 Statements: []Statement{ 709 NewStatement( 710 Allow, 711 NewPrincipal("*"), 712 NewActionSet(PutObjectAction), 713 NewResourceSet(NewResource("mybucket", "/myobject*")), 714 condition.NewFunctions(), 715 ), 716 NewStatement( 717 Deny, 718 NewPrincipal("*"), 719 NewActionSet(GetObjectAction), 720 NewResourceSet(NewResource("mybucket", "/yourobject*")), 721 condition.NewFunctions(func1), 722 ), 723 }, 724 } 725 726 case3Data := []byte(`{ 727 "ID": "MyPolicyForMyBucket1", 728 "Version": "2012-10-17", 729 "Statement": [ 730 { 731 "Effect": "Allow", 732 "Principal": { 733 "AWS": [ 734 "Q3AM3UQ867SPQQA43P2F" 735 ] 736 }, 737 "Action": "s3:PutObject", 738 "Resource": "arn:aws:s3:::mybucket/myobject*" 739 }, 740 { 741 "Effect": "Allow", 742 "Principal": "*", 743 "Action": "s3:PutObject", 744 "Resource": "arn:aws:s3:::mybucket/myobject*" 745 } 746 ] 747 }`) 748 case3Policy := Policy{ 749 ID: "MyPolicyForMyBucket1", 750 Version: DefaultVersion, 751 Statements: []Statement{ 752 NewStatement( 753 Allow, 754 NewPrincipal("Q3AM3UQ867SPQQA43P2F"), 755 NewActionSet(PutObjectAction), 756 NewResourceSet(NewResource("mybucket", "/myobject*")), 757 condition.NewFunctions(), 758 ), 759 NewStatement( 760 Allow, 761 NewPrincipal("*"), 762 NewActionSet(PutObjectAction), 763 NewResourceSet(NewResource("mybucket", "/myobject*")), 764 condition.NewFunctions(), 765 ), 766 }, 767 } 768 769 case4Data := []byte(`{ 770 "ID": "MyPolicyForMyBucket1", 771 "Version": "2012-10-17", 772 "Statement": [ 773 { 774 "Effect": "Allow", 775 "Principal": "*", 776 "Action": "s3:PutObject", 777 "Resource": "arn:aws:s3:::mybucket/myobject*" 778 }, 779 { 780 "Effect": "Allow", 781 "Principal": "*", 782 "Action": "s3:GetObject", 783 "Resource": "arn:aws:s3:::mybucket/myobject*" 784 } 785 ] 786 }`) 787 case4Policy := Policy{ 788 ID: "MyPolicyForMyBucket1", 789 Version: DefaultVersion, 790 Statements: []Statement{ 791 NewStatement( 792 Allow, 793 NewPrincipal("*"), 794 NewActionSet(PutObjectAction), 795 NewResourceSet(NewResource("mybucket", "/myobject*")), 796 condition.NewFunctions(), 797 ), 798 NewStatement( 799 Allow, 800 NewPrincipal("*"), 801 NewActionSet(GetObjectAction), 802 NewResourceSet(NewResource("mybucket", "/myobject*")), 803 condition.NewFunctions(), 804 ), 805 }, 806 } 807 808 case5Data := []byte(`{ 809 "ID": "MyPolicyForMyBucket1", 810 "Version": "2012-10-17", 811 "Statement": [ 812 { 813 "Effect": "Allow", 814 "Principal": "*", 815 "Action": "s3:PutObject", 816 "Resource": "arn:aws:s3:::mybucket/myobject*" 817 }, 818 { 819 "Effect": "Allow", 820 "Principal": "*", 821 "Action": "s3:PutObject", 822 "Resource": "arn:aws:s3:::mybucket/yourobject*" 823 } 824 ] 825 }`) 826 case5Policy := Policy{ 827 ID: "MyPolicyForMyBucket1", 828 Version: DefaultVersion, 829 Statements: []Statement{ 830 NewStatement( 831 Allow, 832 NewPrincipal("*"), 833 NewActionSet(PutObjectAction), 834 NewResourceSet(NewResource("mybucket", "/myobject*")), 835 condition.NewFunctions(), 836 ), 837 NewStatement( 838 Allow, 839 NewPrincipal("*"), 840 NewActionSet(PutObjectAction), 841 NewResourceSet(NewResource("mybucket", "/yourobject*")), 842 condition.NewFunctions(), 843 ), 844 }, 845 } 846 847 case6Data := []byte(`{ 848 "ID": "MyPolicyForMyBucket1", 849 "Version": "2012-10-17", 850 "Statement": [ 851 { 852 "Effect": "Allow", 853 "Principal": "*", 854 "Action": "s3:PutObject", 855 "Resource": "arn:aws:s3:::mybucket/myobject*", 856 "Condition": { 857 "IpAddress": { 858 "aws:SourceIp": "192.168.1.0/24" 859 } 860 } 861 }, 862 { 863 "Effect": "Allow", 864 "Principal": "*", 865 "Action": "s3:PutObject", 866 "Resource": "arn:aws:s3:::mybucket/myobject*", 867 "Condition": { 868 "IpAddress": { 869 "aws:SourceIp": "192.168.2.0/24" 870 } 871 } 872 } 873 ] 874 }`) 875 _, IPNet2, err := net.ParseCIDR("192.168.2.0/24") 876 if err != nil { 877 t.Fatalf("unexpected error. %v\n", err) 878 } 879 func2, err := condition.NewIPAddressFunc( 880 condition.AWSSourceIP, 881 IPNet2, 882 ) 883 if err != nil { 884 t.Fatalf("unexpected error. %v\n", err) 885 } 886 887 case6Policy := Policy{ 888 ID: "MyPolicyForMyBucket1", 889 Version: DefaultVersion, 890 Statements: []Statement{ 891 NewStatement( 892 Allow, 893 NewPrincipal("*"), 894 NewActionSet(PutObjectAction), 895 NewResourceSet(NewResource("mybucket", "/myobject*")), 896 condition.NewFunctions(func1), 897 ), 898 NewStatement( 899 Allow, 900 NewPrincipal("*"), 901 NewActionSet(PutObjectAction), 902 NewResourceSet(NewResource("mybucket", "/myobject*")), 903 condition.NewFunctions(func2), 904 ), 905 }, 906 } 907 908 case7Data := []byte(`{ 909 "ID": "MyPolicyForMyBucket1", 910 "Version": "2012-10-17", 911 "Statement": [ 912 { 913 "Effect": "Allow", 914 "Principal": "*", 915 "Action": "s3:GetBucketLocation", 916 "Resource": "arn:aws:s3:::mybucket" 917 } 918 ] 919 }`) 920 921 case7Policy := Policy{ 922 ID: "MyPolicyForMyBucket1", 923 Version: DefaultVersion, 924 Statements: []Statement{ 925 NewStatement( 926 Allow, 927 NewPrincipal("*"), 928 NewActionSet(GetBucketLocationAction), 929 NewResourceSet(NewResource("mybucket", "")), 930 condition.NewFunctions(), 931 ), 932 }, 933 } 934 935 case8Data := []byte(`{ 936 "ID": "MyPolicyForMyBucket1", 937 "Version": "2012-10-17", 938 "Statement": [ 939 { 940 "Effect": "Allow", 941 "Principal": "*", 942 "Action": "s3:GetBucketLocation", 943 "Resource": "arn:aws:s3:::*" 944 } 945 ] 946 }`) 947 948 case8Policy := Policy{ 949 ID: "MyPolicyForMyBucket1", 950 Version: DefaultVersion, 951 Statements: []Statement{ 952 NewStatement( 953 Allow, 954 NewPrincipal("*"), 955 NewActionSet(GetBucketLocationAction), 956 NewResourceSet(NewResource("*", "")), 957 condition.NewFunctions(), 958 ), 959 }, 960 } 961 962 case9Data := []byte(`{ 963 "ID": "MyPolicyForMyBucket1", 964 "Version": "17-10-2012", 965 "Statement": [ 966 { 967 "Effect": "Allow", 968 "Principal": "*", 969 "Action": "s3:PutObject", 970 "Resource": "arn:aws:s3:::mybucket/myobject*" 971 } 972 ] 973 }`) 974 975 case10Data := []byte(`{ 976 "ID": "MyPolicyForMyBucket1", 977 "Version": "2012-10-17", 978 "Statement": [ 979 { 980 "Effect": "Allow", 981 "Principal": "*", 982 "Action": "s3:PutObject", 983 "Resource": "arn:aws:s3:::mybucket/myobject*" 984 }, 985 { 986 "Effect": "Allow", 987 "Principal": "*", 988 "Action": "s3:PutObject", 989 "Resource": "arn:aws:s3:::mybucket/myobject*" 990 } 991 ] 992 }`) 993 994 case10Policy := Policy{ 995 ID: "MyPolicyForMyBucket1", 996 Version: DefaultVersion, 997 Statements: []Statement{ 998 NewStatement( 999 Allow, 1000 NewPrincipal("*"), 1001 NewActionSet(PutObjectAction), 1002 NewResourceSet(NewResource("mybucket", "myobject*")), 1003 condition.NewFunctions(), 1004 ), 1005 }, 1006 } 1007 1008 case11Data := []byte(`{ 1009 "ID": "MyPolicyForMyBucket1", 1010 "Version": "2012-10-17", 1011 "Statement": [ 1012 { 1013 "Effect": "Allow", 1014 "Principal": "*", 1015 "Action": "s3:PutObject", 1016 "Resource": "arn:aws:s3:::mybucket/myobject*" 1017 }, 1018 { 1019 "Effect": "Deny", 1020 "Principal": "*", 1021 "Action": "s3:PutObject", 1022 "Resource": "arn:aws:s3:::mybucket/myobject*" 1023 } 1024 ] 1025 }`) 1026 1027 case11Policy := Policy{ 1028 ID: "MyPolicyForMyBucket1", 1029 Version: DefaultVersion, 1030 Statements: []Statement{ 1031 NewStatement( 1032 Allow, 1033 NewPrincipal("*"), 1034 NewActionSet(PutObjectAction), 1035 NewResourceSet(NewResource("mybucket", "myobject*")), 1036 condition.NewFunctions(), 1037 ), 1038 NewStatement( 1039 Deny, 1040 NewPrincipal("*"), 1041 NewActionSet(PutObjectAction), 1042 NewResourceSet(NewResource("mybucket", "myobject*")), 1043 condition.NewFunctions(), 1044 ), 1045 }, 1046 } 1047 1048 testCases := []struct { 1049 data []byte 1050 expectedResult Policy 1051 expectErr bool 1052 }{ 1053 {case1Data, case1Policy, false}, 1054 {case2Data, case2Policy, false}, 1055 {case3Data, case3Policy, false}, 1056 {case4Data, case4Policy, false}, 1057 {case5Data, case5Policy, false}, 1058 {case6Data, case6Policy, false}, 1059 {case7Data, case7Policy, false}, 1060 {case8Data, case8Policy, false}, 1061 // Invalid version error. 1062 {case9Data, Policy{}, true}, 1063 // Duplicate statement success, duplicate statement removed. 1064 {case10Data, case10Policy, false}, 1065 // Duplicate statement success (Effect differs). 1066 {case11Data, case11Policy, false}, 1067 } 1068 1069 for i, testCase := range testCases { 1070 var result Policy 1071 err := json.Unmarshal(testCase.data, &result) 1072 expectErr := (err != nil) 1073 1074 if expectErr != testCase.expectErr { 1075 t.Errorf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) 1076 } 1077 1078 if !testCase.expectErr { 1079 if !reflect.DeepEqual(result, testCase.expectedResult) { 1080 t.Errorf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result) 1081 } 1082 } 1083 } 1084 } 1085 1086 func TestPolicyValidate(t *testing.T) { 1087 case1Policy := Policy{ 1088 Version: DefaultVersion, 1089 Statements: []Statement{ 1090 NewStatement( 1091 Allow, 1092 NewPrincipal("*"), 1093 NewActionSet(PutObjectAction), 1094 NewResourceSet(NewResource("mybucket", "/myobject*")), 1095 condition.NewFunctions(), 1096 ), 1097 }, 1098 } 1099 1100 func1, err := condition.NewNullFunc( 1101 condition.S3XAmzCopySource, 1102 true, 1103 ) 1104 if err != nil { 1105 t.Fatalf("unexpected error. %v\n", err) 1106 } 1107 func2, err := condition.NewNullFunc( 1108 condition.S3XAmzServerSideEncryption, 1109 false, 1110 ) 1111 if err != nil { 1112 t.Fatalf("unexpected error. %v\n", err) 1113 } 1114 case2Policy := Policy{ 1115 ID: "MyPolicyForMyBucket1", 1116 Version: DefaultVersion, 1117 Statements: []Statement{ 1118 NewStatement( 1119 Allow, 1120 NewPrincipal("*"), 1121 NewActionSet(GetObjectAction, PutObjectAction), 1122 NewResourceSet(NewResource("mybucket", "myobject*")), 1123 condition.NewFunctions(func1, func2), 1124 ), 1125 }, 1126 } 1127 1128 testCases := []struct { 1129 policy Policy 1130 bucketName string 1131 expectErr bool 1132 }{ 1133 {case1Policy, "mybucket", false}, 1134 {case2Policy, "yourbucket", true}, 1135 {case1Policy, "yourbucket", true}, 1136 } 1137 1138 for i, testCase := range testCases { 1139 err := testCase.policy.Validate(testCase.bucketName) 1140 expectErr := (err != nil) 1141 1142 if expectErr != testCase.expectErr { 1143 t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) 1144 } 1145 } 1146 } 1147 1148 func TestPolicyMerge(t *testing.T) { 1149 testCases := []struct { 1150 policy string 1151 }{ 1152 {`{ 1153 "Version": "2012-10-17", 1154 "Id": "S3PolicyId1", 1155 "Statement": [ 1156 { 1157 "Sid": "statement1", 1158 "Effect": "Deny", 1159 "Principal": "*", 1160 "Action":["s3:GetObject", "s3:PutObject"], 1161 "Resource": "arn:aws:s3:::awsexamplebucket1/*" 1162 } 1163 ] 1164 }`}, 1165 {`{ 1166 "Version": "2012-10-17", 1167 "Id": "S3PolicyId1", 1168 "Statement": [ 1169 { 1170 "Sid": "statement1", 1171 "Effect": "Allow", 1172 "Principal": "*", 1173 "Action":"s3:GetObject", 1174 "Resource": "arn:aws:s3:::awsexamplebucket1/*", 1175 "Condition" : { 1176 "IpAddress" : { 1177 "aws:SourceIp": "192.0.2.0/24" 1178 }, 1179 "NotIpAddress" : { 1180 "aws:SourceIp": "192.0.2.188/32" 1181 } 1182 } 1183 } 1184 ] 1185 }`}, 1186 {`{ 1187 "Version": "2012-10-17", 1188 "Statement": [ 1189 { 1190 "Sid": "cross-account permission to user in your own account", 1191 "Effect": "Allow", 1192 "Principal": { 1193 "AWS": "arn:aws:iam::123456789012:user/Dave" 1194 }, 1195 "Action": "s3:PutObject", 1196 "Resource": "arn:aws:s3:::awsexamplebucket1/*" 1197 }, 1198 { 1199 "Sid": "Deny your user permission to upload object if copy source is not /bucket/folder", 1200 "Effect": "Deny", 1201 "Principal": { 1202 "AWS": "arn:aws:iam::123456789012:user/Dave" 1203 }, 1204 "Action": "s3:PutObject", 1205 "Resource": "arn:aws:s3:::awsexamplebucket1/*", 1206 "Condition": { 1207 "StringNotLike": { 1208 "s3:x-amz-copy-source": "awsexamplebucket1/public/*" 1209 } 1210 } 1211 } 1212 ] 1213 }`}, 1214 } 1215 1216 for i, testCase := range testCases { 1217 var p Policy 1218 err := json.Unmarshal([]byte(testCase.policy), &p) 1219 if err != nil { 1220 t.Fatalf("case %v: unexpected error: %v", i+1, err) 1221 } 1222 1223 var clonedPolicy Policy 1224 clonedPolicy = clonedPolicy.Merge(p) 1225 1226 j, err := json.Marshal(clonedPolicy) 1227 if err != nil { 1228 t.Fatalf("case %v: unexpected error: %v", i+1, err) 1229 } 1230 1231 err = json.Unmarshal(j, &clonedPolicy) 1232 if err != nil { 1233 t.Fatalf("case %v: unexpected error: %v", i+1, err) 1234 } 1235 1236 if !clonedPolicy.Statements[0].Equals(p.Statements[0]) { 1237 t.Fatalf("case %v: different policy outcome", i+1) 1238 } 1239 } 1240 }