xpeditis2.0/apps/backend/src/application/controllers/users.security.spec.ts

89 lines
3.2 KiB
TypeScript

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