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:
@@ -4,6 +4,7 @@ import * as userService from '../services/user.service.js';
|
||||
import { logChange } from '../services/audit.service.js';
|
||||
import { ApiResponse } from '../types/index.js';
|
||||
import { pickUserCreate, pickUserUpdate } from '../utils/sanitize.js';
|
||||
import { validatePasswordComplexity } from '../utils/passwordGenerator.js';
|
||||
|
||||
// Users
|
||||
export async function getUsers(req: Request, res: Response): Promise<void> {
|
||||
@@ -51,7 +52,18 @@ export async function getUser(req: Request, res: Response): Promise<void> {
|
||||
export async function createUser(req: Request, res: Response): Promise<void> {
|
||||
try {
|
||||
// Whitelist: nur erlaubte Felder aus req.body übernehmen (Mass-Assignment-Schutz)
|
||||
const user = await userService.createUser(pickUserCreate(req.body) as any);
|
||||
const data = pickUserCreate(req.body) as any;
|
||||
if (data?.password) {
|
||||
const c = validatePasswordComplexity(data.password);
|
||||
if (!c.ok) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Passwort erfüllt Mindestanforderungen nicht: ' + c.errors.join(', '),
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
const user = await userService.createUser(data);
|
||||
await logChange({
|
||||
req, action: 'CREATE', resourceType: 'User',
|
||||
resourceId: user.id.toString(),
|
||||
@@ -71,6 +83,16 @@ export async function updateUser(req: Request, res: Response): Promise<void> {
|
||||
const userId = parseInt(req.params.id);
|
||||
// Whitelist: nur erlaubte Felder aus req.body übernehmen (Mass-Assignment-Schutz)
|
||||
const data = pickUserUpdate(req.body);
|
||||
if ((data as any)?.password) {
|
||||
const c = validatePasswordComplexity((data as any).password);
|
||||
if (!c.ok) {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error: 'Passwort erfüllt Mindestanforderungen nicht: ' + c.errors.join(', '),
|
||||
} as ApiResponse);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Vorherigen Stand laden für Audit – inkl. Rollen, damit hasGdprAccess /
|
||||
// hasDeveloperAccess (versteckte Rollen) korrekt verglichen werden.
|
||||
|
||||
Reference in New Issue
Block a user