import { Request, Response } from "express"; import { PermissionModule, permissionModules, PermissionObject, PermissionSection, permissionSections, PermissionString, PermissionType, permissionTypes, sectionsAndModules, } from "../type/permissionTypes"; import ForbiddenRequestException from "../exceptions/forbiddenRequestException"; export default class PermissionHelper { static can( permissions: PermissionObject, type: PermissionType | "admin", section: PermissionSection, module?: PermissionModule ) { if (type == "admin") return permissions?.admin ?? permissions?.adminByOwner ?? false; if (permissions?.admin || permissions?.adminByOwner) return true; if ( (!module && permissions[section] != undefined && (permissions[section]?.all == "*" || permissions[section]?.all?.includes(type))) || permissions[section]?.all == "*" || permissions[section]?.all?.includes(type) ) return true; if (module && (permissions[section]?.[module] == "*" || permissions[section]?.[module]?.includes(type))) return true; return false; } static canSome( permissions: PermissionObject, checks: Array<{ requiredPermissions: PermissionType | "admin"; section: PermissionSection; module?: PermissionModule; }> ) { checks.reduce((prev, curr) => { return prev || this.can(permissions, curr.requiredPermissions, curr.section, curr.module); }, false); } static canSection( permissions: PermissionObject, type: PermissionType | "admin", section: PermissionSection ): boolean { if (type == "admin") return permissions?.admin ?? permissions?.adminByOwner ?? false; if (permissions?.admin || permissions?.adminByOwner) return true; if ( permissions[section]?.all == "*" || permissions[section]?.all?.includes(type) || permissions[section] != undefined ) return true; return false; } static canSomeSection( permissions: PermissionObject, checks: Array<{ requiredPermissions: PermissionType | "admin"; section: PermissionSection; }> ): boolean { return checks.reduce((prev, curr) => { return prev || this.can(permissions, curr.requiredPermissions, curr.section); }, false); } static canValue(permissions: PermissionObject, key: string, emptyIfAdmin: boolean = false): string { if (emptyIfAdmin && (permissions.admin || permissions.adminByOwner)) return ""; return permissions?.additional?.[key] ?? ""; } static passCheckMiddleware( requiredPermissions: PermissionType | "admin", section: PermissionSection, module?: PermissionModule ): (req: Request, res: Response, next: Function) => void { return (req: Request, res: Response, next: Function) => { const permissions = req.permissions; const isOwner = req.isOwner; if (isOwner || this.can(permissions, requiredPermissions, section, module)) { next(); } else { throw new ForbiddenRequestException(`missing permission for ${section}.${module}.${requiredPermissions}`); } }; } static passCheckSomeMiddleware( checks: Array<{ requiredPermissions: PermissionType | "admin"; section: PermissionSection; module?: PermissionModule; }> ): (req: Request, res: Response, next: Function) => void { return (req: Request, res: Response, next: Function) => { const permissions = req.permissions; const isOwner = req.isOwner; if (isOwner || this.canSome(permissions, checks)) { next(); } else { let permissionsToPass = checks.reduce((prev, curr) => { return prev + (prev != " or " ? "" : "") + `${curr.section}.${curr.module}.${curr.requiredPermissions}`; }, ""); throw new ForbiddenRequestException(`missing permission for ${permissionsToPass}`); } }; } static sectionPassCheckMiddleware( requiredPermissions: PermissionType | "admin", section: PermissionSection ): (req: Request, res: Response, next: Function) => void { return (req: Request, res: Response, next: Function) => { const permissions = req.permissions; const isOwner = req.isOwner; if (isOwner || this.canSection(permissions, requiredPermissions, section)) { next(); } else { throw new ForbiddenRequestException(`missing permission for ${section}.${module}.${requiredPermissions}`); } }; } static sectionPassCheckSomeMiddleware( checks: Array<{ requiredPermissions: PermissionType | "admin"; section: PermissionSection }> ): (req: Request, res: Response, next: Function) => void { return (req: Request, res: Response, next: Function) => { const permissions = req.permissions; const isOwner = req.isOwner; if (isOwner || this.canSomeSection(permissions, checks)) { next(); } else { let permissionsToPass = checks.reduce((prev, curr) => { return prev + (prev != " or " ? "" : "") + `${curr.section}.${curr.requiredPermissions}`; }, ""); throw new ForbiddenRequestException(`missing permission for ${permissionsToPass}`); } }; } static isAdminMiddleware(): (req: Request, res: Response, next: Function) => void { return (req: Request, res: Response, next: Function) => { const permissions = req.permissions; const isOwner = req.isOwner; if (isOwner || permissions.admin) { next(); } else { throw new ForbiddenRequestException(`missing admin permission`); } }; } static convertToObject(permissions: Array, isOwner: boolean = false): PermissionObject { let isAdmin = permissions.includes("*"); let additional: { [key: string]: string } = {}; let additionalPermissions = permissions.map((e) => e.split(".")).filter((e) => e[0] == "additional") as Array< ["additional", string, string] >; for (let split of additionalPermissions) { let module = sectionsAndModules.additional.find((a) => a.key == split[1]); if (!isAdmin || (isAdmin && !module.emptyIfAdmin)) additional[split[1]] = split[2]; } if (isAdmin) { return { admin: true, adminByOwner: isOwner, ...(Object.keys(additional).length > 0 && { additional }), }; } let output: PermissionObject = {}; let splitPermissions = permissions.map((e) => e.split(".")).filter((e) => e[0] != "additional") as Array< [PermissionSection, PermissionModule | PermissionType | "*", PermissionType | "*"] >; for (let split of splitPermissions) { if (!output[split[0]]) { output[split[0]] = {}; } if (split[1] == "*" || output[split[0]].all == "*") { output[split[0]] = { all: "*" }; } else if (permissionTypes.includes(split[1] as PermissionType)) { if (!output[split[0]].all || !Array.isArray(output[split[0]].all)) { output[split[0]].all = []; } const permissionIndex = permissionTypes.indexOf(split[1] as PermissionType); const appliedPermissions = permissionTypes.slice(0, permissionIndex + 1); if (output[split[0]].all != "*") { output[split[0]].all = [ ...new Set([...output[split[0]].all, ...appliedPermissions]), ] as Array; } } else { if (split[2] == "*" || output[split[0]][split[1] as PermissionModule] == "*") { output[split[0]][split[1] as PermissionModule] = "*"; } else { if ( !output[split[0]][split[1] as PermissionModule] || !Array.isArray(output[split[0]][split[1] as PermissionModule]) ) { output[split[0]][split[1] as PermissionModule] = []; } const permissionIndex = permissionTypes.indexOf(split[2] as PermissionType); const appliedPermissions = permissionTypes.slice(0, permissionIndex + 1); output[split[0]][split[1] as PermissionModule] = appliedPermissions; if (output[split[0]][split[1] as PermissionModule] != "*") { output[split[0]][split[1] as PermissionModule] = [ ...new Set([...output[split[0]][split[1] as PermissionModule], ...appliedPermissions]), ] as Array; } } } } return { adminByOwner: isOwner, ...output, ...(Object.keys(additional).length > 0 && { additional }), }; } static convertToStringArray(permissions: PermissionObject): Array { let isAdmin = permissions?.admin; let additional: Array = []; let additionalPermissions = Object.entries(permissions?.additional ?? {}); for (let add of additionalPermissions) { additional.push(`additional.${add[0]}.${add[1]}`); } if (isAdmin) { return ["*", ...additional]; } let output: Array = []; let sections = Object.keys(permissions).filter((m: PermissionSection) => permissionSections.includes(m) ) as Array; for (let section of sections) { if (permissions[section].all) { let types = permissions[section].all; if (types == "*" || types.length == permissionTypes.length) { output.push(`${section}.*`); } else { for (let type of types) { output.push(`${section}.${type}`); } } } let modules = Object.keys(permissions[section]).filter((m: PermissionModule) => permissionModules.includes(m) ) as Array; for (let module of modules) { let types = permissions[section][module]; if (types == "*" || types.length == permissionTypes.length) { output.push(`${section}.${module}.*`); } else { for (let type of types) { output.push(`${section}.${module}.${type}`); } } } } return [...output, ...additional]; } static getWhatToAdd(before: Array, after: Array): Array { return after.filter((permission) => !before.includes(permission)); } static getWhatToRemove(before: Array, after: Array): Array { return before.filter((permission) => !after.includes(permission)); } }