security: full hardening pass — auth guards, rate limiting, headers, internal secret
- Add requireAdmin() to all unprotected admin server actions (clients/new, clients/[id], timer-actions — 17 functions total) - Protect /api/internal/* endpoints with X-Internal-Secret header (proxy.ts sends it; routes reject requests without it) - Randomize auto-generated client slugs with 4-char suffix to prevent enumeration via predictable name-based slugs - Add in-memory rate limiting to /api/client/approve (20/min) and /api/client/comment (10/min) per IP - Add security headers: X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, X-DNS-Prefetch-Control - Reduce JWT session from 30 days to 7 days with daily rotation - Remove hardcoded NEXTAUTH_URL from Dockerfile (pass via Coolify env) - Genericize client API error messages to not leak data structure - Update .env.example with all required variables including INTERNAL_SECRET Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,20 @@
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { getServerSession } from "next-auth";
|
||||
import { authOptions } from "@/lib/auth";
|
||||
import { db } from "@/db";
|
||||
|
||||
async function requireAdmin() {
|
||||
const session = await getServerSession(authOptions);
|
||||
if (!session) throw new Error("Non autorizzato");
|
||||
}
|
||||
import { time_entries, projects } from "@/db/schema";
|
||||
import { eq, isNull, asc } from "drizzle-orm";
|
||||
import { nanoid } from "nanoid";
|
||||
|
||||
export async function startTimer(projectId: string): Promise<{ entryId: string }> {
|
||||
await requireAdmin();
|
||||
// Stop any currently running session before starting a new one
|
||||
const running = await db
|
||||
.select({ id: time_entries.id })
|
||||
@@ -38,6 +46,7 @@ export async function startTimer(projectId: string): Promise<{ entryId: string }
|
||||
}
|
||||
|
||||
export async function startTimerForClient(clientId: string): Promise<{ entryId: string }> {
|
||||
await requireAdmin();
|
||||
const projectRows = await db
|
||||
.select({ id: projects.id })
|
||||
.from(projects)
|
||||
@@ -50,6 +59,7 @@ export async function startTimerForClient(clientId: string): Promise<{ entryId:
|
||||
}
|
||||
|
||||
export async function stopTimer(entryId: string): Promise<void> {
|
||||
await requireAdmin();
|
||||
const rows = await db
|
||||
.select({ started_at: time_entries.started_at })
|
||||
.from(time_entries)
|
||||
|
||||
Reference in New Issue
Block a user