Neo platform - First Version
This commit is contained in:
35
backend/src/rbac/permissions.guard.ts
Normal file
35
backend/src/rbac/permissions.guard.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { RbacService } from './rbac.service';
|
||||
import { PERMISSIONS_KEY } from './rbac.decorator';
|
||||
|
||||
@Injectable()
|
||||
export class PermissionsGuard implements CanActivate {
|
||||
constructor(
|
||||
private reflector: Reflector,
|
||||
private rbacService: RbacService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const requiredPermissions = this.reflector.getAllAndOverride<string[]>(
|
||||
PERMISSIONS_KEY,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
|
||||
if (!requiredPermissions) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const user = request.user;
|
||||
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.rbacService.hasAllPermissions(
|
||||
user.userId,
|
||||
requiredPermissions,
|
||||
);
|
||||
}
|
||||
}
|
||||
8
backend/src/rbac/rbac.decorator.ts
Normal file
8
backend/src/rbac/rbac.decorator.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { SetMetadata } from '@nestjs/common';
|
||||
|
||||
export const PERMISSIONS_KEY = 'permissions';
|
||||
export const Permissions = (...permissions: string[]) =>
|
||||
SetMetadata(PERMISSIONS_KEY, permissions);
|
||||
|
||||
export const ROLES_KEY = 'roles';
|
||||
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
|
||||
8
backend/src/rbac/rbac.module.ts
Normal file
8
backend/src/rbac/rbac.module.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { RbacService } from './rbac.service';
|
||||
|
||||
@Module({
|
||||
providers: [RbacService],
|
||||
exports: [RbacService],
|
||||
})
|
||||
export class RbacModule {}
|
||||
103
backend/src/rbac/rbac.service.ts
Normal file
103
backend/src/rbac/rbac.service.ts
Normal file
@@ -0,0 +1,103 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class RbacService {
|
||||
constructor(private prisma: PrismaService) {}
|
||||
|
||||
async getUserPermissions(userId: string): Promise<string[]> {
|
||||
const userRoles = await this.prisma.userRole.findMany({
|
||||
where: { userId },
|
||||
include: {
|
||||
role: {
|
||||
include: {
|
||||
rolePermissions: {
|
||||
include: {
|
||||
permission: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const permissions = new Set<string>();
|
||||
userRoles.forEach((userRole) => {
|
||||
userRole.role.rolePermissions.forEach((rp) => {
|
||||
permissions.add(rp.permission.name);
|
||||
});
|
||||
});
|
||||
|
||||
return Array.from(permissions);
|
||||
}
|
||||
|
||||
async getUserRoles(userId: string): Promise<string[]> {
|
||||
const userRoles = await this.prisma.userRole.findMany({
|
||||
where: { userId },
|
||||
include: {
|
||||
role: true,
|
||||
},
|
||||
});
|
||||
|
||||
return userRoles.map((ur) => ur.role.name);
|
||||
}
|
||||
|
||||
async hasPermission(userId: string, permission: string): Promise<boolean> {
|
||||
const permissions = await this.getUserPermissions(userId);
|
||||
return permissions.includes(permission);
|
||||
}
|
||||
|
||||
async hasRole(userId: string, role: string): Promise<boolean> {
|
||||
const roles = await this.getUserRoles(userId);
|
||||
return roles.includes(role);
|
||||
}
|
||||
|
||||
async hasAnyPermission(
|
||||
userId: string,
|
||||
permissions: string[],
|
||||
): Promise<boolean> {
|
||||
const userPermissions = await this.getUserPermissions(userId);
|
||||
return permissions.some((p) => userPermissions.includes(p));
|
||||
}
|
||||
|
||||
async hasAllPermissions(
|
||||
userId: string,
|
||||
permissions: string[],
|
||||
): Promise<boolean> {
|
||||
const userPermissions = await this.getUserPermissions(userId);
|
||||
return permissions.every((p) => userPermissions.includes(p));
|
||||
}
|
||||
|
||||
async assignRole(userId: string, roleId: string) {
|
||||
return this.prisma.userRole.create({
|
||||
data: {
|
||||
userId,
|
||||
roleId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async removeRole(userId: string, roleId: string) {
|
||||
return this.prisma.userRole.deleteMany({
|
||||
where: {
|
||||
userId,
|
||||
roleId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async syncPermissionsToRole(roleId: string, permissionIds: string[]) {
|
||||
// Remove existing permissions
|
||||
await this.prisma.rolePermission.deleteMany({
|
||||
where: { roleId },
|
||||
});
|
||||
|
||||
// Add new permissions
|
||||
await this.prisma.rolePermission.createMany({
|
||||
data: permissionIds.map((permissionId) => ({
|
||||
roleId,
|
||||
permissionId,
|
||||
})),
|
||||
});
|
||||
}
|
||||
}
|
||||
33
backend/src/rbac/roles.guard.ts
Normal file
33
backend/src/rbac/roles.guard.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { RbacService } from './rbac.service';
|
||||
import { ROLES_KEY } from './rbac.decorator';
|
||||
|
||||
@Injectable()
|
||||
export class RolesGuard implements CanActivate {
|
||||
constructor(
|
||||
private reflector: Reflector,
|
||||
private rbacService: RbacService,
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const requiredRoles = this.reflector.getAllAndOverride<string[]>(
|
||||
ROLES_KEY,
|
||||
[context.getHandler(), context.getClass()],
|
||||
);
|
||||
|
||||
if (!requiredRoles) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const user = request.user;
|
||||
|
||||
if (!user) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const userRoles = await this.rbacService.getUserRoles(user.userId);
|
||||
return requiredRoles.some((role) => userRoles.includes(role));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user