Multi-User-Plattform: jeder Benutzer hat eigene, isolierte Daten
- Auth via Session-Cookie + Login-Seite (scrypt, lib/password.js, sessions-Tabelle) - AsyncLocalStorage (lib/context.js) propagiert aktuellen Benutzer durch alle Libs - user_id auf allen Datentabellen (FK->users ON DELETE CASCADE), per-user PK/UNIQUE (app_state, settings, prompts, design, jobangebote) und per-user Dateispeicher (data/<dir>/<userId>/) - Alle Queries in server.js + lib/api.js nach user_id scope-iert - Pro-Benutzer-Konfiguration (Ollama/Mail/CalDAV/API-Token) in app_state, Live gelesen via config.get(); Hintergrund-Loops (IMAP/CalDAV) iterieren alle Benutzer - REST-API /api/v1: X-API-Key loest den Token zu einem Benutzer auf, Anfragen operieren nur auf dessen Daten - Admin-Panel /admin: Benutzer anlegen, Passwort zuruecksetzen, loeschen (mit Daten) - Idempotente Migration (lib/migrate-multiuser.js + scripts/migrate-to-multiuser.js): bestehende Daten werden dem Benutzer admin:admin zugeordnet Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
// Password hashing using Node's built-in scrypt + a per-hash random salt.
|
||||
//
|
||||
// No external dependency (bcrypt would need a native build step). scrypt is
|
||||
// memory-hard and well suited for interactive logins. Hash format:
|
||||
// "<saltHex>:<hashHex>" (salt is 16 bytes, hash is 64 bytes)
|
||||
|
||||
const crypto = require('crypto');
|
||||
|
||||
const KEYLEN = 64;
|
||||
|
||||
function hash(password) {
|
||||
const salt = crypto.randomBytes(16).toString('hex');
|
||||
const out = crypto.scryptSync(password, salt, KEYLEN).toString('hex');
|
||||
return `${salt}:${out}`;
|
||||
}
|
||||
|
||||
function verify(password, stored) {
|
||||
if (typeof stored !== 'string' || !stored.includes(':')) return false;
|
||||
const idx = stored.indexOf(':');
|
||||
const salt = stored.slice(0, idx);
|
||||
const expected = stored.slice(idx + 1);
|
||||
if (!salt || !expected) return false;
|
||||
let computed;
|
||||
try {
|
||||
computed = crypto.scryptSync(password, salt, KEYLEN).toString('hex');
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
if (computed.length !== expected.length) return false;
|
||||
// Constant-time compare to avoid timing side channels.
|
||||
return crypto.timingSafeEqual(Buffer.from(computed), Buffer.from(expected));
|
||||
}
|
||||
|
||||
module.exports = { hash, verify };
|
||||
Reference in New Issue
Block a user