GithubHelp home page GithubHelp logo

nestjsx / nest-access-control Goto Github PK

View Code? Open in Web Editor NEW
1.1K 18.0 79.0 971 KB

Role and Attribute based Access Control for Nestjs πŸ”

License: MIT License

JavaScript 41.28% TypeScript 58.72%
nestjs typescript addon helper access-control permissions

nest-access-control's Introduction

Nestjsx Logo

A set of opinionated NestJS extensions and modules

Travis Coverage Status

Packages

  • @nestjsx/common - A set of NestJs decorators, interfaces and configuration for application modules bootstraping.

Tests

npm run test

nest-access-control's People

Contributors

0xflotus avatar bashleigh avatar coolsamk7 avatar creaux avatar dantman avatar dependabot[bot] avatar floross avatar fulminant avatar ghjeon avatar majidkn avatar morb0 avatar overbit avatar rjlopezdev avatar ruslanguns avatar shekohex avatar tupe12334 avatar vegerot avatar yuval-hazaz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nest-access-control's Issues

ForRootAsync is not working

ForRootAsync is not working. I have written it in app.module.ts.

   AccessControlModule.forRootAsync({
       inject: [RolePermissionsService],
      useFactory: async (roleService: RolePermissionsService): Promise<RolesBuilder> => {
        return new RolesBuilder(await roleService.getAll());
      },
    }), 

roleService.getAll() return result as below array.

 [
    { role: 'admin', resource: 'video', action: 'create:any', attributes: '*, !views' },
    { role: 'admin', resource: 'video', action: 'read:any', attributes: '*' },
    { role: 'admin', resource: 'video', action: 'update:any', attributes: '*, !views' },
    { role: 'admin', resource: 'video', action: 'delete:any', attributes: '*' },

    { role: 'user', resource: 'video', action: 'create:own', attributes: '*, !rating, !views' },
    { role: 'user', resource: 'video', action: 'read:any', attributes: '*' },
    { role: 'user', resource: 'video', action: 'update:own', attributes: '*, !rating, !views' },
    { role: 'user', resource: 'video', action: 'delete:own', attributes: '*' }
];

i am getting this error.
image

UseRoles resource on BaseController that is extended by the resource controller

Hi all, I have a BaseController and a BaseService that are extended by all the other controllers and services of my resoursces. I'm trying to implement the UseRoles decorator on the BaseController without success.

I have tried two methods:

  1. Passing the resource in constructor but I can't access the this because "Object is possibly 'undefined'".
  2. With a variable outside the BaseController class, but I get AccessControlError: Invalid resource: "" . In that case it seems that the UseRoles don't get the updated value passed in the constructor
//  base.controller.ts
const resource = "";
export class BaseController<T> {
  protected readonly baseService: BaseService<T>;
  protected readonly resource: string;
  protected constructor(baseService: BaseService<T>, collection: string) {
    this.baseService = baseService;
    this.resource = collection; // 1st method
    resource = collection; // 2nd method
  }

  @UseGuards(ACGuard)
  @UseRoles({ resource: this.resource, action: 'read', possession: 'own' }) // 1st method ==> error this Object is possibly 'undefined'
  @UseRoles({ resource: resource, action: 'read', possession: 'own' }) // 2nd method error AccessControlError: Invalid resource: ""
  @Get()
  findAll(): Promise<T[]> {
    return this.baseService.findAll();
  }
}
// cat.controllers.ts
export class CatController extends BaseController<Cat> {
  constructor(private readonly catService: CatService) {
    super(catService, 'cat');
  }
}
// dog.controllers.ts
export class DogController extends BaseController<Dog> {
  constructor(private readonly dogService: DogService) {
    super(dogService, 'dog');
  }
}

Thanks in advance

Question [ExceptionsHandler] Invalid role(s): []

Tengo este problema mi rol es un Entity Rol y no un array de strings, por esta razΓ³n obtengo este error
AccessControlError: Invalid role(s): []

Existe soporte para esto, debido a que mi lΓ³gica es de uno a muchos entre un Rol y un User en la base de datos.
}
"nombre": "admin",
"nombreUsuario": "admin",
"email": "[email protected]",
"rol": {
"id": 1,
"appRol": "administrador"
}
}

AccessControlError when roles array is empty

When roles array in user object is empty (no roles assigned to user) then it throws an error instead of giving Forbidden Resource exception

AccessControlError: Invalid role(s): []

Is behavior meant to be like this?

unnecessary peer deps

There is a lot of unnecessary peer dependencies, we dont need them because its not in use

Have two roles ADMIN , USER . ADMIN can manipulate all the resources but the User can readAny and update only its own resources .How can I achieve this.

import { RolesBuilder } from "nest-access-control";
import { Role } from "@prisma/client";

export const roles: RolesBuilder = new RolesBuilder();
roles
  .grant(Role.USER).createOwn('users').deleteOwn('users').readAny('users').updateOwn('users')
  .grant(Role.ADMIN).extend(Role.USER).updateAny('users').deleteAny('users'); 
  @UseRoles({ possession: 'any', action: 'update', resource: "users" })
  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto, @UserRoles() userRoles: any) {
    console.log(userRoles);
    return this.userService.update(id, updateUserDto);
  }

Persist Roles in database

Hi community,
I've just started working with NestJS and I needed to implement access-control in front-end(Angular) and back-end. I found this PR very helpful but I'm wondering if we could store the roles in database instead of a file, so they would be dynamic.
Do you guys have any Ideas ?

Resource Ownership checking & Getting owned resources.

I had a quick look at the library but I can't see any docs around these 2 features:

  • checking if resourceType X with resourceId Y is owned by User A.
  • Get me a list of owned resourceType X instances for User A.

We want to implement those two features and I was wondering if you have any thoughts or plans for those.

InjectRolesBuilder error

When trying use InjectRolesBuilder get error: TypeError: metatype is not a constructor

constructor(@InjectRolesBuilder() private readonly roleBuilder: RolesBuilder) {}
TypeError: metatype is not a constructor
    at Injector.instantiateClass (...\node_modules\@nestjs\core\injector\injector.js:214:19)
    at callback (...\node_modules\@nestjs\core\injector\injector.js:67:41)
    at process._tickCallback (internal/process/next_tick.js:68:7)

grant access for every resource ?

Hi i'm trying to grant all CRUD permissions to my admin user for every resource I thought using *

roles
.grant(AppRoles.ADMIN)
  .createAny("*")
  .readAny("*")
  .updateAny("*")
  .deleteAny("*")


would work , but it throws an error, "cannot use reserved name", How can I implement this so I don't write down all the resources ?

thanks,

What if you want to change users roles/permissions

Hello,

What if you're running a node cluster and a user changes roles/permissions. Since it's all being captured on the server instance, each node cluster would have to be updated as well. Is there a way to use redis? Or how would this be handled?

Thanks

New graphql dependency is a braking change

Hi! The new change introduced here #63
Breaks exisitng projects, I'd suggest maybe using inline require so that it's only imported if needed? My project doesn't use graphql and I don't feel like adding it just to satisfy my pipeline. Thanks!

improper version expression in peer dependency

Hi.

Recent version of package.json has critical problem to describe the version ranges of @nestjs/graphql package.

When I tried to add recent version of this package with my project using @nestjs/[email protected],

➜  *** git:(release/v0.2) βœ— npm install nest-access-control --save
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: [email protected]
npm WARN Found: @nestjs/[email protected]
npm WARN node_modules/@nestjs/graphql
npm WARN   peer @nestjs/graphql@"^10.0.0" from @nestjs/[email protected]
npm WARN   node_modules/@nestjs/apollo
npm WARN     @nestjs/apollo@"^10.0.0" from the root project
npm WARN   1 more (the root project)
npm WARN 
npm WARN Could not resolve dependency:
npm WARN peer @nestjs/graphql@"^10.0.0" from @nestjs/[email protected]
npm WARN node_modules/@nestjs/apollo
npm WARN   @nestjs/apollo@"^10.0.0" from the root project
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: [email protected]
npm WARN Found: @nestjs/[email protected]
npm WARN node_modules/@nestjs/graphql
npm WARN   peer @nestjs/graphql@"^10.0.0" from @nestjs/[email protected]
npm WARN   node_modules/@nestjs/apollo
npm WARN     @nestjs/apollo@"^10.0.0" from the root project
npm WARN   1 more (the root project)
npm WARN 
npm WARN Could not resolve dependency:
npm WARN peerOptional @nestjs/graphql@"^7.0.0 | ^8.0.0 | ^9.0.0 | ^10.0.0" from [email protected]
npm WARN node_modules/nest-access-control
npm WARN   nest-access-control@"*" from the root project
npm WARN ERESOLVE overriding peer dependency
npm WARN While resolving: [email protected]
npm WARN Found: @nestjs/graphql@undefined
npm WARN node_modules/@nestjs/graphql
npm WARN   @nestjs/graphql@"^10.0.0" from the root project
npm WARN 
npm WARN Could not resolve dependency:
npm WARN peerOptional @nestjs/graphql@"^7.0.0 | ^8.0.0 | ^9.0.0 | ^10.0.0" from [email protected]
npm WARN node_modules/nest-access-control
npm WARN   nest-access-control@"*" from the root project

