import { Request, Response, NextFunction } from 'express'; import { Container } from 'typedi'; import { Ability } from '@casl/ability'; import LruCache from 'lru-cache'; import HasTenancyService from '@/services/Tenancy/TenancyService'; import { IRole, IRolePremission, ISystemUser } from '@/interfaces'; // store abilities of 1000 most active users export const ABILITIES_CACHE = new LruCache(1000); /** * Retrieve ability for the given role. * @param {} role * @returns */ function getAbilityForRole(role) { const rules = getAbilitiesRolesConds(role); return new Ability(rules); } /** * Retrieve abilities of the given role. * @param {IRole} role * @returns {} */ function getAbilitiesRolesConds(role: IRole) { switch (role.slug) { case 'admin': // predefined role. return getSuperAdminRules(); default: return getRulesFromRolePermissions(role.permissions || []); } } /** * Retrieve the super admin rules. * @returns {} */ function getSuperAdminRules() { return [{ action: 'manage', subject: 'all' }]; } /** * Retrieve CASL rules from role permissions. * @param {IRolePremission[]} permissions - * @returns {} */ function getRulesFromRolePermissions(permissions: IRolePremission[]) { return permissions .filter((permission: IRolePremission) => permission.value) .map((permission: IRolePremission) => { return { action: permission.ability, subject: permission.subject, }; }); } /** * Retrieve ability for user. * @param {ISystemUser} user * @param {number} tenantId * @returns {} */ async function getAbilityForUser(user: ISystemUser, tenantId: number) { const tenancy = Container.get(HasTenancyService); const { User } = tenancy.models(tenantId); const tenantUser = await User.query() .findOne('systemUserId', user.id) .withGraphFetched('role.permissions'); return getAbilityForRole(tenantUser.role); } /** * * @param {Request} request - * @param {Response} response - * @param {NextFunction} next - */ export default async (req: Request, res: Response, next: NextFunction) => { const { tenantId, user } = req; if (ABILITIES_CACHE.has(req.user.id)) { req.ability = ABILITIES_CACHE.get(req.user.id); } else { req.ability = await getAbilityForUser(req.user, tenantId); ABILITIES_CACHE.set(req.user.id, req.ability); } next(); };