From 31bf7825c0b45a3317f59ebf26724a55103b37e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nuno=20G=C3=B3is?= Date: Fri, 12 Apr 2024 10:01:57 +0100 Subject: [PATCH] chore: SCIM guard for groups (#6845) https://linear.app/unleash/issue/2-2111/api-should-not-allow-manual-management-of-scim-managed-groups-in Introduces a SCIM guard for SCIM groups. SCIM groups should be managed exclusively by the SCIM client, not Unleash. We decided to be restrictive for now, completely covering all of the write methods, but may fine-tune some of this at a later stage. Will eventually be followed up by a UI-centric PR. --- frontend/src/interfaces/group.ts | 1 + src/lib/db/group-store.ts | 2 ++ src/lib/openapi/spec/group-schema.ts | 7 +++++++ src/lib/services/group-service.ts | 5 +++++ src/lib/types/group.ts | 5 +++++ 5 files changed, 20 insertions(+) diff --git a/frontend/src/interfaces/group.ts b/frontend/src/interfaces/group.ts index 6abdf24010..1273f083bc 100644 --- a/frontend/src/interfaces/group.ts +++ b/frontend/src/interfaces/group.ts @@ -11,6 +11,7 @@ export interface IGroup { userCount?: number; mappingsSSO: string[]; rootRole?: number; + scimId?: string; } export interface IGroupUser extends IUser { diff --git a/src/lib/db/group-store.ts b/src/lib/db/group-store.ts index bb5b3112b7..77107c1624 100644 --- a/src/lib/db/group-store.ts +++ b/src/lib/db/group-store.ts @@ -30,6 +30,7 @@ const GROUP_COLUMNS = [ 'created_at', 'created_by', 'root_role_id', + 'scim_id', ]; const rowToGroup = (row) => { @@ -44,6 +45,7 @@ const rowToGroup = (row) => { createdAt: row.created_at, createdBy: row.created_by, rootRole: row.root_role_id, + scimId: row.scim_id, }); }; diff --git a/src/lib/openapi/spec/group-schema.ts b/src/lib/openapi/spec/group-schema.ts index 39419bf3c0..2dde629dfe 100644 --- a/src/lib/openapi/spec/group-schema.ts +++ b/src/lib/openapi/spec/group-schema.ts @@ -75,6 +75,13 @@ export const groupSchema = { type: 'integer', minimum: 0, }, + scimId: { + description: + 'The SCIM ID of the group, only present if managed by SCIM', + type: 'string', + nullable: true, + example: '01HTMEXAMPLESCIMID7SWWGHN7', + }, }, components: { schemas: { diff --git a/src/lib/services/group-service.ts b/src/lib/services/group-service.ts index 7da34492cf..11f5e0d728 100644 --- a/src/lib/services/group-service.ts +++ b/src/lib/services/group-service.ts @@ -91,6 +91,11 @@ export class GroupService { return this.mapGroupWithUsers(group, groupUsers, users); } + async isScimGroup(id: number): Promise { + const group = await this.groupStore.get(id); + return Boolean(group.scimId); + } + async createGroup( group: ICreateGroupModel, userName: string, diff --git a/src/lib/types/group.ts b/src/lib/types/group.ts index f0b539321e..d75944f736 100644 --- a/src/lib/types/group.ts +++ b/src/lib/types/group.ts @@ -10,6 +10,7 @@ export interface IGroup { createdAt?: Date; userCount?: number; createdBy?: string; + scimId?: string; } export interface IGroupUser { @@ -75,6 +76,8 @@ export default class Group implements IGroup { mappingsSSO: string[]; + scimId?: string; + constructor({ id, name, @@ -83,6 +86,7 @@ export default class Group implements IGroup { rootRole, createdBy, createdAt, + scimId, }: IGroup) { if (!id) { throw new ValidationError('Id is required', [], undefined); @@ -97,5 +101,6 @@ export default class Group implements IGroup { this.mappingsSSO = mappingsSSO; this.createdBy = createdBy; this.createdAt = createdAt; + this.scimId = scimId; } }