import { Inject , ForbiddenException , Injectable , BadRequestException , UnprocessableEntityException } from "@nestjs/common" ; import * as _ from "lodash" ; import { Op } from "sequelize" ; import { BaseService } from "../../../../base.service" ; import { JwtUserData } from "../../auth.interfaces" ; import { PermissionDto } from "../dto" ; import { LoggerDiToken , LoggerService } from "../../../logger" ; import { RolePermissionDto , RoleDto , AclDto , AclDashboardDto } from "../../dto" ; import { DbDiToken , Permission , RolePermission , PermissionName , RoleName , Role } from "../../../db" ; @ Injectable ( ) export class PermissionService extends BaseService { public constructor ( @ Inject ( DbDiToken . PERMISSION_REPOSITORY ) private readonly permissionRepository , @ Inject ( DbDiToken . ROLE_REPOSITORY ) private readonly roleRepository , @ Inject ( DbDiToken . ROLE_PERMISSION_REPOSITORY ) private readonly rolePermissionRepository , @ Inject ( LoggerDiToken . LOGGER ) private readonly logger ) { super ( ) ; } public async isAllowed ( user , permissions , throwOnError = false ) { if ( user . role . name === RoleName . SUPER_ADMIN ) { return true ; } const ids = _ . map ( await this . permissionRepository . findAll ( { attributes : [ "id" ] , where : { name : { [ Op . in ] : permissions } } } ) , p => p . get ( "id" ) ) ; const results = await Promise . mapSeries ( ids , permissionId => { return this . exists < RolePermission > ( this . rolePermissionRepository , { roleId : user . role . id , permissionId , isEnabled : true } ) ; } ) ; const allPermissionsGranted = _ . every ( results , x => ! ! x ) ; if ( ! allPermissionsGranted ) { const nonGrantedPermissionsIndexes = _ . map ( results , ( res , index ) => ! ! res ? - 1 : index ) ; const nonGrantedPermissions = _ . map ( nonGrantedPermissionsIndexes , i => permissions [ i ] ) ; if ( throwOnError ) { this . logger . error ( `Unauthorized access attempt: user #${ user . id } has no permissions granted: ${ nonGrantedPermissions }` ) ; throw new ForbiddenException ( ) ; } } return allPermissionsGranted ; } public async updateRolePermission ( data ) { const [ updatedAmount ] = await this . rolePermissionRepository . update ( data , { where : _ . pick ( data , [ "roleId" , "permissionId" ] ) , fields : [ "isEnabled" ] , } ) ; if ( updatedAmount !== 1 ) { throw new BadRequestException ( `Invalid payload` ) ; } } public async getACLDashboard ( ) { const permissionNames = _ . values ( PermissionName ) ; const roles = _ . map ( await this . roleRepository . findAll ( { where : { name : { [ Op . ne ] : RoleName . SUPER_ADMIN } } } ) , role => role . toJSON ( ) ) ; const permissions = _ . map ( await this . permissionRepository . findAll ( ) , p => p . toJSON ( ) ) ; const rolePermissionsDtos = _ . map ( await this . rolePermissionRepository . findAll ( { include : [ { model : Role , as : "role" } , { model : Permission , as : "permission" } ] } ) , rp => rp . toJSON ( ) ) ; return { data : _ . map ( permissionNames , ( permissionName ) => { const permission = _ . find ( permissions , { name : permissionName } ) ; if ( ! permission ) { throw new UnprocessableEntityException ( `Permission mismatch: permission ${ permissionName } has no record in database. Aborting` ) ; } const acl = _ . map ( roles , ( role ) => { const isEnabled = permission && ! ! _ . find ( rolePermissionsDtos , { roleId : role . id , permissionId : permission . id , isEnabled : true } ) ; return { isEnabled , role : role . name , roleId : role . id , } ; } ) ; return { name : permissionName , permissionId : permission && permission . id , acl , } ; } ) , roles , } ; } public async getACL ( user ) { if ( user . role . name === RoleName . SUPER_ADMIN ) { const allpermission = _ . values ( PermissionName ) ; return _ . zipObject ( allpermission , _ . times ( allpermission . length , x => true ) ) ; } const userRolePermissions = _ . map ( await this . rolePermissionRepository . findAll ( { where : { roleId : user . role . id } , include : [ { model : Permission , as : "permission" } ] } ) , role => role . toJSON ( ) ) ; return _ . zipObject ( _ . map ( userRolePermissions , p => p . permission . name ) , _ . map ( userRolePermissions , p => p . isEnabled ) ) ; } }