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