import { ForbiddenException, Logger } from '@nestjs/common'; import { User, UserRole } from '@domain/entities/user.entity'; import { UserRepository } from '@domain/ports/out/user.repository'; import { UsersController } from './users.controller'; import { SubscriptionService } from '../services/subscription.service'; import { UserPayload } from '../decorators/current-user.decorator'; import { UserRole as DtoUserRole } from '../dto/user.dto'; describe('administrator target protection', () => { it('does not log a temporary password when creating an account', async () => { const log = jest.spyOn(Logger.prototype, 'log').mockImplementation(() => undefined); const warn = jest.spyOn(Logger.prototype, 'warn').mockImplementation(() => undefined); try { const controller = new UsersController( { findByEmail: async () => null, save: async (user: User) => user, } as unknown as UserRepository, {} as SubscriptionService, {} as never, { assertKeepsAnActiveAdmin: jest.fn() } as never, { sendUserInvitation: jest.fn().mockResolvedValue(undefined) } as never, { findById: jest.fn().mockResolvedValue(null) } as never ); await controller.createUser( { email: 'new@example.org', firstName: 'New', lastName: 'User', organizationId: 'org-1', role: DtoUserRole.USER, password: 'test-only-Temporary-password-42', }, { id: 'admin', email: 'admin@example.org', role: 'ADMIN', organizationId: 'org-1', firstName: 'A', lastName: 'B', } ); expect(JSON.stringify([...log.mock.calls, ...warn.mock.calls])).not.toContain( 'test-only-Temporary-password-42' ); } finally { log.mockRestore(); warn.mockRestore(); } }); const actor: UserPayload = { id: 'manager', role: 'MANAGER', organizationId: 'org-1', email: 'manager@example.org', firstName: 'Test', lastName: 'Manager', }; it.each([UserRole.ADMIN, UserRole.USER])('enforces target hierarchy for %s', async role => { const user = User.create({ id: 'target', role, organizationId: actor.organizationId, email: 'target@example.org', firstName: 'Original', lastName: 'User', passwordHash: 'test-hash', }); const save = jest.fn(async () => user); const controller = new UsersController( { findById: jest.fn(async () => user), save } as unknown as UserRepository, {} as SubscriptionService, {} as never, { assertKeepsAnActiveAdmin: jest.fn() } as never, { sendUserInvitation: jest.fn().mockResolvedValue(undefined) } as never, { findById: jest.fn().mockResolvedValue(null) } as never ); const result = controller.updateUser(user.id, { firstName: 'Changed' }, actor); if (role === UserRole.ADMIN) { await expect(result).rejects.toBeInstanceOf(ForbiddenException); expect(save).not.toHaveBeenCalled(); expect(user.firstName).toBe('Original'); } else { await expect(result).resolves.toHaveProperty('firstName', 'Changed'); expect(save).toHaveBeenCalled(); } }); });