mirror of
https://github.com/zitadel/zitadel.git
synced 2025-03-01 16:47:24 +00:00

# Which Problems Are Solved * Adds support for the patch user SCIM v2 endpoint # How the Problems Are Solved * Adds support for the patch user SCIM v2 endpoint under `PATCH /scim/v2/{orgID}/Users/{id}` # Additional Context Part of #8140
113 lines
3.4 KiB
Go
113 lines
3.4 KiB
Go
package patch
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"github.com/zitadel/zitadel/internal/api/scim/resources/filter"
|
|
"github.com/zitadel/zitadel/internal/api/scim/serrors"
|
|
"github.com/zitadel/zitadel/internal/zerrors"
|
|
)
|
|
|
|
func applyAddPatch(patcher ResourcePatcher, op *Operation, value interface{}) error {
|
|
if op.Path == nil {
|
|
return flattenAndApplyPatchOperations(patcher, op, value)
|
|
}
|
|
|
|
result, err := patcher.FilterEvaluator().Evaluate(reflect.ValueOf(value), op.Path)
|
|
if err != nil {
|
|
return serrors.ThrowInvalidPath(zerrors.ThrowInvalidArgument(err, "SCIM-opzz8", "Failed to evaluate path"))
|
|
}
|
|
|
|
evaluationResult, ok := result.(*filter.SimpleValueEvaluationResult)
|
|
if !ok {
|
|
return serrors.ThrowInvalidPath(zerrors.ThrowInvalidArgument(nil, "SCIM-opty8", "Filtered paths are not allowed for add patch operations"))
|
|
}
|
|
|
|
if evaluationResult.Value.Kind() != reflect.Slice {
|
|
return applyReplacePatchSimple(patcher, evaluationResult, op.Value, op.valueIsArray)
|
|
}
|
|
|
|
elementType := evaluationResult.Value.Type().Elem()
|
|
newElementsSlice, err := unmarshalPatchValuesSlice(elementType, op.Value, op.valueIsArray)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
oldLen := evaluationResult.Value.Len()
|
|
newSlice := reflect.MakeSlice(reflect.SliceOf(elementType), oldLen, oldLen+newElementsSlice.Len())
|
|
|
|
// copy over existing values
|
|
reflect.Copy(newSlice, evaluationResult.Value)
|
|
|
|
// according to the RFC only "new" values should be added
|
|
// existing values should be replaced
|
|
newSlice, modifiedIndexes := addOrReplaceByValue(newSlice, newElementsSlice)
|
|
|
|
evaluationResult.Value.Set(newSlice)
|
|
if err = ensureSinglePrimaryAdded(evaluationResult.Value, newElementsSlice, modifiedIndexes); err != nil {
|
|
return err
|
|
}
|
|
|
|
return patcher.Added(evaluationResult.PathSegments)
|
|
}
|
|
|
|
func ensureSinglePrimaryAdded(resultSlice, newSlice reflect.Value, modifiedIndexes map[int]bool) error {
|
|
modifiedValues := make([]reflect.Value, newSlice.Len())
|
|
for i := 0; i < newSlice.Len(); i++ {
|
|
modifiedValues[i] = newSlice.Index(i)
|
|
}
|
|
|
|
return ensureSinglePrimary(resultSlice, modifiedValues, modifiedIndexes)
|
|
}
|
|
|
|
func addOrReplaceByValue(entries, newEntries reflect.Value) (reflect.Value, map[int]bool) {
|
|
modifiedIndexes := make(map[int]bool, newEntries.Len())
|
|
if entries.Len() == 0 {
|
|
for i := 0; i < newEntries.Len(); i++ {
|
|
modifiedIndexes[i] = true
|
|
}
|
|
|
|
return newEntries, modifiedIndexes
|
|
}
|
|
|
|
valueField := entries.Index(0).Elem().FieldByName(fieldNameValue)
|
|
if !valueField.IsValid() || valueField.Kind() != reflect.String {
|
|
entriesLen := entries.Len()
|
|
for i := 0; i < newEntries.Len(); i++ {
|
|
modifiedIndexes[i+entriesLen] = true
|
|
}
|
|
|
|
return reflect.AppendSlice(entries, newEntries), modifiedIndexes
|
|
}
|
|
|
|
existingValueIndexes := make(map[string]int, entries.Len())
|
|
for i := 0; i < entries.Len(); i++ {
|
|
value := entries.Index(i).Elem().FieldByName(fieldNameValue).String()
|
|
if _, ok := existingValueIndexes[value]; ok {
|
|
continue
|
|
}
|
|
|
|
existingValueIndexes[value] = i
|
|
}
|
|
|
|
entriesLen := entries.Len()
|
|
for i := 0; i < newEntries.Len(); i++ {
|
|
newEntry := newEntries.Index(i)
|
|
value := newEntry.Elem().FieldByName(fieldNameValue).String()
|
|
index, valueExists := existingValueIndexes[value]
|
|
|
|
// according to the rfc if the value already exists it should be replaced
|
|
if valueExists {
|
|
entries.Index(index).Set(newEntry)
|
|
modifiedIndexes[index] = true
|
|
continue
|
|
}
|
|
|
|
entries = reflect.Append(entries, newEntry)
|
|
modifiedIndexes[entriesLen] = true
|
|
entriesLen++
|
|
}
|
|
|
|
return entries, modifiedIndexes
|
|
}
|