From 6b51c7269a4ed70bff18172e349fca78c0f71182 Mon Sep 17 00:00:00 2001 From: Akhil Mohan Date: Thu, 28 Mar 2024 15:29:54 +0530 Subject: [PATCH] feat(server): removed name and description and fixed api for user privileges --- .../identity-project-additional-privilege.ts | 2 - .../project-user-additional-privilege.ts | 2 - ...ity-project-additional-privilege-router.ts | 11 ++- .../v1/user-additional-privilege-router.ts | 77 ++++++++++++------- ...ty-project-additional-privilege-service.ts | 42 +++++++--- ...tity-project-additional-privilege-types.ts | 6 -- .../services/permission/permission-service.ts | 16 ++-- ...oject-user-additional-privilege-service.ts | 61 ++++++++++++--- ...project-user-additional-privilege-types.ts | 8 +- .../routes/v1/project-membership-router.ts | 16 +--- .../src/server/routes/v1/project-router.ts | 14 ---- .../project-membership-dal.ts | 50 +----------- 12 files changed, 148 insertions(+), 157 deletions(-) diff --git a/backend/src/db/schemas/identity-project-additional-privilege.ts b/backend/src/db/schemas/identity-project-additional-privilege.ts index 0e548dd93..7a9dbe19e 100644 --- a/backend/src/db/schemas/identity-project-additional-privilege.ts +++ b/backend/src/db/schemas/identity-project-additional-privilege.ts @@ -9,9 +9,7 @@ import { TImmutableDBKeys } from "./models"; export const IdentityProjectAdditionalPrivilegeSchema = z.object({ id: z.string().uuid(), - name: z.string(), slug: z.string(), - description: z.string().nullable().optional(), projectMembershipId: z.string().uuid(), isTemporary: z.boolean().default(false), temporaryMode: z.string().nullable().optional(), diff --git a/backend/src/db/schemas/project-user-additional-privilege.ts b/backend/src/db/schemas/project-user-additional-privilege.ts index c20d83543..0fd0e5faa 100644 --- a/backend/src/db/schemas/project-user-additional-privilege.ts +++ b/backend/src/db/schemas/project-user-additional-privilege.ts @@ -9,9 +9,7 @@ import { TImmutableDBKeys } from "./models"; export const ProjectUserAdditionalPrivilegeSchema = z.object({ id: z.string().uuid(), - name: z.string(), slug: z.string(), - description: z.string().nullable().optional(), projectMembershipId: z.string().uuid(), isTemporary: z.boolean().default(false), temporaryMode: z.string().nullable().optional(), diff --git a/backend/src/ee/routes/v1/identity-project-additional-privilege-router.ts b/backend/src/ee/routes/v1/identity-project-additional-privilege-router.ts index 86061d33c..18c196409 100644 --- a/backend/src/ee/routes/v1/identity-project-additional-privilege-router.ts +++ b/backend/src/ee/routes/v1/identity-project-additional-privilege-router.ts @@ -31,8 +31,6 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F message: "Slug must be a valid slug" }) ), - name: z.string().trim().min(1), - description: z.string().trim().optional(), permissions: z.any().array(), isPackedPermission: z.boolean().optional().default(true), isTemporary: z.literal(false).default(false) @@ -48,8 +46,6 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F .refine((v) => slugify(v) === v, { message: "Slug must be a valid slug" }), - name: z.string().trim(), - description: z.string().trim().optional(), permissions: z.any().array(), isTemporary: z.literal(true), isPackedPermission: z.boolean().optional().default(true), @@ -70,6 +66,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F actorId: req.permission.id, actor: req.permission.type, actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, ...req.body, permissions: JSON.stringify( req.body.isPackedPermission ? req.body.permissions : packRules(req.body.permissions) @@ -96,8 +93,6 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F .refine((v) => slugify(v) === v, { message: "Slug must be a valid slug" }), - name: z.string().trim(), - description: z.string().trim().optional(), permissions: z.any().array(), isPackedPermission: z.boolean().optional().default(true), isTemporary: z.boolean(), @@ -118,6 +113,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F actorId: req.permission.id, actor: req.permission.type, actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, ...req.body, permissions: req.body.permissions ? JSON.stringify(req.body.isPackedPermission ? req.body.permissions : packRules(req.body.permissions)) @@ -146,6 +142,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F const privilege = await server.services.identityProjectAdditionalPrivilege.deleteById({ actorId: req.permission.id, actor: req.permission.type, + actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, privilegeId: req.params.privilegeId }); @@ -170,6 +167,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F handler: async (req) => { const privilege = await server.services.identityProjectAdditionalPrivilege.getPrivilegeDetailsById({ actorId: req.permission.id, + actorAuthMethod: req.permission.authMethod, actor: req.permission.type, actorOrgId: req.permission.orgId, privilegeId: req.params.privilegeId @@ -197,6 +195,7 @@ export const registerIdentityProjectAdditionalPrivilegeRouter = async (server: F const privileges = await server.services.identityProjectAdditionalPrivilege.listIdentityProjectPrivileges({ actorId: req.permission.id, actor: req.permission.type, + actorAuthMethod: req.permission.authMethod, actorOrgId: req.permission.orgId, projectId: req.body.projectId, identityId: req.body.identityId diff --git a/backend/src/ee/routes/v1/user-additional-privilege-router.ts b/backend/src/ee/routes/v1/user-additional-privilege-router.ts index cd161ab5a..d0643b22c 100644 --- a/backend/src/ee/routes/v1/user-additional-privilege-router.ts +++ b/backend/src/ee/routes/v1/user-additional-privilege-router.ts @@ -5,7 +5,6 @@ import { z } from "zod"; import { ProjectUserAdditionalPrivilegeSchema } from "@app/db/schemas"; import { ProjectUserAdditionalPrivilegeTemporaryMode } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-types"; import { alphaNumericNanoId } from "@app/lib/nanoid"; -import { zpStr } from "@app/lib/zod"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -17,36 +16,29 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr body: z.union([ z.object({ projectMembershipId: z.string(), - // to disallow empty string - slug: zpStr( - z - .string() - .max(60) - .trim() - .optional() - .default(`privilege-${slugify(alphaNumericNanoId(12))}`) - .refine((v) => slugify(v) === v, { - message: "Slug must be a valid slug" - }) - ), - name: z.string().trim(), - description: z.string().trim().optional(), + slug: z + .string() + .min(1) + .max(60) + .trim() + .default(`privilege-${slugify(alphaNumericNanoId(12))}`) + .refine((v) => slugify(v) === v, { + message: "Slug must be a valid slug" + }), permissions: z.any().array(), isTemporary: z.literal(false).default(false) }), z.object({ projectMembershipId: z.string(), - slug: zpStr( - z - .string() - .max(60) - .trim() - .optional() - .default(`privilege-${slugify(alphaNumericNanoId(12))}`) - .refine((v) => slugify(v) === v, { - message: "Slug must be a valid slug" - }) - ), + slug: z + .string() + .min(1) + .max(60) + .trim() + .default(`privilege-${slugify(alphaNumericNanoId(12))}`) + .refine((v) => slugify(v) === v, { + message: "Slug must be a valid slug" + }), name: z.string().trim(), description: z.string().trim().optional(), permissions: z.any().array(), @@ -68,6 +60,7 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr actorId: req.permission.id, actor: req.permission.type, actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, ...req.body, permissions: JSON.stringify(req.body.permissions) }); @@ -88,11 +81,10 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr .string() .max(60) .trim() + .refine((v) => v.toLowerCase() === v, "Slug must be lowercase") .refine((v) => slugify(v) === v, { message: "Slug must be a valid slug" }), - name: z.string().trim(), - description: z.string().trim().optional(), permissions: z.any().array(), isTemporary: z.boolean(), temporaryMode: z.nativeEnum(ProjectUserAdditionalPrivilegeTemporaryMode), @@ -112,6 +104,7 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr actorId: req.permission.id, actor: req.permission.type, actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, ...req.body, permissions: req.body.permissions ? JSON.stringify(req.body.permissions) : undefined, privilegeId: req.params.privilegeId @@ -139,12 +132,39 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr actorId: req.permission.id, actor: req.permission.type, actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, privilegeId: req.params.privilegeId }); return { privilege }; } }); + server.route({ + url: "/", + method: "GET", + schema: { + querystring: z.object({ + projectMembershipId: z.string() + }), + response: { + 200: z.object({ + privileges: ProjectUserAdditionalPrivilegeSchema.array() + }) + } + }, + onRequest: verifyAuth([AuthMode.JWT]), + handler: async (req) => { + const privileges = await server.services.projectUserAdditionalPrivilege.listPrivileges({ + actorId: req.permission.id, + actor: req.permission.type, + actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, + projectMembershipId: req.query.projectMembershipId + }); + return { privileges }; + } + }); + server.route({ url: "/:privilegeId", method: "GET", @@ -164,6 +184,7 @@ export const registerUserAdditionalPrivilegeRouter = async (server: FastifyZodPr actorId: req.permission.id, actor: req.permission.type, actorOrgId: req.permission.orgId, + actorAuthMethod: req.permission.authMethod, privilegeId: req.params.privilegeId }); return { privilege }; diff --git a/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts b/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts index 59729917c..3a919d983 100644 --- a/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts +++ b/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-service.ts @@ -34,7 +34,6 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ permissionService }: TIdentityProjectAdditionalPrivilegeServiceFactoryDep) => { const create = async ({ - name, slug, actor, actorId, @@ -42,7 +41,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ identityId, permissions: customPermission, actorOrgId, - description, + actorAuthMethod, ...dto }: TCreateIdentityPrivilegeDTO) => { const identityProjectMembership = await identityProjectDAL.findOne({ identityId, projectId }); @@ -53,6 +52,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actor, actorId, identityProjectMembership.projectId, + actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); @@ -60,6 +60,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ ActorType.IDENTITY, identityId, identityProjectMembership.projectId, + actorAuthMethod, actorOrgId ); const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission); @@ -76,9 +77,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ const additionalPrivilege = await identityProjectAdditionalPrivilegeDAL.create({ projectMembershipId: identityProjectMembership.id, slug, - permissions: customPermission, - name, - description + permissions: customPermission }); return additionalPrivilege; } @@ -88,8 +87,6 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ projectMembershipId: identityProjectMembership.id, slug, permissions: customPermission, - name, - description, isTemporary: true, temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative, temporaryRange: dto.temporaryRange, @@ -99,7 +96,14 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ return additionalPrivilege; }; - const updateById = async ({ privilegeId, actorOrgId, actor, actorId, ...dto }: TUpdateIdentityPrivilegeDTO) => { + const updateById = async ({ + privilegeId, + actorOrgId, + actor, + actorId, + actorAuthMethod, + ...dto + }: TUpdateIdentityPrivilegeDTO) => { const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findById(privilegeId); if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" }); @@ -110,6 +114,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actor, actorId, identityProjectMembership.projectId, + actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); @@ -117,6 +122,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ ActorType.IDENTITY, identityProjectMembership.identityId, identityProjectMembership.projectId, + actorAuthMethod, actorOrgId ); const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission); @@ -155,7 +161,13 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ return additionalPrivilege; }; - const deleteById = async ({ actorId, actor, actorOrgId, privilegeId }: TDeleteIdentityPrivilegeDTO) => { + const deleteById = async ({ + actorId, + actor, + actorOrgId, + privilegeId, + actorAuthMethod + }: TDeleteIdentityPrivilegeDTO) => { const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findById(privilegeId); if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" }); @@ -166,6 +178,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actor, actorId, identityProjectMembership.projectId, + actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); @@ -173,6 +186,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ ActorType.IDENTITY, identityProjectMembership.identityId, identityProjectMembership.projectId, + actorAuthMethod, actorOrgId ); const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission); @@ -187,7 +201,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ privilegeId, actorOrgId, actor, - actorId + actorId, + actorAuthMethod }: TGetIdentityPrivilegeDetailsDTO) => { const identityPrivilege = await identityProjectAdditionalPrivilegeDAL.findById(privilegeId); if (!identityPrivilege) throw new BadRequestError({ message: "Identity additional privilege not found" }); @@ -199,6 +214,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actor, actorId, identityProjectMembership.projectId, + actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); @@ -206,6 +222,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ ActorType.IDENTITY, identityProjectMembership.identityId, identityProjectMembership.projectId, + actorAuthMethod, actorOrgId ); const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission); @@ -220,7 +237,8 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ identityId, actorOrgId, actor, - actorId + actorId, + actorAuthMethod }: TListIdentityPrivilegesDTO) => { const identityProjectMembership = await identityProjectDAL.findOne({ projectId, identityId }); if (!identityProjectMembership) throw new BadRequestError({ message: `Failed to find identity` }); @@ -229,6 +247,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ actor, actorId, identityProjectMembership.projectId, + actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Identity); @@ -236,6 +255,7 @@ export const identityProjectAdditionalPrivilegeServiceFactory = ({ ActorType.IDENTITY, identityProjectMembership.identityId, identityProjectMembership.projectId, + actorAuthMethod, actorOrgId ); const hasRequiredPriviledges = isAtLeastAsPrivileged(permission, identityRolePermission); diff --git a/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types.ts b/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types.ts index 76a8ffb62..0d2b64cc5 100644 --- a/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types.ts +++ b/backend/src/ee/services/identity-project-additional-privilege/identity-project-additional-privilege-types.ts @@ -9,18 +9,14 @@ export type TCreateIdentityPrivilegeDTO = ( permissions: unknown; identityId: string; projectId: string; - name: string; slug: string; - description?: string; isTemporary: false; } | { permissions: unknown; identityId: string; projectId: string; - name: string; slug: string; - description?: string; isTemporary: true; temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative; temporaryRange: string; @@ -32,9 +28,7 @@ export type TCreateIdentityPrivilegeDTO = ( export type TUpdateIdentityPrivilegeDTO = { privilegeId: string } & Omit & Partial<{ permissions: unknown; - name: string; slug: string; - description?: string; isTemporary: boolean; temporaryMode: IdentityProjectAdditionalPrivilegeTemporaryMode.Relative; temporaryRange: string; diff --git a/backend/src/ee/services/permission/permission-service.ts b/backend/src/ee/services/permission/permission-service.ts index 8062a1c43..d836e88df 100644 --- a/backend/src/ee/services/permission/permission-service.ts +++ b/backend/src/ee/services/permission/permission-service.ts @@ -180,10 +180,12 @@ export const permissionServiceFactory = ({ authMethod: ActorAuthMethod, userOrgId?: string ): Promise> => { - const membership = await permissionDAL.getProjectPermission(userId, projectId); - if (!membership) throw new UnauthorizedError({ name: "User not in project" }); + const userProjectPermission = await permissionDAL.getProjectPermission(userId, projectId); + if (!userProjectPermission) throw new UnauthorizedError({ name: "User not in project" }); - if (membership.roles.some(({ role, permissions }) => role === ProjectMembershipRole.Custom && !permissions)) { + if ( + userProjectPermission.roles.some(({ role, permissions }) => role === ProjectMembershipRole.Custom && !permissions) + ) { throw new BadRequestError({ name: "Custom permission not found" }); } @@ -192,11 +194,11 @@ export const permissionServiceFactory = ({ // Extra: This means that when users are using API keys to make requests, they can't use slug-based routes. // Slug-based routes depend on the organization ID being present on the request, since project slugs aren't globally unique, and we need a way to filter by organization. - if (userOrgId !== "API_KEY" && membership.orgId !== userOrgId) { + if (userOrgId !== "API_KEY" && userProjectPermission.orgId !== userOrgId) { throw new UnauthorizedError({ name: "You are not logged into this organization" }); } - validateOrgSAML(authMethod, membership.orgAuthEnforced); + validateOrgSAML(authMethod, userProjectPermission.orgAuthEnforced); // join two permissions and pass to build the final permission set const rolePermissions = userProjectPermission.roles?.map(({ role, permissions }) => ({ role, permissions })) || []; @@ -210,7 +212,9 @@ export const permissionServiceFactory = ({ permission: buildProjectPermission(rolePermissions.concat(additionalPrivileges)), membership: userProjectPermission, hasRole: (role: string) => - membership.roles.findIndex(({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug) !== -1 + userProjectPermission.roles.findIndex( + ({ role: slug, customRoleSlug }) => role === slug || slug === customRoleSlug + ) !== -1 }; }; diff --git a/backend/src/ee/services/project-user-additional-privilege/project-user-additional-privilege-service.ts b/backend/src/ee/services/project-user-additional-privilege/project-user-additional-privilege-service.ts index e1f3ba823..c9ff2c7e0 100644 --- a/backend/src/ee/services/project-user-additional-privilege/project-user-additional-privilege-service.ts +++ b/backend/src/ee/services/project-user-additional-privilege/project-user-additional-privilege-service.ts @@ -12,6 +12,7 @@ import { TCreateUserPrivilegeDTO, TDeleteUserPrivilegeDTO, TGetUserPrivilegeDetailsDTO, + TListUserPrivilegesDTO, TUpdateUserPrivilegeDTO } from "./project-user-additional-privilege-types"; @@ -31,13 +32,12 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ permissionService }: TProjectUserAdditionalPrivilegeServiceFactoryDep) => { const create = async ({ - name, slug, actor, actorId, permissions: customPermission, actorOrgId, - description, + actorAuthMethod, projectMembershipId, ...dto }: TCreateUserPrivilegeDTO) => { @@ -48,6 +48,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ actor, actorId, projectMembership.projectId, + actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member); @@ -59,9 +60,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ const additionalPrivilege = await projectUserAdditionalPrivilegeDAL.create({ projectMembershipId, slug, - permissions: customPermission, - name, - description + permissions: customPermission }); return additionalPrivilege; } @@ -71,8 +70,6 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ projectMembershipId, slug, permissions: customPermission, - name, - description, isTemporary: true, temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative, temporaryRange: dto.temporaryRange, @@ -82,7 +79,14 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ return additionalPrivilege; }; - const updateById = async ({ privilegeId, actorOrgId, actor, actorId, ...dto }: TUpdateUserPrivilegeDTO) => { + const updateById = async ({ + privilegeId, + actorOrgId, + actor, + actorId, + actorAuthMethod, + ...dto + }: TUpdateUserPrivilegeDTO) => { const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId); if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" }); @@ -93,6 +97,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ actor, actorId, projectMembership.projectId, + actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member); @@ -129,7 +134,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ return additionalPrivilege; }; - const deleteById = async ({ actorId, actor, actorOrgId, privilegeId }: TDeleteUserPrivilegeDTO) => { + const deleteById = async ({ actorId, actor, actorOrgId, actorAuthMethod, privilegeId }: TDeleteUserPrivilegeDTO) => { const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId); if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" }); @@ -140,6 +145,7 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ actor, actorId, projectMembership.projectId, + actorAuthMethod, actorOrgId ); ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member); @@ -148,7 +154,13 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ return deletedPrivilege; }; - const getPrivilegeDetailsById = async ({ privilegeId, actorOrgId, actor, actorId }: TGetUserPrivilegeDetailsDTO) => { + const getPrivilegeDetailsById = async ({ + privilegeId, + actorOrgId, + actor, + actorId, + actorAuthMethod + }: TGetUserPrivilegeDetailsDTO) => { const userPrivilege = await projectUserAdditionalPrivilegeDAL.findById(privilegeId); if (!userPrivilege) throw new BadRequestError({ message: "User additional privilege not found" }); @@ -159,17 +171,42 @@ export const projectUserAdditionalPrivilegeServiceFactory = ({ actor, actorId, projectMembership.projectId, + actorAuthMethod, actorOrgId ); - ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Edit, ProjectPermissionSub.Member); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member); return userPrivilege; }; + const listPrivileges = async ({ + projectMembershipId, + actorOrgId, + actor, + actorId, + actorAuthMethod + }: TListUserPrivilegesDTO) => { + const projectMembership = await projectMembershipDAL.findById(projectMembershipId); + if (!projectMembership) throw new BadRequestError({ message: "Project membership not found" }); + + const { permission } = await permissionService.getProjectPermission( + actor, + actorId, + projectMembership.projectId, + actorAuthMethod, + actorOrgId + ); + ForbiddenError.from(permission).throwUnlessCan(ProjectPermissionActions.Read, ProjectPermissionSub.Member); + + const userPrivileges = await projectUserAdditionalPrivilegeDAL.find({ projectMembershipId }); + return userPrivileges; + }; + return { create, updateById, deleteById, - getPrivilegeDetailsById + getPrivilegeDetailsById, + listPrivileges }; }; diff --git a/backend/src/ee/services/project-user-additional-privilege/project-user-additional-privilege-types.ts b/backend/src/ee/services/project-user-additional-privilege/project-user-additional-privilege-types.ts index efc8e81b1..572474270 100644 --- a/backend/src/ee/services/project-user-additional-privilege/project-user-additional-privilege-types.ts +++ b/backend/src/ee/services/project-user-additional-privilege/project-user-additional-privilege-types.ts @@ -8,17 +8,13 @@ export type TCreateUserPrivilegeDTO = ( | { permissions: unknown; projectMembershipId: string; - name: string; slug: string; - description?: string; isTemporary: false; } | { permissions: unknown; projectMembershipId: string; - name: string; slug: string; - description?: string; isTemporary: true; temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative; temporaryRange: string; @@ -30,9 +26,7 @@ export type TCreateUserPrivilegeDTO = ( export type TUpdateUserPrivilegeDTO = { privilegeId: string } & Omit & Partial<{ permissions: unknown; - name: string; slug: string; - description?: string; isTemporary: boolean; temporaryMode: ProjectUserAdditionalPrivilegeTemporaryMode.Relative; temporaryRange: string; @@ -42,3 +36,5 @@ export type TUpdateUserPrivilegeDTO = { privilegeId: string } & Omit & { privilegeId: string }; export type TGetUserPrivilegeDetailsDTO = Omit & { privilegeId: string }; + +export type TListUserPrivilegesDTO = Omit & { projectMembershipId: string }; diff --git a/backend/src/server/routes/v1/project-membership-router.ts b/backend/src/server/routes/v1/project-membership-router.ts index ea0559463..f277e556c 100644 --- a/backend/src/server/routes/v1/project-membership-router.ts +++ b/backend/src/server/routes/v1/project-membership-router.ts @@ -40,20 +40,6 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider lastName: true, id: true }).merge(UserEncryptionKeysSchema.pick({ publicKey: true })), - additionalPrivileges: z.array( - z.object({ - id: z.string(), - name: z.string(), - slug: z.string(), - description: z.string().optional().nullable(), - isTemporary: z.boolean(), - temporaryMode: z.string().optional().nullable(), - temporaryRange: z.string().nullable().optional(), - temporaryAccessStartTime: z.date().nullable().optional(), - temporaryAccessEndTime: z.date().nullable().optional(), - createdAt: z.date() - }) - ), roles: z.array( z.object({ id: z.string(), @@ -172,7 +158,7 @@ export const registerProjectMembershipRouter = async (server: FastifyZodProvider ]) ) .min(1) - .refine((data) => data.some(({ isTemporary }) => !isTemporary), "At least long lived role is required") + .refine((data) => data.some(({ isTemporary }) => !isTemporary), "At least one long lived role is required") .describe(PROJECTS.UPDATE_USER_MEMBERSHIP.roles) }), response: { diff --git a/backend/src/server/routes/v1/project-router.ts b/backend/src/server/routes/v1/project-router.ts index 95619a510..55f1fdda9 100644 --- a/backend/src/server/routes/v1/project-router.ts +++ b/backend/src/server/routes/v1/project-router.ts @@ -73,20 +73,6 @@ export const registerProjectRouter = async (server: FastifyZodProvider) => { lastName: true, id: true }).merge(UserEncryptionKeysSchema.pick({ publicKey: true })), - additionalPrivileges: z.array( - z.object({ - id: z.string(), - name: z.string(), - slug: z.string(), - description: z.string().optional().nullable(), - isTemporary: z.boolean(), - temporaryMode: z.string().optional().nullable(), - temporaryRange: z.string().nullable().optional(), - temporaryAccessStartTime: z.date().nullable().optional(), - temporaryAccessEndTime: z.date().nullable().optional(), - createdAt: z.date() - }) - ), roles: z.array( z.object({ id: z.string(), diff --git a/backend/src/services/project-membership/project-membership-dal.ts b/backend/src/services/project-membership/project-membership-dal.ts index fae6c26ba..8faab487e 100644 --- a/backend/src/services/project-membership/project-membership-dal.ts +++ b/backend/src/services/project-membership/project-membership-dal.ts @@ -29,11 +29,6 @@ export const projectMembershipDALFactory = (db: TDbClient) => { `${TableName.ProjectUserMembershipRole}.customRoleId`, `${TableName.ProjectRoles}.id` ) - .leftJoin( - TableName.ProjectUserAdditionalPrivilege, - `${TableName.ProjectMembership}.id`, - `${TableName.ProjectUserAdditionalPrivilege}.projectMembershipId` - ) .select( db.ref("id").withSchema(TableName.ProjectMembership), db.ref("isGhost").withSchema(TableName.Users), @@ -52,23 +47,7 @@ export const projectMembershipDALFactory = (db: TDbClient) => { db.ref("isTemporary").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryRange").withSchema(TableName.ProjectUserMembershipRole), db.ref("temporaryAccessStartTime").withSchema(TableName.ProjectUserMembershipRole), - db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole), - db.ref("id").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApId"), - db.ref("name").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApName"), - db.ref("description").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApDescription"), - db.ref("slug").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApSlug"), - db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryMode"), - db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApIsTemporary"), - db.ref("createdAt").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApCreatedAt"), - db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryRange"), - db - .ref("temporaryAccessStartTime") - .withSchema(TableName.ProjectUserAdditionalPrivilege) - .as("userApTemporaryAccessStartTime"), - db - .ref("temporaryAccessEndTime") - .withSchema(TableName.ProjectUserAdditionalPrivilege) - .as("userApTemporaryAccessEndTime") + db.ref("temporaryAccessEndTime").withSchema(TableName.ProjectUserMembershipRole) ) .where({ isGhost: false }); @@ -108,33 +87,6 @@ export const projectMembershipDALFactory = (db: TDbClient) => { temporaryAccessStartTime, isTemporary }) - }, - { - label: "additionalPrivileges" as const, - key: "userApId", - mapper: ({ - userApId, - userApDescription, - userApName, - userApSlug, - userApIsTemporary, - userApTemporaryMode, - userApTemporaryRange, - userApTemporaryAccessEndTime, - userApTemporaryAccessStartTime, - userApCreatedAt - }) => ({ - id: userApId, - name: userApName, - description: userApDescription, - slug: userApSlug, - temporaryRange: userApTemporaryRange, - temporaryMode: userApTemporaryMode, - temporaryAccessEndTime: userApTemporaryAccessEndTime, - temporaryAccessStartTime: userApTemporaryAccessStartTime, - isTemporary: userApIsTemporary, - createdAt: userApCreatedAt - }) } ] });