1package vault
2
3import (
4	"context"
5	"reflect"
6	"sync"
7	"testing"
8	"time"
9
10	"github.com/hashicorp/vault/helper/namespace"
11	"github.com/hashicorp/vault/sdk/logical"
12)
13
14func TestACL_NewACL(t *testing.T) {
15	t.Run("root-ns", func(t *testing.T) {
16		t.Parallel()
17		testNewACL(t, namespace.RootNamespace)
18	})
19}
20
21func testNewACL(t *testing.T, ns *namespace.Namespace) {
22	ctx := namespace.ContextWithNamespace(context.Background(), ns)
23	policy := []*Policy{{Name: "root"}}
24	_, err := NewACL(ctx, policy)
25	switch ns.ID {
26	case namespace.RootNamespaceID:
27		if err != nil {
28			t.Fatal(err)
29		}
30	default:
31		if err == nil {
32			t.Fatal("expected an error")
33		}
34	}
35}
36
37func TestACL_MFAMethods(t *testing.T) {
38	t.Run("root-ns", func(t *testing.T) {
39		t.Parallel()
40		testACLMFAMethods(t, namespace.RootNamespace)
41	})
42}
43
44func testACLMFAMethods(t *testing.T, ns *namespace.Namespace) {
45	mfaRules := `
46path "secret/foo/*" {
47	mfa_methods = ["mfa_method_1", "mfa_method_2", "mfa_method_3"]
48}
49path "secret/exact/path" {
50	mfa_methods = ["mfa_method_4", "mfa_method_5"]
51}
52path "secret/split/definition" {
53	mfa_methods = ["mfa_method_6", "mfa_method_7"]
54}
55path "secret/split/definition" {
56	mfa_methods = ["mfa_method_7", "mfa_method_8", "mfa_method_9"]
57}
58	`
59
60	policy, err := ParseACLPolicy(ns, mfaRules)
61	if err != nil {
62		t.Fatal(err)
63	}
64
65	ctx := namespace.ContextWithNamespace(context.Background(), ns)
66	acl, err := NewACL(ctx, []*Policy{policy})
67	if err != nil {
68		t.Fatal(err)
69	}
70
71	request := &logical.Request{
72		Operation: logical.UpdateOperation,
73		Path:      "secret/foo/testing/glob/pattern",
74	}
75
76	actual := acl.AllowOperation(ctx, request, false).MFAMethods
77	expected := []string{"mfa_method_1", "mfa_method_2", "mfa_method_3"}
78
79	if !reflect.DeepEqual(expected, actual) {
80		t.Fatalf("bad: MFA methods; expected: %#v\n actual: %#v\n", expected, actual)
81	}
82
83	request.Path = "secret/exact/path"
84	actual = acl.AllowOperation(ctx, request, false).MFAMethods
85	expected = []string{"mfa_method_4", "mfa_method_5"}
86
87	if !reflect.DeepEqual(expected, actual) {
88		t.Fatalf("bad: MFA methods; expected: %#v\n actual: %#v\n", expected, actual)
89	}
90
91	request.Path = "secret/split/definition"
92	actual = acl.AllowOperation(ctx, request, false).MFAMethods
93	expected = []string{"mfa_method_6", "mfa_method_7", "mfa_method_8", "mfa_method_9"}
94
95	if !reflect.DeepEqual(expected, actual) {
96		t.Fatalf("bad: MFA methods; expected: %#v\n actual: %#v\n", expected, actual)
97	}
98}
99
100func TestACL_Capabilities(t *testing.T) {
101	t.Run("root-ns", func(t *testing.T) {
102		t.Parallel()
103		policy := []*Policy{{Name: "root"}}
104		ctx := namespace.RootContext(nil)
105		acl, err := NewACL(ctx, policy)
106		if err != nil {
107			t.Fatalf("err: %v", err)
108		}
109
110		actual := acl.Capabilities(ctx, "any/path")
111		expected := []string{"root"}
112		if !reflect.DeepEqual(actual, expected) {
113			t.Fatalf("bad: got\n%#v\nexpected\n%#v\n", actual, expected)
114		}
115		testACLCapabilities(t, namespace.RootNamespace)
116	})
117}
118
119func testACLCapabilities(t *testing.T, ns *namespace.Namespace) {
120	// Create the root policy ACL
121	ctx := namespace.ContextWithNamespace(context.Background(), ns)
122	policy, err := ParseACLPolicy(ns, aclPolicy)
123	if err != nil {
124		t.Fatalf("err: %v", err)
125	}
126
127	acl, err := NewACL(ctx, []*Policy{policy})
128	if err != nil {
129		t.Fatalf("err: %v", err)
130	}
131
132	actual := acl.Capabilities(ctx, "dev")
133	expected := []string{"deny"}
134	if !reflect.DeepEqual(actual, expected) {
135		t.Fatalf("bad: path: %s\ngot\n%#v\nexpected\n%#v\n", "deny", actual, expected)
136	}
137
138	actual = acl.Capabilities(ctx, "dev/")
139	expected = []string{"sudo", "read", "list", "update", "delete", "create"}
140	if !reflect.DeepEqual(actual, expected) {
141		t.Fatalf("bad: path: %s\ngot\n%#v\nexpected\n%#v\n", "dev/", actual, expected)
142	}
143
144	actual = acl.Capabilities(ctx, "stage/aws/test")
145	expected = []string{"sudo", "read", "list", "update"}
146	if !reflect.DeepEqual(actual, expected) {
147		t.Fatalf("bad: path: %s\ngot\n%#v\nexpected\n%#v\n", "stage/aws/test", actual, expected)
148	}
149}
150
151func TestACL_Root(t *testing.T) {
152	t.Run("root-ns", func(t *testing.T) {
153		t.Parallel()
154		testACLRoot(t, namespace.RootNamespace)
155	})
156}
157
158func testACLRoot(t *testing.T, ns *namespace.Namespace) {
159	// Create the root policy ACL. Always create on root namespace regardless of
160	// which namespace to ACL check on.
161	policy := []*Policy{{Name: "root"}}
162	acl, err := NewACL(namespace.RootContext(nil), policy)
163	if err != nil {
164		t.Fatalf("err: %v", err)
165	}
166
167	request := new(logical.Request)
168	request.Operation = logical.UpdateOperation
169	request.Path = "sys/mount/foo"
170	ctx := namespace.ContextWithNamespace(context.Background(), ns)
171
172	authResults := acl.AllowOperation(ctx, request, false)
173	if !authResults.RootPrivs {
174		t.Fatalf("expected root")
175	}
176	if !authResults.Allowed {
177		t.Fatalf("expected permissions")
178	}
179}
180
181func TestACL_Single(t *testing.T) {
182	t.Run("root-ns", func(t *testing.T) {
183		t.Parallel()
184		testACLSingle(t, namespace.RootNamespace)
185	})
186}
187
188func testACLSingle(t *testing.T, ns *namespace.Namespace) {
189	policy, err := ParseACLPolicy(ns, aclPolicy)
190	if err != nil {
191		t.Fatalf("err: %v", err)
192	}
193
194	ctx := namespace.ContextWithNamespace(context.Background(), ns)
195	acl, err := NewACL(ctx, []*Policy{policy})
196	if err != nil {
197		t.Fatalf("err: %v", err)
198	}
199
200	// Type of operation is not important here as we only care about checking
201	// sudo/root
202	ctx = namespace.ContextWithNamespace(context.Background(), ns)
203	request := new(logical.Request)
204	request.Operation = logical.ReadOperation
205	request.Path = "sys/mount/foo"
206
207	authResults := acl.AllowOperation(ctx, request, false)
208	if authResults.RootPrivs {
209		t.Fatalf("unexpected root")
210	}
211
212	type tcase struct {
213		op        logical.Operation
214		path      string
215		allowed   bool
216		rootPrivs bool
217	}
218	tcases := []tcase{
219		{logical.ReadOperation, "root", false, false},
220		{logical.HelpOperation, "root", true, false},
221
222		{logical.ReadOperation, "dev/foo", true, true},
223		{logical.UpdateOperation, "dev/foo", true, true},
224
225		{logical.DeleteOperation, "stage/foo", true, false},
226		{logical.ListOperation, "stage/aws/foo", true, true},
227		{logical.UpdateOperation, "stage/aws/foo", true, true},
228		{logical.UpdateOperation, "stage/aws/policy/foo", true, true},
229
230		{logical.DeleteOperation, "prod/foo", false, false},
231		{logical.UpdateOperation, "prod/foo", false, false},
232		{logical.ReadOperation, "prod/foo", true, false},
233		{logical.ListOperation, "prod/foo", true, false},
234		{logical.ReadOperation, "prod/aws/foo", false, false},
235
236		{logical.ReadOperation, "foo/bar", true, true},
237		{logical.ListOperation, "foo/bar", false, true},
238		{logical.UpdateOperation, "foo/bar", false, true},
239		{logical.CreateOperation, "foo/bar", true, true},
240
241		// Path segment wildcards
242		{logical.ReadOperation, "test/foo/bar/segment", false, false},
243		{logical.ReadOperation, "test/foo/segment", true, false},
244		{logical.ReadOperation, "test/bar/segment", true, false},
245		{logical.ReadOperation, "test/segment/at/frond", false, false},
246		{logical.ReadOperation, "test/segment/at/front", true, false},
247		{logical.ReadOperation, "test/segment/at/end/foo", true, false},
248		{logical.ReadOperation, "test/segment/at/end/foo/", false, false},
249		{logical.ReadOperation, "test/segment/at/end/v2/foo/", true, false},
250		{logical.ReadOperation, "test/segment/wildcard/at/foo/", true, false},
251		{logical.ReadOperation, "test/segment/wildcard/at/end", true, false},
252		{logical.ReadOperation, "test/segment/wildcard/at/end/", true, false},
253
254		// Path segment wildcards vs glob
255		{logical.ReadOperation, "1/2/3/4", false, false},
256		{logical.ReadOperation, "1/2/3", true, false},
257		{logical.UpdateOperation, "1/2/3", false, false},
258		{logical.UpdateOperation, "1/2/3/4", true, false},
259		{logical.CreateOperation, "1/2/3/4/5", true, false},
260	}
261
262	for _, tc := range tcases {
263		ctx := namespace.ContextWithNamespace(context.Background(), ns)
264		request := new(logical.Request)
265		request.Operation = tc.op
266		request.Path = tc.path
267
268		authResults := acl.AllowOperation(ctx, request, false)
269		if authResults.Allowed != tc.allowed {
270			t.Fatalf("bad: case %#v: %v, %v", tc, authResults.Allowed, authResults.RootPrivs)
271		}
272		if authResults.RootPrivs != tc.rootPrivs {
273			t.Fatalf("bad: case %#v: %v, %v", tc, authResults.Allowed, authResults.RootPrivs)
274		}
275	}
276}
277
278func TestACL_Layered(t *testing.T) {
279	t.Run("root-ns", func(t *testing.T) {
280		t.Parallel()
281		policy1, err := ParseACLPolicy(namespace.RootNamespace, aclPolicy)
282		if err != nil {
283			t.Fatalf("err: %v", err)
284		}
285
286		policy2, err := ParseACLPolicy(namespace.RootNamespace, aclPolicy2)
287		if err != nil {
288			t.Fatalf("err: %v", err)
289		}
290		acl, err := NewACL(namespace.RootContext(nil), []*Policy{policy1, policy2})
291		if err != nil {
292			t.Fatalf("err: %v", err)
293		}
294		testLayeredACL(t, acl, namespace.RootNamespace)
295	})
296}
297
298func testLayeredACL(t *testing.T, acl *ACL, ns *namespace.Namespace) {
299	// Type of operation is not important here as we only care about checking
300	// sudo/root
301	ctx := namespace.ContextWithNamespace(context.Background(), ns)
302	request := new(logical.Request)
303	request.Operation = logical.ReadOperation
304	request.Path = "sys/mount/foo"
305
306	authResults := acl.AllowOperation(ctx, request, false)
307	if authResults.RootPrivs {
308		t.Fatalf("unexpected root")
309	}
310
311	type tcase struct {
312		op        logical.Operation
313		path      string
314		allowed   bool
315		rootPrivs bool
316	}
317	tcases := []tcase{
318		{logical.ReadOperation, "root", false, false},
319		{logical.HelpOperation, "root", true, false},
320
321		{logical.ReadOperation, "dev/foo", true, true},
322		{logical.UpdateOperation, "dev/foo", true, true},
323		{logical.ReadOperation, "dev/hide/foo", false, false},
324		{logical.UpdateOperation, "dev/hide/foo", false, false},
325
326		{logical.DeleteOperation, "stage/foo", true, false},
327		{logical.ListOperation, "stage/aws/foo", true, true},
328		{logical.UpdateOperation, "stage/aws/foo", true, true},
329		{logical.UpdateOperation, "stage/aws/policy/foo", false, false},
330
331		{logical.DeleteOperation, "prod/foo", true, false},
332		{logical.UpdateOperation, "prod/foo", true, false},
333		{logical.ReadOperation, "prod/foo", true, false},
334		{logical.ListOperation, "prod/foo", true, false},
335		{logical.ReadOperation, "prod/aws/foo", false, false},
336
337		{logical.ReadOperation, "sys/status", false, false},
338		{logical.UpdateOperation, "sys/seal", true, true},
339
340		{logical.ReadOperation, "foo/bar", false, false},
341		{logical.ListOperation, "foo/bar", false, false},
342		{logical.UpdateOperation, "foo/bar", false, false},
343		{logical.CreateOperation, "foo/bar", false, false},
344	}
345
346	for _, tc := range tcases {
347		ctx := namespace.ContextWithNamespace(context.Background(), ns)
348		request := new(logical.Request)
349		request.Operation = tc.op
350		request.Path = tc.path
351
352		authResults := acl.AllowOperation(ctx, request, false)
353		if authResults.Allowed != tc.allowed {
354			t.Fatalf("bad: case %#v: %v, %v", tc, authResults.Allowed, authResults.RootPrivs)
355		}
356		if authResults.RootPrivs != tc.rootPrivs {
357			t.Fatalf("bad: case %#v: %v, %v", tc, authResults.Allowed, authResults.RootPrivs)
358		}
359	}
360}
361
362func TestACL_ParseMalformedPolicy(t *testing.T) {
363	_, err := ParseACLPolicy(namespace.RootNamespace, `name{}`)
364	if err == nil {
365		t.Fatalf("expected error")
366	}
367}
368
369func TestACL_PolicyMerge(t *testing.T) {
370	t.Run("root-ns", func(t *testing.T) {
371		t.Parallel()
372		testACLPolicyMerge(t, namespace.RootNamespace)
373	})
374}
375
376func testACLPolicyMerge(t *testing.T, ns *namespace.Namespace) {
377	policy, err := ParseACLPolicy(ns, mergingPolicies)
378	if err != nil {
379		t.Fatalf("err: %v", err)
380	}
381	ctx := namespace.ContextWithNamespace(context.Background(), ns)
382	acl, err := NewACL(ctx, []*Policy{policy})
383	if err != nil {
384		t.Fatalf("err: %v", err)
385	}
386
387	type tcase struct {
388		path           string
389		minWrappingTTL *time.Duration
390		maxWrappingTTL *time.Duration
391		allowed        map[string][]interface{}
392		denied         map[string][]interface{}
393		required       []string
394	}
395
396	createDuration := func(seconds int) *time.Duration {
397		ret := time.Duration(seconds) * time.Second
398		return &ret
399	}
400
401	tcases := []tcase{
402		{"foo/bar", nil, nil, nil, map[string][]interface{}{"zip": {}, "baz": {}}, []string{"baz"}},
403		{"hello/universe", createDuration(50), createDuration(200), map[string][]interface{}{"foo": {}, "bar": {}}, nil, []string{"foo", "bar"}},
404		{"allow/all", nil, nil, map[string][]interface{}{"*": {}, "test": {}, "test1": {"foo"}}, nil, nil},
405		{"allow/all1", nil, nil, map[string][]interface{}{"*": {}, "test": {}, "test1": {"foo"}}, nil, nil},
406		{"deny/all", nil, nil, nil, map[string][]interface{}{"*": {}, "test": {}}, nil},
407		{"deny/all1", nil, nil, nil, map[string][]interface{}{"*": {}, "test": {}}, nil},
408		{"value/merge", nil, nil, map[string][]interface{}{"test": {3, 4, 1, 2}}, map[string][]interface{}{"test": {3, 4, 1, 2}}, nil},
409		{"value/empty", nil, nil, map[string][]interface{}{"empty": {}}, map[string][]interface{}{"empty": {}}, nil},
410	}
411
412	for _, tc := range tcases {
413		policyPath := ns.Path + tc.path
414		raw, ok := acl.exactRules.Get(policyPath)
415		if !ok {
416			t.Fatalf("Could not find acl entry for path %s", policyPath)
417		}
418
419		p := raw.(*ACLPermissions)
420		if !reflect.DeepEqual(tc.allowed, p.AllowedParameters) {
421			t.Fatalf("Allowed parameters did not match, Expected: %#v, Got: %#v", tc.allowed, p.AllowedParameters)
422		}
423		if !reflect.DeepEqual(tc.denied, p.DeniedParameters) {
424			t.Fatalf("Denied parameters did not match, Expected: %#v, Got: %#v", tc.denied, p.DeniedParameters)
425		}
426		if !reflect.DeepEqual(tc.required, p.RequiredParameters) {
427			t.Fatalf("Required parameters did not match, Expected: %#v, Got: %#v", tc.required, p.RequiredParameters)
428		}
429		if tc.minWrappingTTL != nil && *tc.minWrappingTTL != p.MinWrappingTTL {
430			t.Fatalf("Min wrapping TTL did not match, Expected: %#v, Got: %#v", tc.minWrappingTTL, p.MinWrappingTTL)
431		}
432		if tc.minWrappingTTL != nil && *tc.maxWrappingTTL != p.MaxWrappingTTL {
433			t.Fatalf("Max wrapping TTL did not match, Expected: %#v, Got: %#v", tc.maxWrappingTTL, p.MaxWrappingTTL)
434		}
435	}
436}
437
438func TestACL_AllowOperation(t *testing.T) {
439	t.Run("root-ns", func(t *testing.T) {
440		t.Parallel()
441		testACLAllowOperation(t, namespace.RootNamespace)
442	})
443}
444
445func testACLAllowOperation(t *testing.T, ns *namespace.Namespace) {
446	policy, err := ParseACLPolicy(ns, permissionsPolicy)
447	if err != nil {
448		t.Fatalf("err: %v", err)
449	}
450	ctx := namespace.ContextWithNamespace(context.Background(), ns)
451	acl, err := NewACL(ctx, []*Policy{policy})
452	if err != nil {
453		t.Fatalf("err: %v", err)
454	}
455	toperations := []logical.Operation{
456		logical.UpdateOperation,
457		logical.CreateOperation,
458	}
459	type tcase struct {
460		path        string
461		wrappingTTL *time.Duration
462		parameters  []string
463		allowed     bool
464	}
465
466	createDuration := func(seconds int) *time.Duration {
467		ret := time.Duration(seconds) * time.Second
468		return &ret
469	}
470
471	tcases := []tcase{
472		{"dev/ops", nil, []string{"zip"}, true},
473		{"foo/bar", nil, []string{"zap"}, false},
474		{"foo/bar", nil, []string{"zip"}, false},
475		{"foo/bar", createDuration(50), []string{"zip"}, false},
476		{"foo/bar", createDuration(450), []string{"zip"}, false},
477		{"foo/bar", createDuration(350), []string{"zip"}, true},
478		{"foo/baz", nil, []string{"hello"}, false},
479		{"foo/baz", createDuration(50), []string{"hello"}, false},
480		{"foo/baz", createDuration(450), []string{"hello"}, true},
481		{"foo/baz", nil, []string{"zap"}, false},
482		{"broken/phone", nil, []string{"steve"}, false},
483		{"working/phone", nil, []string{""}, false},
484		{"working/phone", createDuration(450), []string{""}, false},
485		{"working/phone", createDuration(350), []string{""}, true},
486		{"hello/world", nil, []string{"one"}, false},
487		{"tree/fort", nil, []string{"one"}, true},
488		{"tree/fort", nil, []string{"foo"}, false},
489		{"fruit/apple", nil, []string{"pear"}, false},
490		{"fruit/apple", nil, []string{"one"}, false},
491		{"cold/weather", nil, []string{"four"}, true},
492		{"var/aws", nil, []string{"cold", "warm", "kitty"}, false},
493		{"var/req", nil, []string{"cold", "warm", "kitty"}, false},
494		{"var/req", nil, []string{"cold", "warm", "kitty", "foo"}, true},
495	}
496
497	for _, tc := range tcases {
498		request := &logical.Request{
499			Path: tc.path,
500			Data: make(map[string]interface{}),
501		}
502
503		for _, parameter := range tc.parameters {
504			request.Data[parameter] = ""
505		}
506		if tc.wrappingTTL != nil {
507			request.WrapInfo = &logical.RequestWrapInfo{
508				TTL: *tc.wrappingTTL,
509			}
510		}
511		for _, op := range toperations {
512			request.Operation = op
513			ctx := namespace.ContextWithNamespace(context.Background(), ns)
514			authResults := acl.AllowOperation(ctx, request, false)
515			if authResults.Allowed != tc.allowed {
516				t.Fatalf("bad: case %#v: %v", tc, authResults.Allowed)
517			}
518		}
519	}
520}
521
522func TestACL_ValuePermissions(t *testing.T) {
523	t.Run("root-ns", func(t *testing.T) {
524		t.Parallel()
525		testACLValuePermissions(t, namespace.RootNamespace)
526	})
527}
528
529func testACLValuePermissions(t *testing.T, ns *namespace.Namespace) {
530	policy, err := ParseACLPolicy(ns, valuePermissionsPolicy)
531	if err != nil {
532		t.Fatalf("err: %v", err)
533	}
534
535	ctx := namespace.ContextWithNamespace(context.Background(), ns)
536	acl, err := NewACL(ctx, []*Policy{policy})
537	if err != nil {
538		t.Fatalf("err: %v", err)
539	}
540
541	toperations := []logical.Operation{
542		logical.UpdateOperation,
543		logical.CreateOperation,
544	}
545	type tcase struct {
546		path       string
547		parameters []string
548		values     []interface{}
549		allowed    bool
550	}
551
552	tcases := []tcase{
553		{"dev/ops", []string{"allow"}, []interface{}{"good"}, true},
554		{"dev/ops", []string{"allow"}, []interface{}{"bad"}, false},
555		{"foo/bar", []string{"deny"}, []interface{}{"bad"}, false},
556		{"foo/bar", []string{"deny"}, []interface{}{"bad glob"}, false},
557		{"foo/bar", []string{"deny"}, []interface{}{"good"}, true},
558		{"foo/bar", []string{"allow"}, []interface{}{"good"}, true},
559		{"foo/bar", []string{"deny"}, []interface{}{nil}, true},
560		{"foo/bar", []string{"allow"}, []interface{}{nil}, true},
561		{"foo/baz", []string{"aLLow"}, []interface{}{"good"}, true},
562		{"foo/baz", []string{"deny"}, []interface{}{"bad"}, false},
563		{"foo/baz", []string{"deny"}, []interface{}{"good"}, false},
564		{"foo/baz", []string{"allow", "deny"}, []interface{}{"good", "bad"}, false},
565		{"foo/baz", []string{"deny", "allow"}, []interface{}{"good", "bad"}, false},
566		{"foo/baz", []string{"deNy", "allow"}, []interface{}{"bad", "good"}, false},
567		{"foo/baz", []string{"aLLow"}, []interface{}{"bad"}, false},
568		{"foo/baz", []string{"Neither"}, []interface{}{"bad"}, false},
569		{"foo/baz", []string{"allow"}, []interface{}{nil}, false},
570		{"fizz/buzz", []string{"allow_multi"}, []interface{}{"good"}, true},
571		{"fizz/buzz", []string{"allow_multi"}, []interface{}{"good1"}, true},
572		{"fizz/buzz", []string{"allow_multi"}, []interface{}{"good2"}, true},
573		{"fizz/buzz", []string{"allow_multi"}, []interface{}{"glob good2"}, false},
574		{"fizz/buzz", []string{"allow_multi"}, []interface{}{"glob good3"}, true},
575		{"fizz/buzz", []string{"allow_multi"}, []interface{}{"bad"}, false},
576		{"fizz/buzz", []string{"allow_multi"}, []interface{}{"bad"}, false},
577		{"fizz/buzz", []string{"allow_multi", "allow"}, []interface{}{"good1", "good"}, true},
578		{"fizz/buzz", []string{"deny_multi"}, []interface{}{"bad2"}, false},
579		{"fizz/buzz", []string{"deny_multi", "allow_multi"}, []interface{}{"good", "good2"}, false},
580		//	{"test/types", []string{"array"}, []interface{}{[1]string{"good"}}, true},
581		{"test/types", []string{"map"}, []interface{}{map[string]interface{}{"good": "one"}}, true},
582		{"test/types", []string{"map"}, []interface{}{map[string]interface{}{"bad": "one"}}, false},
583		{"test/types", []string{"int"}, []interface{}{1}, true},
584		{"test/types", []string{"int"}, []interface{}{3}, false},
585		{"test/types", []string{"bool"}, []interface{}{false}, true},
586		{"test/types", []string{"bool"}, []interface{}{true}, false},
587		{"test/star", []string{"anything"}, []interface{}{true}, true},
588		{"test/star", []string{"foo"}, []interface{}{true}, true},
589		{"test/star", []string{"bar"}, []interface{}{false}, true},
590		{"test/star", []string{"bar"}, []interface{}{true}, false},
591	}
592
593	for _, tc := range tcases {
594		request := &logical.Request{
595			Path: tc.path,
596			Data: make(map[string]interface{}),
597		}
598		ctx := namespace.ContextWithNamespace(context.Background(), ns)
599
600		for i, parameter := range tc.parameters {
601			request.Data[parameter] = tc.values[i]
602		}
603		for _, op := range toperations {
604			request.Operation = op
605			authResults := acl.AllowOperation(ctx, request, false)
606			if authResults.Allowed != tc.allowed {
607				t.Fatalf("bad: case %#v: %v", tc, authResults.Allowed)
608			}
609		}
610	}
611}
612
613func TestACL_SegmentWildcardPriority(t *testing.T) {
614	ns := namespace.RootNamespace
615	ctx := namespace.ContextWithNamespace(context.Background(), ns)
616	type poltest struct {
617		policy string
618		path   string
619	}
620
621	// These test cases should each have a read rule and an update rule, where
622	// the update rule wins out due to being more specific.
623	poltests := []poltest{
624
625		{
626			// Verify edge conditions.  Here '*' is more specific both because
627			// of first wildcard position (0 vs -1/infinity) and #wildcards.
628			`
629path "+/*" { capabilities = ["read"] }
630path "*" { capabilities = ["update"] }
631`,
632			"foo/bar/bar/baz",
633		},
634		{
635			// Verify edge conditions.  Here '+/*' is less specific because of
636			// first wildcard position.
637			`
638path "+/*" { capabilities = ["read"] }
639path "foo/+/*" { capabilities = ["update"] }
640`,
641			"foo/bar/bar/baz",
642		},
643		{
644			// Verify that more wildcard segments is lower priority.
645			`
646path "foo/+/+/*" { capabilities = ["read"] }
647path "foo/+/bar/baz" { capabilities = ["update"] }
648`,
649			"foo/bar/bar/baz",
650		},
651		{
652			// Verify that more wildcard segments is lower priority.
653			`
654path "foo/+/+/baz" { capabilities = ["read"] }
655path "foo/+/bar/baz" { capabilities = ["update"] }
656`,
657			"foo/bar/bar/baz",
658		},
659		{
660			// Verify that first wildcard position is lower priority.
661			// '(' is used here because it is lexicographically smaller than "+"
662			`
663path "foo/+/(ar/baz" { capabilities = ["read"] }
664path "foo/(ar/+/baz" { capabilities = ["update"] }
665`,
666			"foo/(ar/(ar/baz",
667		},
668
669		{
670			// Verify that a glob has lower priority, even if the prefix is the
671			// same otherwise.
672			`
673path "foo/bar/+/baz*" { capabilities = ["read"] }
674path "foo/bar/+/baz" { capabilities = ["update"] }
675`,
676			"foo/bar/bar/baz",
677		},
678		{
679			// Verify that a shorter prefix has lower priority.
680			`
681path "foo/bar/+/b*" { capabilities = ["read"] }
682path "foo/bar/+/ba*" { capabilities = ["update"] }
683`,
684			"foo/bar/bar/baz",
685		},
686	}
687
688	for i, pt := range poltests {
689		policy, err := ParseACLPolicy(ns, pt.policy)
690		if err != nil {
691			t.Fatalf("err: %v", err)
692		}
693
694		acl, err := NewACL(ctx, []*Policy{policy})
695		if err != nil {
696			t.Fatalf("err: %v", err)
697		}
698
699		request := new(logical.Request)
700		request.Path = pt.path
701
702		request.Operation = logical.UpdateOperation
703		authResults := acl.AllowOperation(ctx, request, false)
704		if !authResults.Allowed {
705			t.Fatalf("bad: case %d %#v: %v", i, pt, authResults.Allowed)
706		}
707
708		request.Operation = logical.ReadOperation
709		authResults = acl.AllowOperation(ctx, request, false)
710		if authResults.Allowed {
711			t.Fatalf("bad: case %d %#v: %v", i, pt, authResults.Allowed)
712		}
713	}
714}
715
716func TestACL_SegmentWildcardPriority_BareMount(t *testing.T) {
717	ns := namespace.RootNamespace
718	ctx := namespace.ContextWithNamespace(context.Background(), ns)
719	type poltest struct {
720		policy    string
721		mountpath string
722		hasperms  bool
723	}
724	// These test cases should have one or more rules and a mount prefix.
725	// hasperms should be true if there are non-deny perms that apply
726	// to the mount prefix or something below it.
727	poltests := []poltest{
728		{
729			`path "+" { capabilities = ["read"] }`,
730			"foo/",
731			true,
732		},
733		{
734			`path "+/*" { capabilities = ["read"] }`,
735			"foo/",
736			true,
737		},
738		{
739			`path "foo/+/+/*" { capabilities = ["read"] }`,
740			"foo/",
741			true,
742		},
743		{
744			`path "foo/+/+/*" { capabilities = ["read"] }`,
745			"foo/bar/",
746			true,
747		},
748		{
749			`path "foo/+/+/*" { capabilities = ["read"] }`,
750			"foo/bar/bar/",
751			true,
752		},
753		{
754			`path "foo/+/+/*" { capabilities = ["read"] }`,
755			"foo/bar/bar/baz/",
756			true,
757		},
758		{
759			`path "foo/+/+/baz" { capabilities = ["read"] }`,
760			"foo/bar/bar/baz/",
761			true,
762		},
763		{
764			`path "foo/+/bar/baz" { capabilities = ["read"] }`,
765			"foo/bar/bar/baz/",
766			true,
767		},
768		{
769			`path "foo/bar/+/baz*" { capabilities = ["read"] }`,
770			"foo/bar/bar/baz/",
771			true,
772		},
773		{
774			`path "foo/bar/+/b*" { capabilities = ["read"] }`,
775			"foo/bar/bar/baz/",
776			true,
777		},
778		{
779			`path "foo/+" { capabilities = ["read"] }`,
780			"foo/",
781			true,
782		},
783	}
784
785	for i, pt := range poltests {
786		policy, err := ParseACLPolicy(ns, pt.policy)
787		if err != nil {
788			t.Fatalf("err: %v", err)
789		}
790
791		acl, err := NewACL(ctx, []*Policy{policy})
792		if err != nil {
793			t.Fatalf("err: %v", err)
794		}
795
796		hasperms := nil != acl.CheckAllowedFromNonExactPaths(pt.mountpath, true)
797		if hasperms != pt.hasperms {
798			t.Fatalf("bad: case %d: %#v", i, pt)
799		}
800
801	}
802}
803
804// NOTE: this test doesn't catch any races ATM
805func TestACL_CreationRace(t *testing.T) {
806	policy, err := ParseACLPolicy(namespace.RootNamespace, valuePermissionsPolicy)
807	if err != nil {
808		t.Fatalf("err: %v", err)
809	}
810
811	var wg sync.WaitGroup
812	stopTime := time.Now().Add(20 * time.Second)
813
814	for i := 0; i < 50; i++ {
815		wg.Add(1)
816		go func() {
817			defer wg.Done()
818			for {
819				if time.Now().After(stopTime) {
820					return
821				}
822				_, err := NewACL(namespace.RootContext(nil), []*Policy{policy})
823				if err != nil {
824					t.Fatalf("err: %v", err)
825				}
826			}
827		}()
828	}
829
830	wg.Wait()
831}
832
833var tokenCreationPolicy = `
834name = "tokenCreation"
835path "auth/token/create*" {
836	capabilities = ["update", "create", "sudo"]
837}
838`
839
840var aclPolicy = `
841name = "DeV"
842path "dev/*" {
843	policy = "sudo"
844}
845path "stage/*" {
846	policy = "write"
847}
848path "stage/aws/*" {
849	policy = "read"
850	capabilities = ["update", "sudo"]
851}
852path "stage/aws/policy/*" {
853	policy = "sudo"
854}
855path "prod/*" {
856	policy = "read"
857}
858path "prod/aws/*" {
859	policy = "deny"
860}
861path "sys/*" {
862	policy = "deny"
863}
864path "foo/bar" {
865	capabilities = ["read", "create", "sudo"]
866}
867path "test/+/segment" {
868	capabilities = ["read"]
869}
870path "+/segment/at/front" {
871	capabilities = ["read"]
872}
873path "test/segment/at/end/+" {
874	capabilities = ["read"]
875}
876path "test/segment/at/end/v2/+/" {
877	capabilities = ["read"]
878}
879path "test/+/wildcard/+/*" {
880	capabilities = ["read"]
881}
882path "test/+/wildcardglob/+/end*" {
883	capabilities = ["read"]
884}
885path "1/2/*" {
886	capabilities = ["create"]
887}
888path "1/2/+" {
889	capabilities = ["read"]
890}
891path "1/2/+/+" {
892	capabilities = ["update"]
893}
894`
895
896var aclPolicy2 = `
897name = "OpS"
898path "dev/hide/*" {
899	policy = "deny"
900}
901path "stage/aws/policy/*" {
902	policy = "deny"
903	# This should have no effect
904	capabilities = ["read", "update", "sudo"]
905}
906path "prod/*" {
907	policy = "write"
908}
909path "sys/seal" {
910	policy = "sudo"
911}
912path "foo/bar" {
913	capabilities = ["deny"]
914}
915`
916
917// test merging
918var mergingPolicies = `
919name = "ops"
920path "foo/bar" {
921	policy = "write"
922	denied_parameters = {
923		"baz" = []
924	}
925	required_parameters = ["baz"]
926}
927path "foo/bar" {
928	policy = "write"
929	denied_parameters = {
930		"zip" = []
931	}
932}
933path "hello/universe" {
934	policy = "write"
935	allowed_parameters = {
936		"foo" = []
937	}
938	required_parameters = ["foo"]
939	max_wrapping_ttl = 300
940	min_wrapping_ttl = 100
941}
942path "hello/universe" {
943	policy = "write"
944	allowed_parameters = {
945		"bar" = []
946	}
947	required_parameters = ["bar"]
948	max_wrapping_ttl = 200
949	min_wrapping_ttl = 50
950}
951path "allow/all" {
952	policy = "write"
953	allowed_parameters = {
954		"test" = []
955		"test1" = ["foo"]
956	}
957}
958path "allow/all" {
959	policy = "write"
960	allowed_parameters = {
961		"*" = []
962	}
963}
964path "allow/all1" {
965	policy = "write"
966	allowed_parameters = {
967		"*" = []
968	}
969}
970path "allow/all1" {
971	policy = "write"
972	allowed_parameters = {
973		"test" = []
974		"test1" = ["foo"]
975	}
976}
977path "deny/all" {
978	policy = "write"
979	denied_parameters = {
980		"test" = []
981	}
982}
983path "deny/all" {
984	policy = "write"
985	denied_parameters = {
986		"*" = []
987	}
988}
989path "deny/all1" {
990	policy = "write"
991	denied_parameters = {
992		"*" = []
993	}
994}
995path "deny/all1" {
996	policy = "write"
997	denied_parameters = {
998		"test" = []
999	}
1000}
1001path "value/merge" {
1002	policy = "write"
1003	allowed_parameters = {
1004		"test" = [1, 2]
1005	}
1006	denied_parameters = {
1007		"test" = [1, 2]
1008	}
1009}
1010path "value/merge" {
1011	policy = "write"
1012	allowed_parameters = {
1013		"test" = [3, 4]
1014	}
1015	denied_parameters = {
1016		"test" = [3, 4]
1017	}
1018}
1019path "value/empty" {
1020	policy = "write"
1021	allowed_parameters = {
1022		"empty" = []
1023	}
1024	denied_parameters = {
1025		"empty" = [1]
1026	}
1027}
1028path "value/empty" {
1029	policy = "write"
1030	allowed_parameters = {
1031		"empty" = [1]
1032	}
1033	denied_parameters = {
1034		"empty" = []
1035	}
1036}
1037`
1038
1039// allow operation testing
1040var permissionsPolicy = `
1041name = "dev"
1042path "dev/*" {
1043	policy = "write"
1044
1045	allowed_parameters = {
1046		"zip" = []
1047	}
1048}
1049path "foo/bar" {
1050	policy = "write"
1051	denied_parameters = {
1052		"zap" = []
1053	}
1054	min_wrapping_ttl = 300
1055	max_wrapping_ttl = 400
1056}
1057path "foo/baz" {
1058	policy = "write"
1059	allowed_parameters = {
1060		"hello" = []
1061	}
1062	denied_parameters = {
1063		"zap" = []
1064	}
1065	min_wrapping_ttl = 300
1066}
1067path "working/phone" {
1068	policy = "write"
1069	max_wrapping_ttl = 400
1070}
1071path "broken/phone" {
1072	policy = "write"
1073	allowed_parameters = {
1074	  "steve" = []
1075	}
1076	denied_parameters = {
1077	  "steve" = []
1078	}
1079}
1080path "hello/world" {
1081	policy = "write"
1082	allowed_parameters = {
1083		"*" = []
1084	}
1085	denied_parameters = {
1086		"*" = []
1087	}
1088}
1089path "tree/fort" {
1090	policy = "write"
1091	allowed_parameters = {
1092		"*" = []
1093	}
1094	denied_parameters = {
1095		"foo" = []
1096	}
1097}
1098path "fruit/apple" {
1099	policy = "write"
1100	allowed_parameters = {
1101		"pear" = []
1102	}
1103	denied_parameters = {
1104		"*" = []
1105	}
1106}
1107path "cold/weather" {
1108	policy = "write"
1109	allowed_parameters = {}
1110	denied_parameters = {}
1111}
1112path "var/aws" {
1113	policy = "write"
1114	allowed_parameters = {
1115		"*" = []
1116	}
1117	denied_parameters = {
1118		"soft" = []
1119		"warm" = []
1120		"kitty" = []
1121	}
1122}
1123path "var/req" {
1124	policy = "write"
1125	required_parameters = ["foo"]
1126}
1127`
1128
1129// allow operation testing
1130var valuePermissionsPolicy = `
1131name = "op"
1132path "dev/*" {
1133	policy = "write"
1134
1135	allowed_parameters = {
1136		"allow" = ["good"]
1137	}
1138}
1139path "foo/bar" {
1140	policy = "write"
1141	denied_parameters = {
1142		"deny" = ["bad*"]
1143	}
1144}
1145path "foo/baz" {
1146	policy = "write"
1147	allowed_parameters = {
1148		"ALLOW" = ["good"]
1149	}
1150	denied_parameters = {
1151		"dEny" = ["bad"]
1152	}
1153}
1154path "fizz/buzz" {
1155	policy = "write"
1156	allowed_parameters = {
1157		"allow_multi" = ["good", "good1", "good2", "*good3"]
1158		"allow" = ["good"]
1159	}
1160	denied_parameters = {
1161		"deny_multi" = ["bad", "bad1", "bad2"]
1162	}
1163}
1164path "test/types" {
1165	policy = "write"
1166	allowed_parameters = {
1167		"map" = [{"good" = "one"}]
1168		"int" = [1, 2]
1169		"bool" = [false]
1170	}
1171	denied_parameters = {
1172	}
1173}
1174path "test/star" {
1175	policy = "write"
1176	allowed_parameters = {
1177		"*" = []
1178		"foo" = []
1179		"bar" = [false]
1180	}
1181	denied_parameters = {
1182	}
1183}
1184`
1185