Vollständiges Zugangs-/Rollen-/Rechtesystem gebaut (Anforderungsdokument): D1-Datenbank, AES-verschlüsselte Codes nur für Owner einsehbar, granulare Permissions, Session-Auth mit Lockout, Zugänge-&-Teammitglieder-Verwaltungsseite, Bewerbungen mit Status/Notizen/Entwürfen
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
/* =====================================================================
|
||||
routes/auth.js — Login / Logout
|
||||
===================================================================== */
|
||||
import { verifyCode, createSession, endSession, isLockedOut, recordFailedAttempt, clearAttempts, clientKey } from "../lib/auth.js";
|
||||
import { logAction } from "../lib/audit.js";
|
||||
import { nowIso } from "../lib/crypto.js";
|
||||
import { json } from "../lib/http.js";
|
||||
|
||||
export async function login(request, env) {
|
||||
const body = await request.json().catch(() => ({}));
|
||||
const code = String(body.code || "");
|
||||
const key = `login:${clientKey(request)}`;
|
||||
|
||||
if (await isLockedOut(env, key)) {
|
||||
return json({ ok: false, error: "Zu viele Fehlversuche. Bitte in ein paar Minuten erneut versuchen." }, 429);
|
||||
}
|
||||
|
||||
const result = await verifyCode(env, code);
|
||||
if (!result.ok) {
|
||||
const { count } = await recordFailedAttempt(env, key);
|
||||
await logAction(env, "anonym", "login.failed", null, { attempt: count });
|
||||
if (result.reason === "locked") return json({ ok: false, error: "Dieser Zugang ist gesperrt." }, 403);
|
||||
if (result.reason === "expired") return json({ ok: false, error: "Dieser Zugang ist abgelaufen." }, 403);
|
||||
return json({ ok: false, error: "Falscher Code." }, 401);
|
||||
}
|
||||
|
||||
await clearAttempts(env, key);
|
||||
|
||||
if (result.isOwner) {
|
||||
const token = await createSession(env, { isOwner: true });
|
||||
await logAction(env, "owner", "login.success", null, null);
|
||||
return json({ ok: true, token, isOwner: true, name: "Dogi", roleName: "Hauptadministrator" });
|
||||
}
|
||||
|
||||
const token = await createSession(env, { isOwner: false, userId: result.user.id });
|
||||
await env.DB.prepare(`UPDATE users SET last_login_at = ? WHERE id = ?`).bind(nowIso(), result.user.id).run();
|
||||
|
||||
const roleRow = result.user.role_id
|
||||
? await env.DB.prepare(`SELECT name, permissions FROM roles WHERE id = ?`).bind(result.user.role_id).first()
|
||||
: null;
|
||||
const overridesRows = await env.DB.prepare(
|
||||
`SELECT permission, allowed FROM user_permission_overrides WHERE user_id = ?`
|
||||
)
|
||||
.bind(result.user.id)
|
||||
.all();
|
||||
const overrides = {};
|
||||
for (const r of overridesRows.results || []) overrides[r.permission] = !!r.allowed;
|
||||
const permissions = roleRow ? JSON.parse(roleRow.permissions || "[]") : [];
|
||||
|
||||
await logAction(env, result.user.username, "login.success", null, null);
|
||||
|
||||
return json({
|
||||
ok: true,
|
||||
token,
|
||||
isOwner: false,
|
||||
name: result.user.name,
|
||||
username: result.user.username,
|
||||
roleName: roleRow?.name || "Ohne Rolle",
|
||||
permissions,
|
||||
overrides,
|
||||
});
|
||||
}
|
||||
|
||||
export async function logout(request, env) {
|
||||
const auth = request.headers.get("Authorization") || "";
|
||||
const token = auth.startsWith("Bearer ") ? auth.slice(7) : null;
|
||||
await endSession(env, token);
|
||||
return json({ ok: true });
|
||||
}
|
||||
Reference in New Issue
Block a user