According to syntax definition of dependencies section in package.json from official document (https://docs.npmjs.com/cli/v8/configuring-npm/package-json#dependencies),

the version of dependent package which allow multiple versions should divided by || operator, not |.

Currently, packages.json describe the version ranges of @nestjs/graphql using | operator.

  "peerDependencies": {
    "@nestjs/graphql": "^7.0.0 | ^8.0.0 | ^9.0.0 | ^10.0.0"
  },
  "peerDependenciesMeta": {
    "@nestjs/graphql": {
      "optional": true
    }
  }

forRootAsync not work when usefactory return promise

version 2.0.1

AccessControlModule.forRootAsync({
            inject: [RoleService],
            useFactory: async (service: RoleService) => {
                return new RolesBuilder(await service.factory());
            },
        }),

and d.ts is

static forRootAsync(options: {
        inject?: Provider[];
        useFactory: (...args: any) => RolesBuilder;
    }): DynamicModule;

not allow promise<T> to return

the error is

 Type 'Promise<RolesBuilder>' is missing the following properties from type 'RolesBuilder': _grants, _isLocked, isLocked, getGrants

Migrate to support Nest 7

With Nest 7 just released it would be helpful to have a new release that support it.

One thing that I noticed broke is the @UserRoles() decorator.
It can't find the user object on the request anymore since request is not passed into the create param decorator factory. createParamDecorator now expects a ExecutionContext instead.

From the migration docs

// Before
import { createParamDecorator } from '@nestjs/common';

export const User = createParamDecorator((data, req) => {
  return req.user;
});

// After
import { createParamDecorator, ExecutionContext } from '@nestjs/common';

export const User = createParamDecorator(
  (data: unknown, ctx: ExecutionContext) => {
    const request = ctx.switchToHttp().getRequest();
    return request.user;
  },
);

Feature: consumible endpoint for permissions

πŸš€ Feature request

Description

In many cases, it is necessary and useful to know the roles and permissions of an API on the front-side.
I have not found any use cases that serve this purpose.
It would be useful to have an endpoint that serves the list of roles and their associated permissions.

Describe the solution you'd like

  • Configuration:
@Module({
  imports: [
    AccessControlModule.forRoles({
      roles: roles,
      serveEndpoint: '/permissions'
    })
  ],
  controllers: [AppController],
  providers: [AppService]
})
export class AppModule {}
  • Consumible

REQUEST: /permissions

RESPONSE:

[
  {
    "rolename": "ADMIN",
    "permissions": [
      {
        "name": "video",
        "can_create": {
          "own": "true",
          "any": "false"
        },
        "can_read": {
          "own": "true",
          "any": "false"
        },
        "can_remove": {
          "own": "true",
          "any": "false"
        },
        "can_update": {
          "own": "true",
          "any": "false",
          "fields": [
            "someField", "otherField"
          ]
        },
        "can_remove": {
          "own": "true",
          "any": "false"
        },
        "can_update": {
          "own": "true",
          "any": "false",
          "fields": [
            "someField", "otherField"
          ]
        }
      },
      {
        "name": "other",
        "can_create": {
          "..."
        },
        "can_read": {
          "..."
        },
        "can_remove": {
         "..."
        },
        "can_update": {
          "..."
        }
      },
      "..."
    ]
  }
]

Thanks for your time & effort πŸ˜„

How to connect to my user table

I build a user, role, permission table and implement some method what I need.
I found this library , I think I can only create user table only, but i want to connect this library and I don't know how to do.
Or...I misunderstand this library use for?

Pass custom prop for roles

Hello,

Thanks for the lib, I appreciate it.
Is it possible to pass a value that should be checked on the user object other than roles for the list of permissions?

Say I have a user that it authenticated and the is like this:

{
  id: 1,
  email: "",
permissions: // Array of permissions that should be used instead of roles
}

Instead of using the forRootAsync method, can we pass this value to the guard or something?

inject dependencies forRootAsync

I am using the forRootAsync method to load roles dynamically from the database however I am getting an error. I am a newbie to Nestjs, so please let me know if I am doing something wrong

Error: Nest can't resolve dependencies of the __roles_builder__ (?). Please make sure that the argument at index [0] is available in the AccessControlModule context.

I am trying to to import the AccessControlModule in the root module and I have a RolesService to inject as a dependency in the forRootAsync method. The code is below

AccessControlModule.forRootAsync({
      inject: [{
        provide: ROLE_REPO,
        useClass: Role
      }],
      useFactory: (service: repo) => {
        return new RolesBuilder([
          ...adminGrants // testing local file grants
        ]);
      }
    })

[Question] Simple RBAC system with route guard by user's role

Is it possible to define a simple RBAC system ('USER', 'EDITOR', 'ADMIN') and guarding the routes by user's role without having to define all permissions? Something like:

# app.roles.ts
import { RolesBuilder } from 'nest-access-control';
export enum AppRoles {
  USER = 'USER',
  EDITOR = 'EDITOR',
  ADMIN = 'ADMIN',
}
export const roles: RolesBuilder = new RolesBuilder();
roles
  .grant(AppRoles.USER)
  .grant(AppRoles.EDITOR)
  .extend(AppRoles.USER)  
  .grant(AppRoles.ADMIN)
  .extend(AppRoles.EDITOR)


# app.controller.ts
@Get('test')
@UseGuards(JwtAuthGuard, ACGuard)
@UseRoles({role: 'ADMIN'})  //  <=== Only admin access
test() {
   ...
}

I see that in the original interface can be defined the role property that sounds good for my case, but it seems that your role.interface.ts accept only resource, action and possession. Or I'm missing something?

Thanks in advance

Why use a separated action and possession?

First off, thanks for your work!

I wanted to ask why in onury/accesscontrol the action also includes the possession, e.g. create:any, read:own and so on, while this package has separated action and possession? It seems that it only increases the boilerplate that we have to decorate each endpoint with.

How fresh grants list when use forRootAsync read grants list from database and changed grants list in database

AccessControlModule

AccessControlModule.forRootAsync({
      imports: [SharedModule],
      inject: [PrismaService],
      useFactory: async (prismaService: PrismaService): Promise<RolesBuilder> => {
        let roles = await prismaService.role.findMany({
          where: {}, include: {
            grants: { include: { permisssion: true } }
          }
        })
        let result = roles.map(role => {
          return role.grants.map(grant => {
            let { resource, action, attributes } = grant.permisssion
            return { role: role.name, resource, action, attributes }
          })
        })
        if (result) {
          let grants = []
          result.forEach((grant) => grants = grants.concat(grant))
          return new RolesBuilder(grants)
        }
        return new RolesBuilder([])
      }
    })

When I changed grant list in database. How do I reload grant list ?

Set new grant list run time

Is there any options to set new grant list run time like when a new permission is set after bootstrapping nestjs?

[SUPPORT] Cannot read property 'roles' of undefined

I have set up all the roles properly and have used decorators in my controller, but it throws an error. What am I doing wrong here, I followed the documentation.

Roles

appRoles
  .grant('superadmin')
  .createAny(['clients', 'users'])
  .readAny(['clients', 'users'])
  .updateAny(['clients', 'users'])
  .deleteAny(['clients', 'users'])

Controller

@UseGuards(JwtAuthGuard, ACGuard)
@UseRoles({
	resource:  'users',
	action:  'read',
	possession:  'any',
})
@Get()
allUsers(@UserRoles() userRoles: any) {
  console.log(userRoles);
  return this.userService.findAll();
}

req.user gives

{
  id: 'uuid',
  firstName: 'John',
  lastName: 'John',
  email: '[email protected]',
  createdAt: 2020-04-23T12:15:41.494Z,
  updatedAt: 2020-04-23T12:15:41.494Z,
  roles: [ 'superadmin' ]
}

The error

[Nest] 29258   - 04/28/2020, 12:11:35 PM   [ExceptionsHandler] Cannot read property 'roles' of undefined +14527ms
TypeError: Cannot read property 'roles' of undefined
    at /redacted/node_modules/nest-access-control/decorators/user-roles.decorators.js:11:45
    at /redacted/node_modules/@nestjs/core/helpers/context-utils.js:32:28
    at resolveParamValue (/redacted/node_modules/@nestjs/core/router/router-execution-context.js:139:31)
    at Array.map (<anonymous>)
    at pipesFn (/redacted/node_modules/@nestjs/core/router/router-execution-context.js:144:45)
    at /redacted/node_modules/@nestjs/core/router/router-execution-context.js:36:36
    at InterceptorsConsumer.intercept (/redacted/node_modules/@nestjs/core/interceptors/interceptors-consumer.js:10:20)
    at /redacted/node_modules/@nestjs/core/router/router-execution-context.js:45:60
    at processTicksAndRejections (internal/process/task_queues.js:93:5)
    at async /redacted/node_modules/@nestjs/core/router/router-proxy.js:8:17

Support for Graphql

Hi!

Actually nestjs-access-control is supporting Graphql?
I have an error when I do a mutation:
"Type Error: Cannot read property "user" of undefined
at ACGuard.getUser
at ACGuard.gerRoles
at ACGuard.canActivate

I changed the code from library directly
the code into ACGuard class in file access-control.guard.js

  async getUser(context) {
      // const request = context.switchToHttp().getRequest();
      // return request.user;

      const request = GqlExecutionContext.create(context).getContext().req;
      return request.user;
  }

To the best of my knowledge the context.switchToHttp().getRequest() is only for REST. In Graphql should be used
GqlExecutionContext.create(context).getContext().req;

After changing the file, the error did not appear again and my code run as I expected.

Please tell if this is correct or if it exists other way to do this.

Thanks!

Add example for possession 'own'

In Access Control docs it says

Note that own requires you to also check for the actual possession

and here it shows how to do it, but I am not sure how to check actual possession with nest-access-control.

So, please add more examples, at least for the roles and actions you have mentioned in app.roles.ts, I would really appreciate it.

Improvement: Role not found

By default if you have an unknown role the server throws an error:

[Nest] 12948   - 2020-05-15 17:03:24   [ExceptionsHandler] Role not found: "ASDFASDF" +325454ms
AccessControlError: Role not found: "ASDFASDF"

and returns an object like this:

{
    "statusCode": 500,
    "message": "Internal server error"
}

In my opinion ACL should just ignore this unknown role. What do you think?

GraphQL support

Hi Nestjs Community!

As I'm still discovering the Nestjs framework and its awesome community, I've found this very nice little package :)

