112 lines
3.1 KiB
Go
Raw Normal View History

package resources
import (
"context"
"strings"
"github.com/zitadel/zitadel/internal/api/scim/metadata"
"github.com/zitadel/zitadel/internal/api/scim/resources/filter"
"github.com/zitadel/zitadel/internal/api/scim/resources/patch"
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/domain"
)
type userPatcher struct {
ctx context.Context
user *ScimUser
metadataChanges map[metadata.Key]*domain.Metadata
metadataKeysToRemove map[metadata.Key]bool
handler *UsersHandler
}
func (h *UsersHandler) applyPatchesToChangeHuman(ctx context.Context, user *ScimUser, operations patch.OperationCollection) (*command.ChangeHuman, error) {
patcher := &userPatcher{
ctx: ctx,
user: user,
metadataChanges: make(map[metadata.Key]*domain.Metadata),
metadataKeysToRemove: make(map[metadata.Key]bool),
handler: h,
}
if err := operations.Apply(patcher, user); err != nil {
return nil, err
}
// we rely on the change detection of the write model to only execute commands that really change data
changeCommand, err := h.mapToChangeHuman(ctx, user)
if err != nil {
return nil, err
}
patcher.applyMetadataChangesToCommand(changeCommand)
return changeCommand, nil
}
func (p *userPatcher) FilterEvaluator() *filter.Evaluator {
return p.handler.filterEvaluator
}
func (p *userPatcher) Added(attributePath []string) error {
return p.updateMetadata(attributePath)
}
func (p *userPatcher) Replaced(attributePath []string) error {
return p.updateMetadata(attributePath)
}
func (p *userPatcher) Removed(attributePath []string) error {
return p.updateMetadata(attributePath)
}
func (p *userPatcher) applyMetadataChangesToCommand(command *command.ChangeHuman) {
command.MetadataKeysToRemove = make([]string, 0, len(p.metadataKeysToRemove))
for key := range p.metadataKeysToRemove {
command.MetadataKeysToRemove = append(command.MetadataKeysToRemove, string(key))
}
command.Metadata = make([]*domain.Metadata, 0, len(p.metadataChanges))
for _, update := range p.metadataChanges {
command.Metadata = append(command.Metadata, update)
}
}
func (p *userPatcher) updateMetadata(attributePath []string) error {
if len(attributePath) == 0 {
return nil
}
// try full path first (e.g. name.middleName)
// try root only if full path did not match (e.g. for entitlements.value only entitlements is mapped)
var ok bool
var keys []metadata.Key
if len(attributePath) > 1 {
keys, ok = metadata.AttributePathToMetadataKeys[strings.Join(attributePath, ".")]
}
if !ok {
keys, ok = metadata.AttributePathToMetadataKeys[attributePath[0]]
if !ok {
return nil
}
}
for _, key := range keys {
value, err := getValueForMetadataKey(p.user, key)
if err != nil {
return err
}
if len(value) > 0 {
delete(p.metadataKeysToRemove, key)
p.metadataChanges[key] = &domain.Metadata{
Key: string(metadata.ScopeKey(p.ctx, key)),
Value: value,
}
} else {
p.metadataKeysToRemove[key] = true
delete(p.metadataChanges, key)
}
}
return nil
}