Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SCIM: implement Groups #1357

Open
wants to merge 34 commits into
base: main
Choose a base branch
from

Conversation

fflorent
Copy link
Collaborator

@fflorent fflorent commented Jan 3, 2025

Context

Following #1199, this PR proposes to implement the /Groups Endpoint.

As an IT asset administrator, I can create groups for my users using a centralized solution (an Identity Provider) so they can easily access to the resources. But these groups and memberships are not pushed to Grist as of today. The administrator is either offered to remove access entirely to Grist, or access to Grist and modify the permissions on the resources. Most of all, as Teams are not represented through the UI, it can be a quite painful task.

Proposed solution

SCIM is a standard proposed by the IETF through RFC7644 and RFC7643 which aims to through a simple Rest API provide solution for the above use cases.

Here is the abstract of the RFC7644 which introduces SCIM:

The System for Cross-domain Identity Management (SCIM) specification is an HTTP-based protocol that makes managing identities in multi-domain scenarios easier to support via a standardized service.
Examples include, but are not limited to, enterprise-to-cloud service providers and inter-cloud scenarios. The specification suite seeks to build upon experience with existing schemas and deployments, placing specific emphasis on simplicity of development and integration, while applying existing authentication, authorization, and privacy models. SCIM's intent is to reduce the cost and complexity of user management operations by providing a common user schema, an extension model, and a service protocol defined by this document.

This PR provides a complement to the now existing /scim/v2/Users endpoint (documented here):

SCIM Endpoint Group Type1 Usage
/scim/v2/Groups Team Represents a team of users in your organisation,
who are granted accesses toa set of resources through Roles
/scim/v2/Roles 2 Role Grants users and teams a certain access
(owner, editor, viewer, ...) to a given resource

Currently only the Roles were represented in the "groups" table. To achieve this distinction, this PR also introduces a "type" column, which only accept team or role values.

This PR provides the implementation of SCIM for Groups (aka Teams), and supports:

  • All the basic actions for Groups (POST /Groups/, PUT /Groups/:id, GET /Groups/, GET /Groups/:id and DELETE /Groups/:id).
  • The POST /Bulk endpoint
  • The POST /Groups/.search by using the Filters (actually to use them, you must have to fetch all the Resources from the DB, the filtering is done in JS, which is probably fine for small projects, I would just be cautious when using big databases + an ORM);
  • The PATCH /Groups/:id endpoint! It reads a resource using the egress method, applies the asked changes, and calls the ingress method to update the record ;
  • The pagination

The endpoint for /Roles are nearly the same, though:

  • Creation (trough POST) and the deletion are not allowed;
  • We can only update the memberships, which is helpful to assign a role to users or teams;
  • And the endpoint also return the resource ID associated to the role through the orgId, workspaceId or docId readonly attributes.

As in #1199, I take advantage of these two libraries: scimmy and scimmy-routers.

Known limitations

  1. You cannot add a group as a member of a Resouce Users group, due to the fact that the depth for groups are limited to 4.
  2. The groups are not displayed in the Users Management popup for now. This may be a bit confusing.

Related issues

Fixes #870

Has this been tested?

  • 👍 yes, I added tests to the test suite
  • 💭 no, because this PR is a draft and still needs work
  • 🙅 no, because this is not relevant here
  • 🙋 no, because I need help

Footnotes

  1. In the Grist terminology, and as specified in the groups.type column in the database.

  2. This is a custom endpoint as allowed by the SCIM standard.

@fflorent fflorent force-pushed the scim-groups-endpoints branch 3 times, most recently from fbce58e to 2c32360 Compare January 3, 2025 19:08
@fflorent fflorent force-pushed the scim-groups-endpoints branch 4 times, most recently from 8ecb5ce to 7f924fe Compare January 17, 2025 12:21
@fflorent fflorent force-pushed the scim-groups-endpoints branch from 0f8575a to 2e91be2 Compare January 31, 2025 12:50
.from(User, 'users')
.where('users.id IN (:...userIds)', {userIds});
return await queryBuilder.getMany();
});
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may be wrong on this one, but it seems these two codes are equivalent.

I can revert if you have doubts.

});

// a test to ensure the TeamMember migration works on databases with existing content
it('can perform TeamMember migration with seed data set', async function() {
it.skip('can perform TeamMember migration with seed data set', async function() {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had to skip this test, unfortunately I have passed several hours on it without success.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you be more specific on what fails?
And add a description in the code of why it's skipped?

@@ -222,402 +229,1170 @@ describe('Scim', () => {
const res = await axios.get(scimUrl('/Me'), anon);
assert.equal(res.status, 401);
});

it.skip('should allow operation like PATCH for kiwi', async function () {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed it, it looks like this won't be fixed (for good reasons I think):
scimmyjs/scimmy-routers#27


@Entity({name: 'groups'})
export class Group extends BaseEntity {
public static readonly ROLE_TYPE = 'role';
public static readonly RESOURCE_USERS_TYPE = 'resource users';
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not very fan of this name, I admit. I found the name team here and it's sounds more accurate and understandable :

// for implementing something like teams in the future. It has no measurable effect on

Do you agree for this renaming?

@fflorent fflorent marked this pull request as ready for review February 2, 2025 21:19
@fflorent fflorent requested a review from hexaltation February 2, 2025 21:19

/**
* SCIMMY Role Schema.
* Heavily inspired by SCIMMY Group Schema.
Copy link
Collaborator

@hexaltation hexaltation Feb 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As the author of the original code is Sam Lee-Lindsay maybe you can add his name.

Suggested change
* Heavily inspired by SCIMMY Group Schema.
* Heavily inspired by SCIMMY Group Schema by Sam Lee-Lindsay.

Does it need that this file have to be under MIT license following this rule

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

is not clear to me

Copy link
Collaborator

@hexaltation hexaltation left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you @fflorent for this huge piece of work.

Two small changes asked.

And one concern about the stickyness of the code with SCIMMY.
How much of of work could it need:

  • To add a layer of abstraction to not use SCIMMY object in higher levels of grist,
  • To replace this lib contributed by only one guy the day he won't maintained it anymore.

But no doubt that it's the best solution for now.

});

// a test to ensure the TeamMember migration works on databases with existing content
it('can perform TeamMember migration with seed data set', async function() {
it.skip('can perform TeamMember migration with seed data set', async function() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you be more specific on what fails?
And add a description in the code of why it's skipped?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support SCIM
2 participants