From 99f0ca32490b4e453c3d2c0010fa476c213b88a3 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Sun, 29 Nov 2020 09:57:46 +0100 Subject: [PATCH] Add support for external ID --- system/scim/group_handler.go | 12 ++++- system/scim/user_handler.go | 12 ++++- tests/system/scim_test.go | 99 +++++++++++++++++++++++++++++++----- 3 files changed, 108 insertions(+), 15 deletions(-) diff --git a/system/scim/group_handler.go b/system/scim/group_handler.go index 99d08f724..298470ff0 100644 --- a/system/scim/group_handler.go +++ b/system/scim/group_handler.go @@ -63,7 +63,17 @@ func (h groupsHandler) createFromJSON(ctx context.Context, j io.Reader) (r *type } // do we need to upsert? - if *payload.Name != "" { + if payload.ExternalId != nil { + var rr types.RoleSet + rr, _, err = svc.Find(types.RoleFilter{Labels: map[string]string{groupLabel_SCIM_externalId: *payload.ExternalId}}) + if err != nil { + return + } + + if len(rr) > 0 { + r = rr[0] + } + } else if payload.Name != nil { r, err = svc.FindByName(*payload.Name) if err != nil && !errors.Is(err, service.RoleErrNotFound()) { return diff --git a/system/scim/user_handler.go b/system/scim/user_handler.go index 42049e7bd..3eddf9abe 100644 --- a/system/scim/user_handler.go +++ b/system/scim/user_handler.go @@ -66,7 +66,17 @@ func (h usersHandler) createFromJSON(ctx context.Context, j io.Reader) (u *types } // do we need to upsert? - if email := payload.Emails.getFirst(); email != "" { + if payload.ExternalId != nil { + var uu types.UserSet + uu, _, err = svc.Find(types.UserFilter{Labels: map[string]string{userLabel_SCIM_externalId: *payload.ExternalId}}) + if err != nil { + return + } + + if len(uu) > 0 { + u = uu[0] + } + } else if email := payload.Emails.getFirst(); email != "" { u, err = svc.FindByEmail(email) if err != nil && !errors.Is(err, service.UserErrNotFound()) { return diff --git a/tests/system/scim_test.go b/tests/system/scim_test.go index b6ae6d0fb..48318bab7 100644 --- a/tests/system/scim_test.go +++ b/tests/system/scim_test.go @@ -85,6 +85,58 @@ func TestScimUserCreate(t *testing.T) { h.a.Equal("baz", u.Handle) } +func TestScimUserCreateOverwrite(t *testing.T) { + h := newHelper(t) + h.clearUsers() + + u := h.createUserWithEmail("foo@bar.com") + + h.scimApiInit(). + Debug(). + Post("/Users"). + JSON(`{"userName":"UPDATED","emails":[{"value":"foo@bar.com"}],"schemas":["urn:ietf:params:scim:schemas:core:2.0:User"]}`). + Expect(t). + Status(http.StatusCreated). + Assert(helpers.AssertNoErrors). + End() + + u, err := store.LookupUserByEmail(context.Background(), service.DefaultStore, "foo@bar.com") + h.a.NoError(err) + h.a.Equal("UPDATED", u.Username) +} + +func TestScimUserExternalID(t *testing.T) { + h := newHelper(t) + h.clearUsers() + + h.scimApiInit(). + Debug(). + Post("/Users"). + JSON(`{"userName":"foo","emails":[{"value":"foo@bar.com"}],"externalId":"foo42","schemas":["urn:ietf:params:scim:schemas:core:2.0:User"]}`). + Expect(t). + Status(http.StatusCreated). + Assert(helpers.AssertNoErrors). + End() + + u, err := store.LookupUserByEmail(context.Background(), service.DefaultStore, "foo@bar.com") + h.a.NoError(err) + h.a.Equal("foo", u.Username) + + h.scimApiInit(). + Debug(). + Post("/Users"). + JSON(`{"userName":"baz","emails":[{"value":"baz@bar.com"}],"externalId":"foo42","schemas":["urn:ietf:params:scim:schemas:core:2.0:User"]}`). + Expect(t). + Status(http.StatusCreated). + Assert(helpers.AssertNoErrors). + End() + + u, err = store.LookupUserByEmail(context.Background(), service.DefaultStore, "baz@bar.com") + h.a.NoError(err) + h.a.Equal("baz", u.Username) + +} + func TestScimUserReplace(t *testing.T) { h := newHelper(t) h.clearUsers() @@ -106,7 +158,7 @@ func TestScimUserReplace(t *testing.T) { ] }`). Expect(t). - //Status(http.StatusNoContent). + Status(http.StatusOK). End() u, err := store.LookupUserByID(context.Background(), service.DefaultStore, u.ID) @@ -151,12 +203,7 @@ func TestScimGroupCreate(t *testing.T) { h.scimApiInit(). Debug(). Post("/Groups"). - JSON(`{ - "schemas": [ - "urn:ietf:params:scim:schemas:core:2.0:Group" - ], - "displayName": "foo" -}`). + JSON(`{"schemas":["urn:ietf:params:scim:schemas:core:2.0:Group"],"displayName":"foo"}`). Expect(t). Status(http.StatusCreated). Assert(helpers.AssertNoErrors). @@ -167,6 +214,37 @@ func TestScimGroupCreate(t *testing.T) { h.a.Equal("foo", u.Name) } +func TestScimGroupExternalId(t *testing.T) { + h := newHelper(t) + h.clearRoles() + + h.scimApiInit(). + Debug(). + Post("/Groups"). + JSON(`{"schemas":["urn:ietf:params:scim:schemas:core:2.0:Group"],"displayName":"foo","externalId":"grp42"}`). + Expect(t). + Status(http.StatusCreated). + Assert(helpers.AssertNoErrors). + End() + + u, err := store.LookupRoleByName(context.Background(), service.DefaultStore, "foo") + h.a.NoError(err) + h.a.Equal("foo", u.Name) + + h.scimApiInit(). + Debug(). + Post("/Groups"). + JSON(`{"schemas":["urn:ietf:params:scim:schemas:core:2.0:Group"],"displayName":"bar","externalId":"grp42"}`). + Expect(t). + Status(http.StatusCreated). + Assert(helpers.AssertNoErrors). + End() + + u, err = store.LookupRoleByName(context.Background(), service.DefaultStore, "bar") + h.a.NoError(err) + h.a.Equal("bar", u.Name) +} + func TestScimGroupReplace(t *testing.T) { h := newHelper(t) h.clearRoles() @@ -176,12 +254,7 @@ func TestScimGroupReplace(t *testing.T) { h.scimApiInit(). Debug(). Put(fmt.Sprintf("/Groups/%d", u.ID)). - JSON(`{ - "schemas": [ - "urn:ietf:params:scim:schemas:core:2.0:Group" - ], - "displayName": "bar" -}`). + JSON(`{"schemas":["urn:ietf:params:scim:schemas:core:2.0:Group"],"displayName":"bar"}`). Expect(t). End()