- Introduced `BootstrapService` to handle admin creation when no admins exist. - Added `/auth/bootstrap-admin` endpoint to consume bootstrap tokens. - Updated `RbacRepository` to support counting admins and assigning roles. - Included unit tests for `BootstrapService` to ensure token behavior and admin assignment.
91 lines
2.1 KiB
TypeScript
91 lines
2.1 KiB
TypeScript
import { Injectable } from "@nestjs/common";
|
|
import { eq } from "drizzle-orm";
|
|
import { DatabaseService } from "../../database/database.service";
|
|
import {
|
|
permissions,
|
|
roles,
|
|
rolesToPermissions,
|
|
usersToRoles,
|
|
} from "../../database/schemas";
|
|
|
|
@Injectable()
|
|
export class RbacRepository {
|
|
constructor(private readonly databaseService: DatabaseService) {}
|
|
|
|
async findRolesByUserId(userId: string) {
|
|
const result = await this.databaseService.db
|
|
.select({
|
|
slug: roles.slug,
|
|
})
|
|
.from(usersToRoles)
|
|
.innerJoin(roles, eq(usersToRoles.roleId, roles.id))
|
|
.where(eq(usersToRoles.userId, userId));
|
|
|
|
return result.map((r) => r.slug);
|
|
}
|
|
|
|
async findPermissionsByUserId(userId: string) {
|
|
const result = await this.databaseService.db
|
|
.select({
|
|
slug: permissions.slug,
|
|
})
|
|
.from(usersToRoles)
|
|
.innerJoin(
|
|
rolesToPermissions,
|
|
eq(usersToRoles.roleId, rolesToPermissions.roleId),
|
|
)
|
|
.innerJoin(permissions, eq(rolesToPermissions.permissionId, permissions.id))
|
|
.where(eq(usersToRoles.userId, userId));
|
|
|
|
return Array.from(new Set(result.map((p) => p.slug)));
|
|
}
|
|
|
|
async countRoles(): Promise<number> {
|
|
const result = await this.databaseService.db
|
|
.select({ count: roles.id })
|
|
.from(roles);
|
|
return result.length;
|
|
}
|
|
|
|
async countAdmins(): Promise<number> {
|
|
const result = await this.databaseService.db
|
|
.select({ count: usersToRoles.userId })
|
|
.from(usersToRoles)
|
|
.innerJoin(roles, eq(usersToRoles.roleId, roles.id))
|
|
.where(eq(roles.slug, "admin"));
|
|
return result.length;
|
|
}
|
|
|
|
async createRole(name: string, slug: string, description?: string) {
|
|
return this.databaseService.db
|
|
.insert(roles)
|
|
.values({
|
|
name,
|
|
slug,
|
|
description,
|
|
})
|
|
.returning();
|
|
}
|
|
|
|
async assignRole(userId: string, roleSlug: string) {
|
|
const role = await this.databaseService.db
|
|
.select()
|
|
.from(roles)
|
|
.where(eq(roles.slug, roleSlug))
|
|
.limit(1);
|
|
|
|
if (!role[0]) {
|
|
throw new Error(`Role with slug ${roleSlug} not found`);
|
|
}
|
|
|
|
return this.databaseService.db
|
|
.insert(usersToRoles)
|
|
.values({
|
|
userId,
|
|
roleId: role[0].id,
|
|
})
|
|
.onConflictDoNothing()
|
|
.returning();
|
|
}
|
|
}
|