Lars 189f9770c6
feat: patch user scim v2 endpoint (#9219)
# 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
2025-01-27 13:36:07 +01:00

74 lines
2.5 KiB
Go

package patch
import (
"reflect"
"github.com/zitadel/logging"
"github.com/zitadel/zitadel/internal/api/scim/resources/filter"
"github.com/zitadel/zitadel/internal/api/scim/serrors"
"github.com/zitadel/zitadel/internal/zerrors"
)
func applyRemovePatch(patcher ResourcePatcher, op *Operation, value interface{}) error {
// the root cannot be removed
if op.Path == nil {
logging.Info("SCIM: remove patch without path")
return serrors.ThrowNoTarget(zerrors.ThrowInvalidArgument(nil, "SCIM-ozzy54", "Remove patch without path is not supported"))
}
result, err := patcher.FilterEvaluator().Evaluate(reflect.ValueOf(value), op.Path)
if err != nil {
return serrors.ThrowInvalidPath(zerrors.ThrowInvalidArgument(err, "SCIM-sd41", "Failed to evaluate path"))
}
switch filterResult := result.(type) {
case *filter.SimpleValueEvaluationResult:
return applyRemovePatchSimple(patcher, filterResult)
case *filter.FilteredValuesEvaluationResult:
return applyRemovePatchFiltered(patcher, filterResult)
}
logging.Errorf("SCIM remove patch: unsupported filter type %T", result)
return serrors.ThrowInvalidPath(zerrors.ThrowInvalidArgument(err, "SCIM-12syw", "Invalid patch path"))
}
func applyRemovePatchSimple(patcher ResourcePatcher, filterResult *filter.SimpleValueEvaluationResult) error {
filterResult.Value.Set(reflect.Zero(filterResult.Value.Type()))
return patcher.Removed(filterResult.PathSegments)
}
func applyRemovePatchFiltered(patcher ResourcePatcher, filterResult *filter.FilteredValuesEvaluationResult) error {
if len(filterResult.Matches) == 0 {
return nil
}
// if a subattribute is selected, set that one to nil instead of removing the elements from the slice
if len(filterResult.PathSegments) > 1 {
for _, match := range filterResult.Matches {
match.Value.Set(reflect.Zero(match.Value.Type()))
}
return patcher.Removed(filterResult.PathSegments)
}
slice := filterResult.Source
sliceLen := slice.Len()
// if all elements are matched, set the field to nil
if sliceLen == len(filterResult.Matches) {
filterResult.Source.Set(reflect.Zero(slice.Type()))
return patcher.Removed(filterResult.PathSegments)
}
// start at the very last matched value to keep correct indexing
for i := len(filterResult.Matches) - 1; i >= 0; i-- {
match := filterResult.Matches[i]
slice = reflect.AppendSlice(slice.Slice(0, match.SourceIndex), slice.Slice(match.SourceIndex+1, sliceLen))
sliceLen--
}
filterResult.Source.Set(slice)
return patcher.Removed(filterResult.PathSegments)
}