Passwort-Komplexität + Portal-Credentials-UX
validatePasswordComplexity (12 Zeichen, Groß/Klein/Zahl/Sonderzeichen) zentral in passwordGenerator.ts; jetzt erzwungen in setPortalPassword, confirmPasswordReset, register, createUser, updateUser. Neue Endpoints: - POST /customers/:id/portal/password/generate → 16-Zeichen Zufallspasswort - POST /customers/:id/portal/send-credentials → Versand per Mail (nur wenn portalEnabled aktiv) Frontend (CustomerDetail): Generate-Button vor Setzen, Send-Credentials nach gesetztem Passwort, Live-Komplexitäts-Hint (✓/○) während Eingabe, alert() durch Toast-Notifications ersetzt. Live-verifiziert: schwaches Passwort → 400 mit Detail-Fehler, komplexes Passwort → 200, Generator liefert 16-Zeichen-Passwort. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -88,6 +88,43 @@ export function generateSimplePassword(length = 12): string {
|
||||
});
|
||||
}
|
||||
|
||||
// ==================== PASSWORD COMPLEXITY VALIDATION ====================
|
||||
|
||||
/**
|
||||
* Mindestanforderungen für vom User vergebene Passwörter.
|
||||
* Generator-Output (generateSecurePassword) erfüllt diese standardmäßig.
|
||||
*/
|
||||
export interface PasswordComplexityResult {
|
||||
ok: boolean;
|
||||
errors: string[];
|
||||
}
|
||||
|
||||
export function validatePasswordComplexity(pw: unknown): PasswordComplexityResult {
|
||||
const errors: string[] = [];
|
||||
if (typeof pw !== 'string') {
|
||||
return { ok: false, errors: ['Passwort fehlt oder ist kein Text'] };
|
||||
}
|
||||
if (pw.length < 12) errors.push('mindestens 12 Zeichen');
|
||||
if (!/[a-z]/.test(pw)) errors.push('mindestens einen Kleinbuchstaben');
|
||||
if (!/[A-Z]/.test(pw)) errors.push('mindestens einen Großbuchstaben');
|
||||
if (!/[0-9]/.test(pw)) errors.push('mindestens eine Ziffer');
|
||||
// Sonderzeichen-Set bewusst breit – auch Leerzeichen + Unicode-Punktuation
|
||||
// zulassen, damit gängige Passwort-Manager-Outputs nicht abgelehnt werden.
|
||||
if (!/[^A-Za-z0-9]/.test(pw)) errors.push('mindestens ein Sonderzeichen');
|
||||
return { ok: errors.length === 0, errors };
|
||||
}
|
||||
|
||||
/**
|
||||
* Wirft mit sprechender Fehlermeldung, wenn das Passwort die Komplexität
|
||||
* nicht erfüllt. Für Aufruf direkt im Controller, der die Exception fängt.
|
||||
*/
|
||||
export function assertPasswordComplexity(pw: unknown): void {
|
||||
const r = validatePasswordComplexity(pw);
|
||||
if (!r.ok) {
|
||||
throw new Error('Passwort erfüllt Mindestanforderungen nicht: ' + r.errors.join(', '));
|
||||
}
|
||||
}
|
||||
|
||||
// Kryptografisch sichere Zufallszahl
|
||||
function getRandomInt(max: number): number {
|
||||
const bytes = randomBytes(4);
|
||||
|
||||
Reference in New Issue
Block a user