feat(auth): implement role seeding on application bootstrap
- Added `onApplicationBootstrap` to seed default roles if none exist. - Introduced `seedRoles` method to handle role creation with logging. - Updated `RbacRepository` with `countRoles` and `createRole` methods. - Added unit tests to ensure role seeding logic functions correctly.
This commit is contained in:
@@ -9,6 +9,8 @@ describe("RbacService", () => {
|
|||||||
const mockRbacRepository = {
|
const mockRbacRepository = {
|
||||||
findRolesByUserId: jest.fn(),
|
findRolesByUserId: jest.fn(),
|
||||||
findPermissionsByUserId: jest.fn(),
|
findPermissionsByUserId: jest.fn(),
|
||||||
|
countRoles: jest.fn(),
|
||||||
|
createRole: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
@@ -58,4 +60,35 @@ describe("RbacService", () => {
|
|||||||
expect(repository.findPermissionsByUserId).toHaveBeenCalledWith(userId);
|
expect(repository.findPermissionsByUserId).toHaveBeenCalledWith(userId);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("seedRoles", () => {
|
||||||
|
it("should be called on application bootstrap", async () => {
|
||||||
|
const seedRolesSpy = jest.spyOn(service, "seedRoles");
|
||||||
|
await service.onApplicationBootstrap();
|
||||||
|
expect(seedRolesSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should seed roles if none exist", async () => {
|
||||||
|
mockRbacRepository.countRoles.mockResolvedValue(0);
|
||||||
|
|
||||||
|
await service.seedRoles();
|
||||||
|
|
||||||
|
expect(repository.countRoles).toHaveBeenCalled();
|
||||||
|
expect(repository.createRole).toHaveBeenCalledTimes(3);
|
||||||
|
expect(repository.createRole).toHaveBeenCalledWith(
|
||||||
|
"Administrator",
|
||||||
|
"admin",
|
||||||
|
"Full system access",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not seed roles if some already exist", async () => {
|
||||||
|
mockRbacRepository.countRoles.mockResolvedValue(3);
|
||||||
|
|
||||||
|
await service.seedRoles();
|
||||||
|
|
||||||
|
expect(repository.countRoles).toHaveBeenCalled();
|
||||||
|
expect(repository.createRole).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,49 @@
|
|||||||
import { Injectable } from "@nestjs/common";
|
import { Injectable, Logger, OnApplicationBootstrap } from "@nestjs/common";
|
||||||
import { RbacRepository } from "./repositories/rbac.repository";
|
import { RbacRepository } from "./repositories/rbac.repository";
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RbacService {
|
export class RbacService implements OnApplicationBootstrap {
|
||||||
|
private readonly logger = new Logger(RbacService.name);
|
||||||
|
|
||||||
constructor(private readonly rbacRepository: RbacRepository) {}
|
constructor(private readonly rbacRepository: RbacRepository) {}
|
||||||
|
|
||||||
|
async onApplicationBootstrap() {
|
||||||
|
this.logger.log("RbacService initialized, checking roles...");
|
||||||
|
await this.seedRoles();
|
||||||
|
}
|
||||||
|
|
||||||
|
async seedRoles() {
|
||||||
|
try {
|
||||||
|
const count = await this.rbacRepository.countRoles();
|
||||||
|
if (count === 0) {
|
||||||
|
this.logger.log("No roles found, seeding default roles...");
|
||||||
|
const defaultRoles = [
|
||||||
|
{ name: "Administrator", slug: "admin", description: "Full system access" },
|
||||||
|
{
|
||||||
|
name: "Moderator",
|
||||||
|
slug: "moderator",
|
||||||
|
description: "Access to moderation tools",
|
||||||
|
},
|
||||||
|
{ name: "User", slug: "user", description: "Standard user access" },
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const role of defaultRoles) {
|
||||||
|
await this.rbacRepository.createRole(
|
||||||
|
role.name,
|
||||||
|
role.slug,
|
||||||
|
role.description,
|
||||||
|
);
|
||||||
|
this.logger.log(`Created role: ${role.slug}`);
|
||||||
|
}
|
||||||
|
this.logger.log("Default roles seeded successfully.");
|
||||||
|
} else {
|
||||||
|
this.logger.log(`${count} roles already exist, skipping seeding.`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.logger.error("Error during roles seeding:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async getUserRoles(userId: string) {
|
async getUserRoles(userId: string) {
|
||||||
return this.rbacRepository.findRolesByUserId(userId);
|
return this.rbacRepository.findRolesByUserId(userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,4 +39,22 @@ export class RbacRepository {
|
|||||||
|
|
||||||
return Array.from(new Set(result.map((p) => p.slug)));
|
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 createRole(name: string, slug: string, description?: string) {
|
||||||
|
return this.databaseService.db
|
||||||
|
.insert(roles)
|
||||||
|
.values({
|
||||||
|
name,
|
||||||
|
slug,
|
||||||
|
description,
|
||||||
|
})
|
||||||
|
.returning();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user