255 lines
8.8 KiB
TypeScript
255 lines
8.8 KiB
TypeScript
import { Request, Response } from "express";
|
|
import {
|
|
PermissionModule,
|
|
permissionModules,
|
|
PermissionObject,
|
|
PermissionSection,
|
|
PermissionString,
|
|
PermissionType,
|
|
permissionTypes,
|
|
} 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 ?? false;
|
|
if (permissions?.admin) 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<boolean>((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 ?? false;
|
|
if (permissions?.admin) 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<boolean>((prev, curr) => {
|
|
return prev || this.can(permissions, curr.requiredPermissions, curr.section);
|
|
}, false);
|
|
}
|
|
|
|
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<string>((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<string>((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<PermissionString>): PermissionObject {
|
|
if (permissions.includes("*")) {
|
|
return {
|
|
admin: true,
|
|
};
|
|
}
|
|
let output: PermissionObject = {};
|
|
let splitPermissions = permissions.map((e) => e.split(".")) 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<PermissionType>;
|
|
}
|
|
} 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<PermissionType>;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return output;
|
|
}
|
|
|
|
static convertToStringArray(permissions: PermissionObject): Array<PermissionString> {
|
|
if (permissions?.admin) {
|
|
return ["*"];
|
|
}
|
|
let output: Array<PermissionString> = [];
|
|
let sections = Object.keys(permissions) as Array<PermissionSection>;
|
|
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<PermissionModule>;
|
|
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;
|
|
}
|
|
|
|
static getWhatToAdd(before: Array<PermissionString>, after: Array<PermissionString>): Array<PermissionString> {
|
|
return after.filter((permission) => !before.includes(permission));
|
|
}
|
|
|
|
static getWhatToRemove(before: Array<PermissionString>, after: Array<PermissionString>): Array<PermissionString> {
|
|
return before.filter((permission) => !after.includes(permission));
|
|
}
|
|
}
|