@nextnm/nestjs-next-guard
v2.2.5
Published
NestJS RBAC Guard that also checks models ownership
Downloads
34
Maintainers
Readme
Introduction
This is simple guard which can protect your routes by Role (RBAC) and also has the ability to check the ownership chain of the requested entity.
Example
As an example consider that you are a user belonging to an organization which has some books associated. Now, if you request a book by id this guard will check if the book that you are trying to fetch belongs to the organization that you are associated and so on. You only need to pass the models chain as well as the property chain.
Installation
npm i @nextnm/nestjs-next-guard
Usage
Caveats
- We only support applications using mongodb.
- You must be sure that in every request that the guard is used there is a user property in the request with an array of roles.
- It will only contemplates situations where you a have an id as request param (GET ("/:id")) or in the body (PUT updating object {_id:"...", "property1":....})
Interface Decorator
export interface ICheckOwnerShip {
requestParam: string; // name of param that has the id mentioned in caveat 3
modelChain: string[]; // the chain of ownership between models
propertyChain: string[]; // array of the properties that link the models
godRole?: string; // the role that will overcome the verification
}
Importing module
import * as mongoose from 'mongoose';
import { NextGuardModule } from '@nextnm/nestjs-next-guard';
...
@Module({
imports: [
DbModule,
NextGuardModule.forRoot(),
],
controllers: [],
providers: [],
exports: [NestjsNextGuardModule],
})
export class YOURModule {}
Importing module (Redis integration)
We support Redis cache to improve performance when we have multiple chain nodes to verify ownership
import * as mongoose from 'mongoose';
import { NextGuardModule } from '@nextnm/nestjs-next-guard';
...
@Module({
imports: [
DbModule,
NextGuardModule.forRoot(
{
redisConfiguration: {
url: 'redis://localhost:6379'
retry_strategy: () => 1000,
mongooseInstance: mongoose,
},
}
),
],
controllers: [],
providers: [],
exports: [NestjsNextGuardModule],
})
export class YOURModule {}
Using decorators
Be aware that both decorators (Roles and CheckOwnerShip) are optional so use them as you want.
1. Use Case
User:
{
_id:ObjectId
}
Site:
{
_id:ObjectId,
user:ObjectId
}
Page:
{
_id:ObjectId,
site:ObjectId
}
Description (A user belongs to an Site)
- The guard will take a look if you have role based permission to use this route
- The guard will look for an "Page" (modelChain[0]) by id equals to the request param;
- From the found Page it will try to grab the property 'site' (propertyChain[0]) and find a Site (modelChain[1]) by id equal to that property (propertyChain[0]).
- From the Site found it will check if the property "user" matches the id of the user making the request.
import { CheckOwnerShip, Roles } from '@nextnm/nestjs-next-guard';
...
@CheckOwnerShip({
requestParam: 'modelId',
propertyChain: ['site', 'user'], // The last property will be compared with the Id of the user making the request
modelChain: ['Page','Site'],
godRole: ExistingRoles.SYS_ADMIN, // If the user has this role not check will be done by the guard
})
@Roles(ExistingRoles.USER, ExistingRoles.ADMIN) // Provide the roles that you allow to execute this method,example: 'USER', 'ADMIN'
@UseGuards(NextGuard)
@Get(':modelId')
async findPageById(@Param('id') id: string) {
//...
}
2. Use case
User:
{
_id:ObjectId,
organization:ObjectId
}
Organization:
{
_id:ObjectId
}
Description (A user belongs to an organization)
- The guard will take a look if you have role based permission to use this route
- The guard will look for an Organization (modelChain[0]) by id equals to the request param;
- From the found Organization it will try to grab the property '_id' (propertyChain[0]) and find a User (modelChain[1]) by id equal to the that property (propertyChain[0]).
- Since there isn't any, it will try to find a User with a property "organization" (propertyChain[1]) equals to the organization "_id" property(propertyChain[0])
- From the User found it will check if the property "_id" matches the id of the user making the request.
import { CheckOwnerShip, Roles } from '@nextnm/nestjs-next-guard';
...
@CheckOwnerShip({
requestParam: 'id',
propertyChain: ['_id','organization','_id'], // The last property will be compared with the Id of the user making the request
modelChain: ['Organization','User'],
godRole: ExistingRoles.SYS_ADMIN, // If the user has this role not check will be done by the guard
})
@Roles(ExistingRoles.USER, ExistingRoles.ADMIN) // Provide the roles that you allow to execute this method,example: 'USER', 'ADMIN'
@UseGuards(AuthGuard('jwt'),NextGuard)
@Get('/:id')
findOrganizationById(@Param() params): Promise<ReadOrganizationDto> {
//...
}
Contributing
Contributions are welcome! See Contributing.
Next steps
- Improve documentation
- Add some tests using Jest and supertest
- Add full support do many to many relationships between models (it doesn't alow having array of ids as relationships)
- Build Policy Based Guard
Author
Nuno Carvalhão (nextnm/nextNC) Site
License
Licensed under the MIT License - see the LICENSE file for details.