You're Invited:Meet the Socket Team at BlackHat and DEF CON in Las Vegas, Aug 4-6.RSVP
Socket
Book a DemoInstallSign in
Socket

@s1seven/nestjs-tools-access-control

Package Overview
Dependencies
Maintainers
5
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@s1seven/nestjs-tools-access-control

S1Seven ACL engine

0.1.15
latest
Source
npmnpm
Version published
Weekly downloads
34
-37.04%
Maintainers
5
Weekly downloads
 
Created
Source

Nest ACL

npm

Fork of nest-access-control, instead making use of role-acl. It offers a great flexibility on how to build condition to grant or deny access to specific resources and related actions.

Installation

npm i @s1seven/microservices-access-control

Example

Constants

In ./src/data-transfer-object/index.ts

export const Actions = {
  ReadOne: 'readOne',
  CreateOne: 'CreateOne',
};
export const Resources = {
  FILE: 'file',
};
export const Roles = {
  USER: 'user',
};

Custom Conditions

In ./src/access-control/data/conditions.ts

import { IDictionary, IFunctionCondition } from '@s1seven/microservices-access-control';

export const conditions: IDictionary<IFunctionCondition> = {
  isWorkspaceMember(context: { user: any; workspaceId: number | string }, _args: any): boolean {
    const { user, workspaceId } = context;
    return workspaceId === 'yeah-right';
  },

  async isWorkspaceOwner(context: { user: any; workspaceId: number | string }, _args: any): Promise<boolean> {
    const { user, workspaceId } = context;
    return Promise.resolve(workspaceId === 'hell-yeah');
  },
};

Grants

In ./src/access-control/data/grants.ts

import { Actions, Resources, Roles } from '../../data-transfer-object';

const customConditions = { isWorkspaceMember: 'custom:isWorkspaceMember', isWorkspaceOwner: 'custom:isWorkspaceOwner' };

export const grants = {
  [Roles.USER]: {
    grants: [
      { resource: Resources.FILE, action: [Actions.CreateOne], attributes: ['*'] },
      {
        resource: Resources.FILE,
        action: Actions.ReadOne,
        attributes: ['*'],
        condition: {
          Fn: customConditions.isWorkspaceMember,
          args: { resource: 'workspace' },
        },
      },
    ],
  },
};

ACLGuard

In ./src/access-control/access-control.guard.ts

import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';
import { getAction, getFeature } from '@nestjsx/crud';
import {
  getAction as GetAclAction,
  getResource,
  InjectRulesBuilder,
  RulesBuilder,
} from '@s1seven/microservices-access-control';
import { Actions, Resources, Roles } from '../data-transfer-object';

@Injectable()
export class ACLGuard<User extends any = any> implements CanActivate {
  constructor(@InjectRulesBuilder() private readonly ruleBuilder: RulesBuilder) {}

  protected async getUser(context: ExecutionContext): Promise<any> {
    const request = context.switchToHttp().getRequest();
    const { user } = request;
    return { user };
  }

  protected async getUserRoles(context: ExecutionContext): Promise<{ user: any; roles: Roles[] }> {
    const { user } = await this.getUser(context);
    if (!user) {
      throw new UnauthorizedException();
    }
    let { roles } = user;
    return { user, roles };
  }

  isSuperAdmin(roles: Roles[]) {
    return roles.some((role) => role === Roles.SUPER_ADMIN);
  }

  protected getPermissionContextByResource = {
    [Resources.FILE]: this.applyWorkspaceContext,
  };

  protected applyWorkspaceContext(_action: Actions, resource: Resources, context: ExecutionContext) {
    const request = context.switchToHttp().getRequest();
    const workspaceId = request.headers['current-workspace'];
    return { workspaceId };
  }

  protected getPermissionContext(_action: Actions, resource: Resources, context: ExecutionContext) {
    return this.getPermissionContextByResource[resource]
      ? this.getPermissionContextByResource[resource](_action, resource, context)
      : {};
  }

  protected getActionAndResource(context: ExecutionContext): { action: Actions; resource: Resources } {
    const handler = context.getHandler();
    const controller = context.getClass();
    const resource = getFeature(controller) || getResource(controller);
    const action = getAction(handler) || GetAclAction(handler);
    return { action, resource };
  }

  public async canActivate(context: ExecutionContext): Promise<boolean> {
    const { user, roles: userRoles } = await this.getUserRoles(context);
    if (this.isSuperAdmin(userRoles)) {
      return true;
    }
    const { action, resource } = this.getActionAndResource(context);
    if (!resource || !action) {
      return true;
    }
    const permissionContext = { user, ...this.getPermissionContext(action, resource, context) };
    const permission = await this.ruleBuilder.can(userRoles).context(permissionContext).execute(action).on(resource);
    return permission.granted;
  }
}

Controller

In ./src/file/file.controller.ts

import { Actions, Resources, Roles } from '../data-transfer-object';
import { ACLGuard } from '../access-control/access-control.guard';
import { Action, Resource } from '@s1seven/microservices-access-control/decorators';

@Resource(Resources.FILE)
@UseGuards(AuthGuard('jwt'), ACLGuard)
export class FileController {
  @Action(Actions.ReadOne)
  @Get('preview/:id')
  preview(@Req() request) {
    return 'done';
  }
}

Application module

In ./src/app.module.ts

import { AccessControlModule, RulesBuilder } from '@s1seven/microservices-access-control';
import { Module } from '@nestjs/common';
import { ACLGuard } from './access-control/access-control.guard';
import { conditions, grants } from './access-control/data';
import { FileController } from './file/file.controller';

@Module({
  imports: [
    AccessControlModule.forRootAsync({
      useFactory: (): RulesBuilder => new RulesBuilder(grants, conditions),
    }),
  ],
  providers: [ACLGuard],
  controllers: [FileController],
})
export class AppModule {}

FAQs

Package last updated on 04 Sep 2023

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts