113 lines
3.4 KiB
Go
Raw Normal View History

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
}