github.com/hernad/nomad@v1.6.112/nomad/structs/acl_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package structs 5 6 import ( 7 "errors" 8 "fmt" 9 "testing" 10 "time" 11 12 "github.com/hashicorp/go-multierror" 13 "github.com/hernad/nomad/ci" 14 "github.com/hernad/nomad/helper/pointer" 15 "github.com/hernad/nomad/helper/uuid" 16 "github.com/shoenig/test/must" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func TestACLToken_Canonicalize(t *testing.T) { 21 testCases := []struct { 22 name string 23 testFn func() 24 }{ 25 { 26 name: "token with accessor", 27 testFn: func() { 28 mockToken := &ACLToken{ 29 AccessorID: uuid.Generate(), 30 SecretID: uuid.Generate(), 31 Name: "my cool token " + uuid.Generate(), 32 Type: "client", 33 Policies: []string{"foo", "bar"}, 34 Roles: []*ACLTokenRoleLink{}, 35 Global: false, 36 CreateTime: time.Now().UTC(), 37 CreateIndex: 10, 38 ModifyIndex: 20, 39 } 40 mockToken.SetHash() 41 copiedMockToken := mockToken.Copy() 42 43 mockToken.Canonicalize() 44 require.Equal(t, copiedMockToken, mockToken) 45 }, 46 }, 47 { 48 name: "token without accessor", 49 testFn: func() { 50 mockToken := &ACLToken{ 51 Name: "my cool token " + uuid.Generate(), 52 Type: "client", 53 Policies: []string{"foo", "bar"}, 54 Global: false, 55 } 56 57 mockToken.Canonicalize() 58 require.NotEmpty(t, mockToken.AccessorID) 59 require.NotEmpty(t, mockToken.SecretID) 60 require.NotEmpty(t, mockToken.CreateTime) 61 }, 62 }, 63 { 64 name: "token with ttl without accessor", 65 testFn: func() { 66 mockToken := &ACLToken{ 67 Name: "my cool token " + uuid.Generate(), 68 Type: "client", 69 Policies: []string{"foo", "bar"}, 70 Global: false, 71 ExpirationTTL: 10 * time.Hour, 72 } 73 74 mockToken.Canonicalize() 75 require.NotEmpty(t, mockToken.AccessorID) 76 require.NotEmpty(t, mockToken.SecretID) 77 require.NotEmpty(t, mockToken.CreateTime) 78 require.NotEmpty(t, mockToken.ExpirationTime) 79 }, 80 }, 81 } 82 83 for _, tc := range testCases { 84 t.Run(tc.name, func(t *testing.T) { 85 tc.testFn() 86 }) 87 } 88 } 89 90 func TestACLTokenValidate(t *testing.T) { 91 ci.Parallel(t) 92 93 testCases := []struct { 94 name string 95 inputACLToken *ACLToken 96 inputExistingACLToken *ACLToken 97 expectedErrorContains string 98 }{ 99 { 100 name: "missing type", 101 inputACLToken: &ACLToken{}, 102 inputExistingACLToken: nil, 103 expectedErrorContains: "client or management", 104 }, 105 { 106 name: "missing policies or roles", 107 inputACLToken: &ACLToken{ 108 Type: ACLClientToken, 109 }, 110 inputExistingACLToken: nil, 111 expectedErrorContains: "missing policies or roles", 112 }, 113 { 114 name: "invalid policies", 115 inputACLToken: &ACLToken{ 116 Type: ACLManagementToken, 117 Policies: []string{"foo"}, 118 }, 119 inputExistingACLToken: nil, 120 expectedErrorContains: "associated with policies or roles", 121 }, 122 { 123 name: "invalid roles", 124 inputACLToken: &ACLToken{ 125 Type: ACLManagementToken, 126 Roles: []*ACLTokenRoleLink{{Name: "foo"}}, 127 }, 128 inputExistingACLToken: nil, 129 expectedErrorContains: "associated with policies or roles", 130 }, 131 { 132 name: "name too long", 133 inputACLToken: &ACLToken{ 134 Type: ACLManagementToken, 135 Name: uuid.Generate() + uuid.Generate() + uuid.Generate() + uuid.Generate() + 136 uuid.Generate() + uuid.Generate() + uuid.Generate() + uuid.Generate(), 137 }, 138 inputExistingACLToken: nil, 139 expectedErrorContains: "name too long", 140 }, 141 { 142 name: "negative TTL", 143 inputACLToken: &ACLToken{ 144 Type: ACLManagementToken, 145 Name: "foo", 146 ExpirationTTL: -1 * time.Hour, 147 }, 148 inputExistingACLToken: nil, 149 expectedErrorContains: "should not be negative", 150 }, 151 { 152 name: "TTL too small", 153 inputACLToken: &ACLToken{ 154 Type: ACLManagementToken, 155 Name: "foo", 156 CreateTime: time.Date(2022, time.July, 11, 16, 23, 0, 0, time.UTC), 157 ExpirationTime: pointer.Of(time.Date(2022, time.July, 11, 16, 23, 10, 0, time.UTC)), 158 }, 159 inputExistingACLToken: nil, 160 expectedErrorContains: "expiration time cannot be less than", 161 }, 162 { 163 name: "TTL too large", 164 inputACLToken: &ACLToken{ 165 Type: ACLManagementToken, 166 Name: "foo", 167 CreateTime: time.Date(2022, time.July, 11, 16, 23, 0, 0, time.UTC), 168 ExpirationTime: pointer.Of(time.Date(2042, time.July, 11, 16, 23, 0, 0, time.UTC)), 169 }, 170 inputExistingACLToken: nil, 171 expectedErrorContains: "expiration time cannot be more than", 172 }, 173 { 174 name: "valid management", 175 inputACLToken: &ACLToken{ 176 Type: ACLManagementToken, 177 Name: "foo", 178 }, 179 inputExistingACLToken: nil, 180 expectedErrorContains: "", 181 }, 182 { 183 name: "valid client", 184 inputACLToken: &ACLToken{ 185 Type: ACLClientToken, 186 Name: "foo", 187 Policies: []string{"foo"}, 188 }, 189 inputExistingACLToken: nil, 190 expectedErrorContains: "", 191 }, 192 } 193 194 for _, tc := range testCases { 195 t.Run(tc.name, func(t *testing.T) { 196 actualOutputError := tc.inputACLToken.Validate(1*time.Minute, 24*time.Hour, tc.inputExistingACLToken) 197 if tc.expectedErrorContains != "" { 198 require.ErrorContains(t, actualOutputError, tc.expectedErrorContains) 199 } else { 200 require.NoError(t, actualOutputError) 201 } 202 }) 203 } 204 } 205 206 func TestACLToken_HasExpirationTime(t *testing.T) { 207 testCases := []struct { 208 name string 209 inputACLToken *ACLToken 210 expectedOutput bool `` 211 }{ 212 { 213 name: "nil acl token", 214 inputACLToken: nil, 215 expectedOutput: false, 216 }, 217 { 218 name: "default empty value", 219 inputACLToken: &ACLToken{}, 220 expectedOutput: false, 221 }, 222 { 223 name: "expiration set to now", 224 inputACLToken: &ACLToken{ 225 ExpirationTime: pointer.Of(time.Now().UTC()), 226 }, 227 expectedOutput: true, 228 }, 229 { 230 name: "expiration set to past", 231 inputACLToken: &ACLToken{ 232 ExpirationTime: pointer.Of(time.Date(2022, time.February, 21, 19, 35, 0, 0, time.UTC)), 233 }, 234 expectedOutput: true, 235 }, 236 { 237 name: "expiration set to future", 238 inputACLToken: &ACLToken{ 239 ExpirationTime: pointer.Of(time.Date(2087, time.April, 25, 12, 0, 0, 0, time.UTC)), 240 }, 241 expectedOutput: true, 242 }, 243 } 244 245 for _, tc := range testCases { 246 t.Run(tc.name, func(t *testing.T) { 247 actualOutput := tc.inputACLToken.HasExpirationTime() 248 require.Equal(t, tc.expectedOutput, actualOutput) 249 }) 250 } 251 } 252 253 func TestACLToken_IsExpired(t *testing.T) { 254 testCases := []struct { 255 name string 256 inputACLToken *ACLToken 257 inputTime time.Time 258 expectedOutput bool 259 }{ 260 { 261 name: "token without expiry", 262 inputACLToken: &ACLToken{}, 263 inputTime: time.Now().UTC(), 264 expectedOutput: false, 265 }, 266 { 267 name: "empty input time", 268 inputACLToken: &ACLToken{}, 269 inputTime: time.Time{}, 270 expectedOutput: false, 271 }, 272 { 273 name: "token not expired", 274 inputACLToken: &ACLToken{ 275 ExpirationTime: pointer.Of(time.Date(2022, time.May, 9, 10, 27, 0, 0, time.UTC)), 276 }, 277 inputTime: time.Date(2022, time.May, 9, 10, 26, 0, 0, time.UTC), 278 expectedOutput: false, 279 }, 280 { 281 name: "token expired", 282 inputACLToken: &ACLToken{ 283 ExpirationTime: pointer.Of(time.Date(2022, time.May, 9, 10, 27, 0, 0, time.UTC)), 284 }, 285 inputTime: time.Date(2022, time.May, 9, 10, 28, 0, 0, time.UTC), 286 expectedOutput: true, 287 }, 288 { 289 name: "empty input time", 290 inputACLToken: &ACLToken{ 291 ExpirationTime: pointer.Of(time.Date(2022, time.May, 9, 10, 27, 0, 0, time.UTC)), 292 }, 293 inputTime: time.Time{}, 294 expectedOutput: true, 295 }, 296 } 297 298 for _, tc := range testCases { 299 t.Run(tc.name, func(t *testing.T) { 300 actualOutput := tc.inputACLToken.IsExpired(tc.inputTime) 301 require.Equal(t, tc.expectedOutput, actualOutput) 302 }) 303 } 304 } 305 306 func TestACLToken_HasRoles(t *testing.T) { 307 testCases := []struct { 308 name string 309 inputToken *ACLToken 310 inputRoleIDs []string 311 expectedOutput bool 312 }{ 313 { 314 name: "client token request all subset", 315 inputToken: &ACLToken{ 316 Type: ACLClientToken, 317 Roles: []*ACLTokenRoleLink{ 318 {ID: "foo"}, 319 {ID: "bar"}, 320 {ID: "baz"}, 321 }, 322 }, 323 inputRoleIDs: []string{"foo", "bar", "baz"}, 324 expectedOutput: true, 325 }, 326 { 327 name: "client token request partial subset", 328 inputToken: &ACLToken{ 329 Type: ACLClientToken, 330 Roles: []*ACLTokenRoleLink{ 331 {ID: "foo"}, 332 {ID: "bar"}, 333 {ID: "baz"}, 334 }, 335 }, 336 inputRoleIDs: []string{"foo", "baz"}, 337 expectedOutput: true, 338 }, 339 { 340 name: "client token request one subset", 341 inputToken: &ACLToken{ 342 Type: ACLClientToken, 343 Roles: []*ACLTokenRoleLink{ 344 {ID: "foo"}, 345 {ID: "bar"}, 346 {ID: "baz"}, 347 }, 348 }, 349 inputRoleIDs: []string{"baz"}, 350 expectedOutput: true, 351 }, 352 { 353 name: "client token request no subset", 354 inputToken: &ACLToken{ 355 Type: ACLClientToken, 356 Roles: []*ACLTokenRoleLink{ 357 {ID: "foo"}, 358 {ID: "bar"}, 359 {ID: "baz"}, 360 }, 361 }, 362 inputRoleIDs: []string{"new"}, 363 expectedOutput: false, 364 }, 365 } 366 367 for _, tc := range testCases { 368 t.Run(tc.name, func(t *testing.T) { 369 actualOutput := tc.inputToken.HasRoles(tc.inputRoleIDs) 370 require.Equal(t, tc.expectedOutput, actualOutput) 371 }) 372 } 373 } 374 375 func TestACLRole_SetHash(t *testing.T) { 376 testCases := []struct { 377 name string 378 inputACLRole *ACLRole 379 expectedOutput []byte 380 }{ 381 { 382 name: "no hash set", 383 inputACLRole: &ACLRole{ 384 Name: "acl-role", 385 Description: "mocked-test-acl-role", 386 Policies: []*ACLRolePolicyLink{ 387 {Name: "mocked-test-policy-1"}, 388 {Name: "mocked-test-policy-2"}, 389 }, 390 CreateIndex: 10, 391 ModifyIndex: 10, 392 Hash: []byte{}, 393 }, 394 expectedOutput: []byte{ 395 122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160, 396 171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102, 397 }, 398 }, 399 { 400 name: "hash set with change", 401 inputACLRole: &ACLRole{ 402 Name: "acl-role", 403 Description: "mocked-test-acl-role", 404 Policies: []*ACLRolePolicyLink{ 405 {Name: "mocked-test-policy-1"}, 406 {Name: "mocked-test-policy-2"}, 407 }, 408 CreateIndex: 10, 409 ModifyIndex: 10, 410 Hash: []byte{ 411 137, 147, 2, 29, 53, 94, 78, 13, 45, 51, 127, 193, 21, 248, 230, 126, 34, 412 106, 216, 73, 248, 219, 209, 146, 204, 107, 185, 2, 89, 255, 198, 5, 413 }, 414 }, 415 expectedOutput: []byte{ 416 122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160, 417 171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102, 418 }, 419 }, 420 } 421 422 for _, tc := range testCases { 423 t.Run(tc.name, func(t *testing.T) { 424 actualOutput := tc.inputACLRole.SetHash() 425 require.Equal(t, tc.expectedOutput, actualOutput) 426 require.Equal(t, tc.inputACLRole.Hash, actualOutput) 427 }) 428 } 429 } 430 431 func TestACLRole_Validate(t *testing.T) { 432 testCases := []struct { 433 name string 434 inputACLRole *ACLRole 435 expectedError bool 436 expectedErrorContains string 437 }{ 438 { 439 name: "role name too long", 440 inputACLRole: &ACLRole{ 441 Name: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 442 }, 443 expectedError: true, 444 expectedErrorContains: "invalid name", 445 }, 446 { 447 name: "role name too short", 448 inputACLRole: &ACLRole{ 449 Name: "", 450 }, 451 expectedError: true, 452 expectedErrorContains: "invalid name", 453 }, 454 { 455 name: "role name with invalid characters", 456 inputACLRole: &ACLRole{ 457 Name: "--#$%$^%_%%_?>", 458 }, 459 expectedError: true, 460 expectedErrorContains: "invalid name", 461 }, 462 { 463 name: "description too long", 464 inputACLRole: &ACLRole{ 465 Name: "acl-role", 466 Description: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", 467 }, 468 expectedError: true, 469 expectedErrorContains: "description longer than", 470 }, 471 { 472 name: "no policies", 473 inputACLRole: &ACLRole{ 474 Name: "acl-role", 475 Description: "", 476 }, 477 expectedError: true, 478 expectedErrorContains: "at least one policy should be specified", 479 }, 480 { 481 name: "valid", 482 inputACLRole: &ACLRole{ 483 Name: "acl-role", 484 Description: "", 485 Policies: []*ACLRolePolicyLink{ 486 {Name: "policy-1"}, 487 }, 488 }, 489 expectedError: false, 490 expectedErrorContains: "", 491 }, 492 } 493 494 for _, tc := range testCases { 495 t.Run(tc.name, func(t *testing.T) { 496 actualOutput := tc.inputACLRole.Validate() 497 if tc.expectedError { 498 require.ErrorContains(t, actualOutput, tc.expectedErrorContains) 499 } else { 500 require.NoError(t, actualOutput) 501 } 502 }) 503 } 504 } 505 506 func TestACLRole_Canonicalize(t *testing.T) { 507 testCases := []struct { 508 name string 509 inputACLRole *ACLRole 510 }{ 511 { 512 name: "no ID set", 513 inputACLRole: &ACLRole{}, 514 }, 515 { 516 name: "id set", 517 inputACLRole: &ACLRole{ID: "some-random-uuid"}, 518 }, 519 } 520 521 for _, tc := range testCases { 522 t.Run(tc.name, func(t *testing.T) { 523 existing := tc.inputACLRole.Copy() 524 tc.inputACLRole.Canonicalize() 525 if existing.ID == "" { 526 require.NotEmpty(t, tc.inputACLRole.ID) 527 } else { 528 require.Equal(t, existing.ID, tc.inputACLRole.ID) 529 } 530 }) 531 } 532 } 533 534 func TestACLRole_Equals(t *testing.T) { 535 testCases := []struct { 536 name string 537 composedACLRole *ACLRole 538 inputACLRole *ACLRole 539 expectedOutput bool 540 }{ 541 { 542 name: "equal with hash set", 543 composedACLRole: &ACLRole{ 544 Name: "acl-role-", 545 Description: "mocked-test-acl-role", 546 Policies: []*ACLRolePolicyLink{ 547 {Name: "mocked-test-policy-1"}, 548 {Name: "mocked-test-policy-2"}, 549 }, 550 CreateIndex: 10, 551 ModifyIndex: 10, 552 Hash: []byte{ 553 122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160, 554 171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102, 555 }, 556 }, 557 inputACLRole: &ACLRole{ 558 Name: "acl-role", 559 Description: "mocked-test-acl-role", 560 Policies: []*ACLRolePolicyLink{ 561 {Name: "mocked-test-policy-1"}, 562 {Name: "mocked-test-policy-2"}, 563 }, 564 CreateIndex: 10, 565 ModifyIndex: 10, 566 Hash: []byte{ 567 122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160, 568 171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102, 569 }, 570 }, 571 expectedOutput: true, 572 }, 573 { 574 name: "equal without hash set", 575 composedACLRole: &ACLRole{ 576 Name: "acl-role", 577 Description: "mocked-test-acl-role", 578 Policies: []*ACLRolePolicyLink{ 579 {Name: "mocked-test-policy-1"}, 580 {Name: "mocked-test-policy-2"}, 581 }, 582 CreateIndex: 10, 583 ModifyIndex: 10, 584 Hash: []byte{}, 585 }, 586 inputACLRole: &ACLRole{ 587 Name: "acl-role", 588 Description: "mocked-test-acl-role", 589 Policies: []*ACLRolePolicyLink{ 590 {Name: "mocked-test-policy-1"}, 591 {Name: "mocked-test-policy-2"}, 592 }, 593 CreateIndex: 10, 594 ModifyIndex: 10, 595 Hash: []byte{}, 596 }, 597 expectedOutput: true, 598 }, 599 { 600 name: "both nil", 601 composedACLRole: nil, 602 inputACLRole: nil, 603 expectedOutput: true, 604 }, 605 { 606 name: "not equal composed nil", 607 composedACLRole: nil, 608 inputACLRole: &ACLRole{ 609 Name: "acl-role", 610 Description: "mocked-test-acl-role", 611 Policies: []*ACLRolePolicyLink{ 612 {Name: "mocked-test-policy-1"}, 613 {Name: "mocked-test-policy-2"}, 614 }, 615 CreateIndex: 10, 616 ModifyIndex: 10, 617 Hash: []byte{ 618 122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160, 619 171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102, 620 }, 621 }, 622 expectedOutput: false, 623 }, 624 { 625 name: "not equal input nil", 626 composedACLRole: &ACLRole{ 627 Name: "acl-role", 628 Description: "mocked-test-acl-role", 629 Policies: []*ACLRolePolicyLink{ 630 {Name: "mocked-test-policy-1"}, 631 {Name: "mocked-test-policy-2"}, 632 }, 633 CreateIndex: 10, 634 ModifyIndex: 10, 635 Hash: []byte{ 636 122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160, 637 171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102, 638 }, 639 }, 640 inputACLRole: nil, 641 expectedOutput: false, 642 }, 643 { 644 name: "not equal with hash set", 645 composedACLRole: &ACLRole{ 646 Name: "acl-role", 647 Description: "mocked-test-acl-role", 648 Policies: []*ACLRolePolicyLink{ 649 {Name: "mocked-test-policy-1"}, 650 {Name: "mocked-test-policy-2"}, 651 }, 652 CreateIndex: 10, 653 ModifyIndex: 10, 654 Hash: []byte{ 655 122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160, 656 171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102, 657 }, 658 }, 659 inputACLRole: &ACLRole{ 660 Name: "acl-role", 661 Description: "mocked-test-acl-role", 662 Policies: []*ACLRolePolicyLink{ 663 {Name: "mocked-test-policy-1"}, 664 }, 665 CreateIndex: 10, 666 ModifyIndex: 10, 667 Hash: []byte{ 668 137, 147, 2, 29, 53, 94, 78, 13, 45, 51, 127, 193, 21, 248, 230, 126, 34, 669 106, 216, 73, 248, 219, 209, 146, 204, 107, 185, 2, 89, 255, 198, 5, 670 }, 671 }, 672 expectedOutput: false, 673 }, 674 { 675 name: "not equal without hash set", 676 composedACLRole: &ACLRole{ 677 Name: "acl-role", 678 Description: "mocked-test-acl-role", 679 Policies: []*ACLRolePolicyLink{ 680 {Name: "mocked-test-policy-1"}, 681 {Name: "mocked-test-policy-2"}, 682 }, 683 CreateIndex: 10, 684 ModifyIndex: 10, 685 Hash: []byte{}, 686 }, 687 inputACLRole: &ACLRole{ 688 Name: "acl-role", 689 Description: "mocked-test-acl-role", 690 Policies: []*ACLRolePolicyLink{ 691 {Name: "mocked-test-policy-1"}, 692 }, 693 CreateIndex: 10, 694 ModifyIndex: 10, 695 Hash: []byte{}, 696 }, 697 expectedOutput: false, 698 }, 699 } 700 701 for _, tc := range testCases { 702 t.Run(tc.name, func(t *testing.T) { 703 actualOutput := tc.composedACLRole.Equal(tc.inputACLRole) 704 require.Equal(t, tc.expectedOutput, actualOutput) 705 }) 706 } 707 } 708 709 func TestACLRole_Copy(t *testing.T) { 710 testCases := []struct { 711 name string 712 inputACLRole *ACLRole 713 }{ 714 { 715 name: "nil input", 716 inputACLRole: nil, 717 }, 718 { 719 name: "general 1", 720 inputACLRole: &ACLRole{ 721 Name: "acl-role", 722 Description: "mocked-test-acl-role", 723 Policies: []*ACLRolePolicyLink{ 724 {Name: "mocked-test-policy-1"}, 725 {Name: "mocked-test-policy-2"}, 726 }, 727 CreateIndex: 10, 728 ModifyIndex: 10, 729 Hash: []byte{ 730 122, 193, 189, 171, 197, 13, 37, 81, 141, 213, 188, 212, 179, 223, 148, 160, 731 171, 141, 155, 136, 21, 128, 252, 100, 149, 195, 236, 148, 94, 70, 173, 102, 732 }, 733 }, 734 }, 735 } 736 737 for _, tc := range testCases { 738 t.Run(tc.name, func(t *testing.T) { 739 actualOutput := tc.inputACLRole.Copy() 740 require.Equal(t, tc.inputACLRole, actualOutput) 741 }) 742 } 743 } 744 745 func TestACLRole_Stub(t *testing.T) { 746 testCases := []struct { 747 name string 748 inputACLRole *ACLRole 749 expectedOutput *ACLRoleListStub 750 }{ 751 { 752 name: "partially hydrated", 753 inputACLRole: &ACLRole{ 754 ID: "1d6332c8-02d7-325e-f675-a9bb4aff0c51", 755 Name: "my-lovely-role", 756 Description: "", 757 Policies: []*ACLRolePolicyLink{ 758 {Name: "my-lovely-policy"}, 759 }, 760 Hash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, 761 CreateIndex: 24, 762 ModifyIndex: 24, 763 }, 764 expectedOutput: &ACLRoleListStub{ 765 ID: "1d6332c8-02d7-325e-f675-a9bb4aff0c51", 766 Name: "my-lovely-role", 767 Description: "", 768 Policies: []*ACLRolePolicyLink{ 769 {Name: "my-lovely-policy"}, 770 }, 771 Hash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, 772 CreateIndex: 24, 773 ModifyIndex: 24, 774 }, 775 }, 776 { 777 name: "hully hydrated", 778 inputACLRole: &ACLRole{ 779 ID: "1d6332c8-02d7-325e-f675-a9bb4aff0c51", 780 Name: "my-lovely-role", 781 Description: "this-is-my-lovely-role", 782 Policies: []*ACLRolePolicyLink{ 783 {Name: "my-lovely-policy"}, 784 }, 785 Hash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, 786 CreateIndex: 24, 787 ModifyIndex: 24, 788 }, 789 expectedOutput: &ACLRoleListStub{ 790 ID: "1d6332c8-02d7-325e-f675-a9bb4aff0c51", 791 Name: "my-lovely-role", 792 Description: "this-is-my-lovely-role", 793 Policies: []*ACLRolePolicyLink{ 794 {Name: "my-lovely-policy"}, 795 }, 796 Hash: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}, 797 CreateIndex: 24, 798 ModifyIndex: 24, 799 }, 800 }, 801 } 802 803 for _, tc := range testCases { 804 t.Run(tc.name, func(t *testing.T) { 805 actualOutput := tc.inputACLRole.Stub() 806 require.Equal(t, tc.expectedOutput, actualOutput) 807 }) 808 } 809 } 810 811 func Test_ACLRolesUpsertRequest(t *testing.T) { 812 req := ACLRolesUpsertRequest{} 813 require.False(t, req.IsRead()) 814 } 815 816 func Test_ACLRolesDeleteByIDRequest(t *testing.T) { 817 req := ACLRolesDeleteByIDRequest{} 818 require.False(t, req.IsRead()) 819 } 820 821 func Test_ACLRolesListRequest(t *testing.T) { 822 req := ACLRolesListRequest{} 823 require.True(t, req.IsRead()) 824 } 825 826 func Test_ACLRolesByIDRequest(t *testing.T) { 827 req := ACLRolesByIDRequest{} 828 require.True(t, req.IsRead()) 829 } 830 831 func Test_ACLRoleByIDRequest(t *testing.T) { 832 req := ACLRoleByIDRequest{} 833 require.True(t, req.IsRead()) 834 } 835 836 func Test_ACLRoleByNameRequest(t *testing.T) { 837 req := ACLRoleByNameRequest{} 838 require.True(t, req.IsRead()) 839 } 840 841 func Test_ACLAuthMethodListRequest(t *testing.T) { 842 req := ACLAuthMethodListRequest{} 843 must.True(t, req.IsRead()) 844 } 845 846 func Test_ACLAuthMethodGetRequest(t *testing.T) { 847 req := ACLAuthMethodGetRequest{} 848 must.True(t, req.IsRead()) 849 } 850 851 func TestACLAuthMethodSetHash(t *testing.T) { 852 ci.Parallel(t) 853 854 am := &ACLAuthMethod{ 855 Name: "foo", 856 Type: "bad type", 857 } 858 out1 := am.SetHash() 859 must.NotNil(t, out1) 860 must.NotNil(t, am.Hash) 861 must.Eq(t, out1, am.Hash) 862 863 am.Type = "good type" 864 out2 := am.SetHash() 865 must.NotNil(t, out2) 866 must.NotNil(t, am.Hash) 867 must.Eq(t, out2, am.Hash) 868 must.NotEq(t, out1, out2) 869 } 870 871 func TestACLAuthMethod_Stub(t *testing.T) { 872 ci.Parallel(t) 873 874 maxTokenTTL, _ := time.ParseDuration("3600s") 875 am := ACLAuthMethod{ 876 Name: fmt.Sprintf("acl-auth-method-%s", uuid.Short()), 877 Type: "acl-auth-mock-type", 878 TokenLocality: "locality", 879 MaxTokenTTL: maxTokenTTL, 880 Default: true, 881 Config: &ACLAuthMethodConfig{ 882 OIDCDiscoveryURL: "http://example.com", 883 OIDCClientID: "mock", 884 OIDCClientSecret: "very secret secret", 885 BoundAudiences: []string{"audience1", "audience2"}, 886 AllowedRedirectURIs: []string{"foo", "bar"}, 887 DiscoveryCaPem: []string{"foo"}, 888 SigningAlgs: []string{"bar"}, 889 ClaimMappings: map[string]string{"foo": "bar"}, 890 ListClaimMappings: map[string]string{"foo": "bar"}, 891 }, 892 CreateTime: time.Now().UTC(), 893 CreateIndex: 10, 894 ModifyIndex: 10, 895 } 896 am.SetHash() 897 898 must.Eq(t, am.Stub(), &ACLAuthMethodStub{ 899 Name: am.Name, 900 Type: am.Type, 901 Default: am.Default, 902 Hash: am.Hash, 903 CreateIndex: am.CreateIndex, 904 ModifyIndex: am.ModifyIndex, 905 }) 906 907 nilAuthMethod := &ACLAuthMethod{} 908 must.Eq(t, nilAuthMethod.Stub(), &ACLAuthMethodStub{}) 909 } 910 911 func TestACLAuthMethod_Equal(t *testing.T) { 912 ci.Parallel(t) 913 914 maxTokenTTL, _ := time.ParseDuration("3600s") 915 am1 := &ACLAuthMethod{ 916 Name: fmt.Sprintf("acl-auth-method-%s", uuid.Short()), 917 Type: "acl-auth-mock-type", 918 TokenLocality: "locality", 919 MaxTokenTTL: maxTokenTTL, 920 Default: true, 921 Config: &ACLAuthMethodConfig{ 922 OIDCDiscoveryURL: "http://example.com", 923 OIDCClientID: "mock", 924 OIDCClientSecret: "very secret secret", 925 BoundAudiences: []string{"audience1", "audience2"}, 926 AllowedRedirectURIs: []string{"foo", "bar"}, 927 DiscoveryCaPem: []string{"foo"}, 928 SigningAlgs: []string{"bar"}, 929 ClaimMappings: map[string]string{"foo": "bar"}, 930 ListClaimMappings: map[string]string{"foo": "bar"}, 931 }, 932 CreateTime: time.Now().UTC(), 933 CreateIndex: 10, 934 ModifyIndex: 10, 935 } 936 am1.SetHash() 937 938 // am2 differs from am1 by 1 nested conf field 939 am2 := am1.Copy() 940 am2.Config.OIDCClientID = "mock2" 941 am2.SetHash() 942 943 tests := []struct { 944 name string 945 method1 *ACLAuthMethod 946 method2 *ACLAuthMethod 947 want bool 948 }{ 949 {"one nil", am1, &ACLAuthMethod{}, false}, 950 {"both nil", &ACLAuthMethod{}, &ACLAuthMethod{}, true}, 951 {"one is different than the other", am1, am2, false}, 952 {"equal", am1, am1.Copy(), true}, 953 } 954 for _, tt := range tests { 955 t.Run(tt.name, func(t *testing.T) { 956 got := tt.method1.Equal(tt.method2) 957 must.Eq(t, got, tt.want, must.Sprintf( 958 "ACLAuthMethod.Equal() got %v, want %v, test case: %s", got, tt.want, tt.name)) 959 }) 960 } 961 } 962 963 func TestACLAuthMethod_Copy(t *testing.T) { 964 ci.Parallel(t) 965 966 maxTokenTTL, _ := time.ParseDuration("3600s") 967 am1 := &ACLAuthMethod{ 968 Name: fmt.Sprintf("acl-auth-method-%s", uuid.Short()), 969 Type: "acl-auth-mock-type", 970 TokenLocality: "locality", 971 MaxTokenTTL: maxTokenTTL, 972 Default: true, 973 Config: &ACLAuthMethodConfig{ 974 OIDCDiscoveryURL: "http://example.com", 975 OIDCClientID: "mock", 976 OIDCClientSecret: "very secret secret", 977 BoundAudiences: []string{"audience1", "audience2"}, 978 AllowedRedirectURIs: []string{"foo", "bar"}, 979 DiscoveryCaPem: []string{"foo"}, 980 SigningAlgs: []string{"bar"}, 981 ClaimMappings: map[string]string{"foo": "bar"}, 982 ListClaimMappings: map[string]string{"foo": "bar"}, 983 }, 984 CreateTime: time.Now().UTC(), 985 CreateIndex: 10, 986 ModifyIndex: 10, 987 } 988 am1.SetHash() 989 990 am2 := am1.Copy() 991 am2.SetHash() 992 must.Eq(t, am1, am2) 993 994 am3 := am1.Copy() 995 am3.Config.AllowedRedirectURIs = []string{"new", "urls"} 996 am3.SetHash() 997 must.NotEq(t, am1, am3) 998 } 999 1000 func TestACLAuthMethod_Validate(t *testing.T) { 1001 ci.Parallel(t) 1002 1003 goodTTL, _ := time.ParseDuration("3600s") 1004 badTTL, _ := time.ParseDuration("3600h") 1005 1006 tests := []struct { 1007 name string 1008 method *ACLAuthMethod 1009 wantErr bool 1010 errContains string 1011 }{ 1012 { 1013 "valid method", 1014 &ACLAuthMethod{ 1015 Name: "mock-auth-method", 1016 Type: "OIDC", 1017 TokenLocality: "local", 1018 MaxTokenTTL: goodTTL, 1019 }, 1020 false, 1021 "", 1022 }, 1023 {"invalid name", &ACLAuthMethod{Name: "is this name invalid?"}, true, "invalid name"}, 1024 {"invalid token locality", &ACLAuthMethod{TokenLocality: "regional"}, true, "invalid token locality"}, 1025 {"invalid type", &ACLAuthMethod{Type: "groovy"}, true, "invalid token type"}, 1026 {"invalid max ttl", &ACLAuthMethod{MaxTokenTTL: badTTL}, true, "invalid token type"}, 1027 } 1028 for _, tt := range tests { 1029 t.Run(tt.name, func(t *testing.T) { 1030 minTTL, _ := time.ParseDuration("10s") 1031 maxTTL, _ := time.ParseDuration("10h") 1032 got := tt.method.Validate(minTTL, maxTTL) 1033 if tt.wantErr { 1034 must.Error(t, got, must.Sprintf( 1035 "ACLAuthMethod.Validate() got error, didn't expect it; test case: %s", tt.name)) 1036 must.StrContains(t, got.Error(), tt.errContains, must.Sprintf( 1037 "ACLAuthMethod.Validate() got %v error message, expected %v; test case: %s", 1038 got, tt.errContains, tt.name)) 1039 } else { 1040 must.NoError(t, got, must.Sprintf( 1041 "ACLAuthMethod.Validate() expected an error but didn't get one; test case: %s", tt.name)) 1042 } 1043 }) 1044 } 1045 } 1046 1047 func TestACLAuthMethod_Merge(t *testing.T) { 1048 ci.Parallel(t) 1049 1050 name := fmt.Sprintf("acl-auth-method-%s", uuid.Short()) 1051 1052 maxTokenTTL, _ := time.ParseDuration("3600s") 1053 am1 := &ACLAuthMethod{ 1054 Name: name, 1055 TokenLocality: "global", 1056 } 1057 am2 := &ACLAuthMethod{ 1058 Name: name, 1059 Type: "OIDC", 1060 TokenLocality: "locality", 1061 MaxTokenTTL: maxTokenTTL, 1062 Default: true, 1063 Config: &ACLAuthMethodConfig{ 1064 OIDCDiscoveryURL: "http://example.com", 1065 OIDCClientID: "mock", 1066 OIDCClientSecret: "very secret secret", 1067 BoundAudiences: []string{"audience1", "audience2"}, 1068 AllowedRedirectURIs: []string{"foo", "bar"}, 1069 DiscoveryCaPem: []string{"foo"}, 1070 SigningAlgs: []string{"bar"}, 1071 ClaimMappings: map[string]string{"foo": "bar"}, 1072 ListClaimMappings: map[string]string{"foo": "bar"}, 1073 }, 1074 CreateTime: time.Now().UTC(), 1075 CreateIndex: 10, 1076 ModifyIndex: 10, 1077 } 1078 1079 am1.Merge(am2) 1080 must.Eq(t, am1.TokenLocality, "global") 1081 minTTL, _ := time.ParseDuration("10s") 1082 maxTTL, _ := time.ParseDuration("10h") 1083 must.NoError(t, am1.Validate(minTTL, maxTTL)) 1084 } 1085 1086 func TestACLAuthMethodConfig_Copy(t *testing.T) { 1087 ci.Parallel(t) 1088 1089 amc1 := &ACLAuthMethodConfig{ 1090 OIDCDiscoveryURL: "http://example.com", 1091 OIDCClientID: "mock", 1092 OIDCClientSecret: "very secret secret", 1093 OIDCScopes: []string{"groups"}, 1094 BoundAudiences: []string{"audience1", "audience2"}, 1095 AllowedRedirectURIs: []string{"foo", "bar"}, 1096 DiscoveryCaPem: []string{"foo"}, 1097 SigningAlgs: []string{"bar"}, 1098 ClaimMappings: map[string]string{"foo": "bar"}, 1099 ListClaimMappings: map[string]string{"foo": "bar"}, 1100 } 1101 1102 amc2 := amc1.Copy() 1103 must.Eq(t, amc1, amc2) 1104 1105 amc3 := amc1.Copy() 1106 amc3.AllowedRedirectURIs = []string{"new", "urls"} 1107 must.NotEq(t, amc1, amc3) 1108 } 1109 1110 func TestACLAuthMethod_Canonicalize(t *testing.T) { 1111 now := time.Now().UTC() 1112 tests := []struct { 1113 name string 1114 inputMethod *ACLAuthMethod 1115 }{ 1116 { 1117 "no create time or modify time set", 1118 &ACLAuthMethod{}, 1119 }, 1120 { 1121 "create time set to now, modify time not set", 1122 &ACLAuthMethod{CreateTime: now}, 1123 }, 1124 { 1125 "both create time and modify time set", 1126 &ACLAuthMethod{CreateTime: now, ModifyTime: now.Add(time.Hour)}, 1127 }, 1128 } 1129 for _, tt := range tests { 1130 t.Run(tt.name, func(t *testing.T) { 1131 existing := tt.inputMethod.Copy() 1132 tt.inputMethod.Canonicalize() 1133 if existing.CreateTime.IsZero() { 1134 must.NotEq(t, time.Time{}, tt.inputMethod.CreateTime) 1135 } else { 1136 must.Eq(t, existing.CreateTime, tt.inputMethod.CreateTime) 1137 } 1138 if existing.ModifyTime.IsZero() { 1139 must.NotEq(t, time.Time{}, tt.inputMethod.ModifyTime) 1140 } 1141 }) 1142 } 1143 } 1144 1145 func TestACLAuthMethod_TokenLocalityIsGlobal(t *testing.T) { 1146 ci.Parallel(t) 1147 1148 globalAuthMethod := &ACLAuthMethod{TokenLocality: "global"} 1149 must.True(t, globalAuthMethod.TokenLocalityIsGlobal()) 1150 1151 localAuthMethod := &ACLAuthMethod{TokenLocality: "local"} 1152 must.False(t, localAuthMethod.TokenLocalityIsGlobal()) 1153 } 1154 1155 func TestACLBindingRule_Canonicalize(t *testing.T) { 1156 ci.Parallel(t) 1157 1158 testCases := []struct { 1159 name string 1160 inputACLBindingRule *ACLBindingRule 1161 }{ 1162 { 1163 name: "new binding rule", 1164 inputACLBindingRule: &ACLBindingRule{}, 1165 }, 1166 { 1167 name: "existing binding rule", 1168 inputACLBindingRule: &ACLBindingRule{ 1169 ID: "some-random-uuid", 1170 CreateTime: time.Now().UTC(), 1171 ModifyTime: time.Now().UTC(), 1172 }, 1173 }, 1174 } 1175 1176 for _, tc := range testCases { 1177 t.Run(tc.name, func(t *testing.T) { 1178 1179 // Make a copy, so we can compare the modified object to it. 1180 copiedBindingRule := tc.inputACLBindingRule.Copy() 1181 1182 tc.inputACLBindingRule.Canonicalize() 1183 1184 if copiedBindingRule.ID == "" { 1185 must.NotEq(t, "", tc.inputACLBindingRule.ID) 1186 must.NotEq(t, copiedBindingRule.CreateTime, tc.inputACLBindingRule.CreateTime) 1187 must.NotEq(t, copiedBindingRule.ModifyTime, tc.inputACLBindingRule.ModifyTime) 1188 } else { 1189 must.Eq(t, copiedBindingRule.ID, tc.inputACLBindingRule.ID) 1190 must.Eq(t, copiedBindingRule.CreateTime, tc.inputACLBindingRule.CreateTime) 1191 must.NotEq(t, copiedBindingRule.ModifyTime, tc.inputACLBindingRule.ModifyTime) 1192 } 1193 }) 1194 } 1195 } 1196 1197 func TestACLBindingRule_Validate(t *testing.T) { 1198 ci.Parallel(t) 1199 1200 testCases := []struct { 1201 name string 1202 inputACLBindingRule *ACLBindingRule 1203 expectedError error 1204 }{ 1205 { 1206 name: "valid policy type rule", 1207 inputACLBindingRule: &ACLBindingRule{ 1208 Description: "some short description", 1209 AuthMethod: "auth0", 1210 Selector: "group_name in list.groups", 1211 BindType: ACLBindingRuleBindTypePolicy, 1212 BindName: "some-policy-name", 1213 }, 1214 expectedError: nil, 1215 }, 1216 { 1217 name: "invalid policy type rule", 1218 inputACLBindingRule: &ACLBindingRule{ 1219 Description: "some short description", 1220 AuthMethod: "auth0", 1221 Selector: "group_name in list.groups", 1222 BindType: ACLBindingRuleBindTypePolicy, 1223 BindName: "", 1224 }, 1225 expectedError: &multierror.Error{ 1226 Errors: []error{ 1227 errors.New("bind name is missing"), 1228 }, 1229 }, 1230 }, 1231 { 1232 name: "valid role type rule", 1233 inputACLBindingRule: &ACLBindingRule{ 1234 Description: "some short description", 1235 AuthMethod: "auth0", 1236 Selector: "group_name in list.groups", 1237 BindType: ACLBindingRuleBindTypeRole, 1238 BindName: "some-role-name", 1239 }, 1240 expectedError: nil, 1241 }, 1242 { 1243 name: "invalid role type rule", 1244 inputACLBindingRule: &ACLBindingRule{ 1245 Description: "some short description", 1246 AuthMethod: "auth0", 1247 Selector: "group_name in list.groups", 1248 BindType: ACLBindingRuleBindTypeRole, 1249 BindName: "", 1250 }, 1251 expectedError: &multierror.Error{ 1252 Errors: []error{ 1253 errors.New("bind name is missing"), 1254 }, 1255 }, 1256 }, 1257 { 1258 name: "valid management type rule", 1259 inputACLBindingRule: &ACLBindingRule{ 1260 Description: "some short description", 1261 AuthMethod: "auth0", 1262 Selector: "group_name in list.groups", 1263 BindType: ACLBindingRuleBindTypeManagement, 1264 BindName: "", 1265 }, 1266 expectedError: nil, 1267 }, 1268 { 1269 name: "invalid management type rule", 1270 inputACLBindingRule: &ACLBindingRule{ 1271 Description: "some short description", 1272 AuthMethod: "auth0", 1273 Selector: "group_name in list.groups", 1274 BindType: ACLBindingRuleBindTypeManagement, 1275 BindName: "some-name", 1276 }, 1277 expectedError: &multierror.Error{ 1278 Errors: []error{ 1279 errors.New("bind name should be empty"), 1280 }, 1281 }, 1282 }, 1283 { 1284 name: "invalid selector", 1285 inputACLBindingRule: &ACLBindingRule{ 1286 Description: "some short description", 1287 AuthMethod: "auth0", 1288 Selector: "group-name in list.groups", 1289 BindType: ACLBindingRuleBindTypePolicy, 1290 BindName: "some-policy-name", 1291 }, 1292 expectedError: &multierror.Error{ 1293 Errors: []error{ 1294 errors.New("selector is invalid: 1:6 (5): no match found, expected: \"!=\", \".\", \"==\", \"[\", [ \\t\\r\\n] or [a-zA-Z0-9_/]"), 1295 }, 1296 }, 1297 }, 1298 { 1299 name: "invalid all", 1300 inputACLBindingRule: &ACLBindingRule{ 1301 Description: uuid.Generate() + uuid.Generate() + uuid.Generate() + 1302 uuid.Generate() + uuid.Generate() + uuid.Generate() + 1303 uuid.Generate() + uuid.Generate(), 1304 AuthMethod: "", 1305 Selector: "group-name in list.groups", 1306 BindType: "", 1307 BindName: "", 1308 }, 1309 expectedError: &multierror.Error{ 1310 Errors: []error{ 1311 errors.New("auth method is missing"), 1312 errors.New("description longer than 256"), 1313 errors.New("bind type is missing"), 1314 errors.New("selector is invalid: 1:6 (5): no match found, expected: \"!=\", \".\", \"==\", \"[\", [ \\t\\r\\n] or [a-zA-Z0-9_/]"), 1315 }, 1316 }, 1317 }, 1318 } 1319 1320 for _, tc := range testCases { 1321 t.Run(tc.name, func(t *testing.T) { 1322 must.Eq(t, tc.expectedError, tc.inputACLBindingRule.Validate()) 1323 }) 1324 } 1325 } 1326 1327 func TestACLBindingRule_Merge(t *testing.T) { 1328 ci.Parallel(t) 1329 1330 id := uuid.Short() 1331 br := &ACLBindingRule{ 1332 ID: id, 1333 Description: "old description", 1334 AuthMethod: "example-acl-auth-method", 1335 BindType: "rule", 1336 BindName: "bind name", 1337 CreateTime: time.Now().UTC(), 1338 CreateIndex: 10, 1339 ModifyIndex: 10, 1340 } 1341 1342 // make a description update 1343 br_description_update := &ACLBindingRule{ 1344 ID: id, 1345 Description: "new description", 1346 } 1347 br_description_update.Merge(br) 1348 must.Eq(t, br_description_update.Description, "new description") 1349 must.Eq(t, br_description_update.BindType, "rule") 1350 } 1351 1352 func TestACLBindingRule_SetHash(t *testing.T) { 1353 ci.Parallel(t) 1354 1355 bindingRule := &ACLBindingRule{ 1356 ID: uuid.Generate(), 1357 AuthMethod: "okta", 1358 } 1359 out1 := bindingRule.SetHash() 1360 must.NotNil(t, out1) 1361 must.NotNil(t, bindingRule.Hash) 1362 must.Eq(t, out1, bindingRule.Hash) 1363 1364 bindingRule.Description = "my lovely rule" 1365 out2 := bindingRule.SetHash() 1366 must.NotNil(t, out2) 1367 must.NotNil(t, bindingRule.Hash) 1368 must.Eq(t, out2, bindingRule.Hash) 1369 must.NotEq(t, out1, out2) 1370 } 1371 1372 func TestACLBindingRule_Equal(t *testing.T) { 1373 ci.Parallel(t) 1374 1375 aclBindingRule1 := &ACLBindingRule{ 1376 ID: uuid.Short(), 1377 Description: "mocked-acl-binding-rule", 1378 AuthMethod: "auth0", 1379 Selector: "engineering in list.roles", 1380 BindType: "role-id", 1381 BindName: "eng-ro", 1382 CreateTime: time.Now().UTC(), 1383 ModifyTime: time.Now().UTC(), 1384 CreateIndex: 10, 1385 ModifyIndex: 10, 1386 } 1387 aclBindingRule1.SetHash() 1388 1389 // Create a second binding rule, and modify this from the first. 1390 aclBindingRule2 := aclBindingRule1.Copy() 1391 aclBindingRule2.Description = "" 1392 aclBindingRule2.SetHash() 1393 1394 testCases := []struct { 1395 name string 1396 method1 *ACLBindingRule 1397 method2 *ACLBindingRule 1398 want bool 1399 }{ 1400 {"one nil", aclBindingRule1, &ACLBindingRule{}, false}, 1401 {"both nil", &ACLBindingRule{}, &ACLBindingRule{}, true}, 1402 {"not equal", aclBindingRule1, aclBindingRule2, false}, 1403 {"equal", aclBindingRule2, aclBindingRule2.Copy(), true}, 1404 } 1405 for _, tc := range testCases { 1406 t.Run(tc.name, func(t *testing.T) { 1407 got := tc.method1.Equal(tc.method2) 1408 must.Eq(t, got, tc.want) 1409 }) 1410 } 1411 } 1412 1413 func TestACLBindingRule_Copy(t *testing.T) { 1414 ci.Parallel(t) 1415 1416 aclBindingRule1 := &ACLBindingRule{ 1417 ID: uuid.Short(), 1418 Description: "mocked-acl-binding-rule", 1419 AuthMethod: "auth0", 1420 Selector: "engineering in list.roles", 1421 BindType: "role-id", 1422 BindName: "eng-ro", 1423 CreateTime: time.Now().UTC(), 1424 ModifyTime: time.Now().UTC(), 1425 CreateIndex: 10, 1426 ModifyIndex: 10, 1427 } 1428 aclBindingRule1.SetHash() 1429 1430 aclBindingRule2 := aclBindingRule1.Copy() 1431 must.Eq(t, aclBindingRule1, aclBindingRule2) 1432 1433 aclBindingRule3 := aclBindingRule1.Copy() 1434 aclBindingRule3.Description = "" 1435 aclBindingRule3.SetHash() 1436 must.NotEq(t, aclBindingRule1, aclBindingRule3) 1437 } 1438 1439 func TestACLBindingRule_Stub(t *testing.T) { 1440 ci.Parallel(t) 1441 1442 aclBindingRule := ACLBindingRule{ 1443 ID: "some-uuid", 1444 Description: "my-binding-rule", 1445 AuthMethod: "auth0", 1446 Selector: "some selector.pattern", 1447 BindType: "role", 1448 BindName: "some-role-id-or-name", 1449 CreateTime: time.Now().UTC(), 1450 ModifyTime: time.Now().UTC(), 1451 CreateIndex: 1309, 1452 ModifyIndex: 9031, 1453 } 1454 aclBindingRule.SetHash() 1455 1456 must.Eq(t, &ACLBindingRuleListStub{ 1457 ID: "some-uuid", 1458 Description: "my-binding-rule", 1459 AuthMethod: "auth0", 1460 Hash: aclBindingRule.Hash, 1461 CreateIndex: 1309, 1462 ModifyIndex: 9031, 1463 }, aclBindingRule.Stub()) 1464 } 1465 1466 func Test_ACLBindingRulesUpsertRequest(t *testing.T) { 1467 ci.Parallel(t) 1468 1469 req := ACLBindingRulesUpsertRequest{} 1470 require.False(t, req.IsRead()) 1471 } 1472 1473 func Test_ACLBindingRulesDeleteRequest(t *testing.T) { 1474 ci.Parallel(t) 1475 1476 req := ACLBindingRulesDeleteRequest{} 1477 require.False(t, req.IsRead()) 1478 } 1479 1480 func Test_ACLBindingRulesListRequest(t *testing.T) { 1481 ci.Parallel(t) 1482 1483 req := ACLBindingRulesListRequest{} 1484 require.True(t, req.IsRead()) 1485 } 1486 1487 func Test_ACLBindingRulesRequest(t *testing.T) { 1488 ci.Parallel(t) 1489 1490 req := ACLBindingRulesRequest{} 1491 require.True(t, req.IsRead()) 1492 } 1493 1494 func Test_ACLBindingRuleRequest(t *testing.T) { 1495 ci.Parallel(t) 1496 1497 req := ACLBindingRuleRequest{} 1498 require.True(t, req.IsRead()) 1499 } 1500 1501 func TestACLOIDCAuthURLRequest(t *testing.T) { 1502 ci.Parallel(t) 1503 1504 req := &ACLOIDCAuthURLRequest{} 1505 must.False(t, req.IsRead()) 1506 } 1507 1508 func TestACLOIDCAuthURLRequest_Validate(t *testing.T) { 1509 ci.Parallel(t) 1510 1511 testRequest := &ACLOIDCAuthURLRequest{} 1512 err := testRequest.Validate() 1513 must.Error(t, err) 1514 must.StrContains(t, err.Error(), "missing auth method name") 1515 must.StrContains(t, err.Error(), "missing client nonce") 1516 must.StrContains(t, err.Error(), "missing redirect URI") 1517 } 1518 1519 func TestACLOIDCCompleteAuthRequest(t *testing.T) { 1520 ci.Parallel(t) 1521 1522 req := &ACLOIDCCompleteAuthRequest{} 1523 must.False(t, req.IsRead()) 1524 } 1525 1526 func TestACLOIDCCompleteAuthRequest_Validate(t *testing.T) { 1527 ci.Parallel(t) 1528 1529 testRequest := &ACLOIDCCompleteAuthRequest{} 1530 err := testRequest.Validate() 1531 must.Error(t, err) 1532 must.StrContains(t, err.Error(), "missing auth method name") 1533 must.StrContains(t, err.Error(), "missing client nonce") 1534 must.StrContains(t, err.Error(), "missing state") 1535 must.StrContains(t, err.Error(), "missing code") 1536 must.StrContains(t, err.Error(), "missing redirect URI") 1537 }