Add group deletion and (name) update support for SCIM integration

This commit is contained in:
Tuan Dang
2024-04-05 10:13:47 -07:00
parent f7b8345da4
commit f8ea421a0e
3 changed files with 142 additions and 45 deletions

View File

@@ -260,7 +260,6 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
} }
}); });
// TODO
server.route({ server.route({
url: "/Groups", url: "/Groups",
method: "POST", method: "POST",
@@ -284,22 +283,15 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
}, },
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]), onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
handler: async (req) => { handler: async (req) => {
console.log("create group");
console.log("create group req.body: ", req.body);
const group = await req.server.services.scim.createScimGroup({ const group = await req.server.services.scim.createScimGroup({
displayName: req.body.displayName, displayName: req.body.displayName,
orgId: req.permission.orgId orgId: req.permission.orgId
}); });
console.log("create group resulting group: ", group);
return group; return group;
} }
}); });
// TODO: GET /api/v1/scim/Groups/8432b0aa-93e0-4b55-af3c-cd3fcc176f81
server.route({ server.route({
url: "/Groups/:groupId", url: "/Groups/:groupId",
method: "GET", method: "GET",
@@ -307,33 +299,93 @@ export const registerScimRouter = async (server: FastifyZodProvider) => {
params: z.object({ params: z.object({
groupId: z.string().trim() groupId: z.string().trim()
}), }),
// body: z.object({ response: {
// schemas: z.array(z.string()), 200: z.object({
// Operations: z.array( schemas: z.array(z.string()),
// z.object({ id: z.string().trim(),
// op: z.string().trim(), displayName: z.string().trim(),
// path: z.string().trim().optional(), members: z.array(
// value: z.union([ z.object({
// z.object({ value: z.string(),
// active: z.boolean() display: z.string()
// }), })
// z.string().trim() ),
// ]) meta: z.object({
// }) resourceType: z.string().trim()
// ) })
// }), })
}
},
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
handler: async (req) => {
const group = await req.server.services.scim.getScimGroup({
groupId: req.params.groupId,
orgId: req.permission.orgId
});
return group;
}
});
server.route({
url: "/Groups/:groupId",
method: "PUT",
schema: {
params: z.object({
groupId: z.string().trim()
}),
body: z.object({
schemas: z.array(z.string()),
id: z.string().trim(),
displayName: z.string().trim(),
members: z.array(z.any()).length(0)
}),
response: {
200: z.object({
schemas: z.array(z.string()),
id: z.string().trim(),
displayName: z.string().trim(),
members: z.array(
z.object({
value: z.string(),
display: z.string()
})
),
meta: z.object({
resourceType: z.string().trim()
})
})
}
},
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
handler: async (req) => {
const group = await req.server.services.scim.updateScimGroupName({
groupId: req.params.groupId,
orgId: req.permission.orgId,
displayName: req.body.displayName
});
return group;
}
});
server.route({
url: "/Groups/:groupId",
method: "DELETE",
schema: {
params: z.object({
groupId: z.string().trim()
}),
response: { response: {
200: z.object({}) 200: z.object({})
} }
}, },
onRequest: verifyAuth([AuthMode.SCIM_TOKEN]), onRequest: verifyAuth([AuthMode.SCIM_TOKEN]),
handler: async (req) => { handler: async (req) => {
console.log("get scim group endpoint start"); const group = await req.server.services.scim.deleteScimGroup({
const group = await req.server.services.scim.getScimGroup({
groupId: req.params.groupId, groupId: req.params.groupId,
orgId: req.permission.orgId orgId: req.permission.orgId
}); });
console.log("get scim group endpoint end: ", group);
return group; return group;
} }
}); });

View File

