- New settings.system permission with migration (idempotent INSERT) - requireAnyPermission helper for route guards accepting multiple perms - Move document numbering + currency/VAT cards from system tab to CompanySettings - Rename security tab to roles, add canManageSystem alongside canManageCompany - TOTP required endpoint and system-info now use settings.system - Roles list now includes user_count - Sidebar Settings link includes settings.system Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
79 lines
2.0 KiB
TypeScript
79 lines
2.0 KiB
TypeScript
import { FastifyRequest, FastifyReply } from "fastify";
|
|
import { verifyAccessToken } from "../services/auth";
|
|
import { error } from "../utils/response";
|
|
import { AuthData } from "../types";
|
|
|
|
export async function requireAuth(
|
|
request: FastifyRequest,
|
|
reply: FastifyReply,
|
|
): Promise<void> {
|
|
const authHeader = request.headers.authorization;
|
|
if (!authHeader?.startsWith("Bearer ")) {
|
|
return error(reply, "Vyžadována autentizace", 401);
|
|
}
|
|
|
|
const token = authHeader.slice(7);
|
|
const authData = await verifyAccessToken(token);
|
|
|
|
if (!authData) {
|
|
return error(reply, "Neplatný nebo expirovaný token", 401);
|
|
}
|
|
|
|
request.authData = authData;
|
|
}
|
|
|
|
export async function optionalAuth(
|
|
request: FastifyRequest,
|
|
_reply: FastifyReply,
|
|
): Promise<void> {
|
|
const authHeader = request.headers.authorization;
|
|
if (!authHeader?.startsWith("Bearer ")) return;
|
|
|
|
const token = authHeader.slice(7);
|
|
request.authData = (await verifyAccessToken(token)) ?? undefined;
|
|
}
|
|
|
|
export function requirePermission(...permissionNames: string[]) {
|
|
return async (
|
|
request: FastifyRequest,
|
|
reply: FastifyReply,
|
|
): Promise<void> => {
|
|
await requireAuth(request, reply);
|
|
if (reply.sent) return;
|
|
|
|
const authData = request.authData!;
|
|
|
|
// Admin has all permissions
|
|
if (authData.roleName === "admin") return;
|
|
|
|
const hasAll = permissionNames.every((p) =>
|
|
authData.permissions.includes(p),
|
|
);
|
|
if (!hasAll) {
|
|
return error(reply, "Nedostatečná oprávnění", 403);
|
|
}
|
|
};
|
|
}
|
|
|
|
export function requireAnyPermission(...permissionNames: string[]) {
|
|
return async (
|
|
request: FastifyRequest,
|
|
reply: FastifyReply,
|
|
): Promise<void> => {
|
|
await requireAuth(request, reply);
|
|
if (reply.sent) return;
|
|
|
|
const authData = request.authData!;
|
|
|
|
// Admin has all permissions
|
|
if (authData.roleName === "admin") return;
|
|
|
|
const hasAny = permissionNames.some((p) =>
|
|
authData.permissions.includes(p),
|
|
);
|
|
if (!hasAny) {
|
|
return error(reply, "Nedostatečná oprávnění", 403);
|
|
}
|
|
};
|
|
}
|