Nestjs ACL

Why the nestjs-acl module?
The nestjs-acl module purpose is to check several access rules at the same time, depending on the current context.
It allows to store, manage and check scenarios of access rules and to check them at any time.
In our case, we needed to finely filter access to different types of resources for different types of users.
Because some scenarios are repetitive, it was important to store these rules without repeating tones of code lines.
nestjs-acl does not replace the nest-access-control module, they are fully compatible and complementary.
nest-access-control is a module to protect methods using decorators. It also allows you to implement your own access logic using the Nestjs Guards, which covers most cases.
Install
Using npm
npm install nestjs-acl --save
Using yarn
yarn add nestjs-acl
Prepare the roles
Create a file that exports an AccessControl instance.
You can refer to the npm package accesscontrol to learn how to manipulate roles
Note: The roles instance could be manipulate at runtime
import { AccessControl } from 'nestjs-acl';
export const roles = new AccessControl({
ADMIN: {
doSomething: {
'create:any': ['*']
},
doSomethingElse: {
'create:any': ['*']
}
},
USER: {
doSomething: {
'create:own': ['*']
}
}
});
Create and register AclRulesCreators
- The AclService will search for matching AclRulesCreator and execute them passing the context.
- If a AclRulesCreator is not found, the check passes (only if option rejectIfNoRule === false)
- If a AclRulesCreator is found, The creator is executed. if option rejectIfNoRule === true and no rule was returned by the creator, the check will fail.
- Returned Rules from AclRulesCreator are tested
- The first rule that is granted will validate the test.
- The first rule that throw an Error will stop the chain and invalidate the test.
- Rules returning false are ignored
- Rules returning Error are concatenated, returned as a single Error if no granted rule was found
export const userCanDoSomething = (opts) => {
const {
context: { user },
data,
sourceData
} = opts;
return [
{
req: opts.rolesBuilder.can(opts.context.user.roles).createOwn('doSomething'),
check: () => opts.data.user === context.user
},
{
req: opts.rolesBuilder.can(opts.context.user.roles).createAny('doSomething')
}
];
};
export const userCanDoSomethingElse = (opts) => {
return [
{
req: opts.rolesBuilder.can(opts.context.user.roles).createAny('doSomethingElse')
}
];
};
Import and register the AclModule module
The AclModule is global and need to be registered once, imported modules can inject the AclService without the need to register the module again.
import { AclModule, AclService } from 'nestjs-acl';
import { roles } from './roles';
import { userCanDoSomething, userCanDoSomethingElse } from './rules';
import { MyProvider } from './provider';
@Module({
imports: [AclModule.register(roles), MyProvider]
})
export class MyModule {
construtor(protected acl: AclService) {
this.acl
.registerRules('userCanDoSomething', userCanDoSomething)
.registerRules('userCanDoSomethingElse', userCanDoSomethingElse);
}
}
Using the AclService
In your modules, providers or controllers you can now inject the AclService instance and use it to check acl rules.
import { Injectable } from '@nestjs/common';
import { AclService } from 'nestjs-acl';
@Injectable()
export class MyProvider {
constructor(protected acl: AclService) {}
async doSomething(user: AclRoles<string>) {
const data = { foo: 'bar', user };
const { rule, data } = await this.acl.check({
id: 'userCanDoSomething',
data,
context: {
user: {
roles: user.roles
}
}
});
}
async doSomethingElse(user: AclRoles<string>) {
const { rule, data } = await this.acl.check({
id: 'userCanDoSomethingElse',
context: {
user: {
roles: user.roles
}
}
});
}
}