1package vault 2 3import ( 4 "reflect" 5 "sort" 6 "strings" 7 "testing" 8 9 "github.com/go-test/deep" 10 "github.com/hashicorp/vault/helper/identity" 11 "github.com/hashicorp/vault/helper/namespace" 12 "github.com/hashicorp/vault/sdk/logical" 13) 14 15func TestIdentityStore_FixOverwrittenMemberGroupIDs(t *testing.T) { 16 c, _, _ := TestCoreUnsealed(t) 17 ctx := namespace.RootContext(nil) 18 19 // Create a group 20 resp, err := c.identityStore.HandleRequest(ctx, &logical.Request{ 21 Path: "group", 22 Operation: logical.UpdateOperation, 23 Data: map[string]interface{}{ 24 "name": "group1", 25 }, 26 }) 27 if err != nil || (resp != nil && resp.IsError()) { 28 t.Fatalf("bad: err: %v\nresp: %#v", err, resp) 29 } 30 31 groupID := resp.Data["id"].(string) 32 33 expectedMemberGroupIDs := []string{groupID} 34 35 // Create another group and add the above created group as its member 36 resp, err = c.identityStore.HandleRequest(ctx, &logical.Request{ 37 Path: "group", 38 Operation: logical.UpdateOperation, 39 Data: map[string]interface{}{ 40 "name": "group2", 41 "policies": "default", 42 "member_group_ids": expectedMemberGroupIDs, 43 }, 44 }) 45 if err != nil || (resp != nil && resp.IsError()) { 46 t.Fatalf("bad: err: %v\nresp: %#v", err, resp) 47 } 48 49 // Create another group and add the above created group as its member 50 resp, err = c.identityStore.HandleRequest(ctx, &logical.Request{ 51 Path: "group/name/group2", 52 Operation: logical.UpdateOperation, 53 Data: map[string]interface{}{ 54 "policies": "default,another-policy", 55 }, 56 }) 57 if err != nil || (resp != nil && resp.IsError()) { 58 t.Fatalf("bad: err: %v\nresp: %#v", err, resp) 59 } 60 61 // Read the group and check if the member_group_ids is intact 62 resp, err = c.identityStore.HandleRequest(ctx, &logical.Request{ 63 Path: "group/name/group2", 64 Operation: logical.ReadOperation, 65 }) 66 if err != nil || (resp != nil && resp.IsError()) { 67 t.Fatalf("bad: err: %v\nresp: %#v", err, resp) 68 } 69 70 if !reflect.DeepEqual(resp.Data["member_group_ids"], expectedMemberGroupIDs) { 71 t.Fatalf("bad: member_group_ids; expected: %#v\n, actual: %#v", expectedMemberGroupIDs, resp.Data["member_group_ids"]) 72 } 73} 74 75func TestIdentityStore_GroupEntityMembershipUpgrade(t *testing.T) { 76 c, keys, rootToken := TestCoreUnsealed(t) 77 78 // Create a group 79 resp, err := c.identityStore.HandleRequest(namespace.RootContext(nil), &logical.Request{ 80 Path: "group", 81 Operation: logical.UpdateOperation, 82 Data: map[string]interface{}{ 83 "name": "testgroup", 84 }, 85 }) 86 if err != nil || (resp != nil && resp.IsError()) { 87 t.Fatalf("bad: err:%v\nresp: %#v", err, resp) 88 } 89 90 // Create a memdb transaction 91 txn := c.identityStore.db.Txn(true) 92 defer txn.Abort() 93 94 // Fetch the above created group 95 group, err := c.identityStore.MemDBGroupByNameInTxn(namespace.RootContext(nil), txn, "testgroup", true) 96 if err != nil { 97 t.Fatal(err) 98 } 99 100 // Manually add an invalid entity as the group's member 101 group.MemberEntityIDs = []string{"invalidentityid"} 102 103 ctx := namespace.RootContext(nil) 104 105 // Persist the group 106 err = c.identityStore.UpsertGroupInTxn(ctx, txn, group, true) 107 if err != nil { 108 t.Fatal(err) 109 } 110 111 txn.Commit() 112 113 // Perform seal and unseal forcing an upgrade 114 err = c.Seal(rootToken) 115 if err != nil { 116 t.Fatal(err) 117 } 118 for i, key := range keys { 119 unseal, err := TestCoreUnseal(c, key) 120 if err != nil { 121 t.Fatal(err) 122 } 123 if i+1 == len(keys) && !unseal { 124 t.Fatalf("failed to unseal") 125 } 126 } 127 128 // Read the group and ensure that invalid entity id is cleaned up 129 group, err = c.identityStore.MemDBGroupByName(namespace.RootContext(nil), "testgroup", false) 130 if err != nil { 131 t.Fatal(err) 132 } 133 134 if len(group.MemberEntityIDs) != 0 { 135 t.Fatalf("bad: member entity IDs; expected: none, actual: %#v", group.MemberEntityIDs) 136 } 137} 138 139func TestIdentityStore_MemberGroupIDDelete(t *testing.T) { 140 ctx := namespace.RootContext(nil) 141 i, _, _ := testIdentityStoreWithGithubAuth(ctx, t) 142 143 // Create a child group 144 resp, err := i.HandleRequest(ctx, &logical.Request{ 145 Path: "group", 146 Operation: logical.UpdateOperation, 147 Data: map[string]interface{}{ 148 "name": "child", 149 }, 150 }) 151 if err != nil || (resp != nil && resp.IsError()) { 152 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 153 } 154 childGroupID := resp.Data["id"].(string) 155 156 // Create a parent group with the above group ID as its child 157 resp, err = i.HandleRequest(ctx, &logical.Request{ 158 Path: "group", 159 Operation: logical.UpdateOperation, 160 Data: map[string]interface{}{ 161 "name": "parent", 162 "member_group_ids": []string{childGroupID}, 163 }, 164 }) 165 if err != nil || (resp != nil && resp.IsError()) { 166 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 167 } 168 169 // Ensure that member group ID is properly updated 170 resp, err = i.HandleRequest(ctx, &logical.Request{ 171 Path: "group/name/parent", 172 Operation: logical.ReadOperation, 173 }) 174 if err != nil || (resp != nil && resp.IsError()) { 175 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 176 } 177 memberGroupIDs := resp.Data["member_group_ids"].([]string) 178 if len(memberGroupIDs) != 1 && memberGroupIDs[0] != childGroupID { 179 t.Fatalf("bad: member group ids; expected: %#v, actual: %#v", []string{childGroupID}, memberGroupIDs) 180 } 181 182 // Clear the member group IDs from the parent group 183 resp, err = i.HandleRequest(ctx, &logical.Request{ 184 Path: "group/name/parent", 185 Operation: logical.UpdateOperation, 186 Data: map[string]interface{}{ 187 "member_group_ids": []string{}, 188 }, 189 }) 190 if err != nil || (resp != nil && resp.IsError()) { 191 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 192 } 193 194 // Ensure that member group ID is properly deleted 195 resp, err = i.HandleRequest(ctx, &logical.Request{ 196 Path: "group/name/parent", 197 Operation: logical.ReadOperation, 198 }) 199 if err != nil || (resp != nil && resp.IsError()) { 200 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 201 } 202 memberGroupIDs = resp.Data["member_group_ids"].([]string) 203 if len(memberGroupIDs) != 0 { 204 t.Fatalf("bad: length of member group ids; expected: %d, actual: %d", 0, len(memberGroupIDs)) 205 } 206} 207 208func TestIdentityStore_CaseInsensitiveGroupName(t *testing.T) { 209 ctx := namespace.RootContext(nil) 210 i, _, _ := testIdentityStoreWithGithubAuth(ctx, t) 211 212 testGroupName := "testGroupName" 213 214 // Create an group with case sensitive name 215 resp, err := i.HandleRequest(ctx, &logical.Request{ 216 Path: "group", 217 Operation: logical.UpdateOperation, 218 Data: map[string]interface{}{ 219 "name": testGroupName, 220 }, 221 }) 222 if err != nil || (resp != nil && resp.IsError()) { 223 t.Fatalf("bad: err:%v\nresp: %#v", err, resp) 224 } 225 groupID := resp.Data["id"].(string) 226 227 // Lookup the group by ID and check that name returned is case sensitive 228 resp, err = i.HandleRequest(ctx, &logical.Request{ 229 Path: "group/id/" + groupID, 230 Operation: logical.ReadOperation, 231 }) 232 if err != nil || (resp != nil && resp.IsError()) { 233 t.Fatalf("bad: err:%v\nresp: %#v", err, resp) 234 } 235 groupName := resp.Data["name"].(string) 236 if groupName != testGroupName { 237 t.Fatalf("bad group name; expected: %q, actual: %q", testGroupName, groupName) 238 } 239 240 // Lookup the group by case sensitive name 241 resp, err = i.HandleRequest(ctx, &logical.Request{ 242 Path: "group/name/" + testGroupName, 243 Operation: logical.ReadOperation, 244 }) 245 if err != nil || (resp != nil && resp.IsError()) { 246 t.Fatalf("bad: err: %v\nresp: %#v", err, resp) 247 } 248 groupName = resp.Data["name"].(string) 249 if groupName != testGroupName { 250 t.Fatalf("bad group name; expected: %q, actual: %q", testGroupName, groupName) 251 } 252 253 // Lookup the group by case insensitive name 254 resp, err = i.HandleRequest(ctx, &logical.Request{ 255 Path: "group/name/" + strings.ToLower(testGroupName), 256 Operation: logical.ReadOperation, 257 }) 258 if err != nil || (resp != nil && resp.IsError()) { 259 t.Fatalf("bad: err: %v\nresp: %#v", err, resp) 260 } 261 groupName = resp.Data["name"].(string) 262 if groupName != testGroupName { 263 t.Fatalf("bad group name; expected: %q, actual: %q", testGroupName, groupName) 264 } 265 266 // Ensure that there is only one group 267 resp, err = i.HandleRequest(ctx, &logical.Request{ 268 Path: "group/name", 269 Operation: logical.ListOperation, 270 }) 271 if err != nil || (resp != nil && resp.IsError()) { 272 t.Fatalf("bad: err: %v\nresp: %#v", err, resp) 273 } 274 if len(resp.Data["keys"].([]string)) != 1 { 275 t.Fatalf("bad length of groups; expected: 1, actual: %d", len(resp.Data["keys"].([]string))) 276 } 277} 278 279func TestIdentityStore_GroupByName(t *testing.T) { 280 ctx := namespace.RootContext(nil) 281 i, _, _ := testIdentityStoreWithGithubAuth(ctx, t) 282 283 // Create an entity using the "name" endpoint 284 resp, err := i.HandleRequest(ctx, &logical.Request{ 285 Path: "group/name/testgroupname", 286 Operation: logical.UpdateOperation, 287 }) 288 if err != nil || (resp != nil && resp.IsError()) { 289 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 290 } 291 if resp == nil { 292 t.Fatalf("expected a non-nil response") 293 } 294 295 // Test the read by name endpoint 296 resp, err = i.HandleRequest(ctx, &logical.Request{ 297 Path: "group/name/testgroupname", 298 Operation: logical.ReadOperation, 299 }) 300 if err != nil || (resp != nil && resp.IsError()) { 301 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 302 } 303 if resp == nil || resp.Data["name"].(string) != "testgroupname" { 304 t.Fatalf("bad entity response: %#v", resp) 305 } 306 307 // Update group metadata using the name endpoint 308 groupMetadata := map[string]string{ 309 "foo": "bar", 310 } 311 resp, err = i.HandleRequest(ctx, &logical.Request{ 312 Path: "group/name/testgroupname", 313 Operation: logical.UpdateOperation, 314 Data: map[string]interface{}{ 315 "metadata": groupMetadata, 316 }, 317 }) 318 if err != nil || (resp != nil && resp.IsError()) { 319 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 320 } 321 322 // Check the updated result 323 resp, err = i.HandleRequest(ctx, &logical.Request{ 324 Path: "group/name/testgroupname", 325 Operation: logical.ReadOperation, 326 }) 327 if err != nil || (resp != nil && resp.IsError()) { 328 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 329 } 330 if resp == nil || !reflect.DeepEqual(resp.Data["metadata"].(map[string]string), groupMetadata) { 331 t.Fatalf("bad group response: %#v", resp) 332 } 333 334 // Delete the group using the name endpoint 335 resp, err = i.HandleRequest(ctx, &logical.Request{ 336 Path: "group/name/testgroupname", 337 Operation: logical.DeleteOperation, 338 }) 339 if err != nil || (resp != nil && resp.IsError()) { 340 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 341 } 342 343 // Check if deletion was successful 344 resp, err = i.HandleRequest(ctx, &logical.Request{ 345 Path: "group/name/testgroupname", 346 Operation: logical.ReadOperation, 347 }) 348 if err != nil || (resp != nil && resp.IsError()) { 349 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 350 } 351 if resp != nil { 352 t.Fatalf("expected a nil response") 353 } 354 355 // Create 2 entities 356 resp, err = i.HandleRequest(ctx, &logical.Request{ 357 Path: "group/name/testgroupname", 358 Operation: logical.UpdateOperation, 359 }) 360 if err != nil || (resp != nil && resp.IsError()) { 361 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 362 } 363 if resp == nil { 364 t.Fatalf("expected a non-nil response") 365 } 366 resp, err = i.HandleRequest(ctx, &logical.Request{ 367 Path: "group/name/testgroupname2", 368 Operation: logical.UpdateOperation, 369 }) 370 if err != nil || (resp != nil && resp.IsError()) { 371 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 372 } 373 if resp == nil { 374 t.Fatalf("expected a non-nil response") 375 } 376 377 // List the entities by name 378 resp, err = i.HandleRequest(ctx, &logical.Request{ 379 Path: "group/name", 380 Operation: logical.ListOperation, 381 }) 382 if err != nil || (resp != nil && resp.IsError()) { 383 t.Fatalf("bad: resp: %#v\nerr: %v", resp, err) 384 } 385 386 expected := []string{"testgroupname2", "testgroupname"} 387 sort.Strings(expected) 388 actual := resp.Data["keys"].([]string) 389 sort.Strings(actual) 390 if !reflect.DeepEqual(expected, actual) { 391 t.Fatalf("bad: group list response; expected: %#v\nactual: %#v", expected, actual) 392 } 393} 394 395func TestIdentityStore_Groups_TypeMembershipAdditions(t *testing.T) { 396 var err error 397 var resp *logical.Response 398 399 ctx := namespace.RootContext(nil) 400 i, _, _ := testIdentityStoreWithGithubAuth(ctx, t) 401 groupReq := &logical.Request{ 402 Path: "group", 403 Operation: logical.UpdateOperation, 404 Data: map[string]interface{}{ 405 "type": "external", 406 "member_entity_ids": "sampleentityid", 407 }, 408 } 409 410 resp, err = i.HandleRequest(ctx, groupReq) 411 if err != nil { 412 t.Fatal(err) 413 } 414 if !resp.IsError() { 415 t.Fatalf("expected an error") 416 } 417 418 groupReq.Data = map[string]interface{}{ 419 "type": "external", 420 "member_group_ids": "samplegroupid", 421 } 422 423 resp, err = i.HandleRequest(ctx, groupReq) 424 if err != nil { 425 t.Fatal(err) 426 } 427 if !resp.IsError() { 428 t.Fatalf("expected an error") 429 } 430} 431 432func TestIdentityStore_Groups_TypeImmutability(t *testing.T) { 433 var err error 434 var resp *logical.Response 435 436 ctx := namespace.RootContext(nil) 437 i, _, _ := testIdentityStoreWithGithubAuth(ctx, t) 438 groupReq := &logical.Request{ 439 Path: "group", 440 Operation: logical.UpdateOperation, 441 } 442 443 resp, err = i.HandleRequest(ctx, groupReq) 444 if err != nil || (resp != nil && resp.IsError()) { 445 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 446 } 447 internalGroupID := resp.Data["id"].(string) 448 449 groupReq.Data = map[string]interface{}{ 450 "type": "external", 451 } 452 resp, err = i.HandleRequest(ctx, groupReq) 453 if err != nil || (resp != nil && resp.IsError()) { 454 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 455 } 456 externalGroupID := resp.Data["id"].(string) 457 458 // Try to mark internal group as external 459 groupReq.Data = map[string]interface{}{ 460 "type": "external", 461 } 462 groupReq.Path = "group/id/" + internalGroupID 463 resp, err = i.HandleRequest(ctx, groupReq) 464 if err != nil { 465 t.Fatal(err) 466 } 467 if !resp.IsError() { 468 t.Fatalf("expected an error") 469 } 470 471 // Try to mark internal group as external 472 groupReq.Data = map[string]interface{}{ 473 "type": "internal", 474 } 475 groupReq.Path = "group/id/" + externalGroupID 476 resp, err = i.HandleRequest(ctx, groupReq) 477 if err != nil { 478 t.Fatal(err) 479 } 480 if !resp.IsError() { 481 t.Fatalf("expected an error") 482 } 483} 484 485func TestIdentityStore_MemDBGroupIndexes(t *testing.T) { 486 var err error 487 ctx := namespace.RootContext(nil) 488 i, _, _ := testIdentityStoreWithGithubAuth(ctx, t) 489 490 // Create a dummy group 491 group := &identity.Group{ 492 ID: "testgroupid", 493 Name: "testgroupname", 494 Metadata: map[string]string{ 495 "testmetadatakey1": "testmetadatavalue1", 496 "testmetadatakey2": "testmetadatavalue2", 497 }, 498 ParentGroupIDs: []string{"testparentgroupid1", "testparentgroupid2"}, 499 MemberEntityIDs: []string{"testentityid1", "testentityid2"}, 500 Policies: []string{"testpolicy1", "testpolicy2"}, 501 BucketKey: i.groupPacker.BucketKey("testgroupid"), 502 } 503 504 // Insert it into memdb 505 txn := i.db.Txn(true) 506 defer txn.Abort() 507 err = i.MemDBUpsertGroupInTxn(txn, group) 508 if err != nil { 509 t.Fatal(err) 510 } 511 txn.Commit() 512 513 // Insert another dummy group 514 group = &identity.Group{ 515 ID: "testgroupid2", 516 Name: "testgroupname2", 517 Metadata: map[string]string{ 518 "testmetadatakey2": "testmetadatavalue2", 519 "testmetadatakey3": "testmetadatavalue3", 520 }, 521 ParentGroupIDs: []string{"testparentgroupid2", "testparentgroupid3"}, 522 MemberEntityIDs: []string{"testentityid2", "testentityid3"}, 523 Policies: []string{"testpolicy2", "testpolicy3"}, 524 BucketKey: i.groupPacker.BucketKey("testgroupid2"), 525 } 526 527 // Insert it into memdb 528 529 txn = i.db.Txn(true) 530 defer txn.Abort() 531 err = i.MemDBUpsertGroupInTxn(txn, group) 532 if err != nil { 533 t.Fatal(err) 534 } 535 txn.Commit() 536 537 var fetchedGroup *identity.Group 538 539 // Fetch group given the name 540 fetchedGroup, err = i.MemDBGroupByName(namespace.RootContext(nil), "testgroupname", false) 541 if err != nil { 542 t.Fatal(err) 543 } 544 if fetchedGroup == nil || fetchedGroup.Name != "testgroupname" { 545 t.Fatalf("failed to fetch an indexed group") 546 } 547 548 // Fetch group given the ID 549 fetchedGroup, err = i.MemDBGroupByID("testgroupid", false) 550 if err != nil { 551 t.Fatal(err) 552 } 553 if fetchedGroup == nil || fetchedGroup.Name != "testgroupname" { 554 t.Fatalf("failed to fetch an indexed group") 555 } 556 557 var fetchedGroups []*identity.Group 558 // Fetch the subgroups of a given group ID 559 fetchedGroups, err = i.MemDBGroupsByParentGroupID("testparentgroupid1", false) 560 if err != nil { 561 t.Fatal(err) 562 } 563 if len(fetchedGroups) != 1 || fetchedGroups[0].Name != "testgroupname" { 564 t.Fatalf("failed to fetch an indexed group") 565 } 566 567 fetchedGroups, err = i.MemDBGroupsByParentGroupID("testparentgroupid2", false) 568 if err != nil { 569 t.Fatal(err) 570 } 571 if len(fetchedGroups) != 2 { 572 t.Fatalf("failed to fetch a indexed groups") 573 } 574 575 // Fetch groups based on member entity ID 576 fetchedGroups, err = i.MemDBGroupsByMemberEntityID("testentityid1", false, false) 577 if err != nil { 578 t.Fatal(err) 579 } 580 if len(fetchedGroups) != 1 || fetchedGroups[0].Name != "testgroupname" { 581 t.Fatalf("failed to fetch an indexed group") 582 } 583 584 fetchedGroups, err = i.MemDBGroupsByMemberEntityID("testentityid2", false, false) 585 if err != nil { 586 t.Fatal(err) 587 } 588 589 if len(fetchedGroups) != 2 { 590 t.Fatalf("failed to fetch groups by entity ID") 591 } 592} 593 594func TestIdentityStore_GroupsCreateUpdate(t *testing.T) { 595 var resp *logical.Response 596 var err error 597 598 ctx := namespace.RootContext(nil) 599 is, _, _ := testIdentityStoreWithGithubAuth(ctx, t) 600 601 // Create an entity and get its ID 602 entityRegisterReq := &logical.Request{ 603 Operation: logical.UpdateOperation, 604 Path: "entity", 605 } 606 resp, err = is.HandleRequest(ctx, entityRegisterReq) 607 if err != nil || (resp != nil && resp.IsError()) { 608 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 609 } 610 entityID1 := resp.Data["id"].(string) 611 612 // Create another entity and get its ID 613 resp, err = is.HandleRequest(ctx, entityRegisterReq) 614 if err != nil || (resp != nil && resp.IsError()) { 615 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 616 } 617 entityID2 := resp.Data["id"].(string) 618 619 // Create a group with the above created 2 entities as its members 620 groupData := map[string]interface{}{ 621 "policies": "testpolicy1,testpolicy2", 622 "metadata": []string{"testkey1=testvalue1", "testkey2=testvalue2"}, 623 "member_entity_ids": []string{entityID1, entityID2}, 624 } 625 626 // Create a group and get its ID 627 groupReq := &logical.Request{ 628 Operation: logical.UpdateOperation, 629 Path: "group", 630 Data: groupData, 631 } 632 resp, err = is.HandleRequest(ctx, groupReq) 633 if err != nil || (resp != nil && resp.IsError()) { 634 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 635 } 636 memberGroupID1 := resp.Data["id"].(string) 637 638 // Create another group and get its ID 639 resp, err = is.HandleRequest(ctx, groupReq) 640 if err != nil || (resp != nil && resp.IsError()) { 641 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 642 } 643 memberGroupID2 := resp.Data["id"].(string) 644 645 // Create a group with the above 2 groups as its members 646 groupData["member_group_ids"] = []string{memberGroupID1, memberGroupID2} 647 resp, err = is.HandleRequest(ctx, groupReq) 648 if err != nil || (resp != nil && resp.IsError()) { 649 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 650 } 651 groupID := resp.Data["id"].(string) 652 653 // Read the group using its iD and check if all the fields are properly 654 // set 655 groupReq = &logical.Request{ 656 Operation: logical.ReadOperation, 657 Path: "group/id/" + groupID, 658 } 659 resp, err = is.HandleRequest(ctx, groupReq) 660 if err != nil || (resp != nil && resp.IsError()) { 661 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 662 } 663 664 expectedData := map[string]interface{}{ 665 "policies": []string{"testpolicy1", "testpolicy2"}, 666 "metadata": map[string]string{ 667 "testkey1": "testvalue1", 668 "testkey2": "testvalue2", 669 }, 670 "parent_group_ids": []string(nil), 671 } 672 expectedData["id"] = resp.Data["id"] 673 expectedData["type"] = resp.Data["type"] 674 expectedData["name"] = resp.Data["name"] 675 expectedData["member_group_ids"] = resp.Data["member_group_ids"] 676 expectedData["member_entity_ids"] = resp.Data["member_entity_ids"] 677 expectedData["creation_time"] = resp.Data["creation_time"] 678 expectedData["last_update_time"] = resp.Data["last_update_time"] 679 expectedData["modify_index"] = resp.Data["modify_index"] 680 expectedData["alias"] = resp.Data["alias"] 681 expectedData["namespace_id"] = "root" 682 683 if diff := deep.Equal(expectedData, resp.Data); diff != nil { 684 t.Fatal(diff) 685 } 686 687 // Update the policies and metadata in the group 688 groupReq.Operation = logical.UpdateOperation 689 groupReq.Data = groupData 690 691 // Update by setting ID in the param 692 groupData["id"] = groupID 693 groupData["policies"] = "updatedpolicy1,updatedpolicy2" 694 groupData["metadata"] = []string{"updatedkey=updatedvalue"} 695 resp, err = is.HandleRequest(ctx, groupReq) 696 if err != nil || (resp != nil && resp.IsError()) { 697 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 698 } 699 700 // Check if updates are reflected 701 groupReq.Operation = logical.ReadOperation 702 resp, err = is.HandleRequest(ctx, groupReq) 703 if err != nil || (resp != nil && resp.IsError()) { 704 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 705 } 706 707 expectedData["policies"] = []string{"updatedpolicy1", "updatedpolicy2"} 708 expectedData["metadata"] = map[string]string{ 709 "updatedkey": "updatedvalue", 710 } 711 expectedData["last_update_time"] = resp.Data["last_update_time"] 712 expectedData["modify_index"] = resp.Data["modify_index"] 713 if !reflect.DeepEqual(expectedData, resp.Data) { 714 t.Fatalf("bad: group data; expected: %#v\n actual: %#v\n", expectedData, resp.Data) 715 } 716} 717 718func TestIdentityStore_GroupsCRUD_ByID(t *testing.T) { 719 var resp *logical.Response 720 var err error 721 ctx := namespace.RootContext(nil) 722 is, _, _ := testIdentityStoreWithGithubAuth(ctx, t) 723 724 // Create an entity and get its ID 725 entityRegisterReq := &logical.Request{ 726 Operation: logical.UpdateOperation, 727 Path: "entity", 728 } 729 resp, err = is.HandleRequest(ctx, entityRegisterReq) 730 if err != nil || (resp != nil && resp.IsError()) { 731 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 732 } 733 entityID1 := resp.Data["id"].(string) 734 735 // Create another entity and get its ID 736 resp, err = is.HandleRequest(ctx, entityRegisterReq) 737 if err != nil || (resp != nil && resp.IsError()) { 738 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 739 } 740 entityID2 := resp.Data["id"].(string) 741 742 // Create a group with the above created 2 entities as its members 743 groupData := map[string]interface{}{ 744 "policies": "testpolicy1,testpolicy2", 745 "metadata": []string{"testkey1=testvalue1", "testkey2=testvalue2"}, 746 "member_entity_ids": []string{entityID1, entityID2}, 747 } 748 749 // Create a group and get its ID 750 groupRegisterReq := &logical.Request{ 751 Operation: logical.UpdateOperation, 752 Path: "group", 753 Data: groupData, 754 } 755 resp, err = is.HandleRequest(ctx, groupRegisterReq) 756 if err != nil || (resp != nil && resp.IsError()) { 757 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 758 } 759 memberGroupID1 := resp.Data["id"].(string) 760 761 // Create another group and get its ID 762 resp, err = is.HandleRequest(ctx, groupRegisterReq) 763 if err != nil || (resp != nil && resp.IsError()) { 764 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 765 } 766 memberGroupID2 := resp.Data["id"].(string) 767 768 // Create a group with the above 2 groups as its members 769 groupData["member_group_ids"] = []string{memberGroupID1, memberGroupID2} 770 resp, err = is.HandleRequest(ctx, groupRegisterReq) 771 if err != nil || (resp != nil && resp.IsError()) { 772 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 773 } 774 groupID := resp.Data["id"].(string) 775 776 // Read the group using its name and check if all the fields are properly 777 // set 778 groupReq := &logical.Request{ 779 Operation: logical.ReadOperation, 780 Path: "group/id/" + groupID, 781 } 782 resp, err = is.HandleRequest(ctx, groupReq) 783 if err != nil || (resp != nil && resp.IsError()) { 784 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 785 } 786 787 expectedData := map[string]interface{}{ 788 "policies": []string{"testpolicy1", "testpolicy2"}, 789 "metadata": map[string]string{ 790 "testkey1": "testvalue1", 791 "testkey2": "testvalue2", 792 }, 793 "parent_group_ids": []string(nil), 794 } 795 expectedData["id"] = resp.Data["id"] 796 expectedData["type"] = resp.Data["type"] 797 expectedData["name"] = resp.Data["name"] 798 expectedData["member_group_ids"] = resp.Data["member_group_ids"] 799 expectedData["member_entity_ids"] = resp.Data["member_entity_ids"] 800 expectedData["creation_time"] = resp.Data["creation_time"] 801 expectedData["last_update_time"] = resp.Data["last_update_time"] 802 expectedData["modify_index"] = resp.Data["modify_index"] 803 expectedData["alias"] = resp.Data["alias"] 804 expectedData["namespace_id"] = "root" 805 806 if diff := deep.Equal(expectedData, resp.Data); diff != nil { 807 t.Fatal(diff) 808 } 809 810 // Update the policies and metadata in the group 811 groupReq.Operation = logical.UpdateOperation 812 groupReq.Data = groupData 813 groupData["policies"] = "updatedpolicy1,updatedpolicy2" 814 groupData["metadata"] = []string{"updatedkey=updatedvalue"} 815 resp, err = is.HandleRequest(ctx, groupReq) 816 if err != nil || (resp != nil && resp.IsError()) { 817 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 818 } 819 820 // Check if updates are reflected 821 groupReq.Operation = logical.ReadOperation 822 resp, err = is.HandleRequest(ctx, groupReq) 823 if err != nil || (resp != nil && resp.IsError()) { 824 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 825 } 826 827 expectedData["policies"] = []string{"updatedpolicy1", "updatedpolicy2"} 828 expectedData["metadata"] = map[string]string{ 829 "updatedkey": "updatedvalue", 830 } 831 expectedData["last_update_time"] = resp.Data["last_update_time"] 832 expectedData["modify_index"] = resp.Data["modify_index"] 833 if !reflect.DeepEqual(expectedData, resp.Data) { 834 t.Fatalf("bad: group data; expected: %#v\n actual: %#v\n", expectedData, resp.Data) 835 } 836 837 // Check if delete is working properly 838 groupReq.Operation = logical.DeleteOperation 839 resp, err = is.HandleRequest(ctx, groupReq) 840 if err != nil || (resp != nil && resp.IsError()) { 841 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 842 } 843 844 groupReq.Operation = logical.ReadOperation 845 resp, err = is.HandleRequest(ctx, groupReq) 846 if err != nil { 847 t.Fatal(err) 848 } 849 if resp != nil { 850 t.Fatalf("expected a nil response") 851 } 852} 853 854func TestIdentityStore_GroupMultiCase(t *testing.T) { 855 var resp *logical.Response 856 var err error 857 ctx := namespace.RootContext(nil) 858 is, _, _ := testIdentityStoreWithGithubAuth(ctx, t) 859 groupRegisterReq := &logical.Request{ 860 Operation: logical.UpdateOperation, 861 Path: "group", 862 } 863 864 // Create 'build' group 865 buildGroupData := map[string]interface{}{ 866 "name": "build", 867 "policies": "buildpolicy", 868 } 869 groupRegisterReq.Data = buildGroupData 870 resp, err = is.HandleRequest(ctx, groupRegisterReq) 871 if err != nil || (resp != nil && resp.IsError()) { 872 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 873 } 874 buildGroupID := resp.Data["id"].(string) 875 876 // Create 'deploy' group 877 deployGroupData := map[string]interface{}{ 878 "name": "deploy", 879 "policies": "deploypolicy", 880 } 881 groupRegisterReq.Data = deployGroupData 882 resp, err = is.HandleRequest(ctx, groupRegisterReq) 883 if err != nil || (resp != nil && resp.IsError()) { 884 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 885 } 886 deployGroupID := resp.Data["id"].(string) 887 888 // Create an entity ID 889 entityRegisterReq := &logical.Request{ 890 Operation: logical.UpdateOperation, 891 Path: "entity", 892 } 893 resp, err = is.HandleRequest(ctx, entityRegisterReq) 894 if err != nil || (resp != nil && resp.IsError()) { 895 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 896 } 897 entityID1 := resp.Data["id"].(string) 898 899 // Add the entity as a member of 'build' group 900 entityIDReq := &logical.Request{ 901 Operation: logical.UpdateOperation, 902 Path: "group/id/" + buildGroupID, 903 Data: map[string]interface{}{ 904 "member_entity_ids": []string{entityID1}, 905 }, 906 } 907 resp, err = is.HandleRequest(ctx, entityIDReq) 908 if err != nil || (resp != nil && resp.IsError()) { 909 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 910 } 911 912 // Add the entity as a member of the 'deploy` group 913 entityIDReq.Path = "group/id/" + deployGroupID 914 resp, err = is.HandleRequest(ctx, entityIDReq) 915 if err != nil || (resp != nil && resp.IsError()) { 916 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 917 } 918 919 policiesResult, err := is.groupPoliciesByEntityID(entityID1) 920 if err != nil { 921 t.Fatal(err) 922 } 923 924 policies := []string{} 925 for _, nsPolicies := range policiesResult { 926 policies = append(policies, nsPolicies...) 927 } 928 sort.Strings(policies) 929 expected := []string{"deploypolicy", "buildpolicy"} 930 sort.Strings(expected) 931 if !reflect.DeepEqual(expected, policies) { 932 t.Fatalf("bad: policies; expected: %#v\nactual:%#v", expected, policies) 933 } 934} 935 936/* 937Test groups hierarchy: 938 ------- eng(entityID3) ------- 939 | | 940 ----- vault ----- -- ops(entityID2) -- 941 | | | | 942 kube(entityID1) identity build deploy 943*/ 944func TestIdentityStore_GroupHierarchyCases(t *testing.T) { 945 var resp *logical.Response 946 var err error 947 ctx := namespace.RootContext(nil) 948 is, _, _ := testIdentityStoreWithGithubAuth(ctx, t) 949 groupRegisterReq := &logical.Request{ 950 Operation: logical.UpdateOperation, 951 Path: "group", 952 } 953 954 // Create 'kube' group 955 kubeGroupData := map[string]interface{}{ 956 "name": "kube", 957 "policies": "kubepolicy", 958 } 959 groupRegisterReq.Data = kubeGroupData 960 resp, err = is.HandleRequest(ctx, groupRegisterReq) 961 if err != nil || (resp != nil && resp.IsError()) { 962 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 963 } 964 kubeGroupID := resp.Data["id"].(string) 965 966 // Create 'identity' group 967 identityGroupData := map[string]interface{}{ 968 "name": "identity", 969 "policies": "identitypolicy", 970 } 971 groupRegisterReq.Data = identityGroupData 972 resp, err = is.HandleRequest(ctx, groupRegisterReq) 973 if err != nil || (resp != nil && resp.IsError()) { 974 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 975 } 976 identityGroupID := resp.Data["id"].(string) 977 978 // Create 'build' group 979 buildGroupData := map[string]interface{}{ 980 "name": "build", 981 "policies": "buildpolicy", 982 } 983 groupRegisterReq.Data = buildGroupData 984 resp, err = is.HandleRequest(ctx, groupRegisterReq) 985 if err != nil || (resp != nil && resp.IsError()) { 986 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 987 } 988 buildGroupID := resp.Data["id"].(string) 989 990 // Create 'deploy' group 991 deployGroupData := map[string]interface{}{ 992 "name": "deploy", 993 "policies": "deploypolicy", 994 } 995 groupRegisterReq.Data = deployGroupData 996 resp, err = is.HandleRequest(ctx, groupRegisterReq) 997 if err != nil || (resp != nil && resp.IsError()) { 998 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 999 } 1000 deployGroupID := resp.Data["id"].(string) 1001 1002 // Create 'vault' with 'kube' and 'identity' as member groups 1003 vaultMemberGroupIDs := []string{kubeGroupID, identityGroupID} 1004 vaultGroupData := map[string]interface{}{ 1005 "name": "vault", 1006 "policies": "vaultpolicy", 1007 "member_group_ids": vaultMemberGroupIDs, 1008 } 1009 groupRegisterReq.Data = vaultGroupData 1010 resp, err = is.HandleRequest(ctx, groupRegisterReq) 1011 if err != nil || (resp != nil && resp.IsError()) { 1012 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 1013 } 1014 vaultGroupID := resp.Data["id"].(string) 1015 1016 // Create 'ops' group with 'build' and 'deploy' as member groups 1017 opsMemberGroupIDs := []string{buildGroupID, deployGroupID} 1018 opsGroupData := map[string]interface{}{ 1019 "name": "ops", 1020 "policies": "opspolicy", 1021 "member_group_ids": opsMemberGroupIDs, 1022 } 1023 groupRegisterReq.Data = opsGroupData 1024 resp, err = is.HandleRequest(ctx, groupRegisterReq) 1025 if err != nil || (resp != nil && resp.IsError()) { 1026 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 1027 } 1028 opsGroupID := resp.Data["id"].(string) 1029 1030 // Create 'eng' group with 'vault' and 'ops' as member groups 1031 engMemberGroupIDs := []string{vaultGroupID, opsGroupID} 1032 engGroupData := map[string]interface{}{ 1033 "name": "eng", 1034 "policies": "engpolicy", 1035 "member_group_ids": engMemberGroupIDs, 1036 } 1037 1038 groupRegisterReq.Data = engGroupData 1039 resp, err = is.HandleRequest(ctx, groupRegisterReq) 1040 if err != nil || (resp != nil && resp.IsError()) { 1041 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 1042 } 1043 engGroupID := resp.Data["id"].(string) 1044 1045 /* 1046 fmt.Printf("engGroupID: %#v\n", engGroupID) 1047 fmt.Printf("vaultGroupID: %#v\n", vaultGroupID) 1048 fmt.Printf("opsGroupID: %#v\n", opsGroupID) 1049 fmt.Printf("kubeGroupID: %#v\n", kubeGroupID) 1050 fmt.Printf("identityGroupID: %#v\n", identityGroupID) 1051 fmt.Printf("buildGroupID: %#v\n", buildGroupID) 1052 fmt.Printf("deployGroupID: %#v\n", deployGroupID) 1053 */ 1054 1055 var memberGroupIDs []string 1056 // Fetch 'eng' group 1057 engGroup, err := is.MemDBGroupByID(engGroupID, false) 1058 if err != nil { 1059 t.Fatal(err) 1060 } 1061 memberGroupIDs, err = is.memberGroupIDsByID(engGroup.ID) 1062 if err != nil { 1063 t.Fatal(err) 1064 } 1065 sort.Strings(memberGroupIDs) 1066 sort.Strings(engMemberGroupIDs) 1067 if !reflect.DeepEqual(engMemberGroupIDs, memberGroupIDs) { 1068 t.Fatalf("bad: group membership IDs; expected: %#v\n actual: %#v\n", engMemberGroupIDs, memberGroupIDs) 1069 } 1070 1071 vaultGroup, err := is.MemDBGroupByID(vaultGroupID, false) 1072 if err != nil { 1073 t.Fatal(err) 1074 } 1075 memberGroupIDs, err = is.memberGroupIDsByID(vaultGroup.ID) 1076 if err != nil { 1077 t.Fatal(err) 1078 } 1079 sort.Strings(memberGroupIDs) 1080 sort.Strings(vaultMemberGroupIDs) 1081 if !reflect.DeepEqual(vaultMemberGroupIDs, memberGroupIDs) { 1082 t.Fatalf("bad: group membership IDs; expected: %#v\n actual: %#v\n", vaultMemberGroupIDs, memberGroupIDs) 1083 } 1084 1085 opsGroup, err := is.MemDBGroupByID(opsGroupID, false) 1086 if err != nil { 1087 t.Fatal(err) 1088 } 1089 memberGroupIDs, err = is.memberGroupIDsByID(opsGroup.ID) 1090 if err != nil { 1091 t.Fatal(err) 1092 } 1093 sort.Strings(memberGroupIDs) 1094 sort.Strings(opsMemberGroupIDs) 1095 if !reflect.DeepEqual(opsMemberGroupIDs, memberGroupIDs) { 1096 t.Fatalf("bad: group membership IDs; expected: %#v\n actual: %#v\n", opsMemberGroupIDs, memberGroupIDs) 1097 } 1098 1099 groupUpdateReq := &logical.Request{ 1100 Operation: logical.UpdateOperation, 1101 } 1102 1103 // Adding 'engGroupID' under 'kubeGroupID' should fail 1104 groupUpdateReq.Path = "group/name/kube" 1105 groupUpdateReq.Data = kubeGroupData 1106 kubeGroupData["member_group_ids"] = []string{engGroupID} 1107 resp, err = is.HandleRequest(ctx, groupUpdateReq) 1108 if err == nil { 1109 t.Fatalf("expected an error response") 1110 } 1111 1112 // Create an entity ID 1113 entityRegisterReq := &logical.Request{ 1114 Operation: logical.UpdateOperation, 1115 Path: "entity", 1116 } 1117 resp, err = is.HandleRequest(ctx, entityRegisterReq) 1118 if err != nil || (resp != nil && resp.IsError()) { 1119 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 1120 } 1121 entityID1 := resp.Data["id"].(string) 1122 1123 // Add the entity as a member of 'kube' group 1124 entityIDReq := &logical.Request{ 1125 Operation: logical.UpdateOperation, 1126 Path: "group/id/" + kubeGroupID, 1127 Data: map[string]interface{}{ 1128 "member_entity_ids": []string{entityID1}, 1129 }, 1130 } 1131 resp, err = is.HandleRequest(ctx, entityIDReq) 1132 if err != nil || (resp != nil && resp.IsError()) { 1133 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 1134 } 1135 1136 // Create a second entity ID 1137 resp, err = is.HandleRequest(ctx, entityRegisterReq) 1138 if err != nil || (resp != nil && resp.IsError()) { 1139 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 1140 } 1141 entityID2 := resp.Data["id"].(string) 1142 1143 // Add the entity as a member of 'ops' group 1144 entityIDReq.Path = "group/id/" + opsGroupID 1145 entityIDReq.Data = map[string]interface{}{ 1146 "member_entity_ids": []string{entityID2}, 1147 } 1148 resp, err = is.HandleRequest(ctx, entityIDReq) 1149 if err != nil || (resp != nil && resp.IsError()) { 1150 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 1151 } 1152 1153 // Create a third entity ID 1154 resp, err = is.HandleRequest(ctx, entityRegisterReq) 1155 if err != nil || (resp != nil && resp.IsError()) { 1156 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 1157 } 1158 entityID3 := resp.Data["id"].(string) 1159 1160 // Add the entity as a member of 'eng' group 1161 entityIDReq.Path = "group/id/" + engGroupID 1162 entityIDReq.Data = map[string]interface{}{ 1163 "member_entity_ids": []string{entityID3}, 1164 "member_group_ids": engMemberGroupIDs, 1165 } 1166 resp, err = is.HandleRequest(ctx, entityIDReq) 1167 if err != nil || (resp != nil && resp.IsError()) { 1168 t.Fatalf("bad: resp: %#v, err: %v", resp, err) 1169 } 1170 1171 policiesResult, err := is.groupPoliciesByEntityID(entityID1) 1172 if err != nil { 1173 t.Fatal(err) 1174 } 1175 var policies []string 1176 for _, nsPolicies := range policiesResult { 1177 policies = append(policies, nsPolicies...) 1178 } 1179 sort.Strings(policies) 1180 expected := []string{"kubepolicy", "vaultpolicy", "engpolicy"} 1181 sort.Strings(expected) 1182 if !reflect.DeepEqual(expected, policies) { 1183 t.Fatalf("bad: policies; expected: %#v\nactual:%#v", expected, policies) 1184 } 1185 1186 policiesResult, err = is.groupPoliciesByEntityID(entityID2) 1187 if err != nil { 1188 t.Fatal(err) 1189 } 1190 policies = nil 1191 for _, nsPolicies := range policiesResult { 1192 policies = append(policies, nsPolicies...) 1193 } 1194 sort.Strings(policies) 1195 expected = []string{"opspolicy", "engpolicy"} 1196 sort.Strings(expected) 1197 if !reflect.DeepEqual(expected, policies) { 1198 t.Fatalf("bad: policies; expected: %#v\nactual:%#v", expected, policies) 1199 } 1200 1201 policiesResult, err = is.groupPoliciesByEntityID(entityID3) 1202 if err != nil { 1203 t.Fatal(err) 1204 } 1205 policies = nil 1206 for _, nsPolicies := range policiesResult { 1207 policies = append(policies, nsPolicies...) 1208 } 1209 1210 if len(policies) != 1 && policies[0] != "engpolicy" { 1211 t.Fatalf("bad: policies; expected: 'engpolicy'\nactual:%#v", policies) 1212 } 1213 1214 groups, inheritedGroups, err := is.groupsByEntityID(entityID1) 1215 if err != nil { 1216 t.Fatal(err) 1217 } 1218 if len(groups) != 1 { 1219 t.Fatalf("bad: length of groups; expected: 1, actual: %d", len(groups)) 1220 } 1221 if len(inheritedGroups) != 2 { 1222 t.Fatalf("bad: length of inheritedGroups; expected: 2, actual: %d", len(inheritedGroups)) 1223 } 1224 1225 groups, inheritedGroups, err = is.groupsByEntityID(entityID2) 1226 if err != nil { 1227 t.Fatal(err) 1228 } 1229 if len(groups) != 1 { 1230 t.Fatalf("bad: length of groups; expected: 1, actual: %d", len(groups)) 1231 } 1232 if len(inheritedGroups) != 1 { 1233 t.Fatalf("bad: length of inheritedGroups; expected: 1, actual: %d", len(inheritedGroups)) 1234 } 1235 1236 groups, inheritedGroups, err = is.groupsByEntityID(entityID3) 1237 if err != nil { 1238 t.Fatal(err) 1239 } 1240 if len(groups) != 1 { 1241 t.Fatalf("bad: length of groups; expected: 1, actual: %d", len(groups)) 1242 } 1243 if len(inheritedGroups) != 0 { 1244 t.Fatalf("bad: length of inheritedGroups; expected: 0, actual: %d", len(inheritedGroups)) 1245 } 1246} 1247