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{&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{&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{&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_PolicyMerge(t *testing.T) { 363 t.Run("root-ns", func(t *testing.T) { 364 t.Parallel() 365 testACLPolicyMerge(t, namespace.RootNamespace) 366 }) 367} 368 369func testACLPolicyMerge(t *testing.T, ns *namespace.Namespace) { 370 policy, err := ParseACLPolicy(ns, mergingPolicies) 371 if err != nil { 372 t.Fatalf("err: %v", err) 373 } 374 ctx := namespace.ContextWithNamespace(context.Background(), ns) 375 acl, err := NewACL(ctx, []*Policy{policy}) 376 if err != nil { 377 t.Fatalf("err: %v", err) 378 } 379 380 type tcase struct { 381 path string 382 minWrappingTTL *time.Duration 383 maxWrappingTTL *time.Duration 384 allowed map[string][]interface{} 385 denied map[string][]interface{} 386 required []string 387 } 388 389 createDuration := func(seconds int) *time.Duration { 390 ret := time.Duration(seconds) * time.Second 391 return &ret 392 } 393 394 tcases := []tcase{ 395 {"foo/bar", nil, nil, nil, map[string][]interface{}{"zip": []interface{}{}, "baz": []interface{}{}}, []string{"baz"}}, 396 {"hello/universe", createDuration(50), createDuration(200), map[string][]interface{}{"foo": []interface{}{}, "bar": []interface{}{}}, nil, []string{"foo", "bar"}}, 397 {"allow/all", nil, nil, map[string][]interface{}{"*": []interface{}{}, "test": []interface{}{}, "test1": []interface{}{"foo"}}, nil, nil}, 398 {"allow/all1", nil, nil, map[string][]interface{}{"*": []interface{}{}, "test": []interface{}{}, "test1": []interface{}{"foo"}}, nil, nil}, 399 {"deny/all", nil, nil, nil, map[string][]interface{}{"*": []interface{}{}, "test": []interface{}{}}, nil}, 400 {"deny/all1", nil, nil, nil, map[string][]interface{}{"*": []interface{}{}, "test": []interface{}{}}, nil}, 401 {"value/merge", nil, nil, map[string][]interface{}{"test": []interface{}{3, 4, 1, 2}}, map[string][]interface{}{"test": []interface{}{3, 4, 1, 2}}, nil}, 402 {"value/empty", nil, nil, map[string][]interface{}{"empty": []interface{}{}}, map[string][]interface{}{"empty": []interface{}{}}, nil}, 403 } 404 405 for _, tc := range tcases { 406 policyPath := ns.Path + tc.path 407 raw, ok := acl.exactRules.Get(policyPath) 408 if !ok { 409 t.Fatalf("Could not find acl entry for path %s", policyPath) 410 } 411 412 p := raw.(*ACLPermissions) 413 if !reflect.DeepEqual(tc.allowed, p.AllowedParameters) { 414 t.Fatalf("Allowed parameters did not match, Expected: %#v, Got: %#v", tc.allowed, p.AllowedParameters) 415 } 416 if !reflect.DeepEqual(tc.denied, p.DeniedParameters) { 417 t.Fatalf("Denied parameters did not match, Expected: %#v, Got: %#v", tc.denied, p.DeniedParameters) 418 } 419 if !reflect.DeepEqual(tc.required, p.RequiredParameters) { 420 t.Fatalf("Required parameters did not match, Expected: %#v, Got: %#v", tc.required, p.RequiredParameters) 421 } 422 if tc.minWrappingTTL != nil && *tc.minWrappingTTL != p.MinWrappingTTL { 423 t.Fatalf("Min wrapping TTL did not match, Expected: %#v, Got: %#v", tc.minWrappingTTL, p.MinWrappingTTL) 424 } 425 if tc.minWrappingTTL != nil && *tc.maxWrappingTTL != p.MaxWrappingTTL { 426 t.Fatalf("Max wrapping TTL did not match, Expected: %#v, Got: %#v", tc.maxWrappingTTL, p.MaxWrappingTTL) 427 } 428 } 429} 430 431func TestACL_AllowOperation(t *testing.T) { 432 t.Run("root-ns", func(t *testing.T) { 433 t.Parallel() 434 testACLAllowOperation(t, namespace.RootNamespace) 435 }) 436} 437 438func testACLAllowOperation(t *testing.T, ns *namespace.Namespace) { 439 policy, err := ParseACLPolicy(ns, permissionsPolicy) 440 if err != nil { 441 t.Fatalf("err: %v", err) 442 } 443 ctx := namespace.ContextWithNamespace(context.Background(), ns) 444 acl, err := NewACL(ctx, []*Policy{policy}) 445 if err != nil { 446 t.Fatalf("err: %v", err) 447 } 448 toperations := []logical.Operation{ 449 logical.UpdateOperation, 450 logical.CreateOperation, 451 } 452 type tcase struct { 453 path string 454 wrappingTTL *time.Duration 455 parameters []string 456 allowed bool 457 } 458 459 createDuration := func(seconds int) *time.Duration { 460 ret := time.Duration(seconds) * time.Second 461 return &ret 462 } 463 464 tcases := []tcase{ 465 {"dev/ops", nil, []string{"zip"}, true}, 466 {"foo/bar", nil, []string{"zap"}, false}, 467 {"foo/bar", nil, []string{"zip"}, false}, 468 {"foo/bar", createDuration(50), []string{"zip"}, false}, 469 {"foo/bar", createDuration(450), []string{"zip"}, false}, 470 {"foo/bar", createDuration(350), []string{"zip"}, true}, 471 {"foo/baz", nil, []string{"hello"}, false}, 472 {"foo/baz", createDuration(50), []string{"hello"}, false}, 473 {"foo/baz", createDuration(450), []string{"hello"}, true}, 474 {"foo/baz", nil, []string{"zap"}, false}, 475 {"broken/phone", nil, []string{"steve"}, false}, 476 {"working/phone", nil, []string{""}, false}, 477 {"working/phone", createDuration(450), []string{""}, false}, 478 {"working/phone", createDuration(350), []string{""}, true}, 479 {"hello/world", nil, []string{"one"}, false}, 480 {"tree/fort", nil, []string{"one"}, true}, 481 {"tree/fort", nil, []string{"foo"}, false}, 482 {"fruit/apple", nil, []string{"pear"}, false}, 483 {"fruit/apple", nil, []string{"one"}, false}, 484 {"cold/weather", nil, []string{"four"}, true}, 485 {"var/aws", nil, []string{"cold", "warm", "kitty"}, false}, 486 {"var/req", nil, []string{"cold", "warm", "kitty"}, false}, 487 {"var/req", nil, []string{"cold", "warm", "kitty", "foo"}, true}, 488 } 489 490 for _, tc := range tcases { 491 request := &logical.Request{ 492 Path: tc.path, 493 Data: make(map[string]interface{}), 494 } 495 496 for _, parameter := range tc.parameters { 497 request.Data[parameter] = "" 498 } 499 if tc.wrappingTTL != nil { 500 request.WrapInfo = &logical.RequestWrapInfo{ 501 TTL: *tc.wrappingTTL, 502 } 503 } 504 for _, op := range toperations { 505 request.Operation = op 506 ctx := namespace.ContextWithNamespace(context.Background(), ns) 507 authResults := acl.AllowOperation(ctx, request, false) 508 if authResults.Allowed != tc.allowed { 509 t.Fatalf("bad: case %#v: %v", tc, authResults.Allowed) 510 } 511 } 512 } 513} 514 515func TestACL_ValuePermissions(t *testing.T) { 516 t.Run("root-ns", func(t *testing.T) { 517 t.Parallel() 518 testACLValuePermissions(t, namespace.RootNamespace) 519 }) 520} 521 522func testACLValuePermissions(t *testing.T, ns *namespace.Namespace) { 523 policy, err := ParseACLPolicy(ns, valuePermissionsPolicy) 524 if err != nil { 525 t.Fatalf("err: %v", err) 526 } 527 528 ctx := namespace.ContextWithNamespace(context.Background(), ns) 529 acl, err := NewACL(ctx, []*Policy{policy}) 530 if err != nil { 531 t.Fatalf("err: %v", err) 532 } 533 534 toperations := []logical.Operation{ 535 logical.UpdateOperation, 536 logical.CreateOperation, 537 } 538 type tcase struct { 539 path string 540 parameters []string 541 values []interface{} 542 allowed bool 543 } 544 545 tcases := []tcase{ 546 {"dev/ops", []string{"allow"}, []interface{}{"good"}, true}, 547 {"dev/ops", []string{"allow"}, []interface{}{"bad"}, false}, 548 {"foo/bar", []string{"deny"}, []interface{}{"bad"}, false}, 549 {"foo/bar", []string{"deny"}, []interface{}{"bad glob"}, false}, 550 {"foo/bar", []string{"deny"}, []interface{}{"good"}, true}, 551 {"foo/bar", []string{"allow"}, []interface{}{"good"}, true}, 552 {"foo/bar", []string{"deny"}, []interface{}{nil}, true}, 553 {"foo/bar", []string{"allow"}, []interface{}{nil}, true}, 554 {"foo/baz", []string{"aLLow"}, []interface{}{"good"}, true}, 555 {"foo/baz", []string{"deny"}, []interface{}{"bad"}, false}, 556 {"foo/baz", []string{"deny"}, []interface{}{"good"}, false}, 557 {"foo/baz", []string{"allow", "deny"}, []interface{}{"good", "bad"}, false}, 558 {"foo/baz", []string{"deny", "allow"}, []interface{}{"good", "bad"}, false}, 559 {"foo/baz", []string{"deNy", "allow"}, []interface{}{"bad", "good"}, false}, 560 {"foo/baz", []string{"aLLow"}, []interface{}{"bad"}, false}, 561 {"foo/baz", []string{"Neither"}, []interface{}{"bad"}, false}, 562 {"foo/baz", []string{"allow"}, []interface{}{nil}, false}, 563 {"fizz/buzz", []string{"allow_multi"}, []interface{}{"good"}, true}, 564 {"fizz/buzz", []string{"allow_multi"}, []interface{}{"good1"}, true}, 565 {"fizz/buzz", []string{"allow_multi"}, []interface{}{"good2"}, true}, 566 {"fizz/buzz", []string{"allow_multi"}, []interface{}{"glob good2"}, false}, 567 {"fizz/buzz", []string{"allow_multi"}, []interface{}{"glob good3"}, true}, 568 {"fizz/buzz", []string{"allow_multi"}, []interface{}{"bad"}, false}, 569 {"fizz/buzz", []string{"allow_multi"}, []interface{}{"bad"}, false}, 570 {"fizz/buzz", []string{"allow_multi", "allow"}, []interface{}{"good1", "good"}, true}, 571 {"fizz/buzz", []string{"deny_multi"}, []interface{}{"bad2"}, false}, 572 {"fizz/buzz", []string{"deny_multi", "allow_multi"}, []interface{}{"good", "good2"}, false}, 573 // {"test/types", []string{"array"}, []interface{}{[1]string{"good"}}, true}, 574 {"test/types", []string{"map"}, []interface{}{map[string]interface{}{"good": "one"}}, true}, 575 {"test/types", []string{"map"}, []interface{}{map[string]interface{}{"bad": "one"}}, false}, 576 {"test/types", []string{"int"}, []interface{}{1}, true}, 577 {"test/types", []string{"int"}, []interface{}{3}, false}, 578 {"test/types", []string{"bool"}, []interface{}{false}, true}, 579 {"test/types", []string{"bool"}, []interface{}{true}, false}, 580 {"test/star", []string{"anything"}, []interface{}{true}, true}, 581 {"test/star", []string{"foo"}, []interface{}{true}, true}, 582 {"test/star", []string{"bar"}, []interface{}{false}, true}, 583 {"test/star", []string{"bar"}, []interface{}{true}, false}, 584 } 585 586 for _, tc := range tcases { 587 request := &logical.Request{ 588 Path: tc.path, 589 Data: make(map[string]interface{}), 590 } 591 ctx := namespace.ContextWithNamespace(context.Background(), ns) 592 593 for i, parameter := range tc.parameters { 594 request.Data[parameter] = tc.values[i] 595 } 596 for _, op := range toperations { 597 request.Operation = op 598 authResults := acl.AllowOperation(ctx, request, false) 599 if authResults.Allowed != tc.allowed { 600 t.Fatalf("bad: case %#v: %v", tc, authResults.Allowed) 601 } 602 } 603 } 604} 605 606func TestACL_SegmentWildcardPriority(t *testing.T) { 607 ns := namespace.RootNamespace 608 ctx := namespace.ContextWithNamespace(context.Background(), ns) 609 type poltest struct { 610 policy string 611 path string 612 } 613 614 // These test cases should each have a read rule and an update rule, where 615 // the update rule wins out due to being more specific. 616 poltests := []poltest{ 617 618 { 619 // Verify edge conditions. Here '*' is more specific both because 620 // of first wildcard position (0 vs -1/infinity) and #wildcards. 621 ` 622path "+/*" { capabilities = ["read"] } 623path "*" { capabilities = ["update"] } 624`, 625 "foo/bar/bar/baz", 626 }, 627 { 628 // Verify edge conditions. Here '+/*' is less specific because of 629 // first wildcard position. 630 ` 631path "+/*" { capabilities = ["read"] } 632path "foo/+/*" { capabilities = ["update"] } 633`, 634 "foo/bar/bar/baz", 635 }, 636 { 637 // Verify that more wildcard segments is lower priority. 638 ` 639path "foo/+/+/*" { capabilities = ["read"] } 640path "foo/+/bar/baz" { capabilities = ["update"] } 641`, 642 "foo/bar/bar/baz", 643 }, 644 { 645 // Verify that more wildcard segments is lower priority. 646 ` 647path "foo/+/+/baz" { capabilities = ["read"] } 648path "foo/+/bar/baz" { capabilities = ["update"] } 649`, 650 "foo/bar/bar/baz", 651 }, 652 { 653 // Verify that first wildcard position is lower priority. 654 // '(' is used here because it is lexicographically smaller than "+" 655 ` 656path "foo/+/(ar/baz" { capabilities = ["read"] } 657path "foo/(ar/+/baz" { capabilities = ["update"] } 658`, 659 "foo/(ar/(ar/baz", 660 }, 661 662 { 663 // Verify that a glob has lower priority, even if the prefix is the 664 // same otherwise. 665 ` 666path "foo/bar/+/baz*" { capabilities = ["read"] } 667path "foo/bar/+/baz" { capabilities = ["update"] } 668`, 669 "foo/bar/bar/baz", 670 }, 671 { 672 // Verify that a shorter prefix has lower priority. 673 ` 674path "foo/bar/+/b*" { capabilities = ["read"] } 675path "foo/bar/+/ba*" { capabilities = ["update"] } 676`, 677 "foo/bar/bar/baz", 678 }, 679 } 680 681 for i, pt := range poltests { 682 policy, err := ParseACLPolicy(ns, pt.policy) 683 if err != nil { 684 t.Fatalf("err: %v", err) 685 } 686 687 acl, err := NewACL(ctx, []*Policy{policy}) 688 if err != nil { 689 t.Fatalf("err: %v", err) 690 } 691 692 request := new(logical.Request) 693 request.Path = pt.path 694 695 request.Operation = logical.UpdateOperation 696 authResults := acl.AllowOperation(ctx, request, false) 697 if !authResults.Allowed { 698 t.Fatalf("bad: case %d %#v: %v", i, pt, authResults.Allowed) 699 } 700 701 request.Operation = logical.ReadOperation 702 authResults = acl.AllowOperation(ctx, request, false) 703 if authResults.Allowed { 704 t.Fatalf("bad: case %d %#v: %v", i, pt, authResults.Allowed) 705 } 706 } 707} 708 709func TestACL_SegmentWildcardPriority_BareMount(t *testing.T) { 710 ns := namespace.RootNamespace 711 ctx := namespace.ContextWithNamespace(context.Background(), ns) 712 type poltest struct { 713 policy string 714 mountpath string 715 hasperms bool 716 } 717 // These test cases should have one or more rules and a mount prefix. 718 // hasperms should be true if there are non-deny perms that apply 719 // to the mount prefix or something below it. 720 poltests := []poltest{ 721 { 722 `path "+" { capabilities = ["read"] }`, 723 "foo/", 724 true, 725 }, 726 { 727 `path "+/*" { capabilities = ["read"] }`, 728 "foo/", 729 true, 730 }, 731 { 732 `path "foo/+/+/*" { capabilities = ["read"] }`, 733 "foo/", 734 true, 735 }, 736 { 737 `path "foo/+/+/*" { capabilities = ["read"] }`, 738 "foo/bar/", 739 true, 740 }, 741 { 742 `path "foo/+/+/*" { capabilities = ["read"] }`, 743 "foo/bar/bar/", 744 true, 745 }, 746 { 747 `path "foo/+/+/*" { capabilities = ["read"] }`, 748 "foo/bar/bar/baz/", 749 true, 750 }, 751 { 752 `path "foo/+/+/baz" { capabilities = ["read"] }`, 753 "foo/bar/bar/baz/", 754 true, 755 }, 756 { 757 `path "foo/+/bar/baz" { capabilities = ["read"] }`, 758 "foo/bar/bar/baz/", 759 true, 760 }, 761 { 762 `path "foo/bar/+/baz*" { capabilities = ["read"] }`, 763 "foo/bar/bar/baz/", 764 true, 765 }, 766 { 767 `path "foo/bar/+/b*" { capabilities = ["read"] }`, 768 "foo/bar/bar/baz/", 769 true, 770 }, 771 { 772 `path "foo/+" { capabilities = ["read"] }`, 773 "foo/", 774 true, 775 }, 776 } 777 778 for i, pt := range poltests { 779 policy, err := ParseACLPolicy(ns, pt.policy) 780 if err != nil { 781 t.Fatalf("err: %v", err) 782 } 783 784 acl, err := NewACL(ctx, []*Policy{policy}) 785 if err != nil { 786 t.Fatalf("err: %v", err) 787 } 788 789 hasperms := nil != acl.CheckAllowedFromNonExactPaths(pt.mountpath, true) 790 if hasperms != pt.hasperms { 791 t.Fatalf("bad: case %d: %#v", i, pt) 792 } 793 794 } 795} 796 797// NOTE: this test doesn't catch any races ATM 798func TestACL_CreationRace(t *testing.T) { 799 policy, err := ParseACLPolicy(namespace.RootNamespace, valuePermissionsPolicy) 800 if err != nil { 801 t.Fatalf("err: %v", err) 802 } 803 804 var wg sync.WaitGroup 805 stopTime := time.Now().Add(20 * time.Second) 806 807 for i := 0; i < 50; i++ { 808 wg.Add(1) 809 go func() { 810 defer wg.Done() 811 for { 812 if time.Now().After(stopTime) { 813 return 814 } 815 _, err := NewACL(namespace.RootContext(nil), []*Policy{policy}) 816 if err != nil { 817 t.Fatalf("err: %v", err) 818 } 819 } 820 }() 821 } 822 823 wg.Wait() 824} 825 826var tokenCreationPolicy = ` 827name = "tokenCreation" 828path "auth/token/create*" { 829 capabilities = ["update", "create", "sudo"] 830} 831` 832 833var aclPolicy = ` 834name = "DeV" 835path "dev/*" { 836 policy = "sudo" 837} 838path "stage/*" { 839 policy = "write" 840} 841path "stage/aws/*" { 842 policy = "read" 843 capabilities = ["update", "sudo"] 844} 845path "stage/aws/policy/*" { 846 policy = "sudo" 847} 848path "prod/*" { 849 policy = "read" 850} 851path "prod/aws/*" { 852 policy = "deny" 853} 854path "sys/*" { 855 policy = "deny" 856} 857path "foo/bar" { 858 capabilities = ["read", "create", "sudo"] 859} 860path "test/+/segment" { 861 capabilities = ["read"] 862} 863path "+/segment/at/front" { 864 capabilities = ["read"] 865} 866path "test/segment/at/end/+" { 867 capabilities = ["read"] 868} 869path "test/segment/at/end/v2/+/" { 870 capabilities = ["read"] 871} 872path "test/+/wildcard/+/*" { 873 capabilities = ["read"] 874} 875path "test/+/wildcardglob/+/end*" { 876 capabilities = ["read"] 877} 878path "1/2/*" { 879 capabilities = ["create"] 880} 881path "1/2/+" { 882 capabilities = ["read"] 883} 884path "1/2/+/+" { 885 capabilities = ["update"] 886} 887` 888 889var aclPolicy2 = ` 890name = "OpS" 891path "dev/hide/*" { 892 policy = "deny" 893} 894path "stage/aws/policy/*" { 895 policy = "deny" 896 # This should have no effect 897 capabilities = ["read", "update", "sudo"] 898} 899path "prod/*" { 900 policy = "write" 901} 902path "sys/seal" { 903 policy = "sudo" 904} 905path "foo/bar" { 906 capabilities = ["deny"] 907} 908` 909 910//test merging 911var mergingPolicies = ` 912name = "ops" 913path "foo/bar" { 914 policy = "write" 915 denied_parameters = { 916 "baz" = [] 917 } 918 required_parameters = ["baz"] 919} 920path "foo/bar" { 921 policy = "write" 922 denied_parameters = { 923 "zip" = [] 924 } 925} 926path "hello/universe" { 927 policy = "write" 928 allowed_parameters = { 929 "foo" = [] 930 } 931 required_parameters = ["foo"] 932 max_wrapping_ttl = 300 933 min_wrapping_ttl = 100 934} 935path "hello/universe" { 936 policy = "write" 937 allowed_parameters = { 938 "bar" = [] 939 } 940 required_parameters = ["bar"] 941 max_wrapping_ttl = 200 942 min_wrapping_ttl = 50 943} 944path "allow/all" { 945 policy = "write" 946 allowed_parameters = { 947 "test" = [] 948 "test1" = ["foo"] 949 } 950} 951path "allow/all" { 952 policy = "write" 953 allowed_parameters = { 954 "*" = [] 955 } 956} 957path "allow/all1" { 958 policy = "write" 959 allowed_parameters = { 960 "*" = [] 961 } 962} 963path "allow/all1" { 964 policy = "write" 965 allowed_parameters = { 966 "test" = [] 967 "test1" = ["foo"] 968 } 969} 970path "deny/all" { 971 policy = "write" 972 denied_parameters = { 973 "test" = [] 974 } 975} 976path "deny/all" { 977 policy = "write" 978 denied_parameters = { 979 "*" = [] 980 } 981} 982path "deny/all1" { 983 policy = "write" 984 denied_parameters = { 985 "*" = [] 986 } 987} 988path "deny/all1" { 989 policy = "write" 990 denied_parameters = { 991 "test" = [] 992 } 993} 994path "value/merge" { 995 policy = "write" 996 allowed_parameters = { 997 "test" = [1, 2] 998 } 999 denied_parameters = { 1000 "test" = [1, 2] 1001 } 1002} 1003path "value/merge" { 1004 policy = "write" 1005 allowed_parameters = { 1006 "test" = [3, 4] 1007 } 1008 denied_parameters = { 1009 "test" = [3, 4] 1010 } 1011} 1012path "value/empty" { 1013 policy = "write" 1014 allowed_parameters = { 1015 "empty" = [] 1016 } 1017 denied_parameters = { 1018 "empty" = [1] 1019 } 1020} 1021path "value/empty" { 1022 policy = "write" 1023 allowed_parameters = { 1024 "empty" = [1] 1025 } 1026 denied_parameters = { 1027 "empty" = [] 1028 } 1029} 1030` 1031 1032//allow operation testing 1033var permissionsPolicy = ` 1034name = "dev" 1035path "dev/*" { 1036 policy = "write" 1037 1038 allowed_parameters = { 1039 "zip" = [] 1040 } 1041} 1042path "foo/bar" { 1043 policy = "write" 1044 denied_parameters = { 1045 "zap" = [] 1046 } 1047 min_wrapping_ttl = 300 1048 max_wrapping_ttl = 400 1049} 1050path "foo/baz" { 1051 policy = "write" 1052 allowed_parameters = { 1053 "hello" = [] 1054 } 1055 denied_parameters = { 1056 "zap" = [] 1057 } 1058 min_wrapping_ttl = 300 1059} 1060path "working/phone" { 1061 policy = "write" 1062 max_wrapping_ttl = 400 1063} 1064path "broken/phone" { 1065 policy = "write" 1066 allowed_parameters = { 1067 "steve" = [] 1068 } 1069 denied_parameters = { 1070 "steve" = [] 1071 } 1072} 1073path "hello/world" { 1074 policy = "write" 1075 allowed_parameters = { 1076 "*" = [] 1077 } 1078 denied_parameters = { 1079 "*" = [] 1080 } 1081} 1082path "tree/fort" { 1083 policy = "write" 1084 allowed_parameters = { 1085 "*" = [] 1086 } 1087 denied_parameters = { 1088 "foo" = [] 1089 } 1090} 1091path "fruit/apple" { 1092 policy = "write" 1093 allowed_parameters = { 1094 "pear" = [] 1095 } 1096 denied_parameters = { 1097 "*" = [] 1098 } 1099} 1100path "cold/weather" { 1101 policy = "write" 1102 allowed_parameters = {} 1103 denied_parameters = {} 1104} 1105path "var/aws" { 1106 policy = "write" 1107 allowed_parameters = { 1108 "*" = [] 1109 } 1110 denied_parameters = { 1111 "soft" = [] 1112 "warm" = [] 1113 "kitty" = [] 1114 } 1115} 1116path "var/req" { 1117 policy = "write" 1118 required_parameters = ["foo"] 1119} 1120` 1121 1122//allow operation testing 1123var valuePermissionsPolicy = ` 1124name = "op" 1125path "dev/*" { 1126 policy = "write" 1127 1128 allowed_parameters = { 1129 "allow" = ["good"] 1130 } 1131} 1132path "foo/bar" { 1133 policy = "write" 1134 denied_parameters = { 1135 "deny" = ["bad*"] 1136 } 1137} 1138path "foo/baz" { 1139 policy = "write" 1140 allowed_parameters = { 1141 "ALLOW" = ["good"] 1142 } 1143 denied_parameters = { 1144 "dEny" = ["bad"] 1145 } 1146} 1147path "fizz/buzz" { 1148 policy = "write" 1149 allowed_parameters = { 1150 "allow_multi" = ["good", "good1", "good2", "*good3"] 1151 "allow" = ["good"] 1152 } 1153 denied_parameters = { 1154 "deny_multi" = ["bad", "bad1", "bad2"] 1155 } 1156} 1157path "test/types" { 1158 policy = "write" 1159 allowed_parameters = { 1160 "map" = [{"good" = "one"}] 1161 "int" = [1, 2] 1162 "bool" = [false] 1163 } 1164 denied_parameters = { 1165 } 1166} 1167path "test/star" { 1168 policy = "write" 1169 allowed_parameters = { 1170 "*" = [] 1171 "foo" = [] 1172 "bar" = [false] 1173 } 1174 denied_parameters = { 1175 } 1176} 1177` 1178