Files
dh-inspeccion-v2/api-v3/src/administration/common/administration-audit.ts
T

73 lines
2.2 KiB
TypeScript

import { isIP } from 'node:net';
import { ConflictException } from '@nestjs/common';
import { EntityManager } from 'typeorm';
import type {
AuthPrincipal,
RequestWithContext,
} from '../../common/http/request-context';
import { AuditSource } from '../../database/entities';
const REQUIRED_RECOVERY_PERMISSIONS = [
'roles.manage',
'users.assign_roles',
] as const;
export function administrationAuditContext(
principal: AuthPrincipal,
request: RequestWithContext,
) {
const candidateIp = request.ip || request.socket.remoteAddress || '';
const rawUserAgent = request.header('user-agent')?.trim();
return {
actorUserId: principal.userId,
actorUsername: principal.username,
requestId: request.requestId,
source: AuditSource.WEB,
ip: isIP(candidateIp) ? candidateIp : null,
userAgent: rawUserAgent ? rawUserAgent.slice(0, 2048) : null,
};
}
export async function assertAdministrativeRecoveryRemains(
manager: EntityManager,
): Promise<void> {
const [row] = (await manager.query(
`
SELECT COUNT(*)::integer AS count
FROM (
SELECT user_account.id
FROM users user_account
INNER JOIN user_roles user_role
ON user_role.user_id = user_account.id
INNER JOIN role_permissions role_permission
ON role_permission.role_id = user_role.role_id
INNER JOIN permissions permission
ON permission.id = role_permission.permission_id
WHERE user_account.status = 'ACTIVE'
AND permission.code = ANY($1::varchar[])
GROUP BY user_account.id
HAVING COUNT(DISTINCT permission.code) = $2
) administrators
`,
[REQUIRED_RECOVERY_PERMISSIONS, REQUIRED_RECOVERY_PERMISSIONS.length],
)) as Array<{ count: number }>;
if (!row || Number(row.count) < 1) {
throw new ConflictException({
code: 'LAST_ADMINISTRATOR_PROTECTED',
message:
'Debe permanecer al menos un usuario activo capaz de administrar roles y asignaciones',
});
}
}
export function isUniqueViolation(error: unknown): boolean {
return (
typeof error === 'object' &&
error !== null &&
'code' in error &&
(error as { code?: unknown }).code === '23505'
);
}