diff --git a/backend/src/ee/services/permission/org-permission.ts b/backend/src/ee/services/permission/org-permission.ts index c4a6ac3fc..64ca8ad41 100644 --- a/backend/src/ee/services/permission/org-permission.ts +++ b/backend/src/ee/services/permission/org-permission.ts @@ -1,7 +1,5 @@ import { AbilityBuilder, createMongoAbility, MongoAbility } from "@casl/ability"; -import { conditionsMatcher } from "@app/lib/casl"; - export enum OrgPermissionActions { Read = "read", Create = "create", @@ -48,7 +46,7 @@ export type OrgPermissionSet = | [OrgPermissionAdminConsoleAction, OrgPermissionSubjects.AdminConsole]; const buildAdminPermission = () => { - const { can, build } = new AbilityBuilder>(createMongoAbility); + const { can, rules } = new AbilityBuilder>(createMongoAbility); // ws permissions can(OrgPermissionActions.Read, OrgPermissionSubjects.Workspace); can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace); @@ -115,13 +113,13 @@ const buildAdminPermission = () => { can(OrgPermissionAdminConsoleAction.AccessAllProjects, OrgPermissionSubjects.AdminConsole); - return build({ conditionsMatcher }); + return rules; }; export const orgAdminPermissions = buildAdminPermission(); const buildMemberPermission = () => { - const { can, build } = new AbilityBuilder>(createMongoAbility); + const { can, rules } = new AbilityBuilder>(createMongoAbility); can(OrgPermissionActions.Read, OrgPermissionSubjects.Workspace); can(OrgPermissionActions.Create, OrgPermissionSubjects.Workspace); @@ -142,14 +140,14 @@ const buildMemberPermission = () => { can(OrgPermissionActions.Edit, OrgPermissionSubjects.Identity); can(OrgPermissionActions.Delete, OrgPermissionSubjects.Identity); - return build({ conditionsMatcher }); + return rules; }; export const orgMemberPermissions = buildMemberPermission(); const buildNoAccessPermission = () => { - const { build } = new AbilityBuilder>(createMongoAbility); - return build({ conditionsMatcher }); + const { rules } = new AbilityBuilder>(createMongoAbility); + return rules; }; export const orgNoAccessPermissions = buildNoAccessPermission(); diff --git a/backend/src/ee/services/permission/permission-dal.ts b/backend/src/ee/services/permission/permission-dal.ts index cd392d3c7..70a06739e 100644 --- a/backend/src/ee/services/permission/permission-dal.ts +++ b/backend/src/ee/services/permission/permission-dal.ts @@ -1,7 +1,13 @@ import { z } from "zod"; import { TDbClient } from "@app/db"; -import { IdentityProjectMembershipRoleSchema, ProjectUserMembershipRolesSchema, TableName } from "@app/db/schemas"; +import { + IdentityProjectMembershipRoleSchema, + OrgMembershipsSchema, + TableName, + TProjectRoles, + TProjects +} from "@app/db/schemas"; import { DatabaseError } from "@app/lib/errors"; import { selectAllTableCols, sqlNestRelationships } from "@app/lib/knex"; @@ -10,18 +16,91 @@ export type TPermissionDALFactory = ReturnType; export const permissionDALFactory = (db: TDbClient) => { const getOrgPermission = async (userId: string, orgId: string) => { try { + const groupSubQuery = db(TableName.Groups) + .where(`${TableName.Groups}.orgId`, orgId) + .join(TableName.UserGroupMembership, (queryBuilder) => { + queryBuilder + .on(`${TableName.UserGroupMembership}.groupId`, `${TableName.Groups}.id`) + .andOn(`${TableName.UserGroupMembership}.userId`, db.raw("?", [userId])); + }) + .leftJoin(TableName.OrgRoles, `${TableName.Groups}.roleId`, `${TableName.OrgRoles}.id`) + .select( + db.ref("id").withSchema(TableName.Groups).as("groupId"), + db.ref("orgId").withSchema(TableName.Groups).as("groupOrgId"), + db.ref("name").withSchema(TableName.Groups).as("groupName"), + db.ref("slug").withSchema(TableName.Groups).as("groupSlug"), + db.ref("role").withSchema(TableName.Groups).as("groupRole"), + db.ref("roleId").withSchema(TableName.Groups).as("groupRoleId"), + db.ref("createdAt").withSchema(TableName.Groups).as("groupCreatedAt"), + db.ref("updatedAt").withSchema(TableName.Groups).as("groupUpdatedAt"), + db.ref("permissions").withSchema(TableName.OrgRoles).as("groupCustomRolePermission") + ); + const membership = await db .replicaNode()(TableName.OrgMembership) - .leftJoin(TableName.OrgRoles, `${TableName.OrgMembership}.roleId`, `${TableName.OrgRoles}.id`) - .join(TableName.Organization, `${TableName.OrgMembership}.orgId`, `${TableName.Organization}.id`) - .where("userId", userId) .where(`${TableName.OrgMembership}.orgId`, orgId) - .select(db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced")) - .select("permissions") - .select(selectAllTableCols(TableName.OrgMembership)) - .first(); + .where(`${TableName.OrgMembership}.userId`, userId) + .leftJoin(TableName.OrgRoles, `${TableName.OrgRoles}.id`, `${TableName.OrgMembership}.roleId`) + .leftJoin[0]>( + groupSubQuery.as("userGroups"), + "userGroups.groupOrgId", + db.raw("?", [orgId]) + ) + .join(TableName.Organization, `${TableName.Organization}.id`, `${TableName.OrgMembership}.orgId`) + .select( + selectAllTableCols(TableName.OrgMembership), + db.ref("slug").withSchema(TableName.OrgRoles).withSchema(TableName.OrgRoles).as("customRoleSlug"), + db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"), + db.ref("groupId").withSchema("userGroups"), + db.ref("groupOrgId").withSchema("userGroups"), + db.ref("groupName").withSchema("userGroups"), + db.ref("groupSlug").withSchema("userGroups"), + db.ref("groupRole").withSchema("userGroups"), + db.ref("groupRoleId").withSchema("userGroups"), + db.ref("groupCreatedAt").withSchema("userGroups"), + db.ref("groupUpdatedAt").withSchema("userGroups"), + db.ref("groupCustomRolePermission").withSchema("userGroups") + ); - return membership; + const [formatedDoc] = sqlNestRelationships({ + data: membership, + key: "id", + parentMapper: (el) => + OrgMembershipsSchema.extend({ + permissions: z.unknown(), + orgAuthEnforced: z.boolean().optional().nullable(), + customRoleSlug: z.string().optional().nullable() + }).parse(el), + childrenMapper: [ + { + key: "groupId", + label: "groups" as const, + mapper: ({ + groupId, + groupUpdatedAt, + groupCreatedAt, + groupRole, + groupRoleId, + groupCustomRolePermission, + groupName, + groupSlug, + groupOrgId + }) => ({ + id: groupId, + updatedAt: groupUpdatedAt, + createdAt: groupCreatedAt, + role: groupRole, + roleId: groupRoleId, + customRolePermission: groupCustomRolePermission, + name: groupName, + slug: groupSlug, + orgId: groupOrgId + }) + } + ] + }); + + return formatedDoc; } catch (error) { throw new DatabaseError({ error, name: "GetOrgPermission" }); } @@ -47,74 +126,31 @@ export const permissionDALFactory = (db: TDbClient) => { const getProjectPermission = async (userId: string, projectId: string) => { try { - const groups: string[] = await db - .replicaNode()(TableName.GroupProjectMembership) - .where(`${TableName.GroupProjectMembership}.projectId`, projectId) - .pluck(`${TableName.GroupProjectMembership}.groupId`); - - const groupDocs = await db - .replicaNode()(TableName.UserGroupMembership) - .where(`${TableName.UserGroupMembership}.userId`, userId) - .whereIn(`${TableName.UserGroupMembership}.groupId`, groups) - .join( - TableName.GroupProjectMembership, - `${TableName.GroupProjectMembership}.groupId`, - `${TableName.UserGroupMembership}.groupId` - ) - .join( + const docs = await db + .replicaNode()(TableName.Users) + .where(`${TableName.Users}.id`, userId) + .leftJoin(TableName.UserGroupMembership, `${TableName.UserGroupMembership}.userId`, `${TableName.Users}.id`) + .leftJoin(TableName.GroupProjectMembership, (queryBuilder) => { + void queryBuilder + .on(`${TableName.GroupProjectMembership}.projectId`, db.raw("?", [projectId])) + .andOn(`${TableName.GroupProjectMembership}.groupId`, `${TableName.UserGroupMembership}.groupId`); + }) + .leftJoin( TableName.GroupProjectMembershipRole, `${TableName.GroupProjectMembershipRole}.projectMembershipId`, `${TableName.GroupProjectMembership}.id` ) - - .leftJoin( - TableName.ProjectRoles, + .leftJoin( + { groupCustomRoles: TableName.ProjectRoles }, `${TableName.GroupProjectMembershipRole}.customRoleId`, - `${TableName.ProjectRoles}.id` + `groupCustomRoles.id` ) - .join(TableName.Project, `${TableName.GroupProjectMembership}.projectId`, `${TableName.Project}.id`) - .join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`) - + .leftJoin(TableName.ProjectMembership, (queryBuilder) => { + void queryBuilder + .on(`${TableName.ProjectMembership}.projectId`, db.raw("?", [projectId])) + .andOn(`${TableName.ProjectMembership}.userId`, `${TableName.Users}.id`); + }) .leftJoin( - TableName.ProjectUserAdditionalPrivilege, - `${TableName.GroupProjectMembership}.projectId`, - `${TableName.Project}.id` - ) - .select(selectAllTableCols(TableName.GroupProjectMembershipRole)) - .select( - db.ref("id").withSchema(TableName.GroupProjectMembership).as("membershipId"), - db.ref("createdAt").withSchema(TableName.GroupProjectMembership).as("membershipCreatedAt"), - db.ref("updatedAt").withSchema(TableName.GroupProjectMembership).as("membershipUpdatedAt"), - db.ref("projectId").withSchema(TableName.GroupProjectMembership), - db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"), - db.ref("orgId").withSchema(TableName.Project), - db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"), - - db.ref("permissions").withSchema(TableName.ProjectRoles).as("permissions"), - // db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("apPermissions") - // Additional Privileges - db.ref("id").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApId"), - db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApPermissions"), - db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryMode"), - db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApIsTemporary"), - db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryRange"), - - db.ref("projectId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApProjectId"), - db.ref("userId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApUserId"), - - db - .ref("temporaryAccessStartTime") - .withSchema(TableName.ProjectUserAdditionalPrivilege) - .as("userApTemporaryAccessStartTime"), - db - .ref("temporaryAccessEndTime") - .withSchema(TableName.ProjectUserAdditionalPrivilege) - .as("userApTemporaryAccessEndTime") - ); - // .select(`${TableName.ProjectRoles}.permissions`); - - const docs = await db(TableName.ProjectMembership) - .join( TableName.ProjectUserMembershipRole, `${TableName.ProjectUserMembershipRole}.projectMembershipId`, `${TableName.ProjectMembership}.id` @@ -124,176 +160,229 @@ export const permissionDALFactory = (db: TDbClient) => { `${TableName.ProjectUserMembershipRole}.customRoleId`, `${TableName.ProjectRoles}.id` ) - .leftJoin( - TableName.ProjectUserAdditionalPrivilege, - `${TableName.ProjectUserAdditionalPrivilege}.projectId`, - `${TableName.ProjectMembership}.projectId` - ) - - .join(TableName.Project, `${TableName.ProjectMembership}.projectId`, `${TableName.Project}.id`) + .leftJoin(TableName.ProjectUserAdditionalPrivilege, (queryBuilder) => { + void queryBuilder + .on(`${TableName.ProjectUserAdditionalPrivilege}.projectId`, db.raw("?", [projectId])) + .andOn(`${TableName.ProjectUserAdditionalPrivilege}.userId`, `${TableName.Users}.id`); + }) + .join(TableName.Project, `${TableName.Project}.id`, db.raw("?", [projectId])) .join(TableName.Organization, `${TableName.Project}.orgId`, `${TableName.Organization}.id`) - .where(`${TableName.ProjectMembership}.userId`, userId) - .where(`${TableName.ProjectMembership}.projectId`, projectId) - .select(selectAllTableCols(TableName.ProjectUserMembershipRole)) .select( + db.ref("id").withSchema(TableName.Users).as("userId"), + // groups specific + db.ref("id").withSchema(TableName.GroupProjectMembership).as("groupMembershipId"), + db.ref("createdAt").withSchema(TableName.GroupProjectMembership).as("groupMembershipCreatedAt"), + db.ref("updatedAt").withSchema(TableName.GroupProjectMembership).as("groupMembershipUpdatedAt"), + db.ref("slug").withSchema("groupCustomRoles").as("userGroupProjectMembershipRoleCustomRoleSlug"), + db.ref("permissions").withSchema("groupCustomRoles").as("userGroupProjectMembershipRolePermission"), + db.ref("id").withSchema(TableName.GroupProjectMembershipRole).as("userGroupProjectMembershipRoleId"), + db.ref("role").withSchema(TableName.GroupProjectMembershipRole).as("userGroupProjectMembershipRole"), + db + .ref("customRoleId") + .withSchema(TableName.GroupProjectMembershipRole) + .as("userGroupProjectMembershipRoleCustomRoleId"), + db + .ref("isTemporary") + .withSchema(TableName.GroupProjectMembershipRole) + .as("userGroupProjectMembershipRoleIsTemporary"), + db + .ref("temporaryMode") + .withSchema(TableName.GroupProjectMembershipRole) + .as("userGroupProjectMembershipRoleTemporaryMode"), + db + .ref("temporaryRange") + .withSchema(TableName.GroupProjectMembershipRole) + .as("userGroupProjectMembershipRoleTemporaryRange"), + db + .ref("temporaryAccessStartTime") + .withSchema(TableName.GroupProjectMembershipRole) + .as("userGroupProjectMembershipRoleTemporaryAccessStartTime"), + db + .ref("temporaryAccessEndTime") + .withSchema(TableName.GroupProjectMembershipRole) + .as("userGroupProjectMembershipRoleTemporaryAccessEndTime"), + // user specific db.ref("id").withSchema(TableName.ProjectMembership).as("membershipId"), db.ref("createdAt").withSchema(TableName.ProjectMembership).as("membershipCreatedAt"), db.ref("updatedAt").withSchema(TableName.ProjectMembership).as("membershipUpdatedAt"), - db.ref("projectId").withSchema(TableName.ProjectMembership), - db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"), - db.ref("orgId").withSchema(TableName.Project), - db.ref("slug").withSchema(TableName.ProjectRoles).as("customRoleSlug"), - db.ref("permissions").withSchema(TableName.ProjectRoles), - db.ref("id").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApId"), - db.ref("permissions").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApPermissions"), - db.ref("temporaryMode").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryMode"), - db.ref("isTemporary").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApIsTemporary"), - db.ref("temporaryRange").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApTemporaryRange"), - - db.ref("projectId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApProjectId"), - db.ref("userId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userApUserId"), - + db.ref("slug").withSchema(TableName.ProjectRoles).as("userProjectMembershipRoleCustomRoleSlug"), + db.ref("permissions").withSchema(TableName.ProjectRoles).as("userProjectCustomRolePermission"), + db.ref("id").withSchema(TableName.ProjectUserMembershipRole).as("userProjectMembershipRoleId"), + db.ref("role").withSchema(TableName.ProjectUserMembershipRole).as("userProjectMembershipRole"), + db + .ref("temporaryMode") + .withSchema(TableName.ProjectUserMembershipRole) + .as("userProjectMembershipRoleTemporaryMode"), + db + .ref("isTemporary") + .withSchema(TableName.ProjectUserMembershipRole) + .as("userProjectMembershipRoleIsTemporary"), + db + .ref("temporaryRange") + .withSchema(TableName.ProjectUserMembershipRole) + .as("userProjectMembershipRoleTemporaryRange"), + db + .ref("temporaryAccessStartTime") + .withSchema(TableName.ProjectUserMembershipRole) + .as("userProjectMembershipRoleTemporaryAccessStartTime"), + db + .ref("temporaryAccessEndTime") + .withSchema(TableName.ProjectUserMembershipRole) + .as("userProjectMembershipRoleTemporaryAccessEndTime"), + db.ref("id").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userAdditionalPrivilegesId"), + db + .ref("permissions") + .withSchema(TableName.ProjectUserAdditionalPrivilege) + .as("userAdditionalPrivilegesPermissions"), + db + .ref("temporaryMode") + .withSchema(TableName.ProjectUserAdditionalPrivilege) + .as("userAdditionalPrivilegesTemporaryMode"), + db + .ref("isTemporary") + .withSchema(TableName.ProjectUserAdditionalPrivilege) + .as("userAdditionalPrivilegesIsTemporary"), + db + .ref("temporaryRange") + .withSchema(TableName.ProjectUserAdditionalPrivilege) + .as("userAdditionalPrivilegesTemporaryRange"), + db.ref("userId").withSchema(TableName.ProjectUserAdditionalPrivilege).as("userAdditionalPrivilegesUserId"), db .ref("temporaryAccessStartTime") .withSchema(TableName.ProjectUserAdditionalPrivilege) - .as("userApTemporaryAccessStartTime"), + .as("userAdditionalPrivilegesTemporaryAccessStartTime"), db .ref("temporaryAccessEndTime") .withSchema(TableName.ProjectUserAdditionalPrivilege) - .as("userApTemporaryAccessEndTime") + .as("userAdditionalPrivilegesTemporaryAccessEndTime"), + // general + db.ref("authEnforced").withSchema(TableName.Organization).as("orgAuthEnforced"), + db.ref("orgId").withSchema(TableName.Project), + db.ref("id").withSchema(TableName.Project).as("projectId") ); - const permission = sqlNestRelationships({ + const [userPermission] = sqlNestRelationships({ data: docs, key: "projectId", - parentMapper: ({ orgId, orgAuthEnforced, membershipId, membershipCreatedAt, membershipUpdatedAt }) => ({ + parentMapper: ({ + orgId, + orgAuthEnforced, + membershipId, + groupMembershipId, + membershipCreatedAt, + groupMembershipCreatedAt, + groupMembershipUpdatedAt, + membershipUpdatedAt + }) => ({ orgId, orgAuthEnforced, userId, - id: membershipId, projectId, - createdAt: membershipCreatedAt, - updatedAt: membershipUpdatedAt + id: membershipId || groupMembershipId, + createdAt: membershipCreatedAt || groupMembershipCreatedAt, + updatedAt: membershipUpdatedAt || groupMembershipUpdatedAt }), childrenMapper: [ { - key: "id", - label: "roles" as const, - mapper: (data) => - ProjectUserMembershipRolesSchema.extend({ - permissions: z.unknown(), - customRoleSlug: z.string().optional().nullable() - }).parse(data) + key: "userGroupProjectMembershipRoleId", + label: "userGroupRoles" as const, + mapper: ({ + userGroupProjectMembershipRoleId, + userGroupProjectMembershipRole, + userGroupProjectMembershipRolePermission, + userGroupProjectMembershipRoleCustomRoleSlug, + userGroupProjectMembershipRoleIsTemporary, + userGroupProjectMembershipRoleTemporaryMode, + userGroupProjectMembershipRoleTemporaryAccessEndTime, + userGroupProjectMembershipRoleTemporaryAccessStartTime, + userGroupProjectMembershipRoleTemporaryRange + }) => ({ + id: userGroupProjectMembershipRoleId, + role: userGroupProjectMembershipRole, + customRoleSlug: userGroupProjectMembershipRoleCustomRoleSlug, + permissions: userGroupProjectMembershipRolePermission, + temporaryRange: userGroupProjectMembershipRoleTemporaryRange, + temporaryMode: userGroupProjectMembershipRoleTemporaryMode, + temporaryAccessStartTime: userGroupProjectMembershipRoleTemporaryAccessStartTime, + temporaryAccessEndTime: userGroupProjectMembershipRoleTemporaryAccessEndTime, + isTemporary: userGroupProjectMembershipRoleIsTemporary + }) }, { - key: "userApId", + key: "userProjectMembershipRoleId", + label: "projecMembershiptRoles" as const, + mapper: ({ + userProjectMembershipRoleId, + userProjectMembershipRole, + userProjectCustomRolePermission, + userProjectMembershipRoleIsTemporary, + userProjectMembershipRoleTemporaryMode, + userProjectMembershipRoleTemporaryRange, + userProjectMembershipRoleTemporaryAccessEndTime, + userProjectMembershipRoleTemporaryAccessStartTime, + userProjectMembershipRoleCustomRoleSlug + }) => ({ + id: userProjectMembershipRoleId, + role: userProjectMembershipRole, + customRoleSlug: userProjectMembershipRoleCustomRoleSlug, + permissions: userProjectCustomRolePermission, + temporaryRange: userProjectMembershipRoleTemporaryRange, + temporaryMode: userProjectMembershipRoleTemporaryMode, + temporaryAccessStartTime: userProjectMembershipRoleTemporaryAccessStartTime, + temporaryAccessEndTime: userProjectMembershipRoleTemporaryAccessEndTime, + isTemporary: userProjectMembershipRoleIsTemporary + }) + }, + { + key: "userAdditionalPrivilegesId", label: "additionalPrivileges" as const, mapper: ({ - userApId, - userApPermissions, - userApIsTemporary, - userApTemporaryMode, - userApTemporaryRange, - userApTemporaryAccessEndTime, - userApTemporaryAccessStartTime + userAdditionalPrivilegesId, + userAdditionalPrivilegesPermissions, + userAdditionalPrivilegesIsTemporary, + userAdditionalPrivilegesTemporaryMode, + userAdditionalPrivilegesTemporaryRange, + userAdditionalPrivilegesTemporaryAccessEndTime, + userAdditionalPrivilegesTemporaryAccessStartTime }) => ({ - id: userApId, - permissions: userApPermissions, - temporaryRange: userApTemporaryRange, - temporaryMode: userApTemporaryMode, - temporaryAccessEndTime: userApTemporaryAccessEndTime, - temporaryAccessStartTime: userApTemporaryAccessStartTime, - isTemporary: userApIsTemporary + id: userAdditionalPrivilegesId, + permissions: userAdditionalPrivilegesPermissions, + temporaryRange: userAdditionalPrivilegesTemporaryRange, + temporaryMode: userAdditionalPrivilegesTemporaryMode, + temporaryAccessStartTime: userAdditionalPrivilegesTemporaryAccessStartTime, + temporaryAccessEndTime: userAdditionalPrivilegesTemporaryAccessEndTime, + isTemporary: userAdditionalPrivilegesIsTemporary }) } ] }); - const groupPermission = groupDocs.length - ? sqlNestRelationships({ - data: groupDocs, - key: "projectId", - parentMapper: ({ orgId, orgAuthEnforced, membershipId, membershipCreatedAt, membershipUpdatedAt }) => ({ - orgId, - orgAuthEnforced, - userId, - id: membershipId, - projectId, - createdAt: membershipCreatedAt, - updatedAt: membershipUpdatedAt - }), - childrenMapper: [ - { - key: "id", - label: "roles" as const, - mapper: (data) => - ProjectUserMembershipRolesSchema.extend({ - permissions: z.unknown(), - customRoleSlug: z.string().optional().nullable() - }).parse(data) - }, - { - key: "userApId", - label: "additionalPrivileges" as const, - mapper: ({ - userApId, - userApProjectId, - userApUserId, - userApPermissions, - userApIsTemporary, - userApTemporaryMode, - userApTemporaryRange, - userApTemporaryAccessEndTime, - userApTemporaryAccessStartTime - }) => ({ - id: userApId, - userId: userApUserId, - projectId: userApProjectId, - permissions: userApPermissions, - temporaryRange: userApTemporaryRange, - temporaryMode: userApTemporaryMode, - temporaryAccessEndTime: userApTemporaryAccessEndTime, - temporaryAccessStartTime: userApTemporaryAccessStartTime, - isTemporary: userApIsTemporary - }) - } - ] - }) - : []; - - if (!permission?.[0] && !groupPermission[0]) return undefined; + if (!userPermission) return undefined; + if (!userPermission?.userGroupRoles?.[0] && !userPermission?.projecMembershiptRoles?.[0]) return undefined; // when introducting cron mode change it here const activeRoles = - permission?.[0]?.roles?.filter( + userPermission?.projecMembershiptRoles?.filter( ({ isTemporary, temporaryAccessEndTime }) => !isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime) ) ?? []; const activeGroupRoles = - groupPermission?.[0]?.roles?.filter( + userPermission?.userGroupRoles?.filter( ({ isTemporary, temporaryAccessEndTime }) => !isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime) ) ?? []; const activeAdditionalPrivileges = - permission?.[0]?.additionalPrivileges?.filter( + userPermission?.additionalPrivileges?.filter( ({ isTemporary, temporaryAccessEndTime }) => !isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime) ) ?? []; - const activeGroupAdditionalPrivileges = - groupPermission?.[0]?.additionalPrivileges?.filter( - ({ isTemporary, temporaryAccessEndTime, userId: apUserId, projectId: apProjectId }) => - apProjectId === projectId && - apUserId === userId && - (!isTemporary || (isTemporary && temporaryAccessEndTime && new Date() < temporaryAccessEndTime)) - ) ?? []; - return { - ...(permission[0] || groupPermission[0]), + ...userPermission, roles: [...activeRoles, ...activeGroupRoles], - additionalPrivileges: [...activeAdditionalPrivileges, ...activeGroupAdditionalPrivileges] + additionalPrivileges: activeAdditionalPrivileges }; } catch (error) { throw new DatabaseError({ error, name: "GetProjectPermission" }); diff --git a/backend/src/ee/services/permission/permission-service.ts b/backend/src/ee/services/permission/permission-service.ts index 72a35a326..d96b8a978 100644 --- a/backend/src/ee/services/permission/permission-service.ts +++ b/backend/src/ee/services/permission/permission-service.ts @@ -20,7 +20,7 @@ import { TServiceTokenDALFactory } from "@app/services/service-token/service-tok import { orgAdminPermissions, orgMemberPermissions, orgNoAccessPermissions, OrgPermissionSet } from "./org-permission"; import { TPermissionDALFactory } from "./permission-dal"; import { validateOrgSAML } from "./permission-fns"; -import { TBuildProjectPermissionDTO } from "./permission-types"; +import { TBuildOrgPermissionDTO, TBuildProjectPermissionDTO } from "./permission-types"; import { buildServiceTokenProjectPermission, projectAdminPermissions, @@ -47,26 +47,29 @@ export const permissionServiceFactory = ({ serviceTokenDAL, projectDAL }: TPermissionServiceFactoryDep) => { - const buildOrgPermission = (role: string, permission?: unknown) => { - switch (role) { - case OrgMembershipRole.Admin: - return orgAdminPermissions; - case OrgMembershipRole.Member: - return orgMemberPermissions; - case OrgMembershipRole.NoAccess: - return orgNoAccessPermissions; - case OrgMembershipRole.Custom: - return createMongoAbility( - unpackRules>>( - permission as PackRule>>[] - ), - { - conditionsMatcher - } - ); - default: - throw new BadRequestError({ name: "OrgRoleInvalid", message: "Org role not found" }); - } + const buildOrgPermission = (orgUserRoles: TBuildOrgPermissionDTO) => { + const rules = orgUserRoles + .map(({ role, permissions }) => { + switch (role) { + case OrgMembershipRole.Admin: + return orgAdminPermissions; + case OrgMembershipRole.Member: + return orgMemberPermissions; + case OrgMembershipRole.NoAccess: + return orgNoAccessPermissions; + case OrgMembershipRole.Custom: + return unpackRules>>( + permissions as PackRule>>[] + ); + default: + throw new BadRequestError({ name: "OrgRoleInvalid", message: "Org role not found" }); + } + }) + .reduce((curr, prev) => prev.concat(curr), []); + + return createMongoAbility(rules, { + conditionsMatcher + }); }; const buildProjectPermission = (projectUserRoles: TBuildProjectPermissionDTO) => { @@ -129,7 +132,13 @@ export const permissionServiceFactory = ({ validateOrgSAML(authMethod, membership.orgAuthEnforced); - return { permission: buildOrgPermission(membership.role, membership.permissions), membership }; + const finalPolicyRoles = [{ role: membership.role, permissions: membership.permissions }].concat( + membership?.groups?.map(({ role, customRolePermission }) => ({ + role, + permissions: customRolePermission + })) || [] + ); + return { permission: buildOrgPermission(finalPolicyRoles), membership }; }; const getIdentityOrgPermission = async (identityId: string, orgId: string) => { @@ -138,7 +147,10 @@ export const permissionServiceFactory = ({ if (membership.role === OrgMembershipRole.Custom && !membership.permissions) { throw new BadRequestError({ name: "Custom permission not found" }); } - return { permission: buildOrgPermission(membership.role, membership.permissions), membership }; + return { + permission: buildOrgPermission([{ role: membership.role, permissions: membership.permissions }]), + membership + }; }; const getOrgPermission = async ( @@ -169,11 +181,11 @@ export const permissionServiceFactory = ({ const orgRole = await orgRoleDAL.findOne({ slug: role, orgId }); if (!orgRole) throw new BadRequestError({ message: "Role not found" }); return { - permission: buildOrgPermission(OrgMembershipRole.Custom, orgRole.permissions), + permission: buildOrgPermission([{ role: OrgMembershipRole.Custom, permissions: orgRole.permissions }]), role: orgRole }; } - return { permission: buildOrgPermission(role, []) }; + return { permission: buildOrgPermission([{ role, permissions: [] }]) }; }; // user permission for a project in an organization diff --git a/backend/src/ee/services/permission/permission-types.ts b/backend/src/ee/services/permission/permission-types.ts index a35958ffd..620e7a61c 100644 --- a/backend/src/ee/services/permission/permission-types.ts +++ b/backend/src/ee/services/permission/permission-types.ts @@ -2,3 +2,8 @@ export type TBuildProjectPermissionDTO = { permissions?: unknown; role: string; }[]; + +export type TBuildOrgPermissionDTO = { + permissions?: unknown; + role: string; +}[]; diff --git a/backend/src/services/org/org-role-service.ts b/backend/src/services/org/org-role-service.ts index 26cfd67eb..d734d7818 100644 --- a/backend/src/services/org/org-role-service.ts +++ b/backend/src/services/org/org-role-service.ts @@ -60,7 +60,7 @@ export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRol name: "Admin", slug: "admin", description: "Complete administration access over the organization", - permissions: packRules(orgAdminPermissions.rules), + permissions: packRules(orgAdminPermissions), createdAt: new Date(), updatedAt: new Date() }; @@ -72,7 +72,7 @@ export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRol name: "Member", slug: "member", description: "Non-administrative role in an organization", - permissions: packRules(orgMemberPermissions.rules), + permissions: packRules(orgMemberPermissions), createdAt: new Date(), updatedAt: new Date() }; @@ -84,7 +84,7 @@ export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRol name: "No Access", slug: "no-access", description: "No access to any resources in the organization", - permissions: packRules(orgNoAccessPermissions.rules), + permissions: packRules(orgNoAccessPermissions), createdAt: new Date(), updatedAt: new Date() }; @@ -151,7 +151,7 @@ export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRol name: "Admin", slug: "admin", description: "Complete administration access over the organization", - permissions: packRules(orgAdminPermissions.rules), + permissions: packRules(orgAdminPermissions), createdAt: new Date(), updatedAt: new Date() }, @@ -161,7 +161,7 @@ export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRol name: "Member", slug: "member", description: "Non-administrative role in an organization", - permissions: packRules(orgMemberPermissions.rules), + permissions: packRules(orgMemberPermissions), createdAt: new Date(), updatedAt: new Date() }, @@ -171,7 +171,7 @@ export const orgRoleServiceFactory = ({ orgRoleDAL, permissionService }: TOrgRol name: "No Access", slug: "no-access", description: "No access to any resources in the organization", - permissions: packRules(orgNoAccessPermissions.rules), + permissions: packRules(orgNoAccessPermissions), createdAt: new Date(), updatedAt: new Date() },