Add privilege management system with related migrations and guards

- Introduced a new `PrivilegesModule` to manage user privileges and access control.
- Added `RequirePrivilege` decorator to enforce privilege checks on controller handlers.
- Implemented `PrivilegesGuard` to handle authorization based on user privileges.
- Created database migrations for `privileges`, `privilege_keys`, and `privilege_details` tables.
- Updated user model to include `is_superadmin` field for enhanced access control.
- Added unit tests for the new privileges functionality and guards to ensure correct behavior.
This commit is contained in:
shancheas
2026-08-24 11:31:13 +07:00
parent 0550cbe764
commit 07550b3167
51 changed files with 3731 additions and 27 deletions
+32 -1
View File
@@ -1,6 +1,7 @@
import { ConflictException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { DateTime } from '../../common/value-objects/date-time/date-time';
import { PrivilegesService } from '../privileges/privileges.service';
import type { User } from './user';
import { UsersRepository } from './users.repository';
import { UsersService } from './users.service';
@@ -8,7 +9,13 @@ import { UsersService } from './users.service';
describe('UsersService', () => {
let service: UsersService;
let repository: jest.Mocked<
Pick<UsersRepository, 'findById' | 'findByUsername' | 'create'>
Pick<
UsersRepository,
'findById' | 'findByUsername' | 'create' | 'updatePrivilegeId'
>
>;
let privilegesService: jest.Mocked<
Pick<PrivilegesService, 'findPrivilegeSummary'>
>;
const now = DateTime.fromUnixMs(1_700_000_000_000);
@@ -16,6 +23,8 @@ describe('UsersService', () => {
id: 'user-1',
username: 'alice',
passwordHash: 'hashed',
privilegeId: null,
isSuperadmin: false,
createdAt: now,
updatedAt: now,
};
@@ -25,12 +34,17 @@ describe('UsersService', () => {
findById: jest.fn(),
findByUsername: jest.fn(),
create: jest.fn(),
updatePrivilegeId: jest.fn(),
};
privilegesService = {
findPrivilegeSummary: jest.fn(),
};
const moduleRef: TestingModule = await Test.createTestingModule({
providers: [
UsersService,
{ provide: UsersRepository, useValue: repository },
{ provide: PrivilegesService, useValue: privilegesService },
],
}).compile();
@@ -68,4 +82,21 @@ describe('UsersService', () => {
repository.findByUsername.mockResolvedValue(sampleUser);
await expect(service.findByUsername('Alice')).resolves.toEqual(sampleUser);
});
it('assignPrivilege validates privilege exists', async () => {
repository.findById.mockResolvedValue(sampleUser);
privilegesService.findPrivilegeSummary.mockResolvedValue({
id: 'priv-1',
name: 'Admin',
code: 'ADMIN',
status: 'active',
});
repository.updatePrivilegeId.mockResolvedValue({
...sampleUser,
privilegeId: 'priv-1',
});
const result = await service.assignPrivilege('user-1', 'priv-1');
expect(result.privilegeId).toBe('priv-1');
});
});