Skip to content

Instantly share code, notes, and snippets.

View minhntm's full-sized avatar
:octocat:

Nguyễn Trí Minh minhntm

:octocat:
  • Tokyo
  • 03:57 (UTC +09:00)
View GitHub Profile
@Controller("invoices")
export class InvoicesController {
constructor(private readonly invoicesService: InvoicesService, private abilityFactory: AbilityFactory) {}
@Get(":id")
async findOne(
@Param("departmentId", ParseIntPipe) departmentId: number,
@Param("invoiceId", ParseIntPipe) invoiceId: number,
@Request() req: AuthenticatedRequest,
): Promise<InvoiceResponseDto> {
const ability = await this.abilityFactory.createForUser(req.user);
interface CaslPermission {
action: PermissionAction;
subject: string;
condition?: PermissionCondition;
}
@Injectable()
export class CaslAbilityFactory {
constructor(private authoService: AuthzService) {}
async createForUser(user: User): Promise<AppAbility> {
const dbPermissions: Permission[] = await this.authoService.findAllPermissionsOfUser(user);
export interface PermissionCondition {}
@Entity("permissions")
export class Permission extends BaseEntity {
// define class attributes...
/**
* @param condition: {"departmentId": "${id}"}
* @param variables: {"id: 1"}
* @return condition after parse: {"departmentId": 1}
*/
{
"id": 1,
"action": "READ",
"objectId": 5, // id of Invoice object, refer to objects table.
"condition": {
"departmentId": "${id}"
}
}
@Global()
@Module({
imports: [
TypeOrmModule.forFeature([UserRepository]),
],
providers: [CaslAbilityFactory, PermissionsGuard, AuthzService],
exports: [CaslAbilityFactory, PermissionsGuard],
})
export class AuthzModule {}
@Post()
@UseGuards(PermissionsGuard)
@CheckPermissions([PermissionAction.CREATE, "Invoice"]) // "Invoice" is the value in name column of objects table
async create(
// create invoice params
): Promise<InvoiceResponseDto> {
// create invoice logic
}
import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
@Injectable()
export class PermissionsGuard implements CanActivate {
constructor(private reflector: Reflector, private abilityFactory: CaslAbilityFactory) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const requiredPermissions =
this.reflector.get<RequiredPermission[]>(PERMISSION_CHECKER_KEY, context.getHandler()) || [];
const req = context.switchToHttp().getRequest();
const user = req.user;
import { CustomDecorator, SetMetadata } from "@nestjs/common";
// action, object
export type RequiredPermission = [PermissionAction, PermissionObjectType]
export const PERMISSION_CHECKER_KEY = "permission_checker_params_key";
export const CheckPermissions = (...params: RequiredPermission[]): CustomDecorator<string> =>
SetMetadata(PERMISSION_CHECKER_KEY, params);
import { Ability } from "@casl/ability";
export enum PermissionAction {
CREATE = "create",
READ = "read",
UPDATE = "update",
DELETE = "delete",
}
export type PermissionObjectType = any;
export type AppAbility = Ability<[PermissionAction, PermissionObjectType]>;
interface CaslPermission {
$ nest g class authz/casl-ability.factory