diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2021-10-17 02:52:05 +0200 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2021-10-17 02:52:05 +0200 |
| commit | 06ecdf2af182034496e2123852deee4a58de1043 (patch) | |
| tree | c8d4eb9664dd368bee5a4bf73dd1e02015ecaf39 /src/app/services/permission/helpers.js | |
making a shoebox
Diffstat (limited to 'src/app/services/permission/helpers.js')
| -rw-r--r-- | src/app/services/permission/helpers.js | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/src/app/services/permission/helpers.js b/src/app/services/permission/helpers.js new file mode 100644 index 0000000..46a7871 --- /dev/null +++ b/src/app/services/permission/helpers.js @@ -0,0 +1,133 @@ +/** + * Permission helpers. + * @module app/services/permission/helpers + */ + +import { PERMISSIONS } from "app/constants"; + +/** + * When assigning a permission (any type), store the ID of the user granting the permission + */ +export function setGrantedByUser(request, response, next) { + request.body.granted_by_user_id = request.user.user_id; + next(); +} + +/** + * Return a middleware function that checks permissions for a specific API method + * @param {object} permissions Service API permission dictionary. See Service API documentation. + * @param {string} action which action to check, read/create/update/destroy + * @return {function} middleware function that checks permissions + */ +export function checkPermission(permissions, action) { + const routePermission = permissions + ? action + ? permissions[action] + : permissions + : null; + return async function checkPermissionsMiddleware(request, response, next) { + const { user } = request; + if (!routePermission) { + // API access must be explicitly enabled. + request.permission = PERMISSIONS.DENY; + next(new Error("PermissionsError")); + } else if (routePermission.roles?.includes(user.role)) { + request.permission = PERMISSIONS.ALLOW; + next(); + } else if (routePermission.owner) { + request.permission = PERMISSIONS.ALLOW_FOR_OWNER; + next(); + } else if (routePermission.check) { + request.permission = await routePermission.check({ + request, + user, + id: request.params?.id, + }); + if (request.permission === PERMISSIONS.DENY) { + next(new Error("PermissionsError")); + } else { + next(); + } + } else { + request.permission = PERMISSIONS.DENY; + next(new Error("PermissionsError")); + } + }; +} + +/** + * Return a middleware function that recursively checks permissions on the parent service of an API method. + * Also checks if the parent object exists to begin with. + * @param {Service} parentService the service to check. + * @return {function} the Express middleware + */ +export function checkParentPermission(parentService) { + return async function checkParentMiddleware(request, response, next) { + let permission; + request.parents = {}; + try { + permission = await checkServicePermission(request, parentService); + } catch (error) { + console.error(error); + permission = PERMISSIONS.DENY; + } + request.permission = permission; + if (permission === PERMISSIONS.DENY) { + next(new Error("PermissionsError")); + } else { + next(); + } + }; +} + +/** + * Helper function to check permissions on a service + * @param {Request} request the Express request we're checking + * @param {Service} service the Service we're checking + * @return {permission} the evaluated permissions + */ +async function checkServicePermission(request, service) { + const user = request.user; + const readPermissions = service.options.permissions.read; + if (!readPermissions) { + return PERMISSIONS.DENY; + } + + /** Check read permissions on any parent objects first. */ + if (service.parent) { + const parentPermission = await checkServicePermission( + request, + service.parent + ); + if (parentPermission === PERMISSIONS.DENY) { + return PERMISSIONS.DENY; + } + } + + /** Make sure this service's object exists before proceeding. */ + const idAttribute = service.Model.prototype.idAttribute; + const objectId = request.params[idAttribute]; + const object = await new service.Model({ [idAttribute]: objectId }).fetch(); + request.parents[service.resource] = object; + + if (!object) { + /** If there is no object, deny */ + return PERMISSIONS.DENY; + } else if (readPermissions.roles?.includes(user.role)) { + /** Pass role permission check */ + return PERMISSIONS.ALLOW; + } else if (readPermissions.owner && object.get("user_id") === user.user_id) { + /** Pass owner permission check */ + return PERMISSIONS.ALLOW; + } else if (readPermissions.check) { + /** Pass custom permission check */ + return await readPermissions.check({ + request, + user, + id: objectId, + }); + } else { + /** No checks passed - deny */ + return PERMISSIONS.DENY; + } +} |