@@ -25,6 +25,7 @@ import {
TCreateScimGroupDTO, TCreateScimGroupDTO,
TCreateScimTokenDTO, TCreateScimTokenDTO,
TCreateScimUserDTO, TCreateScimUserDTO,
TDeleteScimGroupDTO,
TDeleteScimTokenDTO, TDeleteScimTokenDTO,
TGetScimGroupDTO, TGetScimGroupDTO,
TGetScimUserDTO, TGetScimUserDTO,
@@ -32,6 +33,7 @@ import {
TListScimUsersDTO, TListScimUsersDTO,
TReplaceScimUserDTO, TReplaceScimUserDTO,
TScimTokenJwtPayload, TScimTokenJwtPayload,
TUpdateScimGroupNameDTO,
TUpdateScimUserDTO TUpdateScimUserDTO
} from "./scim-types"; } from "./scim-types";
@@ -44,7 +46,7 @@ type TScimServiceFactoryDep = {
>; >;
projectDAL: Pick<TProjectDALFactory, "find">; projectDAL: Pick<TProjectDALFactory, "find">;
projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find" | "delete">; projectMembershipDAL: Pick<TProjectMembershipDALFactory, "find" | "delete">;
groupDAL: Pick<TGroupDALFactory, "create" | "findOne" | "findAllGroupMembers">; groupDAL: Pick<TGroupDALFactory, "create" | "findOne" | "findAllGroupMembers" | "update" | "delete">;
licenseService: Pick<TLicenseServiceFactory, "getPlan">; licenseService: Pick<TLicenseServiceFactory, "getPlan">;
permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">; permissionService: Pick<TPermissionServiceFactory, "getOrgPermission">;
smtpService: TSmtpService; smtpService: TSmtpService;
@@ -431,13 +433,6 @@ export const scimServiceFactory = ({
}; };
const createScimGroup = async ({ displayName, orgId }: TCreateScimGroupDTO) => { const createScimGroup = async ({ displayName, orgId }: TCreateScimGroupDTO) => {
// TODO 1: impl basic
// TODO 2: impl linking case
console.log("createScimGroup args: ", {
displayName,
orgId
});
const group = await groupDAL.create({ const group = await groupDAL.create({
name: displayName, name: displayName,
slug: slugify(`${displayName}-${alphaNumericNanoId(4)}`), slug: slugify(`${displayName}-${alphaNumericNanoId(4)}`),
@@ -445,8 +440,6 @@ export const scimServiceFactory = ({
role: OrgMembershipRole.NoAccess role: OrgMembershipRole.NoAccess
}); });
console.log("createScimGroup the group: ", group);
return buildScimGroup({ return buildScimGroup({
groupId: group.id, groupId: group.id,
name: group.name, name: group.name,
@@ -455,21 +448,19 @@ export const scimServiceFactory = ({
}; };
const getScimGroup = async ({ groupId, orgId }: TGetScimGroupDTO) => { const getScimGroup = async ({ groupId, orgId }: TGetScimGroupDTO) => {
console.log("getScimGroup args: ", {
groupId,
orgId
});
const group = await groupDAL.findOne({ const group = await groupDAL.findOne({
id: groupId, id: groupId,
orgId orgId
}); });
console.log("getScimGroup group: ", group); if (!group) {
const users = await groupDAL.findAllGroupMembers(group.orgId, group.id); throw new ScimRequestError({
console.log("getScimGroup users: ", users); detail: "Group Not Found",
status: 404
});
}
// TODO: get members const users = await groupDAL.findAllGroupMembers(group.orgId, group.id);
return buildScimGroup({ return buildScimGroup({
groupId: group.id, groupId: group.id,
@@ -483,6 +474,47 @@ export const scimServiceFactory = ({
}); });
}; };
const updateScimGroupName = async ({ groupId, orgId, displayName }: TUpdateScimGroupNameDTO) => {
const [group] = await groupDAL.update(
{
id: groupId,
orgId
},
{
name: displayName
}
);
if (!group) {
throw new ScimRequestError({
detail: "Group Not Found",
status: 404
});
}
return buildScimGroup({
groupId: group.id,
name: group.name,
members: []
});
};
const deleteScimGroup = async ({ groupId, orgId }: TDeleteScimGroupDTO) => {
const [group] = await groupDAL.delete({
id: groupId,
orgId
});
if (!group) {
throw new ScimRequestError({
detail: "Group Not Found",
status: 404
});
}
return {}; // intentionally return empty object upon success
};
const fnValidateScimToken = async (token: TScimTokenJwtPayload) => { const fnValidateScimToken = async (token: TScimTokenJwtPayload) => {
const scimToken = await scimDAL.findById(token.scimTokenId); const scimToken = await scimDAL.findById(token.scimTokenId);
if (!scimToken) throw new UnauthorizedError(); if (!scimToken) throw new UnauthorizedError();
@@ -517,6 +549,8 @@ export const scimServiceFactory = ({
replaceScimUser, replaceScimUser,
createScimGroup, createScimGroup,
getScimGroup, getScimGroup,
deleteScimGroup,
updateScimGroupName,
fnValidateScimToken fnValidateScimToken
}; };
}; };

View File

@@ -69,6 +69,17 @@ export type TGetScimGroupDTO = {
orgId: string; orgId: string;
}; };
export type TUpdateScimGroupNameDTO = {
groupId: string;
orgId: string;
displayName: string;
};
export type TDeleteScimGroupDTO = {
groupId: string;
orgId: string;
};
export type TScimTokenJwtPayload = { export type TScimTokenJwtPayload = {
scimTokenId: string; scimTokenId: string;
authTokenType: string; authTokenType: string;