diff --git a/system/scim/group_handler.go b/system/scim/group_handler.go index fb0afa6da..92cb441b6 100644 --- a/system/scim/group_handler.go +++ b/system/scim/group_handler.go @@ -159,22 +159,22 @@ func (h groupsHandler) patch(w http.ResponseWriter, r *http.Request) { // validate and collect operations for _, op := range payload.Operations { - if op.Path != "members" { - // allow only "members" path - sendError(w, newErrorfResponse(http.StatusBadRequest, "unsupported path: %q", op.Path)) - return - } - // iterate through operation's values, load user and schedule op - for _, userExternalId := range op.Value { - u, err = lookupUserByExternalId(ctx, h.userSvc, h.externalIdValidator, userExternalId.Value) + for path, userExternalId := range op.Value { + if path != "members" { + // allow only "members" path + sendError(w, newErrorfResponse(http.StatusBadRequest, "unsupported path: %q", op.Path)) + return + } + + u, err = lookupUserByExternalId(ctx, h.userSvc, h.externalIdValidator, userExternalId) if err != nil { sendError(w, err) return } if u == nil { - sendError(w, newErrorfResponse(http.StatusBadRequest, "no such user: %q", userExternalId.Value)) + sendError(w, newErrorfResponse(http.StatusBadRequest, "no such user: %q", userExternalId)) return } diff --git a/system/scim/patch_payload_test.go b/system/scim/patch_payload_test.go new file mode 100644 index 000000000..f4891fa05 --- /dev/null +++ b/system/scim/patch_payload_test.go @@ -0,0 +1,38 @@ +package scim + +import ( + "fmt" + "github.com/stretchr/testify/require" + "strings" + "testing" +) + +func TestOperationsRequestDecodeJSON(t *testing.T) { + const user1Id = `00000000-0000-0000-0000-000000000001` + var ( + req = require.New(t) + payload operationsRequest + + s = strings.NewReader(fmt.Sprintf( + `{"Operations":[{"op":"add","path":"members[value eq \"%s\"]"}],"schemas":["urn:ietf:params:scim:schemas:core:2.0:PatchOp"]}`, + user1Id, + )) + + expectedPayload = operationsRequest{ + Schemas: []string{"urn:ietf:params:scim:schemas:core:2.0:PatchOp"}, + Operations: []operationRequest{ + { + Operation: "add", + Path: fmt.Sprintf("members[value eq \"%s\"]", user1Id), + Value: map[string]string{ + "members": user1Id, + }, + }, + }, + } + ) + + err := payload.decodeJSON(s) + req.NoError(err) + req.Equal(expectedPayload, payload) +} diff --git a/system/scim/patch_payloads.go b/system/scim/patch_payloads.go index 7df2ea577..4ed7f3675 100644 --- a/system/scim/patch_payloads.go +++ b/system/scim/patch_payloads.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "io" + "regexp" ) const ( @@ -22,9 +23,7 @@ type ( operationRequest struct { Operation string `json:"op"` Path string `json:"path"` - Value []struct { - Value string `json:"value"` - } `json:"value"` + Value map[string]string } ) @@ -33,5 +32,19 @@ func (req *operationsRequest) decodeJSON(r io.Reader) error { return fmt.Errorf("could not decode operations payload: %w", err) } + // Compiles req.Path and updates req.Value + for i, op := range req.Operations { + r := regexp.MustCompile(`([a-z]\w+)\[value eq \"([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12})\"\]`) + strings := r.FindStringSubmatch(op.Path) + if len(strings) == 3 { + key := strings[1] + if op.Value == nil { + op.Value = make(map[string]string) + } + op.Value[key] = strings[2] + req.Operations[i].Value = op.Value + } + } + return nil } diff --git a/tests/system/scim_test.go b/tests/system/scim_test.go index 68e796e20..0287e5874 100644 --- a/tests/system/scim_test.go +++ b/tests/system/scim_test.go @@ -349,7 +349,7 @@ func TestScimPatchingGroupMembership(t *testing.T) { h.scimApiInit(scimSetWithExternalId, scimSetWithUUIDValidator). Patch(fmt.Sprintf("/Groups/%s", groupId)). JSON(fmt.Sprintf( - `{"Operations":[{"op":"add","path":"members","value":[{"value":%q}]}],"schemas":["urn:ietf:params:scim:schemas:core:2.0:PatchOp"]}`, + `{"Operations":[{"op":"add","path":"members[value eq \"%s\"]"}],"schemas":["urn:ietf:params:scim:schemas:core:2.0:PatchOp"]}`, user1Id, )). Expect(t). @@ -363,7 +363,7 @@ func TestScimPatchingGroupMembership(t *testing.T) { h.scimApiInit(scimSetWithExternalId, scimSetWithUUIDValidator). Patch(fmt.Sprintf("/Groups/%s", groupId)). JSON(fmt.Sprintf( - `{"Operations":[{"op":"add","path":"members","value":[{"value":%q}]},{"op":"remove","path":"members","value":[{"value":%q}]}],"schemas":["urn:ietf:params:scim:schemas:core:2.0:PatchOp"]}`, + `{"Operations":[{"op":"add","path":"members[value eq \"%[1]s\"]"},{"op":"remove","path":"members[value eq \"%[2]s\"]"}],"schemas":["urn:ietf:params:scim:schemas:core:2.0:PatchOp"]}`, user2Id, user1Id, )).