1package vault
2
3import (
4	"fmt"
5	"reflect"
6	"sort"
7	"testing"
8	"time"
9
10	"github.com/hashicorp/vault/helper/namespace"
11	"github.com/hashicorp/vault/sdk/logical"
12)
13
14func TestCapabilities_DerivedPolicies(t *testing.T) {
15	var resp *logical.Response
16	var err error
17
18	ctx := namespace.RootContext(nil)
19	i, _, c := testIdentityStoreWithGithubAuth(ctx, t)
20
21	policy1 := `
22name = "policy1"
23path "secret/sample" {
24	capabilities = ["update", "create", "sudo"]
25}
26`
27	policy2 := `
28name = "policy2"
29path "secret/sample" {
30	capabilities = ["read", "delete"]
31}
32`
33
34	policy3 := `
35name = "policy3"
36path "secret/sample" {
37	capabilities = ["list", "list"]
38}
39`
40	// Create the above policies
41	policy, _ := ParseACLPolicy(namespace.RootNamespace, policy1)
42	err = c.policyStore.SetPolicy(ctx, policy)
43	if err != nil {
44		t.Fatalf("err: %v", err)
45	}
46
47	policy, _ = ParseACLPolicy(namespace.RootNamespace, policy2)
48	err = c.policyStore.SetPolicy(ctx, policy)
49	if err != nil {
50		t.Fatalf("err: %v", err)
51	}
52
53	policy, _ = ParseACLPolicy(namespace.RootNamespace, policy3)
54	err = c.policyStore.SetPolicy(ctx, policy)
55	if err != nil {
56		t.Fatalf("err: %v", err)
57	}
58
59	// Create an entity and assign policy1 to it
60	entityReq := &logical.Request{
61		Path:      "entity",
62		Operation: logical.UpdateOperation,
63		Data: map[string]interface{}{
64			"policies": "policy1",
65		},
66	}
67	resp, err = i.HandleRequest(ctx, entityReq)
68	if err != nil || (resp != nil && resp.IsError()) {
69		t.Fatalf("bad: resp: %#v\nerr: %#v\n", resp, err)
70	}
71	entityID := resp.Data["id"].(string)
72
73	// Create a token for the entity and assign policy2 on the token
74	ent := &logical.TokenEntry{
75		ID:       "capabilitiestoken",
76		Path:     "secret/sample",
77		Policies: []string{"policy2"},
78		EntityID: entityID,
79		TTL:      time.Hour,
80	}
81	testMakeTokenDirectly(t, c.tokenStore, ent)
82
83	actual, err := c.Capabilities(ctx, "capabilitiestoken", "secret/sample")
84	if err != nil {
85		t.Fatalf("err: %v", err)
86	}
87	expected := []string{"create", "read", "sudo", "delete", "update"}
88	sort.Strings(actual)
89	sort.Strings(expected)
90	if !reflect.DeepEqual(actual, expected) {
91		t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected)
92	}
93
94	// Create a group and add the above created entity to it
95	groupReq := &logical.Request{
96		Path:      "group",
97		Operation: logical.UpdateOperation,
98		Data: map[string]interface{}{
99			"member_entity_ids": []string{entityID},
100			"policies":          "policy3",
101		},
102	}
103	resp, err = i.HandleRequest(ctx, groupReq)
104	if err != nil || (resp != nil && resp.IsError()) {
105		t.Fatalf("bad: resp: %#v\nerr: %#v\n", resp, err)
106	}
107
108	actual, err = c.Capabilities(namespace.RootContext(nil), "capabilitiestoken", "secret/sample")
109	if err != nil {
110		t.Fatalf("err: %v", err)
111	}
112	expected = []string{"create", "read", "sudo", "delete", "update", "list"}
113	sort.Strings(actual)
114	sort.Strings(expected)
115	if !reflect.DeepEqual(actual, expected) {
116		t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected)
117	}
118}
119
120func TestCapabilities_TemplatedPolicies(t *testing.T) {
121	var resp *logical.Response
122	var err error
123	i, _, c := testIdentityStoreWithGithubAuth(namespace.RootContext(nil), t)
124	// Create an entity and assign policy1 to it
125	entityReq := &logical.Request{
126		Path:      "entity",
127		Operation: logical.UpdateOperation,
128	}
129	resp, err = i.HandleRequest(namespace.RootContext(nil), entityReq)
130	if err != nil || (resp != nil && resp.IsError()) {
131		t.Fatalf("bad: resp: %#v\nerr: %#v\n", resp, err)
132	}
133	entityID := resp.Data["id"].(string)
134
135	// Create a token for the entity and assign policy2 on the token
136	ent := &logical.TokenEntry{
137		ID:       "capabilitiestoken",
138		Path:     "auth/token/create",
139		Policies: []string{"testpolicy"},
140		EntityID: entityID,
141		TTL:      time.Hour,
142	}
143	testMakeTokenDirectly(t, c.tokenStore, ent)
144
145	tCases := []struct {
146		policy   string
147		path     string
148		expected []string
149	}{
150		{
151			`name = "testpolicy"
152			path "secret/{{identity.entity.id}}/sample" {
153				capabilities = ["update", "create"]
154			}
155			`,
156			fmt.Sprintf("secret/%s/sample", entityID),
157			[]string{"update", "create"},
158		},
159		{
160			`{"name": "testpolicy", "path": {"secret/{{identity.entity.id}}/sample": {"capabilities": ["read", "create"]}}}`,
161			fmt.Sprintf("secret/%s/sample", entityID),
162			[]string{"read", "create"},
163		},
164		{
165			`{"name": "testpolicy", "path": {"secret/sample": {"capabilities": ["read"]}}}`,
166			"secret/sample",
167			[]string{"read"},
168		},
169	}
170	for _, tCase := range tCases {
171		// Create the above policies
172		policy, err := ParseACLPolicy(namespace.RootNamespace, tCase.policy)
173		if err != nil {
174			t.Fatalf("err: %v", err)
175		}
176		err = c.policyStore.SetPolicy(namespace.RootContext(nil), policy)
177		if err != nil {
178			t.Fatalf("err: %v", err)
179		}
180		actual, err := c.Capabilities(namespace.RootContext(nil), "capabilitiestoken", tCase.path)
181		if err != nil {
182			t.Fatalf("err: %v", err)
183		}
184		sort.Strings(actual)
185		sort.Strings(tCase.expected)
186		if !reflect.DeepEqual(actual, tCase.expected) {
187			t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, tCase.expected)
188		}
189	}
190}
191
192func TestCapabilities(t *testing.T) {
193	c, _, token := TestCoreUnsealed(t)
194
195	actual, err := c.Capabilities(namespace.RootContext(nil), token, "path")
196	if err != nil {
197		t.Fatalf("err: %s", err)
198	}
199	expected := []string{"root"}
200	if !reflect.DeepEqual(actual, expected) {
201		t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected)
202	}
203
204	// Create a policy
205	policy, _ := ParseACLPolicy(namespace.RootNamespace, aclPolicy)
206	err = c.policyStore.SetPolicy(namespace.RootContext(nil), policy)
207	if err != nil {
208		t.Fatalf("err: %v", err)
209	}
210
211	// Create a token for the policy
212	ent := &logical.TokenEntry{
213		ID:       "capabilitiestoken",
214		Path:     "testpath",
215		Policies: []string{"dev"},
216		TTL:      time.Hour,
217	}
218	testMakeTokenDirectly(t, c.tokenStore, ent)
219
220	actual, err = c.Capabilities(namespace.RootContext(nil), "capabilitiestoken", "foo/bar")
221	if err != nil {
222		t.Fatalf("err: %s", err)
223	}
224	expected = []string{"create", "read", "sudo"}
225	if !reflect.DeepEqual(actual, expected) {
226		t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected)
227	}
228}
229