1package vault 2 3import ( 4 "context" 5 "reflect" 6 "strings" 7 "testing" 8 9 "github.com/hashicorp/vault/helper/namespace" 10 "github.com/hashicorp/vault/sdk/helper/jsonutil" 11 "github.com/hashicorp/vault/sdk/logical" 12) 13 14func TestAuth_ReadOnlyViewDuringMount(t *testing.T) { 15 c, _, _ := TestCoreUnsealed(t) 16 c.credentialBackends["noop"] = func(ctx context.Context, config *logical.BackendConfig) (logical.Backend, error) { 17 err := config.StorageView.Put(ctx, &logical.StorageEntry{ 18 Key: "bar", 19 Value: []byte("baz"), 20 }) 21 if err == nil || !strings.Contains(err.Error(), logical.ErrSetupReadOnly.Error()) { 22 t.Fatalf("expected a read-only error") 23 } 24 return &NoopBackend{ 25 BackendType: logical.TypeCredential, 26 }, nil 27 } 28 29 me := &MountEntry{ 30 Table: credentialTableType, 31 Path: "foo", 32 Type: "noop", 33 } 34 err := c.enableCredential(namespace.RootContext(nil), me) 35 if err != nil { 36 t.Fatalf("err: %v", err) 37 } 38} 39 40func TestCore_DefaultAuthTable(t *testing.T) { 41 c, keys, _ := TestCoreUnsealed(t) 42 verifyDefaultAuthTable(t, c.auth) 43 44 // Start a second core with same physical 45 conf := &CoreConfig{ 46 Physical: c.physical, 47 DisableMlock: true, 48 } 49 c2, err := NewCore(conf) 50 if err != nil { 51 t.Fatalf("err: %v", err) 52 } 53 for i, key := range keys { 54 unseal, err := TestCoreUnseal(c2, key) 55 if err != nil { 56 t.Fatalf("err: %v", err) 57 } 58 if i+1 == len(keys) && !unseal { 59 t.Fatalf("should be unsealed") 60 } 61 } 62 63 // Verify matching mount tables 64 if !reflect.DeepEqual(c.auth, c2.auth) { 65 t.Fatalf("mismatch: %v %v", c.auth, c2.auth) 66 } 67} 68 69func TestCore_EnableCredential(t *testing.T) { 70 c, keys, _ := TestCoreUnsealed(t) 71 c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 72 return &NoopBackend{ 73 BackendType: logical.TypeCredential, 74 }, nil 75 } 76 77 me := &MountEntry{ 78 Table: credentialTableType, 79 Path: "foo", 80 Type: "noop", 81 } 82 err := c.enableCredential(namespace.RootContext(nil), me) 83 if err != nil { 84 t.Fatalf("err: %v", err) 85 } 86 87 match := c.router.MatchingMount(namespace.RootContext(nil), "auth/foo/bar") 88 if match != "auth/foo/" { 89 t.Fatalf("missing mount, match: %q", match) 90 } 91 92 conf := &CoreConfig{ 93 Physical: c.physical, 94 DisableMlock: true, 95 } 96 c2, err := NewCore(conf) 97 if err != nil { 98 t.Fatalf("err: %v", err) 99 } 100 c2.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 101 return &NoopBackend{ 102 BackendType: logical.TypeCredential, 103 }, nil 104 } 105 for i, key := range keys { 106 unseal, err := TestCoreUnseal(c2, key) 107 if err != nil { 108 t.Fatalf("err: %v", err) 109 } 110 if i+1 == len(keys) && !unseal { 111 t.Fatalf("should be unsealed") 112 } 113 } 114 115 // Verify matching auth tables 116 if !reflect.DeepEqual(c.auth, c2.auth) { 117 t.Fatalf("mismatch: %v %v", c.auth, c2.auth) 118 } 119} 120 121// Test that the local table actually gets populated as expected with local 122// entries, and that upon reading the entries from both are recombined 123// correctly 124func TestCore_EnableCredential_Local(t *testing.T) { 125 c, _, _ := TestCoreUnsealed(t) 126 c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 127 return &NoopBackend{ 128 BackendType: logical.TypeCredential, 129 }, nil 130 } 131 132 c.auth = &MountTable{ 133 Type: credentialTableType, 134 Entries: []*MountEntry{ 135 &MountEntry{ 136 Table: credentialTableType, 137 Path: "noop/", 138 Type: "noop", 139 UUID: "abcd", 140 Accessor: "noop-abcd", 141 BackendAwareUUID: "abcde", 142 NamespaceID: namespace.RootNamespaceID, 143 namespace: namespace.RootNamespace, 144 }, 145 &MountEntry{ 146 Table: credentialTableType, 147 Path: "noop2/", 148 Type: "noop", 149 UUID: "bcde", 150 Accessor: "noop-bcde", 151 BackendAwareUUID: "bcdea", 152 NamespaceID: namespace.RootNamespaceID, 153 namespace: namespace.RootNamespace, 154 }, 155 }, 156 } 157 158 // Both should set up successfully 159 err := c.setupCredentials(context.Background()) 160 if err != nil { 161 t.Fatal(err) 162 } 163 164 rawLocal, err := c.barrier.Get(context.Background(), coreLocalAuthConfigPath) 165 if err != nil { 166 t.Fatal(err) 167 } 168 if rawLocal == nil { 169 t.Fatal("expected non-nil local credential") 170 } 171 localCredentialTable := &MountTable{} 172 if err := jsonutil.DecodeJSON(rawLocal.Value, localCredentialTable); err != nil { 173 t.Fatal(err) 174 } 175 if len(localCredentialTable.Entries) > 0 { 176 t.Fatalf("expected no entries in local credential table, got %#v", localCredentialTable) 177 } 178 179 c.auth.Entries[1].Local = true 180 if err := c.persistAuth(context.Background(), c.auth, nil); err != nil { 181 t.Fatal(err) 182 } 183 184 rawLocal, err = c.barrier.Get(context.Background(), coreLocalAuthConfigPath) 185 if err != nil { 186 t.Fatal(err) 187 } 188 if rawLocal == nil { 189 t.Fatal("expected non-nil local credential") 190 } 191 localCredentialTable = &MountTable{} 192 if err := jsonutil.DecodeJSON(rawLocal.Value, localCredentialTable); err != nil { 193 t.Fatal(err) 194 } 195 if len(localCredentialTable.Entries) != 1 { 196 t.Fatalf("expected one entry in local credential table, got %#v", localCredentialTable) 197 } 198 199 oldCredential := c.auth 200 if err := c.loadCredentials(context.Background()); err != nil { 201 t.Fatal(err) 202 } 203 204 if !reflect.DeepEqual(oldCredential, c.auth) { 205 t.Fatalf("expected\n%#v\ngot\n%#v\n", oldCredential, c.auth) 206 } 207 208 if len(c.auth.Entries) != 2 { 209 t.Fatalf("expected two credential entries, got %#v", localCredentialTable) 210 } 211} 212 213func TestCore_EnableCredential_twice_409(t *testing.T) { 214 c, _, _ := TestCoreUnsealed(t) 215 c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 216 return &NoopBackend{ 217 BackendType: logical.TypeCredential, 218 }, nil 219 } 220 221 me := &MountEntry{ 222 Table: credentialTableType, 223 Path: "foo", 224 Type: "noop", 225 } 226 err := c.enableCredential(namespace.RootContext(nil), me) 227 if err != nil { 228 t.Fatalf("err: %v", err) 229 } 230 231 // 2nd should be a 409 error 232 err2 := c.enableCredential(namespace.RootContext(nil), me) 233 switch err2.(type) { 234 case logical.HTTPCodedError: 235 if err2.(logical.HTTPCodedError).Code() != 409 { 236 t.Fatalf("invalid code given") 237 } 238 default: 239 t.Fatalf("expected a different error type") 240 } 241} 242 243func TestCore_EnableCredential_Token(t *testing.T) { 244 c, _, _ := TestCoreUnsealed(t) 245 me := &MountEntry{ 246 Table: credentialTableType, 247 Path: "foo", 248 Type: "token", 249 } 250 err := c.enableCredential(namespace.RootContext(nil), me) 251 if err.Error() != "token credential backend cannot be instantiated" { 252 t.Fatalf("err: %v", err) 253 } 254} 255 256func TestCore_DisableCredential(t *testing.T) { 257 c, keys, _ := TestCoreUnsealed(t) 258 c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 259 return &NoopBackend{ 260 BackendType: logical.TypeCredential, 261 }, nil 262 } 263 264 err := c.disableCredential(namespace.RootContext(nil), "foo") 265 if err != nil && !strings.HasPrefix(err.Error(), "no matching mount") { 266 t.Fatal(err) 267 } 268 269 me := &MountEntry{ 270 Table: credentialTableType, 271 Path: "foo", 272 Type: "noop", 273 } 274 err = c.enableCredential(namespace.RootContext(nil), me) 275 if err != nil { 276 t.Fatalf("err: %v", err) 277 } 278 279 err = c.disableCredential(namespace.RootContext(nil), "foo") 280 if err != nil { 281 t.Fatalf("err: %v", err) 282 } 283 284 match := c.router.MatchingMount(namespace.RootContext(nil), "auth/foo/bar") 285 if match != "" { 286 t.Fatalf("backend present") 287 } 288 289 conf := &CoreConfig{ 290 Physical: c.physical, 291 DisableMlock: true, 292 } 293 c2, err := NewCore(conf) 294 if err != nil { 295 t.Fatalf("err: %v", err) 296 } 297 for i, key := range keys { 298 unseal, err := TestCoreUnseal(c2, key) 299 if err != nil { 300 t.Fatalf("err: %v", err) 301 } 302 if i+1 == len(keys) && !unseal { 303 t.Fatalf("should be unsealed") 304 } 305 } 306 307 // Verify matching mount tables 308 if !reflect.DeepEqual(c.auth, c2.auth) { 309 t.Fatalf("mismatch: %v %v", c.auth, c2.auth) 310 } 311} 312 313func TestCore_DisableCredential_Protected(t *testing.T) { 314 c, _, _ := TestCoreUnsealed(t) 315 err := c.disableCredential(namespace.RootContext(nil), "token") 316 if err.Error() != "token credential backend cannot be disabled" { 317 t.Fatalf("err: %v", err) 318 } 319} 320 321func TestCore_DisableCredential_Cleanup(t *testing.T) { 322 noop := &NoopBackend{ 323 Login: []string{"login"}, 324 BackendType: logical.TypeCredential, 325 } 326 c, _, _ := TestCoreUnsealed(t) 327 c.credentialBackends["noop"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 328 return noop, nil 329 } 330 331 me := &MountEntry{ 332 Table: credentialTableType, 333 Path: "foo", 334 Type: "noop", 335 } 336 err := c.enableCredential(namespace.RootContext(nil), me) 337 if err != nil { 338 t.Fatalf("err: %v", err) 339 } 340 341 // Store the view 342 view := c.router.MatchingStorageByAPIPath(namespace.RootContext(nil), "auth/foo/") 343 344 // Inject data 345 se := &logical.StorageEntry{ 346 Key: "plstodelete", 347 Value: []byte("test"), 348 } 349 if err := view.Put(context.Background(), se); err != nil { 350 t.Fatalf("err: %v", err) 351 } 352 353 // Generate a new token auth 354 noop.Response = &logical.Response{ 355 Auth: &logical.Auth{ 356 Policies: []string{"foo"}, 357 }, 358 } 359 r := &logical.Request{ 360 Operation: logical.ReadOperation, 361 Path: "auth/foo/login", 362 } 363 resp, err := c.HandleRequest(namespace.RootContext(nil), r) 364 if err != nil { 365 t.Fatalf("err: %v", err) 366 } 367 if resp.Auth.ClientToken == "" { 368 t.Fatalf("bad: %#v", resp) 369 } 370 371 // Disable should cleanup 372 err = c.disableCredential(namespace.RootContext(nil), "foo") 373 if err != nil { 374 t.Fatalf("err: %v", err) 375 } 376 377 // Token should be revoked 378 te, err := c.tokenStore.Lookup(namespace.RootContext(nil), resp.Auth.ClientToken) 379 if err != nil { 380 t.Fatalf("err: %v", err) 381 } 382 if te != nil { 383 t.Fatalf("bad: %#v", te) 384 } 385 386 // View should be empty 387 out, err := logical.CollectKeys(context.Background(), view) 388 if err != nil { 389 t.Fatalf("err: %v", err) 390 } 391 if len(out) != 0 { 392 t.Fatalf("bad: %#v", out) 393 } 394} 395 396func TestDefaultAuthTable(t *testing.T) { 397 c, _, _ := TestCoreUnsealed(t) 398 table := c.defaultAuthTable() 399 verifyDefaultAuthTable(t, table) 400} 401 402func verifyDefaultAuthTable(t *testing.T, table *MountTable) { 403 if len(table.Entries) != 1 { 404 t.Fatalf("bad: %v", table.Entries) 405 } 406 if table.Type != credentialTableType { 407 t.Fatalf("bad: %v", *table) 408 } 409 for idx, entry := range table.Entries { 410 switch idx { 411 case 0: 412 if entry.Path != "token/" { 413 t.Fatalf("bad: %v", entry) 414 } 415 if entry.Type != "token" { 416 t.Fatalf("bad: %v", entry) 417 } 418 } 419 if entry.Description == "" { 420 t.Fatalf("bad: %v", entry) 421 } 422 if entry.UUID == "" { 423 t.Fatalf("bad: %v", entry) 424 } 425 } 426} 427 428func TestCore_CredentialInitialize(t *testing.T) { 429 { 430 backend := &InitializableBackend{ 431 &NoopBackend{ 432 BackendType: logical.TypeCredential, 433 }, false} 434 435 c, _, _ := TestCoreUnsealed(t) 436 c.credentialBackends["initable"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 437 return backend, nil 438 } 439 440 me := &MountEntry{ 441 Table: credentialTableType, 442 Path: "foo/", 443 Type: "initable", 444 } 445 err := c.enableCredential(namespace.RootContext(nil), me) 446 if err != nil { 447 t.Fatalf("err: %v", err) 448 } 449 450 if !backend.isInitialized { 451 t.Fatal("backend is not initialized") 452 } 453 } 454 { 455 backend := &InitializableBackend{ 456 &NoopBackend{ 457 BackendType: logical.TypeCredential, 458 }, false} 459 460 c, _, _ := TestCoreUnsealed(t) 461 c.credentialBackends["initable"] = func(context.Context, *logical.BackendConfig) (logical.Backend, error) { 462 return backend, nil 463 } 464 465 c.auth = &MountTable{ 466 Type: credentialTableType, 467 Entries: []*MountEntry{ 468 &MountEntry{ 469 Table: credentialTableType, 470 Path: "foo/", 471 Type: "initable", 472 UUID: "abcd", 473 Accessor: "initable-abcd", 474 BackendAwareUUID: "abcde", 475 NamespaceID: namespace.RootNamespaceID, 476 namespace: namespace.RootNamespace, 477 }, 478 }, 479 } 480 481 err := c.setupCredentials(context.Background()) 482 if err != nil { 483 t.Fatal(err) 484 } 485 486 // run the postUnseal funcs, so that the backend will be inited 487 for _, f := range c.postUnsealFuncs { 488 f() 489 } 490 491 if !backend.isInitialized { 492 t.Fatal("backend is not initialized") 493 } 494 } 495} 496