Extends SCIM implementation to support role membership management
- Updates request payload for patching group - Updates tests
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)).
|
||||
|
||||
Reference in New Issue
Block a user