Neo platform - First Version

This commit is contained in:
Francisco Gaona
2025-11-25 12:21:14 +01:00
commit 484af68571
59 changed files with 3699 additions and 0 deletions

View 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,
);
}
}

View 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);

View File

@@ -0,0 +1,8 @@
import { Module } from '@nestjs/common';
import { RbacService } from './rbac.service';
@Module({
providers: [RbacService],
exports: [RbacService],
})
export class RbacModule {}

View 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,
})),
});
}
}

View 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));
}
}