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