It is very convenient and it allowed me to implement quite easily some ABAC control on the API I'm working on.

Nevertheless, the only Access Control guard it offers (ACGuard) is not compatible with the context request used within GraphQL resolvers.

So I was wondering if you were thinking about implementing either an additional guard or even making the actual one compatible with both classical and graphql requests.

Actually, I think the implementation would be quite straightforward and I would gladly participate in.

Have a nice day!

Using filter

How can I use filter function in the controller to return the filtered data?
To filter data with all user roles rules we need acess to permission object.

How can a user edit elements with common characteristics?

A user has the director role, he belongs to a company, he can also create and edit work groups that belong to that company. How can I get him to edit all the groups belonging to his own company, but not the groups of other companies?

Unclear what the video - title attribute does in the example

The sample contains this code

roles
  .grant(AppRoles.USER_CREATE_ANY_VIDEO) // define new or modify existing role. also takes an array.
    .createOwn('video') // equivalent to .createOwn('video', ['*'])
    .deleteOwn('video')
    .readAny('video')
  .grant(AppRoles.ADMIN_UPDATE_OWN_VIDEO) // switch to another role without breaking the chain
    .extend(AppRoles.USER_CREATE_ANY_VIDEO) // inherit role capabilities. also takes an array
    .updateAny('video', ['title']) // explicitly defined attributes
    .deleteAny('video');

What exactly does the title attribute do in the "updateAny" line?
Where is it checked?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.