Skip to content

Commit

Permalink
case insensitive identity names
Browse files Browse the repository at this point in the history
  • Loading branch information
vishalnayak committed Oct 2, 2018
1 parent 6d79e86 commit 750917d
Show file tree
Hide file tree
Showing 10 changed files with 417 additions and 32 deletions.
20 changes: 14 additions & 6 deletions vault/identity_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,31 @@ func (c *Core) IdentityStore() *IdentityStore {
return c.identityStore
}

// NewIdentityStore creates a new identity store
func NewIdentityStore(ctx context.Context, core *Core, config *logical.BackendConfig, logger log.Logger) (*IdentityStore, error) {
func (i *IdentityStore) resetDB(ctx context.Context) error {
var err error

// Create a new in-memory database for the identity store
db, err := memdb.NewMemDB(identityStoreSchema())
i.db, err = memdb.NewMemDB(identityStoreSchema(!i.disableLowerCasedNames))
if err != nil {
return nil, errwrap.Wrapf("failed to create memdb for identity store: {{err}}", err)
return err
}

return nil
}

func NewIdentityStore(ctx context.Context, core *Core, config *logical.BackendConfig, logger log.Logger) (*IdentityStore, error) {
iStore := &IdentityStore{
view: config.StorageView,
db: db,
logger: logger,
core: core,
}

// Create a memdb instance, which by default, operates on lower cased
// identity names
err := iStore.resetDB(ctx)
if err != nil {
return nil, err
}

entitiesPackerLogger := iStore.logger.Named("storagepacker").Named("entities")
core.AddLogger(entitiesPackerLogger)
groupsPackerLogger := iStore.logger.Named("storagepacker").Named("groups")
Expand Down
10 changes: 6 additions & 4 deletions vault/identity_store_aliases.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,6 @@ func (i *IdentityStore) handleAliasUpdateCommon() framework.OperationFunc {
if entity == nil {
return nil, fmt.Errorf("existing alias is not associated with an entity")
}
if canonicalID == "" || entity.ID == canonicalID {
// Nothing to do
return nil, nil
}
}

resp := &logical.Response{}
Expand Down Expand Up @@ -255,6 +251,12 @@ func (i *IdentityStore) handleAliasUpdateCommon() framework.OperationFunc {
return nil, err
}

for index, item := range entity.Aliases {
if item.ID == alias.ID {
entity.Aliases[index] = alias
}
}

// Index entity and its aliases in MemDB and persist entity along with
// aliases in storage. If the alias is being transferred over from
// one entity to another, previous entity needs to get refreshed in MemDB
Expand Down
84 changes: 84 additions & 0 deletions vault/identity_store_aliases_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,97 @@ package vault

import (
"reflect"
"strings"
"testing"

"github.com/hashicorp/vault/helper/identity"
"github.com/hashicorp/vault/helper/namespace"
"github.com/hashicorp/vault/logical"
)

func TestIdentityStore_CaseInsensitiveEntityAliasName(t *testing.T) {
ctx := namespace.RootContext(nil)
i, accessor, _ := testIdentityStoreWithGithubAuth(ctx, t)

// Create an entity
resp, err := i.HandleRequest(ctx, &logical.Request{
Path: "entity",
Operation: logical.UpdateOperation,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err:%v\nresp: %#v", err, resp)
}
entityID := resp.Data["id"].(string)

testAliasName := "testAliasName"
// Create a case sensitive alias name
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "entity-alias",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"mount_accessor": accessor,
"canonical_id": entityID,
"name": testAliasName,
},
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err:%v\nresp: %#v", err, resp)
}
aliasID := resp.Data["id"].(string)

// Ensure that reading the alias returns case sensitive alias name
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "entity-alias/id/" + aliasID,
Operation: logical.ReadOperation,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err:%v\nresp: %#v", err, resp)
}
aliasName := resp.Data["name"].(string)
if aliasName != testAliasName {
t.Fatalf("bad alias name; expected: %q, actual: %q", testAliasName, aliasName)
}

// Overwrite the alias using lower cased alias name. This shouldn't error.
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "entity-alias/id/" + aliasID,
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"mount_accessor": accessor,
"canonical_id": entityID,
"name": strings.ToLower(testAliasName),
},
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err:%v\nresp: %#v", err, resp)
}

// Ensure that reading the alias returns lower cased alias name
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "entity-alias/id/" + aliasID,
Operation: logical.ReadOperation,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err:%v\nresp: %#v", err, resp)
}
aliasName = resp.Data["name"].(string)
if aliasName != strings.ToLower(testAliasName) {
t.Fatalf("bad alias name; expected: %q, actual: %q", testAliasName, aliasName)
}

// Ensure that there is one entity alias
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "entity-alias/id",
Operation: logical.ListOperation,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err:%v\nresp: %#v", err, resp)
}
if len(resp.Data["keys"].([]string)) != 1 {
t.Fatalf("bad length of entity aliases; expected: 1, actual: %d", len(resp.Data["keys"].([]string)))
}
}

