1package vault 2 3import ( 4 "strings" 5 "testing" 6 "time" 7 8 "github.com/go-test/deep" 9 "github.com/hashicorp/vault/helper/namespace" 10) 11 12var rawPolicy = strings.TrimSpace(` 13# Developer policy 14name = "dev" 15 16# Deny all paths by default 17path "*" { 18 policy = "deny" 19} 20 21# Allow full access to staging 22path "stage/*" { 23 policy = "sudo" 24} 25 26# Limited read privilege to production 27path "prod/version" { 28 policy = "read" 29} 30 31# Read access to foobar 32# Also tests stripping of leading slash and parsing of min/max as string and 33# integer 34path "/foo/bar" { 35 policy = "read" 36 min_wrapping_ttl = 300 37 max_wrapping_ttl = "1h" 38} 39 40# Add capabilities for creation and sudo to foobar 41# This will be separate; they are combined when compiled into an ACL 42# Also tests reverse string/int handling to the above 43path "foo/bar" { 44 capabilities = ["create", "sudo"] 45 min_wrapping_ttl = "300s" 46 max_wrapping_ttl = 3600 47} 48 49# Check that only allowed_parameters are being added to foobar 50path "foo/bar" { 51 capabilities = ["create", "sudo"] 52 allowed_parameters = { 53 "zip" = [] 54 "zap" = [] 55 } 56} 57 58# Check that only denied_parameters are being added to bazbar 59path "baz/bar" { 60 capabilities = ["create", "sudo"] 61 denied_parameters = { 62 "zip" = [] 63 "zap" = [] 64 } 65} 66 67# Check that both allowed and denied parameters are being added to bizbar 68path "biz/bar" { 69 capabilities = ["create", "sudo"] 70 allowed_parameters = { 71 "zim" = [] 72 "zam" = [] 73 } 74 denied_parameters = { 75 "zip" = [] 76 "zap" = [] 77 } 78} 79path "test/types" { 80 capabilities = ["create", "sudo"] 81 allowed_parameters = { 82 "map" = [{"good" = "one"}] 83 "int" = [1, 2] 84 } 85 denied_parameters = { 86 "string" = ["test"] 87 "bool" = [false] 88 } 89} 90path "test/req" { 91 capabilities = ["create", "sudo"] 92 required_parameters = ["foo"] 93} 94path "test/mfa" { 95 capabilities = ["create", "sudo"] 96 mfa_methods = ["my_totp", "my_totp2"] 97} 98path "test/+/segment" { 99 capabilities = ["create", "sudo"] 100} 101path "test/segment/at/end/+" { 102 capabilities = ["create", "sudo"] 103} 104path "test/segment/at/end/v2/+/" { 105 capabilities = ["create", "sudo"] 106} 107path "test/+/wildcard/+/*" { 108 capabilities = ["create", "sudo"] 109} 110path "test/+/wildcard/+/end*" { 111 capabilities = ["create", "sudo"] 112} 113`) 114 115func TestPolicy_Parse(t *testing.T) { 116 p, err := ParseACLPolicy(namespace.RootNamespace, rawPolicy) 117 if err != nil { 118 t.Fatalf("err: %v", err) 119 } 120 121 if p.Name != "dev" { 122 t.Fatalf("bad name: %q", p.Name) 123 } 124 125 expect := []*PathRules{ 126 { 127 Path: "", 128 Policy: "deny", 129 Capabilities: []string{ 130 "deny", 131 }, 132 Permissions: &ACLPermissions{CapabilitiesBitmap: DenyCapabilityInt}, 133 IsPrefix: true, 134 }, 135 { 136 Path: "stage/", 137 Policy: "sudo", 138 Capabilities: []string{ 139 "create", 140 "read", 141 "update", 142 "delete", 143 "list", 144 "sudo", 145 }, 146 Permissions: &ACLPermissions{ 147 CapabilitiesBitmap: (CreateCapabilityInt | ReadCapabilityInt | UpdateCapabilityInt | DeleteCapabilityInt | ListCapabilityInt | SudoCapabilityInt), 148 }, 149 IsPrefix: true, 150 }, 151 { 152 Path: "prod/version", 153 Policy: "read", 154 Capabilities: []string{ 155 "read", 156 "list", 157 }, 158 Permissions: &ACLPermissions{CapabilitiesBitmap: (ReadCapabilityInt | ListCapabilityInt)}, 159 }, 160 { 161 Path: "foo/bar", 162 Policy: "read", 163 Capabilities: []string{ 164 "read", 165 "list", 166 }, 167 MinWrappingTTLHCL: 300, 168 MaxWrappingTTLHCL: "1h", 169 Permissions: &ACLPermissions{ 170 CapabilitiesBitmap: (ReadCapabilityInt | ListCapabilityInt), 171 MinWrappingTTL: 300 * time.Second, 172 MaxWrappingTTL: 3600 * time.Second, 173 }, 174 }, 175 { 176 Path: "foo/bar", 177 Capabilities: []string{ 178 "create", 179 "sudo", 180 }, 181 MinWrappingTTLHCL: "300s", 182 MaxWrappingTTLHCL: 3600, 183 Permissions: &ACLPermissions{ 184 CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), 185 MinWrappingTTL: 300 * time.Second, 186 MaxWrappingTTL: 3600 * time.Second, 187 }, 188 }, 189 { 190 Path: "foo/bar", 191 Capabilities: []string{ 192 "create", 193 "sudo", 194 }, 195 AllowedParametersHCL: map[string][]interface{}{"zip": {}, "zap": {}}, 196 Permissions: &ACLPermissions{ 197 CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), 198 AllowedParameters: map[string][]interface{}{"zip": {}, "zap": {}}, 199 }, 200 }, 201 { 202 Path: "baz/bar", 203 Capabilities: []string{ 204 "create", 205 "sudo", 206 }, 207 DeniedParametersHCL: map[string][]interface{}{"zip": []interface{}{}, "zap": []interface{}{}}, 208 Permissions: &ACLPermissions{ 209 CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), 210 DeniedParameters: map[string][]interface{}{"zip": []interface{}{}, "zap": []interface{}{}}, 211 }, 212 }, 213 { 214 Path: "biz/bar", 215 Capabilities: []string{ 216 "create", 217 "sudo", 218 }, 219 AllowedParametersHCL: map[string][]interface{}{"zim": {}, "zam": {}}, 220 DeniedParametersHCL: map[string][]interface{}{"zip": {}, "zap": {}}, 221 Permissions: &ACLPermissions{ 222 CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), 223 AllowedParameters: map[string][]interface{}{"zim": {}, "zam": {}}, 224 DeniedParameters: map[string][]interface{}{"zip": {}, "zap": {}}, 225 }, 226 }, 227 { 228 Path: "test/types", 229 Policy: "", 230 Capabilities: []string{ 231 "create", 232 "sudo", 233 }, 234 AllowedParametersHCL: map[string][]interface{}{"map": []interface{}{map[string]interface{}{"good": "one"}}, "int": []interface{}{1, 2}}, 235 DeniedParametersHCL: map[string][]interface{}{"string": []interface{}{"test"}, "bool": []interface{}{false}}, 236 Permissions: &ACLPermissions{ 237 CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), 238 AllowedParameters: map[string][]interface{}{"map": []interface{}{map[string]interface{}{"good": "one"}}, "int": []interface{}{1, 2}}, 239 DeniedParameters: map[string][]interface{}{"string": []interface{}{"test"}, "bool": []interface{}{false}}, 240 }, 241 IsPrefix: false, 242 }, 243 { 244 Path: "test/req", 245 Capabilities: []string{ 246 "create", 247 "sudo", 248 }, 249 RequiredParametersHCL: []string{"foo"}, 250 Permissions: &ACLPermissions{ 251 CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), 252 RequiredParameters: []string{"foo"}, 253 }, 254 }, 255 { 256 Path: "test/mfa", 257 Capabilities: []string{ 258 "create", 259 "sudo", 260 }, 261 MFAMethodsHCL: []string{ 262 "my_totp", 263 "my_totp2", 264 }, 265 Permissions: &ACLPermissions{ 266 CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), 267 MFAMethods: []string{ 268 "my_totp", 269 "my_totp2", 270 }, 271 }, 272 }, 273 { 274 Path: "test/+/segment", 275 Capabilities: []string{ 276 "create", 277 "sudo", 278 }, 279 Permissions: &ACLPermissions{ 280 CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), 281 }, 282 HasSegmentWildcards: true, 283 }, 284 { 285 Path: "test/segment/at/end/+", 286 Capabilities: []string{ 287 "create", 288 "sudo", 289 }, 290 Permissions: &ACLPermissions{ 291 CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), 292 }, 293 HasSegmentWildcards: true, 294 }, 295 { 296 Path: "test/segment/at/end/v2/+/", 297 Capabilities: []string{ 298 "create", 299 "sudo", 300 }, 301 Permissions: &ACLPermissions{ 302 CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), 303 }, 304 HasSegmentWildcards: true, 305 }, 306 { 307 Path: "test/+/wildcard/+/*", 308 Capabilities: []string{ 309 "create", 310 "sudo", 311 }, 312 Permissions: &ACLPermissions{ 313 CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), 314 }, 315 HasSegmentWildcards: true, 316 }, 317 { 318 Path: "test/+/wildcard/+/end*", 319 Capabilities: []string{ 320 "create", 321 "sudo", 322 }, 323 Permissions: &ACLPermissions{ 324 CapabilitiesBitmap: (CreateCapabilityInt | SudoCapabilityInt), 325 }, 326 HasSegmentWildcards: true, 327 }, 328 } 329 330 if diff := deep.Equal(p.Paths, expect); diff != nil { 331 t.Error(diff) 332 } 333} 334 335func TestPolicy_ParseBadRoot(t *testing.T) { 336 _, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(` 337name = "test" 338bad = "foo" 339nope = "yes" 340`)) 341 if err == nil { 342 t.Fatalf("expected error") 343 } 344 345 if !strings.Contains(err.Error(), `invalid key "bad" on line 2`) { 346 t.Errorf("bad error: %q", err) 347 } 348 349 if !strings.Contains(err.Error(), `invalid key "nope" on line 3`) { 350 t.Errorf("bad error: %q", err) 351 } 352} 353 354func TestPolicy_ParseBadPath(t *testing.T) { 355 // The wrong spelling is intended here 356 _, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(` 357path "/" { 358 capabilities = ["read"] 359 capabilites = ["read"] 360} 361`)) 362 if err == nil { 363 t.Fatalf("expected error") 364 } 365 366 if !strings.Contains(err.Error(), `invalid key "capabilites" on line 3`) { 367 t.Errorf("bad error: %s", err) 368 } 369} 370 371func TestPolicy_ParseBadPolicy(t *testing.T) { 372 _, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(` 373path "/" { 374 policy = "banana" 375} 376`)) 377 if err == nil { 378 t.Fatalf("expected error") 379 } 380 381 if !strings.Contains(err.Error(), `path "/": invalid policy "banana"`) { 382 t.Errorf("bad error: %s", err) 383 } 384} 385 386func TestPolicy_ParseBadWrapping(t *testing.T) { 387 _, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(` 388path "/" { 389 policy = "read" 390 min_wrapping_ttl = 400 391 max_wrapping_ttl = 200 392} 393`)) 394 if err == nil { 395 t.Fatalf("expected error") 396 } 397 398 if !strings.Contains(err.Error(), `max_wrapping_ttl cannot be less than min_wrapping_ttl`) { 399 t.Errorf("bad error: %s", err) 400 } 401} 402 403func TestPolicy_ParseBadCapabilities(t *testing.T) { 404 _, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(` 405path "/" { 406 capabilities = ["read", "banana"] 407} 408`)) 409 if err == nil { 410 t.Fatalf("expected error") 411 } 412 413 if !strings.Contains(err.Error(), `path "/": invalid capability "banana"`) { 414 t.Errorf("bad error: %s", err) 415 } 416} 417 418func TestPolicy_ParseBadSegmentWildcard(t *testing.T) { 419 _, err := ParseACLPolicy(namespace.RootNamespace, strings.TrimSpace(` 420path "foo/+*" { 421 capabilities = ["read"] 422} 423`)) 424 if err == nil { 425 t.Fatalf("expected error") 426 } 427 428 if !strings.Contains(err.Error(), `path "foo/+*": invalid use of wildcards ('+*' is forbidden)`) { 429 t.Errorf("bad error: %s", err) 430 } 431} 432