From e17eb30aed10d9f869acd300eaa31d2177ed1972 Mon Sep 17 00:00:00 2001 From: Julian Krauser Date: Fri, 31 Jan 2025 11:07:58 +0100 Subject: [PATCH] change: allow read to related data from allowed modules --- src/helpers/permissionHelper.ts | 77 ++++++++++++++++++++--- src/routes/admin/index.ts | 104 ++++++++++++++++++++++++++++---- 2 files changed, 158 insertions(+), 23 deletions(-) diff --git a/src/helpers/permissionHelper.ts b/src/helpers/permissionHelper.ts index 990e97d..86b27e2 100644 --- a/src/helpers/permissionHelper.ts +++ b/src/helpers/permissionHelper.ts @@ -32,6 +32,19 @@ export default class PermissionHelper { 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", @@ -48,6 +61,18 @@ export default class PermissionHelper { 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 passCheckMiddleware( requiredPermissions: PermissionType | "admin", section: PermissionSection, @@ -60,11 +85,29 @@ export default class PermissionHelper { if (isOwner || this.can(permissions, requiredPermissions, section, module)) { next(); } else { - throw new ForbiddenRequestException( - `missing permission for ${section}.${module}.${ - Array.isArray(requiredPermissions) ? requiredPermissions.join("|") : requiredPermissions - }` - ); + 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}`); } }; } @@ -80,11 +123,25 @@ export default class PermissionHelper { if (isOwner || this.canSection(permissions, requiredPermissions, section)) { next(); } else { - throw new ForbiddenRequestException( - `missing permission for ${section}.${module}.${ - Array.isArray(requiredPermissions) ? requiredPermissions.join("|") : requiredPermissions - }` - ); + 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}`); } }; } diff --git a/src/routes/admin/index.ts b/src/routes/admin/index.ts index 016db3e..c291c15 100644 --- a/src/routes/admin/index.ts +++ b/src/routes/admin/index.ts @@ -27,42 +27,120 @@ import preventWebapiAccess from "../../middleware/preventWebApiAccess"; var router = express.Router({ mergeParams: true }); -router.use("/award", PermissionHelper.passCheckMiddleware("read", "settings", "award"), award); +router.use( + "/award", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "award" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), + award +); router.use( "/communicationtype", - PermissionHelper.passCheckMiddleware("read", "settings", "communication_type"), + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "communication_type" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), communicationType ); router.use( "/executiveposition", - PermissionHelper.passCheckMiddleware("read", "settings", "executive_position"), + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "executive_position" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), executivePosition ); router.use( "/membershipstatus", - PermissionHelper.passCheckMiddleware("read", "settings", "membership_status"), + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "membership_status" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), membershipStatus ); -router.use("/qualification", PermissionHelper.passCheckMiddleware("read", "settings", "qualification"), qualification); -router.use("/salutation", PermissionHelper.passCheckMiddleware("read", "settings", "salutation"), salutation); -router.use("/calendartype", PermissionHelper.passCheckMiddleware("read", "settings", "calendar_type"), calendarType); +router.use( + "/qualification", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "qualification" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), + qualification +); +router.use( + "/salutation", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "salutation" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), + salutation +); +router.use( + "/calendartype", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "calendar_type" }, + { requiredPermissions: "read", section: "club", module: "calendar" }, + ]), + calendarType +); router.use("/querystore", PermissionHelper.passCheckMiddleware("read", "settings", "query_store"), queryStore); router.use("/template", PermissionHelper.passCheckMiddleware("read", "settings", "template"), template); -router.use("/templateusage", PermissionHelper.passCheckMiddleware("read", "settings", "template_usage"), templateUsage); +router.use( + "/templateusage", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "template_usage" }, + { requiredPermissions: "read", section: "settings", module: "template" }, + ]), + templateUsage +); router.use( "/newsletterconfig", - PermissionHelper.passCheckMiddleware("read", "settings", "newsletter_config"), + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "settings", module: "newsletter_config" }, + { requiredPermissions: "read", section: "settings", module: "communication_type" }, + ]), newsletterConfig ); router.use("/member", PermissionHelper.passCheckMiddleware("read", "club", "member"), member); -router.use("/protocol", PermissionHelper.passCheckMiddleware("read", "club", "protocol"), protocol); +router.use( + "/protocol", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "club", module: "protocol" }, + { requiredPermissions: "read", section: "club", module: "member" }, + ]), + protocol +); router.use("/calendar", PermissionHelper.passCheckMiddleware("read", "club", "calendar"), calendar); -router.use("/querybuilder", PermissionHelper.passCheckMiddleware("read", "club", "query"), queryBuilder); -router.use("/newsletter", PermissionHelper.passCheckMiddleware("read", "club", "newsletter"), newsletter); +router.use( + "/querybuilder", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "club", module: "query" }, + { requiredPermissions: "read", section: "settings", module: "query_store" }, + ]), + queryBuilder +); +router.use( + "/newsletter", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "club", module: "newsletter" }, + { requiredPermissions: "read", section: "club", module: "member" }, + { requiredPermissions: "read", section: "club", module: "calendar" }, + { requiredPermissions: "read", section: "club", module: "query" }, + { requiredPermissions: "read", section: "settings", module: "query_store" }, + ]), + newsletter +); router.use("/role", PermissionHelper.passCheckMiddleware("read", "user", "role"), role); -router.use("/user", PermissionHelper.passCheckMiddleware("read", "user", "user"), user); +router.use( + "/user", + PermissionHelper.passCheckSomeMiddleware([ + { requiredPermissions: "read", section: "user", module: "user" }, + { requiredPermissions: "read", section: "user", module: "role" }, + ]), + user +); router.use("/invite", PermissionHelper.passCheckMiddleware("read", "user", "user"), invite); router.use("/webapi", preventWebapiAccess, PermissionHelper.passCheckMiddleware("read", "user", "webapi"), api);