security: fix all Critical and High findings from FLAWS_REPORT audit

- Auth: pessimistic locking on login tokens and refresh token rotation,
  backup code attempt counter, rate limiting verification
- Schema: unique constraints on business numbers, FK relations,
  unsigned/signed alignment, attendance duplicate prevention
- Invoices/PDFs: DOMPurify sanitization, bounded queries in stats
  and alerts, VAT rounding, Puppeteer error handling
- Orders/Offers: transactional parent+child creation, Zod NaN
  refinement, status enums, uniqueness checks
- Projects/Files: path traversal protection, streamed uploads,
  permission guards, query param validation
- Attendance/HR: duplicate checks, ownership validation, GPS
  restrictions, trip distance validation
- Frontend: modal lock reference counting, XSS escaping in print
  HTML, ref mutation fixes, accessibility attributes

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-04-24 00:58:35 +02:00
parent 122eee175e
commit 528e55991b
57 changed files with 2355 additions and 1010 deletions

View File

@@ -91,7 +91,10 @@ export async function getUser(id: number) {
});
}
export async function createUser(data: CreateUserData) {
export async function createUser(
data: CreateUserData,
callerRoleName?: string,
) {
const username = data.username.trim();
const email = data.email.trim();
const firstName = data.first_name.trim();
@@ -113,6 +116,15 @@ export async function createUser(data: CreateUserData) {
return { error: "E-mail již existuje", status: 409 } as const;
}
if (data.role_id) {
const targetRole = await prisma.roles.findUnique({
where: { id: Number(data.role_id) },
});
if (targetRole?.name === "admin" && callerRoleName !== "admin") {
return { error: "Nelze přiřadit roli admin", status: 403 } as const;
}
}
const passwordHash = await bcrypt.hash(
data.password,
config.security.bcryptCost,
@@ -133,12 +145,71 @@ export async function createUser(data: CreateUserData) {
return { user } as const;
}
export async function updateUser(id: number, body: UpdateUserData) {
export async function updateUser(
id: number,
body: UpdateUserData,
callerRoleName?: string,
) {
const existing = await prisma.users.findUnique({ where: { id } });
if (!existing) {
return { error: "Uživatel nenalezen", status: 404 } as const;
}
if (body.role_id !== undefined) {
if (body.role_id) {
const targetRole = await prisma.roles.findUnique({
where: { id: Number(body.role_id) },
});
if (targetRole?.name === "admin" && callerRoleName !== "admin") {
return { error: "Nelze přiřadit roli admin", status: 403 } as const;
}
}
if (
existing.role_id !== null &&
Number(body.role_id) !== existing.role_id
) {
const currentRole = await prisma.roles.findUnique({
where: { id: existing.role_id },
});
if (currentRole?.name === "admin") {
const adminRole = await prisma.roles.findFirst({
where: { name: "admin" },
});
if (adminRole) {
const activeAdminCount = await prisma.users.count({
where: { role_id: adminRole.id, is_active: true },
});
if (activeAdminCount <= 1) {
return {
error: "Nelze odebrat roli poslednímu aktivnímu administrátorovi",
status: 400,
} as const;
}
}
}
}
}
if (body.is_active !== undefined && !body.is_active) {
if (existing.role_id !== null && existing.is_active) {
const adminRole = await prisma.roles.findFirst({
where: { name: "admin" },
});
if (adminRole && existing.role_id === adminRole.id) {
const activeAdminCount = await prisma.users.count({
where: { role_id: adminRole.id, is_active: true },
});
if (activeAdminCount <= 1) {
return {
error: "Nelze deaktivovat posledního aktivního administrátora",
status: 400,
} as const;
}
}
}
}
const data: Record<string, unknown> = {};
if (body.username !== undefined) {