Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[VAULT-3252] Disallow alias creation if entity/accessor combination exists #12747

Merged
merged 11 commits into from Oct 14, 2021
3 changes: 3 additions & 0 deletions changelog/12747.txt
@@ -0,0 +1,3 @@
```release-note:bug
core/identity: Disallow entity alias creation when an alias for the specified entity and mount exists
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it's no longer just about creation, maybe reword?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

```
6 changes: 6 additions & 0 deletions vault/identity_store_aliases.go
Expand Up @@ -267,6 +267,12 @@ func (i *IdentityStore) handleAliasCreate(ctx context.Context, req *logical.Requ
}
}

for _, alias := range entity.Aliases {
pmmukh marked this conversation as resolved.
Show resolved Hide resolved
if alias.MountAccessor == mountAccessor {
ccapurso marked this conversation as resolved.
Show resolved Hide resolved
return logical.ErrorResponse("Alias already exists for requested entity and mount accessor"), nil
pmmukh marked this conversation as resolved.
Show resolved Hide resolved
}
}

entity.Aliases = append(entity.Aliases, alias)

// ID creation and other validations; This is more useful for new entities
Expand Down
50 changes: 50 additions & 0 deletions vault/identity_store_aliases_test.go
Expand Up @@ -405,6 +405,56 @@ func TestIdentityStore_AliasUpdate(t *testing.T) {
}
}

// Test that alias creation fails if an alias for the specified mount
// and entity has already been created
func TestIdentityStore_AliasCreate_DuplicateAccessor(t *testing.T) {
var err error
var resp *logical.Response
ctx := namespace.RootContext(nil)
is, githubAccessor, _ := testIdentityStoreWithGithubAuth(ctx, t)

resp, err = is.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)

aliasData := map[string]interface{}{
"name": "testaliasname",
"mount_accessor": githubAccessor,
"canonical_id": entityID,
}

aliasReq := &logical.Request{
Operation: logical.UpdateOperation,
Path: "entity-alias",
Data: aliasData,
}

// This will create an alias and a corresponding entity
resp, err = is.HandleRequest(ctx, aliasReq)
if err != nil || (resp != nil && resp.IsError()) {
t.Fatalf("err:%v resp:%#v", err, resp)
}

aliasData["name"] = "testaliasname2"
aliasReq = &logical.Request{
Operation: logical.UpdateOperation,
Path: "entity-alias",
Data: aliasData,
}

// This will try to create a new alias with the same accessor and entity
resp, err = is.HandleRequest(ctx, aliasReq)
pmmukh marked this conversation as resolved.
Show resolved Hide resolved

if resp == nil || !resp.IsError() {
t.Fatalf("expected an error as alias already exists for this accessor and entity")
}
}

func TestIdentityStore_AliasUpdate_ByID(t *testing.T) {
var err error
var resp *logical.Response
Expand Down
25 changes: 25 additions & 0 deletions vault/identity_store_util.go
Expand Up @@ -341,6 +341,12 @@ func (i *IdentityStore) loadEntities(ctx context.Context) error {
}
}

duplicateAccessors := getDuplicateAccessorsOnAliases(ctx, entity.Aliases)

// Log a warning if an entity has multiple aliases with the same accessor
if len(duplicateAccessors) > 0 {
pmmukh marked this conversation as resolved.
Show resolved Hide resolved
i.logger.Warn("Duplicate aliases present for the same entity and accessor", "entity_name", entity.Name, "namespace_id", entity.NamespaceID, "duplicate_accessors", duplicateAccessors)
pmmukh marked this conversation as resolved.
Show resolved Hide resolved
}
// Only update MemDB and don't hit the storage again
err = i.upsertEntity(nsCtx, entity, nil, false)
if err != nil {
Expand All @@ -360,6 +366,25 @@ func (i *IdentityStore) loadEntities(ctx context.Context) error {
return nil
}

// getDuplicateAccessorsOnAliases returns a list of duplicate accessors present in the
// passed in list of aliases
func getDuplicateAccessorsOnAliases(ctx context.Context, aliases []*identity.Alias) []string {
pmmukh marked this conversation as resolved.
Show resolved Hide resolved
accessorCounts := make(map[string]int)
duplicateAccessors := make([]string, 0)
pmmukh marked this conversation as resolved.
Show resolved Hide resolved

for _, alias := range aliases {
accessorCounts[alias.MountAccessor] += 1
}

for accessor, accessorCount := range accessorCounts {
if accessorCount > 1 {
duplicateAccessors = append(duplicateAccessors, accessor)
}
}

return duplicateAccessors
}

// upsertEntityInTxn either creates or updates an existing entity. The
// operations will be updated in both MemDB and storage. If 'persist' is set to
// false, then storage will not be updated. When an alias is transferred from
Expand Down