open-vault/vault/identity_store_test.go
Vishal Nayak 81c66efd6d
AppRole/Identity: Fix for race when creating an entity during login (#3932)
* possible fix for race in approle login while creating entity

* Add a test that hits the login request concurrently

* address review comments
2018-02-09 10:40:56 -05:00

292 lines
7.6 KiB
Go

package vault
import (
"context"
"testing"
"time"
credGithub "github.com/hashicorp/vault/builtin/credential/github"
"github.com/hashicorp/vault/logical"
)
func TestIdentityStore_CreateOrFetchEntity(t *testing.T) {
is, ghAccessor, _ := testIdentityStoreWithGithubAuth(t)
alias := &logical.Alias{
MountType: "github",
MountAccessor: ghAccessor,
Name: "githubuser",
}
entity, err := is.CreateOrFetchEntity(alias)
if err != nil {
t.Fatal(err)
}
if entity == nil {
t.Fatalf("expected a non-nil entity")
}
if len(entity.Aliases) != 1 {
t.Fatalf("bad: length of aliases; expected: 1, actual: %d", len(entity.Aliases))
}
if entity.Aliases[0].Name != alias.Name {
t.Fatalf("bad: alias name; expected: %q, actual: %q", alias.Name, entity.Aliases[0].Name)
}
entity, err = is.CreateOrFetchEntity(alias)
if err != nil {
t.Fatal(err)
}
if entity == nil {
t.Fatalf("expected a non-nil entity")
}
if len(entity.Aliases) != 1 {
t.Fatalf("bad: length of aliases; expected: 1, actual: %d", len(entity.Aliases))
}
if entity.Aliases[0].Name != alias.Name {
t.Fatalf("bad: alias name; expected: %q, actual: %q", alias.Name, entity.Aliases[0].Name)
}
}
func TestIdentityStore_EntityByAliasFactors(t *testing.T) {
var err error
var resp *logical.Response
is, ghAccessor, _ := testIdentityStoreWithGithubAuth(t)
registerData := map[string]interface{}{
"name": "testentityname",
"metadata": []string{"someusefulkey=someusefulvalue"},
"policies": []string{"testpolicy1", "testpolicy2"},
}
registerReq := &logical.Request{
Operation: logical.UpdateOperation,
Path: "entity",
Data: registerData,
}
// Register the entity
resp, err = is.HandleRequest(context.Background(), registerReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
idRaw, ok := resp.Data["id"]
if !ok {
t.Fatalf("entity id not present in response")
}
entityID := idRaw.(string)
if entityID == "" {
t.Fatalf("invalid entity id")
}
aliasData := map[string]interface{}{
"entity_id": entityID,
"name": "alias_name",
"mount_accessor": ghAccessor,
}
aliasReq := &logical.Request{
Operation: logical.UpdateOperation,
Path: "alias",
Data: aliasData,
}
resp, err = is.HandleRequest(context.Background(), aliasReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
if resp == nil {
t.Fatalf("expected a non-nil response")
}
entity, err := is.entityByAliasFactors(ghAccessor, "alias_name", false)
if err != nil {
t.Fatal(err)
}
if entity == nil {
t.Fatalf("expected a non-nil entity")
}
if entity.ID != entityID {
t.Fatalf("bad: entity ID; expected: %q actual: %q", entityID, entity.ID)
}
}
func TestIdentityStore_WrapInfoInheritance(t *testing.T) {
var err error
var resp *logical.Response
core, is, ts, _ := testCoreWithIdentityTokenGithub(t)
registerData := map[string]interface{}{
"name": "testentityname",
"metadata": []string{"someusefulkey=someusefulvalue"},
"policies": []string{"testpolicy1", "testpolicy2"},
}
registerReq := &logical.Request{
Operation: logical.UpdateOperation,
Path: "entity",
Data: registerData,
}
// Register the entity
resp, err = is.HandleRequest(context.Background(), registerReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}
idRaw, ok := resp.Data["id"]
if !ok {
t.Fatalf("entity id not present in response")
}
entityID := idRaw.(string)
if entityID == "" {
t.Fatalf("invalid entity id")
}
// Create a token which has EntityID set and has update permissions to
// sys/wrapping/wrap
te := &TokenEntry{
Path: "test",
Policies: []string{"default", responseWrappingPolicyName},
EntityID: entityID,
}
if err := ts.create(context.Background(), te); err != nil {
t.Fatal(err)
}
wrapReq := &logical.Request{
Path: "sys/wrapping/wrap",
ClientToken: te.ID,
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"foo": "bar",
},
WrapInfo: &logical.RequestWrapInfo{
TTL: time.Duration(5 * time.Second),
},
}
resp, err = core.HandleRequest(wrapReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v, err: %v", resp, err)
}
if resp.WrapInfo == nil {
t.Fatalf("expected a non-nil WrapInfo")
}
if resp.WrapInfo.WrappedEntityID != entityID {
t.Fatalf("bad: WrapInfo in response not having proper entity ID set; expected: %q, actual:%q", entityID, resp.WrapInfo.WrappedEntityID)
}
}
func TestIdentityStore_TokenEntityInheritance(t *testing.T) {
_, ts, _, _ := TestCoreWithTokenStore(t)
// Create a token which has EntityID set
te := &TokenEntry{
Path: "test",
Policies: []string{"dev", "prod"},
EntityID: "testentityid",
}
if err := ts.create(context.Background(), te); err != nil {
t.Fatal(err)
}
// Create a child token; this should inherit the EntityID
tokenReq := &logical.Request{
Operation: logical.UpdateOperation,
Path: "create",
ClientToken: te.ID,
}
resp, err := ts.HandleRequest(context.Background(), tokenReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v err: %v", err, resp)
}
if resp.Auth.EntityID != te.EntityID {
t.Fatalf("bad: entity ID; expected: %v, actual: %v", te.EntityID, resp.Auth.EntityID)
}
// Create an orphan token; this should not inherit the EntityID
tokenReq.Path = "create-orphan"
resp, err = ts.HandleRequest(context.Background(), tokenReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: resp: %#v err: %v", err, resp)
}
if resp.Auth.EntityID != "" {
t.Fatalf("expected entity ID to be not set")
}
}
func testCoreWithIdentityTokenGithub(t *testing.T) (*Core, *IdentityStore, *TokenStore, string) {
is, ghAccessor, core := testIdentityStoreWithGithubAuth(t)
ts := testTokenStore(t, core)
return core, is, ts, ghAccessor
}
func testCoreWithIdentityTokenGithubRoot(t *testing.T) (*Core, *IdentityStore, *TokenStore, string, string) {
is, ghAccessor, core, root := testIdentityStoreWithGithubAuthRoot(t)
ts := testTokenStore(t, core)
return core, is, ts, ghAccessor, root
}
func testIdentityStoreWithGithubAuth(t *testing.T) (*IdentityStore, string, *Core) {
is, ghA, c, _ := testIdentityStoreWithGithubAuthRoot(t)
return is, ghA, c
}
// testIdentityStoreWithGithubAuth returns an instance of identity store which
// is mounted by default. This function also enables the github auth backend to
// assist with testing aliases and entities that require an valid mount
// accessor of an auth backend.
func testIdentityStoreWithGithubAuthRoot(t *testing.T) (*IdentityStore, string, *Core, string) {
// Add github credential factory to core config
err := AddTestCredentialBackend("github", credGithub.Factory)
if err != nil {
t.Fatalf("err: %s", err)
}
c, _, root := TestCoreUnsealed(t)
meGH := &MountEntry{
Table: credentialTableType,
Path: "github/",
Type: "github",
Description: "github auth",
}
err = c.enableCredential(context.Background(), meGH)
if err != nil {
t.Fatal(err)
}
// Identity store will be mounted by now, just fetch it from router
identitystore := c.router.MatchingBackend("identity/")
if identitystore == nil {
t.Fatalf("failed to fetch identity store from router")
}
return identitystore.(*IdentityStore), meGH.Accessor, c, root
}
func TestIdentityStore_MetadataKeyRegex(t *testing.T) {
key := "validVALID012_-=+/"
if !metaKeyFormatRegEx(key) {
t.Fatal("failed to accept valid metadata key")
}
key = "a:b"
if metaKeyFormatRegEx(key) {
t.Fatal("accepted invalid metadata key")
}
}