summaryrefslogtreecommitdiff
path: root/src/app/services/permission/helpers.js
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2021-10-17 02:52:05 +0200
committerJules Laplace <julescarbon@gmail.com>2021-10-17 02:52:05 +0200
commit06ecdf2af182034496e2123852deee4a58de1043 (patch)
treec8d4eb9664dd368bee5a4bf73dd1e02015ecaf39 /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.js133
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;
+ }
+}