This module is compatible with
nestjs@^8.0.0.
First, install the module:
npm install @knodes/nest-casl
Then, declare a provider that generate a CASL Ability from the current request.
From src/ability-factory.service.ts
@Injectable()
export class AbilityFactory implements CaslAbilityFactory {
    // Here, \`request\` is the express or fastify request. You might get infos from it.
    public createFromRequest( _request: unknown ): PureAbility {
        const abilityBuilder = new AbilityBuilder( PureAbility );
        abilityBuilder.can( 'feed', 'cat' );
        abilityBuilder.can( 'hug', 'cat' );
        abilityBuilder.cannot( 'rename', 'cat' );
        return abilityBuilder.build();
    }
}
Import the CaslModule in your AppModule, and configure it to use your ability factory.
From src/app.module.ts
@Module( {
    imports: [
        CaslModule.withConfig( ( { abilityFactory: AbilityFactory } ) ),
        // ....
    ],
} )
export class AppModule {}
You can now start using policy decorators (Policy and PoliciesMask) in your controllers !
You can protect all methods of your controller using the Policy class decorator.
From src/cat-owner.controller.ts
@Controller( '/cat/owner' )
@Policy( { action: 'rename', subject: 'cat' } )
export class CatOwnerController {
    // Given the ability builder above, this method will always reject.
    @Post( 'rename' )
    public rename( @Body() _name: string ){
        // ...
    }
}
This decorator can also be used to protect individual methods.
From src/cat-care.controller.ts
@Controller( '/cat/care' )
export class CatCareController {
    // Okay, you can feed.
    @Get( 'feed' )
    @Policy( { action: 'feed', subject: 'cat' } )
    public feed(){
        // ...
    }
    // Well, I guess he won't bite.
    @Get( 'hug' )
    @Policy( { action: 'hug', subject: 'cat' } )
    public hug(){
        // ...
    }
}
If you want to group various policies in the same decorator at the controller level, use the PoliciesMask decorator.
From src/cat.controller.ts
@Controller( '/cat' )
@PoliciesMask( {
    feed: { action: 'feed', subject: 'cat' },
    hug: { action: 'hug', subject: 'cat' },
    rename: { action: 'rename', subject: 'cat' },
} )
export class CatController {
    @Get( 'feed' )
    public feed(){
        // ...
    }
    @Get( 'hug' )
    public hug(){
        // ...
    }
    @Post( 'rename' )
    public rename( @Body() _name: string ){
        // ...
    }
}
Check the tests !
From test/cats.e2e-spec.ts
describe( 'Basic usage', () => {
    let app: INestApplication;
    beforeAll( async () => {
        const moduleRef = await Test.createTestingModule( {
            imports: [ CaslModule.withConfig( { abilityFactory: AbilityFactory } ) ],
            controllers: [
                CatOwnerController, CatCareController, CatController,
            ],
        } ).compile();
        app = moduleRef.createNestApplication();
        await app.init();
    } );
    describe( 'CatOwnerController', () => {
        it( 'should not be able to rename a cat', () => request( app.getHttpServer() )
            .post( '/cat/owner/rename' )
            .expect( HttpStatus.FORBIDDEN ) );
    } );
    describe( 'CatCareController', () => {
        it( 'should be able to feed the cat', () => request( app.getHttpServer() )
            .get( '/cat/care/feed' )
            .expect( HttpStatus.OK ) );
        it( 'should be able to hug the cat', () => request( app.getHttpServer() )
            .get( '/cat/care/hug' )
            .expect( HttpStatus.OK ) );
    } );
    describe( 'PoliciesMask', () => {
        it( 'should not be able to rename a cat', () => request( app.getHttpServer() )
            .post( '/cat/rename' )
            .expect( HttpStatus.FORBIDDEN ) );
        it( 'should be able to feed the cat', () => request( app.getHttpServer() )
            .get( '/cat/feed' )
            .expect( HttpStatus.OK ) );
        it( 'should be able to hug the cat', () => request( app.getHttpServer() )
            .get( '/cat/hug' )
            .expect( HttpStatus.OK ) );
    } );
} );
Generated using TypeDoc