Files
bigcapital/packages/server/src/api/middleware/AuthorizationMiddleware.ts
2023-02-03 11:57:50 +02:00

93 lines
2.3 KiB
TypeScript

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();
};