mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-21 22:51:09 +02:00
This PR introduces a new type of conflict resolution for duplicate Entities and Groups. Renaming provides a way of preventing Vault from entering case-sensitive mode, which is the current behavior for any kind of duplicate. Renames append the conflicting identity artifact's UUID to its name and updates a metadata field to indicate the pre-existing artifact's UUID. The feature is gated by the force-identity-deduplication activation flag. In order to maintain consistent behavior between the reporting resolver and the rename operation, we need to adjust the behavior of generated reports. Previously, they intentionally preserved existing Group merge determinism, wherein the last MemDB update would win and all others would be renamed. This approach is more complicated for the rename resolver, since we would need to update any duplicated entity in the cache while inserting the new duplicate (resulting in two MemDB operations). Though we can ensure atomic updates of the two identity artifacts with transactions (which we could get for groups with a minor adjustment, and we will get along with batching of Entity upserts on load), it's far simpler to just rename all but the first insert as proposed in the current PR. Since the feature is gated by an activation flag with appropriate warnings of potential changes via the reporting resolver, we opt for simplicity over maintaining pre-existing behavior. We can revisit this assumption later if we think alignment with existing behavior outweighs any potential complexity in the rename operation. Entity alias resolution is left alone as a destructive merge operation to prevent a potentially high-impact change in existing behavior.
176 lines
3.5 KiB
Go
176 lines
3.5 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package identity
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
proto "github.com/golang/protobuf/proto"
|
|
"github.com/hashicorp/vault/sdk/logical"
|
|
)
|
|
|
|
func (g *Group) Clone() (*Group, error) {
|
|
if g == nil {
|
|
return nil, fmt.Errorf("nil group")
|
|
}
|
|
|
|
marshaledGroup, err := proto.Marshal(g)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal group: %w", err)
|
|
}
|
|
|
|
var clonedGroup Group
|
|
err = proto.Unmarshal(marshaledGroup, &clonedGroup)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal group: %w", err)
|
|
}
|
|
|
|
return &clonedGroup, nil
|
|
}
|
|
|
|
func (e *Entity) Clone() (*Entity, error) {
|
|
if e == nil {
|
|
return nil, fmt.Errorf("nil entity")
|
|
}
|
|
|
|
marshaledEntity, err := proto.Marshal(e)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal entity: %w", err)
|
|
}
|
|
|
|
var clonedEntity Entity
|
|
err = proto.Unmarshal(marshaledEntity, &clonedEntity)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal entity: %w", err)
|
|
}
|
|
|
|
return &clonedEntity, nil
|
|
}
|
|
|
|
func (e *Entity) UpsertAlias(alias *Alias) {
|
|
for i, item := range e.Aliases {
|
|
if item.ID == alias.ID {
|
|
e.Aliases[i] = alias
|
|
return
|
|
}
|
|
}
|
|
e.Aliases = append(e.Aliases, alias)
|
|
}
|
|
|
|
func (e *Entity) DeleteAliasByID(aliasID string) {
|
|
idx := -1
|
|
for i, item := range e.Aliases {
|
|
if item.ID == aliasID {
|
|
idx = i
|
|
break
|
|
}
|
|
}
|
|
|
|
if idx < 0 {
|
|
return
|
|
}
|
|
|
|
e.Aliases = append(e.Aliases[:idx], e.Aliases[idx+1:]...)
|
|
}
|
|
|
|
func (p *Alias) Clone() (*Alias, error) {
|
|
if p == nil {
|
|
return nil, fmt.Errorf("nil alias")
|
|
}
|
|
|
|
marshaledAlias, err := proto.Marshal(p)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal alias: %w", err)
|
|
}
|
|
|
|
var clonedAlias Alias
|
|
err = proto.Unmarshal(marshaledAlias, &clonedAlias)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal alias: %w", err)
|
|
}
|
|
|
|
return &clonedAlias, nil
|
|
}
|
|
|
|
// ToSDKAlias converts the provided alias to an SDK compatible alias.
|
|
func ToSDKAlias(a *Alias) *logical.Alias {
|
|
if a == nil {
|
|
return nil
|
|
}
|
|
metadata := make(map[string]string, len(a.Metadata))
|
|
for k, v := range a.Metadata {
|
|
metadata[k] = v
|
|
}
|
|
|
|
return &logical.Alias{
|
|
Name: a.Name,
|
|
ID: a.ID,
|
|
MountAccessor: a.MountAccessor,
|
|
MountType: a.MountType,
|
|
Metadata: metadata,
|
|
NamespaceID: a.NamespaceID,
|
|
CustomMetadata: a.CustomMetadata,
|
|
}
|
|
}
|
|
|
|
// ToSDKEntity converts the provided entity to an SDK compatible entity.
|
|
func ToSDKEntity(e *Entity) *logical.Entity {
|
|
if e == nil {
|
|
return nil
|
|
}
|
|
|
|
aliases := make([]*logical.Alias, len(e.Aliases))
|
|
|
|
for i, a := range e.Aliases {
|
|
aliases[i] = ToSDKAlias(a)
|
|
}
|
|
|
|
metadata := make(map[string]string, len(e.Metadata))
|
|
for k, v := range e.Metadata {
|
|
metadata[k] = v
|
|
}
|
|
|
|
return &logical.Entity{
|
|
ID: e.ID,
|
|
Name: e.Name,
|
|
Disabled: e.Disabled,
|
|
Aliases: aliases,
|
|
Metadata: metadata,
|
|
NamespaceID: e.NamespaceID,
|
|
}
|
|
}
|
|
|
|
// ToSDKGroup converts the provided group to an SDK compatible group.
|
|
func ToSDKGroup(g *Group) *logical.Group {
|
|
if g == nil {
|
|
return nil
|
|
}
|
|
|
|
metadata := make(map[string]string, len(g.Metadata))
|
|
for k, v := range g.Metadata {
|
|
metadata[k] = v
|
|
}
|
|
|
|
return &logical.Group{
|
|
ID: g.ID,
|
|
Name: g.Name,
|
|
Metadata: metadata,
|
|
NamespaceID: g.NamespaceID,
|
|
}
|
|
}
|
|
|
|
// ToSDKGroups converts the provided group list to an SDK compatible group list.
|
|
func ToSDKGroups(groups []*Group) []*logical.Group {
|
|
if groups == nil {
|
|
return nil
|
|
}
|
|
|
|
ret := make([]*logical.Group, len(groups))
|
|
|
|
for i, g := range groups {
|
|
ret[i] = ToSDKGroup(g)
|
|
}
|
|
return ret
|
|
}
|