storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/bucket/policy/statement_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 TestStatementIsAllowed(t *testing.T) { 29 case1Statement := NewStatement( 30 Allow, 31 NewPrincipal("*"), 32 NewActionSet(GetBucketLocationAction, PutObjectAction), 33 NewResourceSet(NewResource("*", "")), 34 condition.NewFunctions(), 35 ) 36 37 case2Statement := NewStatement( 38 Allow, 39 NewPrincipal("*"), 40 NewActionSet(GetObjectAction, PutObjectAction), 41 NewResourceSet(NewResource("mybucket", "/myobject*")), 42 condition.NewFunctions(), 43 ) 44 45 _, IPNet1, err := net.ParseCIDR("192.168.1.0/24") 46 if err != nil { 47 t.Fatalf("unexpected error. %v\n", err) 48 } 49 func1, err := condition.NewIPAddressFunc( 50 condition.AWSSourceIP, 51 IPNet1, 52 ) 53 if err != nil { 54 t.Fatalf("unexpected error. %v\n", err) 55 } 56 57 case3Statement := NewStatement( 58 Allow, 59 NewPrincipal("*"), 60 NewActionSet(GetObjectAction, PutObjectAction), 61 NewResourceSet(NewResource("mybucket", "/myobject*")), 62 condition.NewFunctions(func1), 63 ) 64 65 case4Statement := NewStatement( 66 Deny, 67 NewPrincipal("*"), 68 NewActionSet(GetObjectAction, PutObjectAction), 69 NewResourceSet(NewResource("mybucket", "/myobject*")), 70 condition.NewFunctions(func1), 71 ) 72 73 anonGetBucketLocationArgs := Args{ 74 AccountName: "Q3AM3UQ867SPQQA43P2F", 75 Action: GetBucketLocationAction, 76 BucketName: "mybucket", 77 ConditionValues: map[string][]string{}, 78 } 79 80 anonPutObjectActionArgs := Args{ 81 AccountName: "Q3AM3UQ867SPQQA43P2F", 82 Action: PutObjectAction, 83 BucketName: "mybucket", 84 ConditionValues: map[string][]string{ 85 "x-amz-copy-source": {"mybucket/myobject"}, 86 "SourceIp": {"192.168.1.10"}, 87 }, 88 ObjectName: "myobject", 89 } 90 91 anonGetObjectActionArgs := Args{ 92 AccountName: "Q3AM3UQ867SPQQA43P2F", 93 Action: GetObjectAction, 94 BucketName: "mybucket", 95 ConditionValues: map[string][]string{}, 96 ObjectName: "myobject", 97 } 98 99 getBucketLocationArgs := Args{ 100 AccountName: "Q3AM3UQ867SPQQA43P2F", 101 Action: GetBucketLocationAction, 102 BucketName: "mybucket", 103 ConditionValues: map[string][]string{}, 104 IsOwner: true, 105 } 106 107 putObjectActionArgs := Args{ 108 AccountName: "Q3AM3UQ867SPQQA43P2F", 109 Action: PutObjectAction, 110 BucketName: "mybucket", 111 ConditionValues: map[string][]string{ 112 "x-amz-copy-source": {"mybucket/myobject"}, 113 "SourceIp": {"192.168.1.10"}, 114 }, 115 IsOwner: true, 116 ObjectName: "myobject", 117 } 118 119 getObjectActionArgs := Args{ 120 AccountName: "Q3AM3UQ867SPQQA43P2F", 121 Action: GetObjectAction, 122 BucketName: "mybucket", 123 ConditionValues: map[string][]string{}, 124 IsOwner: true, 125 ObjectName: "myobject", 126 } 127 128 testCases := []struct { 129 statement Statement 130 args Args 131 expectedResult bool 132 }{ 133 {case1Statement, anonGetBucketLocationArgs, true}, 134 {case1Statement, anonPutObjectActionArgs, true}, 135 {case1Statement, anonGetObjectActionArgs, false}, 136 {case1Statement, getBucketLocationArgs, true}, 137 {case1Statement, putObjectActionArgs, true}, 138 {case1Statement, getObjectActionArgs, false}, 139 140 {case2Statement, anonGetBucketLocationArgs, false}, 141 {case2Statement, anonPutObjectActionArgs, true}, 142 {case2Statement, anonGetObjectActionArgs, true}, 143 {case2Statement, getBucketLocationArgs, false}, 144 {case2Statement, putObjectActionArgs, true}, 145 {case2Statement, getObjectActionArgs, true}, 146 147 {case3Statement, anonGetBucketLocationArgs, false}, 148 {case3Statement, anonPutObjectActionArgs, true}, 149 {case3Statement, anonGetObjectActionArgs, false}, 150 {case3Statement, getBucketLocationArgs, false}, 151 {case3Statement, putObjectActionArgs, true}, 152 {case3Statement, getObjectActionArgs, false}, 153 154 {case4Statement, anonGetBucketLocationArgs, true}, 155 {case4Statement, anonPutObjectActionArgs, false}, 156 {case4Statement, anonGetObjectActionArgs, true}, 157 {case4Statement, getBucketLocationArgs, true}, 158 {case4Statement, putObjectActionArgs, false}, 159 {case4Statement, getObjectActionArgs, true}, 160 } 161 162 for i, testCase := range testCases { 163 result := testCase.statement.IsAllowed(testCase.args) 164 165 if result != testCase.expectedResult { 166 t.Fatalf("case %v: expected: %v, got: %v\n", i+1, testCase.expectedResult, result) 167 } 168 } 169 } 170 171 func TestStatementIsValid(t *testing.T) { 172 _, IPNet1, err := net.ParseCIDR("192.168.1.0/24") 173 if err != nil { 174 t.Fatalf("unexpected error. %v\n", err) 175 } 176 func1, err := condition.NewIPAddressFunc( 177 condition.AWSSourceIP, 178 IPNet1, 179 ) 180 if err != nil { 181 t.Fatalf("unexpected error. %v\n", err) 182 } 183 184 func2, err := condition.NewStringEqualsFunc( 185 condition.S3XAmzCopySource, 186 "mybucket/myobject", 187 ) 188 if err != nil { 189 t.Fatalf("unexpected error. %v\n", err) 190 } 191 192 testCases := []struct { 193 statement Statement 194 expectErr bool 195 }{ 196 // Invalid effect error. 197 {NewStatement( 198 Effect("foo"), 199 NewPrincipal("*"), 200 NewActionSet(GetBucketLocationAction, PutObjectAction), 201 NewResourceSet(NewResource("*", "")), 202 condition.NewFunctions(), 203 ), true}, 204 // Invalid principal error. 205 {NewStatement( 206 Allow, 207 NewPrincipal(), 208 NewActionSet(GetBucketLocationAction, PutObjectAction), 209 NewResourceSet(NewResource("*", "")), 210 condition.NewFunctions(), 211 ), true}, 212 // Empty actions error. 213 {NewStatement( 214 Allow, 215 NewPrincipal("*"), 216 NewActionSet(), 217 NewResourceSet(NewResource("*", "")), 218 condition.NewFunctions(), 219 ), true}, 220 // Empty resources error. 221 {NewStatement( 222 Allow, 223 NewPrincipal("*"), 224 NewActionSet(GetBucketLocationAction, PutObjectAction), 225 NewResourceSet(), 226 condition.NewFunctions(), 227 ), true}, 228 // Unsupported resource found for object action. 229 {NewStatement( 230 Allow, 231 NewPrincipal("*"), 232 NewActionSet(GetBucketLocationAction, PutObjectAction), 233 NewResourceSet(NewResource("mybucket", "")), 234 condition.NewFunctions(), 235 ), true}, 236 // Unsupported resource found for bucket action. 237 {NewStatement( 238 Allow, 239 NewPrincipal("*"), 240 NewActionSet(GetBucketLocationAction, PutObjectAction), 241 NewResourceSet(NewResource("mybucket", "myobject*")), 242 condition.NewFunctions(), 243 ), true}, 244 // Unsupported condition key for action. 245 {NewStatement( 246 Allow, 247 NewPrincipal("*"), 248 NewActionSet(GetObjectAction, PutObjectAction), 249 NewResourceSet(NewResource("mybucket", "myobject*")), 250 condition.NewFunctions(func1, func2), 251 ), true}, 252 {NewStatement( 253 Deny, 254 NewPrincipal("*"), 255 NewActionSet(GetObjectAction, PutObjectAction), 256 NewResourceSet(NewResource("mybucket", "myobject*")), 257 condition.NewFunctions(func1), 258 ), false}, 259 } 260 261 for i, testCase := range testCases { 262 err := testCase.statement.isValid() 263 expectErr := (err != nil) 264 265 if expectErr != testCase.expectErr { 266 t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) 267 } 268 } 269 } 270 271 func TestStatementMarshalJSON(t *testing.T) { 272 case1Statement := NewStatement( 273 Allow, 274 NewPrincipal("*"), 275 NewActionSet(PutObjectAction), 276 NewResourceSet(NewResource("mybucket", "/myobject*")), 277 condition.NewFunctions(), 278 ) 279 case1Statement.SID = "SomeId1" 280 case1Data := []byte(`{"Sid":"SomeId1","Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"]}`) 281 282 func1, err := condition.NewNullFunc( 283 condition.S3XAmzCopySource, 284 true, 285 ) 286 if err != nil { 287 t.Fatalf("unexpected error. %v\n", err) 288 } 289 case2Statement := NewStatement( 290 Allow, 291 NewPrincipal("*"), 292 NewActionSet(PutObjectAction), 293 NewResourceSet(NewResource("mybucket", "/myobject*")), 294 condition.NewFunctions(func1), 295 ) 296 case2Data := []byte(`{"Effect":"Allow","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"Null":{"s3:x-amz-copy-source":[true]}}}`) 297 298 func2, err := condition.NewNullFunc( 299 condition.S3XAmzServerSideEncryption, 300 false, 301 ) 302 if err != nil { 303 t.Fatalf("unexpected error. %v\n", err) 304 } 305 case3Statement := NewStatement( 306 Deny, 307 NewPrincipal("*"), 308 NewActionSet(PutObjectAction), 309 NewResourceSet(NewResource("mybucket", "/myobject*")), 310 condition.NewFunctions(func2), 311 ) 312 case3Data := []byte(`{"Effect":"Deny","Principal":{"AWS":["*"]},"Action":["s3:PutObject"],"Resource":["arn:aws:s3:::mybucket/myobject*"],"Condition":{"Null":{"s3:x-amz-server-side-encryption":[false]}}}`) 313 314 case4Statement := NewStatement( 315 Allow, 316 NewPrincipal("*"), 317 NewActionSet(GetObjectAction, PutObjectAction), 318 NewResourceSet(NewResource("mybucket", "myobject*")), 319 condition.NewFunctions(func1, func2), 320 ) 321 322 testCases := []struct { 323 statement Statement 324 expectedResult []byte 325 expectErr bool 326 }{ 327 {case1Statement, case1Data, false}, 328 {case2Statement, case2Data, false}, 329 {case3Statement, case3Data, false}, 330 // Invalid statement error. 331 {case4Statement, nil, true}, 332 } 333 334 for i, testCase := range testCases { 335 result, err := json.Marshal(testCase.statement) 336 expectErr := (err != nil) 337 338 if expectErr != testCase.expectErr { 339 t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) 340 } 341 342 if !testCase.expectErr { 343 if !reflect.DeepEqual(result, testCase.expectedResult) { 344 t.Fatalf("case %v: result: expected: %v, got: %v", i+1, string(testCase.expectedResult), string(result)) 345 } 346 } 347 } 348 } 349 350 func TestStatementUnmarshalJSON(t *testing.T) { 351 case1Data := []byte(`{ 352 "Sid": "SomeId1", 353 "Effect": "Allow", 354 "Principal": "*", 355 "Action": "s3:PutObject", 356 "Resource": "arn:aws:s3:::mybucket/myobject*" 357 }`) 358 case1Statement := NewStatement( 359 Allow, 360 NewPrincipal("*"), 361 NewActionSet(PutObjectAction), 362 NewResourceSet(NewResource("mybucket", "/myobject*")), 363 condition.NewFunctions(), 364 ) 365 case1Statement.SID = "SomeId1" 366 367 case2Data := []byte(`{ 368 "Effect": "Allow", 369 "Principal": "*", 370 "Action": "s3:PutObject", 371 "Resource": "arn:aws:s3:::mybucket/myobject*", 372 "Condition": { 373 "Null": { 374 "s3:x-amz-copy-source": true 375 } 376 } 377 }`) 378 func1, err := condition.NewNullFunc( 379 condition.S3XAmzCopySource, 380 true, 381 ) 382 if err != nil { 383 t.Fatalf("unexpected error. %v\n", err) 384 } 385 case2Statement := NewStatement( 386 Allow, 387 NewPrincipal("*"), 388 NewActionSet(PutObjectAction), 389 NewResourceSet(NewResource("mybucket", "/myobject*")), 390 condition.NewFunctions(func1), 391 ) 392 393 case3Data := []byte(`{ 394 "Effect": "Deny", 395 "Principal": { 396 "AWS": "*" 397 }, 398 "Action": [ 399 "s3:PutObject", 400 "s3:GetObject" 401 ], 402 "Resource": "arn:aws:s3:::mybucket/myobject*", 403 "Condition": { 404 "Null": { 405 "s3:x-amz-server-side-encryption": "false" 406 } 407 } 408 }`) 409 func2, err := condition.NewNullFunc( 410 condition.S3XAmzServerSideEncryption, 411 false, 412 ) 413 if err != nil { 414 t.Fatalf("unexpected error. %v\n", err) 415 } 416 case3Statement := NewStatement( 417 Deny, 418 NewPrincipal("*"), 419 NewActionSet(PutObjectAction, GetObjectAction), 420 NewResourceSet(NewResource("mybucket", "/myobject*")), 421 condition.NewFunctions(func2), 422 ) 423 424 case4Data := []byte(`{ 425 "Effect": "Allow", 426 "Principal": "Q3AM3UQ867SPQQA43P2F", 427 "Action": "s3:PutObject", 428 "Resource": "arn:aws:s3:::mybucket/myobject*" 429 }`) 430 431 case5Data := []byte(`{ 432 "Principal": "*", 433 "Action": "s3:PutObject", 434 "Resource": "arn:aws:s3:::mybucket/myobject*" 435 }`) 436 437 case6Data := []byte(`{ 438 "Effect": "Allow", 439 "Action": "s3:PutObject", 440 "Resource": "arn:aws:s3:::mybucket/myobject*" 441 }`) 442 443 case7Data := []byte(`{ 444 "Effect": "Allow", 445 "Principal": "*", 446 "Resource": "arn:aws:s3:::mybucket/myobject*" 447 }`) 448 449 case8Data := []byte(`{ 450 "Effect": "Allow", 451 "Principal": "*", 452 "Action": "s3:PutObject" 453 }`) 454 455 case9Data := []byte(`{ 456 "Effect": "Allow", 457 "Principal": "*", 458 "Action": "s3:PutObject", 459 "Resource": "arn:aws:s3:::mybucket/myobject*", 460 "Condition": { 461 } 462 }`) 463 464 case10Data := []byte(`{ 465 "Effect": "Deny", 466 "Principal": { 467 "AWS": "*" 468 }, 469 "Action": [ 470 "s3:PutObject", 471 "s3:GetObject" 472 ], 473 "Resource": "arn:aws:s3:::mybucket/myobject*", 474 "Condition": { 475 "StringEquals": { 476 "s3:x-amz-copy-source": "yourbucket/myobject*" 477 } 478 } 479 }`) 480 481 testCases := []struct { 482 data []byte 483 expectedResult Statement 484 expectErr bool 485 }{ 486 {case1Data, case1Statement, false}, 487 {case2Data, case2Statement, false}, 488 {case3Data, case3Statement, false}, 489 // JSON unmarshaling error. 490 {case4Data, Statement{}, true}, 491 // Invalid effect error. 492 {case5Data, Statement{}, true}, 493 // empty principal error. 494 {case6Data, Statement{}, true}, 495 // Empty action error. 496 {case7Data, Statement{}, true}, 497 // Empty resource error. 498 {case8Data, Statement{}, true}, 499 // Empty condition error. 500 {case9Data, Statement{}, true}, 501 // Unsupported condition key error. 502 {case10Data, Statement{}, true}, 503 } 504 505 for i, testCase := range testCases { 506 var result Statement 507 err := json.Unmarshal(testCase.data, &result) 508 expectErr := (err != nil) 509 510 if expectErr != testCase.expectErr { 511 t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) 512 } 513 514 if !testCase.expectErr { 515 if !reflect.DeepEqual(result, testCase.expectedResult) { 516 t.Fatalf("case %v: result: expected: %v, got: %v", i+1, testCase.expectedResult, result) 517 } 518 } 519 } 520 } 521 522 func TestStatementValidate(t *testing.T) { 523 case1Statement := NewStatement( 524 Allow, 525 NewPrincipal("*"), 526 NewActionSet(PutObjectAction), 527 NewResourceSet(NewResource("mybucket", "/myobject*")), 528 condition.NewFunctions(), 529 ) 530 531 func1, err := condition.NewNullFunc( 532 condition.S3XAmzCopySource, 533 true, 534 ) 535 if err != nil { 536 t.Fatalf("unexpected error. %v\n", err) 537 } 538 func2, err := condition.NewNullFunc( 539 condition.S3XAmzServerSideEncryption, 540 false, 541 ) 542 if err != nil { 543 t.Fatalf("unexpected error. %v\n", err) 544 } 545 case2Statement := NewStatement( 546 Allow, 547 NewPrincipal("*"), 548 NewActionSet(GetObjectAction, PutObjectAction), 549 NewResourceSet(NewResource("mybucket", "myobject*")), 550 condition.NewFunctions(func1, func2), 551 ) 552 553 testCases := []struct { 554 statement Statement 555 bucketName string 556 expectErr bool 557 }{ 558 {case1Statement, "mybucket", false}, 559 {case2Statement, "mybucket", true}, 560 {case1Statement, "yourbucket", true}, 561 } 562 563 for i, testCase := range testCases { 564 err := testCase.statement.Validate(testCase.bucketName) 565 expectErr := (err != nil) 566 567 if expectErr != testCase.expectErr { 568 t.Fatalf("case %v: error: expected: %v, got: %v", i+1, testCase.expectErr, expectErr) 569 } 570 } 571 }