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 }