nest-authz

repository·master·Indexed 41 days ago

https://github.com/apache/casbin-nest-authz

A NestJS authorization module based on Node-Casbin that enables RBAC and ABAC implementation using decorators or services. Version 2.17.0 provides the AuthZModule for configuration, AuthZGuard for route protection, and the @UsePermissions decorator for defining access control requirements including possession checks and dynamic resource extraction from context.

Tokens
10.9K
Snippets
28
Records
44
Agent score
47%

What's inside nest-authz

  1. Initialize AuthZModule in AppModule

    master

    To use nest-authz, you must register the AuthZModule in your AppModule. You can configure the enforcer using two methods: providing a model and policy directly, or using an enforcerProvider (the preferred method).

    Configuration Options

    OptionTypeDescription
    modelstringPath to the Casbin model file.
    policystringPath to the Casbin policy file or an adapter.
    enablePossessionbooleanEnables the use of possession (AuthPossession.(ANY|OWN|OWN_ANY)) for actions.
    userFromContextfunctionREQUIRED. Accepts ExecutionContext and returns the user (string, object, or null). The AuthZGuard uses this to determine permissions.
    resourceFromContextfunctionOPTIONAL. Accepts ExecutionContext and PermissionData and returns an AuthResource. Used as the default for all Permissions where resourceFromContext: true is set.
    enforcerProviderobjectOptional enforcer provider (preferred method).
    importsArrayOptional list of imported modules required by this module.
    import { TypeOrmModule } from '@nestjs/typeorm';
    
    @Module({
      imports: [
        AuthZModule.register({
          model: 'model.conf',
          policy: TypeORMAdapter.newAdapter({
            type: 'mysql',
            host: 'localhost',
            port: 3306,
            username: 'root',
            password: 'password',
            database: 'nestdb'
          }),
          userFromContext: (ctx) => {
            const request = ctx.switchToHttp().getRequest();
            return request.user && request.user.username;
          },
          resourceFromContext: (ctx, perm) => {
            const request = ctx.switchToHttp().getRequest();
            return { type: perm.resource, id: request.id };
          }
        }),
      ],
      controllers: [AppController],
      providers: [AppService]
    })
  2. Configure the enforcer using enforcerProvider (Preferred)

    master

    The preferred way to configure the enforcer is by providing an enforcerProvider. This allows you to use NestJS dependency injection and factory patterns to initialize the Casbin enforcer, for example, by using a ConfigService to load settings.

    import { TypeOrmModule } from '@nestjs/typeorm';
    import { ConfigModule, ConfigService } from './config.module';
    import { AUTHZ_ENFORCER } from 'nest-authz';
    
    @Module({
      imports: [
        ConfigModule,
        AuthZModule.register({
          imports: [ConfigModule],
          enforcerProvider: {
            provide: AUTHZ_ENFORCER,
            useFactory: async (configSrv: ConfigService) => {
              const config = await configSrv.getAuthConfig();
              return casbin.newEnforcer(config.model, config.policy);
            },
            inject: [ConfigService],
          },
          userFromContext: (ctx) => {
            const request = ctx.switchToHttp().getRequest();
            return request.user && {
              username: request.user.username, 
              isAdmin: request.user.isAdmin 
            };
          }
        }),
      ],
      controllers: [AppController],
      providers: [AppService]
    });
  3. Define AuthAction and AuthPossession for permission checks

    master

    When defining or checking permissions, you can use the AuthAction and AuthPossession enums to specify the nature of the operation and the ownership requirement.

    AuthAction combines a verb (like create, update, read, delete) with a scope (any or own).

    AuthPossession defines the ownership relationship:

    • any: The user can perform the action on any resource.
    • own: The user can only perform the action on resources they own.
    • own|any: A combined scope.
    // Example of using AuthAction values
    const action = AuthAction.READ_OWN;
    const possession = AuthPossession.OWN;
  4. How AuthZManagementService works

    master

    The AuthZManagementService acts as a high-level NestJS wrapper around the Casbin Enforcer. It is designed to provide administrative access to the underlying Casbin engine, allowing developers to perform enforcement checks, manage policies (add/remove/update), and inspect the current rule set directly from within a NestJS application.

    Warning: This service is marked as @deprecated. It is intended to be replaced by AuthZService in a future release. For new implementations, consider looking for the replacement service.

  5. Use ABAC with object-based resources in @UsePermissions

    master

    To support Attribute-Based Access Control (ABAC), you can pass an object to the resource property instead of a simple string. This allows you to define more granular resource types and operations.

    @UsePermissions({
      action: AuthActionVerb.READ,
      resource: {type: 'User', operation: 'single'},
      possession: AuthPossession.ANY
    })
    async userById(id: string) {}
    
    @UsePermissions({
      action: AuthActionVerb.READ,
      resource: {type: 'User', operation: 'batch'},
      possession: AuthPossession.ANY
    })
    async findAllUsers() {}
  6. Check permissions using @UsePermissions decorator

    master

    The @UsePermissions decorator is the most common way to enforce authorization on controller methods. You must also include AuthZGuard in your @UseGuards() list.

    Decorator Options

    OptionTypeDescription
    actionAuthActionVerbAn enum value representing the action (e.g., READ).
    resourcestring or objectThe resource string or an object (for ABAC) the request is accessing.
    possessionAuthPossessionEnum value (ANY, OWN, OWN_ANY). Defaults to AuthPossession.ANY.
    isOwnfunctionFunction accepting ExecutionContext that returns boolean. Determines if the user owns the resource. Defaults to a function returning false.
    resourceFromContextboolean or functionIf true, uses the default resourceFromContext from module initialization. If a function, it overrides the default. If false/undefined, the resource option is used as-is.
      @Get('users')
      @UseGuards(AuthZGuard)
      @UsePermissions({
        action: AuthActionVerb.READ,
        resource: 'USER',
        possession: AuthPossession.ANY
      })
      async findAllUsers() {}
  7. Check permissions manually using AuthZService

    master

    If you need to perform authorization checks inside a method's body rather than at the route level, inject AuthZService. This service provides a wrapper around the Casbin API to check permissions for a specific user.

    import { Controller, Get, UnauthorizedException, Req } from '@nestjs/common';
    import {
      AuthZGuard,
      AuthZService,
      AuthActionVerb,
      AuthPossession,
      UsePermissions
    } from 'nest-authz';
    
    @Controller()
    export class AppController {
      constructor(private readonly authzSrv: AuthZService) {}
    
      @Get('users')
      async findAllUsers(@Req() request: Request) {
        let username = request.user['username'];
        
        // Check permission manually
        const isPermitted = await this.authzSrv.hasPermissionForUser(username, "user", "read:any");
        
        if (!isPermitted) {
          throw new UnauthorizedException(
            'You are not authorized to read users list'
          );
        }
        // ...
      }
    }
  8. Perform dynamic resource checks with resourceFromContext

    master

    When you need to authorize access to a specific resource instance (e.g., a specific user ID from the request), use the resourceFromContext option within the @UsePermissions decorator to extract the resource ID dynamically.

    @UsePermissions({
      action: AuthActionVerb.READ,
      resource: 'User',
      resourceFromContext: (ctx, perm) => {
        const req = ctx.switchToHttp().getRequest();
        return { type: perm.resource, id: req.id };
      }
    })
    async userById(id: string) {}
  9. Configure AuthZModule via register() options

    master

    The AuthZModule.register(options) method accepts an AuthZModuleOptions object.

    Key configuration requirements:

    • Enforcer Setup: You must provide either an enforcerProvider (a NestJS provider for the Casbin enforcer) OR both model and policy.
    • Policy: Can be a string (representing a file path) or a Promise that resolves to a policy.
    • enablePossession: A boolean option that defaults to true if not explicitly provided.
    • imports: An array of additional modules to import during registration.

    Available exported services and guards provided by the module:

    • AuthZGuard
    • AuthZService
    • AuthZRBACService
    • AuthZManagementService
    • AUTHZ_ENFORCER (Injection token for the Casbin enforcer)
    • AUTHZ_MODULE_OPTIONS (Injection token for the module options)
  10. Configure AuthZModuleOptions

    master

    When initializing the AuthZModule, you must provide an options object of type AuthZModuleOptions. This configuration defines how users and resources are extracted from the request context, how the Casbin model and policy are loaded, and how permissions are checked.

    Required Options

    • userFromContext: A function that takes the NestJS ExecutionContext and returns an AuthUser. This is used to identify the subject performing the action.

    Optional Options

    • model: A string representing the Casbin model definition.
    • policy: A string or a Promise that resolves to the policy data.
    • enablePossession: A boolean to enable/disable possession-based authorization logic.
    • resourceFromContext: An optional function to extract the resource being accessed from the ExecutionContext.
    • batchApproval: An optional BatchApproval configuration for handling multiple permission checks.
    • enforcerProvider: An optional NestJS Provider used to supply a custom Casbin enforcer.
    • imports: An optional list of NestJS modules required by this module.
    const options: AuthZModuleOptions = {
      model: '...', // Casbin model string
      policy: '...', // Casbin policy string or promise
      userFromContext: (context: ExecutionContext) => {
        const request = context.switchToHttp().getRequest();
        return request.user; // Returns AuthUser
      },
      resourceFromContext: (context: ExecutionContext) => {
        // Logic to extract resource from context
      },
      enablePossession: true,
    };
  11. Manage Casbin policies and grouping rules

    master

    These functions allow you to manipulate the underlying policy rules (permissions) and grouping rules (roles/hierarchies) stored in the enforcer.

    Policy (Permission) Management

    • addPolicy(enforcer, ...params) / addPolicies(enforcer, rules): Adds new permission rules.
    • removePolicy(enforcer, ...params) / removePolicies(enforcer, rules): Removes permission rules.
    • updatePolicy(enforcer, oldRule, newRule): Updates an existing rule.
    • hasPolicy(enforcer, ...params): Checks if a specific policy rule exists.
    • getPolicy(enforcer): Retrieves all permission rules.
    • getFilteredPolicy(enforcer, fieldIndex, ...fieldValues): Retrieves policies filtered by specific field values.

    Grouping (Role) Management

    • addGroupingPolicy(enforcer, ...params) / addGroupingPolicies(enforcer, rules): Adds role hierarchy rules.
    • removeGroupingPolicy(enforcer, ...params) / removeGroupingPolicies(enforcer, rules): Removes role hierarchy rules.
    • updateGroupingPolicy(enforcer, oldRule, newRule): Updates a grouping rule.
    • hasGroupingPolicy(enforcer, ...params): Checks if a grouping rule exists.
    • getGroupingPolicy(enforcer): Retrieves all grouping rules.

    Named Policies

    If your model uses named types (e.g., g2), use the Named variants:

    • addNamedPolicy(enforcer, ptype, ...params)
    • removeNamedPolicy(enforcer, ptype, ...params)
    • getNamedPolicy(enforcer, ptype)
    • updateNamedPolicy(enforcer, ptype, oldRule, newRule)