add rbac module
This commit is contained in:
parent
99cb459fb7
commit
5de299c0c1
7
src/modules/rbac/decorators/permission.decorator.ts
Normal file
7
src/modules/rbac/decorators/permission.decorator.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
|
||||
import { PERMISSION_CHECKERS } from '../constants';
|
||||
import { PermissionChecker } from '../types';
|
||||
|
||||
export const Permision = (...checkers: PermissionChecker[]) =>
|
||||
SetMetadata(PERMISSION_CHECKERS, checkers);
|
100
src/modules/rbac/guards/rbac.guard.ts
Normal file
100
src/modules/rbac/guards/rbac.guard.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import { createMongoAbility } from '@casl/ability';
|
||||
import { ExecutionContext, ForbiddenException } from '@nestjs/common';
|
||||
import { ModuleRef, Reflector } from '@nestjs/core';
|
||||
|
||||
import { isNil } from 'lodash';
|
||||
|
||||
import { JwtAuthGuard } from '@/modules/user/guards';
|
||||
|
||||
import { UserRepository } from '@/modules/user/repositories';
|
||||
import { TokenService } from '@/modules/user/services';
|
||||
|
||||
import { PERMISSION_CHECKERS } from '../constants';
|
||||
import { PermissionEntity } from '../entities/permission.entity';
|
||||
import { RbacResolver } from '../rbac.resolver';
|
||||
|
||||
import { CheckerParams, PermissionChecker } from '../types';
|
||||
|
||||
export class RbacGuard extends JwtAuthGuard {
|
||||
constructor(
|
||||
protected reflector: Reflector,
|
||||
protected resolver: RbacResolver,
|
||||
protected tokenService: TokenService,
|
||||
protected userRepository: UserRepository,
|
||||
protected modeleRef: ModuleRef,
|
||||
) {
|
||||
super(reflector, tokenService);
|
||||
}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const authCheck = await super.canActivate(context);
|
||||
|
||||
if (!authCheck) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
|
||||
const checkers = this.reflector.getAllAndOverride<PermissionChecker[]>(
|
||||
PERMISSION_CHECKERS,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
if (isNil(checkers) || checkers.length < 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const result = await checkPermissions({
|
||||
resolver: this.resolver,
|
||||
repository: this.userRepository,
|
||||
checkers,
|
||||
moduleRef: this.modeleRef,
|
||||
request: context.switchToHttp().getRequest(),
|
||||
});
|
||||
if (!result) {
|
||||
throw new ForbiddenException();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查权限
|
||||
* @param CheckerParams
|
||||
*/
|
||||
export async function checkPermissions({
|
||||
checkers,
|
||||
moduleRef,
|
||||
resolver,
|
||||
repository,
|
||||
request,
|
||||
}: CheckerParams) {
|
||||
if (isNil(request.user)) {
|
||||
return false;
|
||||
}
|
||||
const user = await repository.findOneOrFail({
|
||||
relations: ['roles.permissions', 'permissions'],
|
||||
where: { id: request.user.id },
|
||||
});
|
||||
let permissions = user.permissions as PermissionEntity[];
|
||||
for (const role of user.roles) {
|
||||
permissions = [...permissions, ...role.permissions];
|
||||
}
|
||||
permissions = permissions.reduce((o, n) => {
|
||||
if (o.find(({ name }) => name === n.name)) {
|
||||
return o;
|
||||
}
|
||||
return [...o, n];
|
||||
}, []);
|
||||
|
||||
const ability = createMongoAbility(
|
||||
permissions.map(({ rule, name }) => {
|
||||
const resolve = resolver.permissions.find((p) => p.name === name);
|
||||
if (!isNil(resolve) && !isNil(resolve.rule.conditions)) {
|
||||
return { ...rule, conditions: resolve.rule.conditions(user) };
|
||||
}
|
||||
return rule;
|
||||
}),
|
||||
);
|
||||
const results = await Promise.all(
|
||||
checkers.map(async (checker) => checker(ability, moduleRef, request)),
|
||||
);
|
||||
return results.every((r) => !!r);
|
||||
}
|
@ -7,8 +7,11 @@ import { FastifyRequest as Request } from 'fastify';
|
||||
|
||||
import { UserEntity } from '../user/entities';
|
||||
|
||||
import { UserRepository } from '../user/repositories';
|
||||
|
||||
import { PermissionEntity } from './entities/permission.entity';
|
||||
import { RoleEntity } from './entities/role.entity';
|
||||
import { RbacResolver } from './rbac.resolver';
|
||||
|
||||
export type Role = Pick<ClassToPlain<RoleEntity>, 'name' | 'label' | 'description'> & {
|
||||
permissions: string[];
|
||||
@ -29,3 +32,11 @@ export type PermissionChecker = (
|
||||
ref?: ModuleRef,
|
||||
request?: Request,
|
||||
) => Promise<boolean>;
|
||||
|
||||
export type CheckerParams = {
|
||||
resolver: RbacResolver;
|
||||
repository: UserRepository;
|
||||
checkers: PermissionChecker[];
|
||||
moduleRef?: ModuleRef;
|
||||
request?: any;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user