// This test is required because MemDB does not take care of ensuring
// uniqueness of indexes that are marked unique.
func TestIdentityStore_AliasSameAliasNames(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion vault/identity_store_entities.go
Original file line number Diff line number Diff line change
Expand Up @@ -467,7 +467,7 @@ func (i *IdentityStore) pathEntityNameDelete() framework.OperationFunc {
defer txn.Abort()

// Fetch the entity using its name
entity, err := i.MemDBEntityByNameInTxn(txn, ctx, entityName, true)
entity, err := i.MemDBEntityByNameInTxn(ctx, txn, entityName, true)
if err != nil {
return nil, err
}
Expand Down
76 changes: 74 additions & 2 deletions vault/identity_store_entities_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"reflect"
"sort"
"strings"
"testing"

uuid "github.com/hashicorp/go-uuid"
Expand All @@ -14,6 +15,77 @@ import (
"github.com/hashicorp/vault/logical"
)

func TestIdentityStore_CaseInsensitiveEntityName(t *testing.T) {
ctx := namespace.RootContext(nil)
i, _, _ := testIdentityStoreWithGithubAuth(ctx, t)

testEntityName := "testEntityName"

// Create an entity with case sensitive name
resp, err := i.HandleRequest(ctx, &logical.Request{
Path: "entity",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"name": testEntityName,
},
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err:%v\nresp: %#v", err, resp)
}
entityID := resp.Data["id"].(string)

// Lookup the entity by ID and check that name returned is case sensitive
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "entity/id/" + entityID,
Operation: logical.ReadOperation,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err:%v\nresp: %#v", err, resp)
}
entityName := resp.Data["name"].(string)
if entityName != testEntityName {
t.Fatalf("bad entity name; expected: %q, actual: %q", testEntityName, entityName)
}

// Lookup the entity by case sensitive name
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "entity/name/" + testEntityName,
Operation: logical.ReadOperation,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
entityName = resp.Data["name"].(string)
if entityName != testEntityName {
t.Fatalf("bad entity name; expected: %q, actual: %q", testEntityName, entityName)
}

// Lookup the entity by case insensitive name
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "entity/name/" + strings.ToLower(testEntityName),
Operation: logical.ReadOperation,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
entityName = resp.Data["name"].(string)
if entityName != testEntityName {
t.Fatalf("bad entity name; expected: %q, actual: %q", testEntityName, entityName)
}

// Ensure that there is only one entity
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "entity/name",
Operation: logical.ListOperation,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
if len(resp.Data["keys"].([]string)) != 1 {
t.Fatalf("bad length of entities; expected: 1, actual: %d", len(resp.Data["keys"].([]string)))
}
}

func TestIdentityStore_EntityByName(t *testing.T) {
ctx := namespace.RootContext(nil)
i, _, _ := testIdentityStoreWithGithubAuth(ctx, t)
Expand Down Expand Up @@ -270,8 +342,8 @@ func TestIdentityStore_EntityCreateUpdate(t *testing.T) {

func TestIdentityStore_CloneImmutability(t *testing.T) {
alias := &identity.Alias{
ID: "testaliasid",
Name: "testaliasname",
ID: "testaliasid",
Name: "testaliasname",
MergedFromCanonicalIDs: []string{"entityid1"},
}

Expand Down
76 changes: 76 additions & 0 deletions vault/identity_store_group_aliases_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package vault

import (
"strings"
"testing"

credLdap "github.com/hashicorp/vault/builtin/credential/ldap"
Expand All @@ -10,6 +11,81 @@ import (
"github.com/hashicorp/vault/logical"
)

func TestIdentityStore_CaseInsensitiveGroupAliasName(t *testing.T) {
ctx := namespace.RootContext(nil)
i, accessor, _ := testIdentityStoreWithGithubAuth(ctx, t)

// Create a group
resp, err := i.HandleRequest(ctx, &logical.Request{
Path: "group",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"type": "external",
},
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err: %v\nresp: %#v", err, resp)
}
groupID := resp.Data["id"].(string)

testAliasName := "testAliasName"

// Create a case sensitive alias name
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "group-alias",
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"mount_accessor": accessor,
"canonical_id": groupID,
"name": testAliasName,
},
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err:%v\nresp: %#v", err, resp)
}
aliasID := resp.Data["id"].(string)

// Ensure that reading the alias returns case sensitive alias name
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "group-alias/id/" + aliasID,
Operation: logical.ReadOperation,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err:%v\nresp: %#v", err, resp)
}
aliasName := resp.Data["name"].(string)
if aliasName != testAliasName {
t.Fatalf("bad alias name; expected: %q, actual: %q", testAliasName, aliasName)
}

// Overwrite the alias using lower cased alias name. This shouldn't error.
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "group-alias/id/" + aliasID,
Operation: logical.UpdateOperation,
Data: map[string]interface{}{
"mount_accessor": accessor,
"canonical_id": groupID,
"name": strings.ToLower(testAliasName),
},
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err:%v\nresp: %#v", err, resp)
}

// Ensure that reading the alias returns lower cased alias name
resp, err = i.HandleRequest(ctx, &logical.Request{
Path: "group-alias/id/" + aliasID,
Operation: logical.ReadOperation,
})
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("bad: err:%v\nresp: %#v", err, resp)
}
aliasName = resp.Data["name"].(string)
if aliasName != strings.ToLower(testAliasName) {
t.Fatalf("bad alias name; expected: %q, actual: %q", testAliasName, aliasName)
}
}

func TestIdentityStore_EnsureNoDanglingGroupAlias(t *testing.T) {
err := AddTestCredentialBackend("userpass", credUserpass.Factory)
if err != nil {
Expand Down
Loading

0 comments on commit 750917d

Please sign in to comment.