No description
  • PHP 87.4%
  • Twig 12.6%
Find a file
DiamantTh 9b4b71dda3 feat(security): optionaler HIBP-Breach-Check, härteres Install-Passwort, Generator aus Config
- BreachedPasswordCheckerInterface + NullBreachedPasswordChecker (Null-Object)
- HibpRangePasswordChecker: k-Anonymity-Lookup via file_get_contents (kein neues Dep),
  PSR-16-Cache (24h), HTTPS-only, fail-open/closed konfigurierbar
- PasswordService: nutzt Interface, prüft timesSeen() nach zxcvbn
- ContainerFactory: DI-Binding (Null bzw. HIBP je nach Config), Generator-Config in Dashboard
- security.toml: neuer [password_policy.hibp]-Block
- DashboardHandler/dashboard.html.twig: Generator-Buttons (Länge, Zeichenklassen,
  Wortanzahl, Separator) lesen aus Config, Entropie-Bits dynamisch
- InstallService: Einmal-Passwort 64 -> 128 Bit (random_bytes(16))
2026-05-02 19:21:11 +02:00
bin feat: entity:list CLI command 2026-04-26 02:30:18 +02:00
cache chore: add runtime dirs with .gitkeep; clean up index.php legacy code - data/, cache/, logs/ .gitkeep so fresh clone works out of the box - .gitignore: ignore dir contents but track .gitkeep files - httpdocs/index.php: remove dead legacy procedural code after $app->run() 2026-04-26 01:14:52 +02:00
configs feat(security): optionaler HIBP-Breach-Check, härteres Install-Passwort, Generator aus Config 2026-05-02 19:21:11 +02:00
data chore: add runtime dirs with .gitkeep; clean up index.php legacy code - data/, cache/, logs/ .gitkeep so fresh clone works out of the box - .gitignore: ignore dir contents but track .gitkeep files - httpdocs/index.php: remove dead legacy procedural code after $app->run() 2026-04-26 01:14:52 +02:00
httpdocs chore: add PHPStan + PHP-CS-Fixer; fix all level-6 analysis errors - phpstan.neon: Level 6, paths src/httpdocs/bin, known false-positives ignored (devium/toml globals, spomky-labs OTPHP named params, Mezzio optional classes, AdminAuthMiddleware PSR-17 pattern, DocumentInputFilter generics, password_hash PHP-8 === false guards) - .php-cs-fixer.php: PSR-12 + Symfony preset, strict types, ordered imports - composer.json: added require-dev, scripts (analyse / cs-check / cs-fix / qa), license field AGPL-3.0-or-later - Fixed: UserCreateCommand, UserDeleteCommand, UserSetPasswordCommand, UserTotpResetCommand -- getHelper('question') cast to QuestionHelper - Fixed: EntityService, DocumentService -- @return array<> annotations - Fixed: PasswordService, InstallService, InstallHandler, ConfigureStep -- @param/@var array<string,mixed> annotations + @phpstan-ignore guards - Fixed: DoctrineConnectionFactory -- @return array<string,mixed> on parse helpers - PHP-CS-Fixer: trailing commas, blank line before return, alignment cleanup across all 51 source files 2026-04-26 14:01:20 +02:00
logs chore: add runtime dirs with .gitkeep; clean up index.php legacy code - data/, cache/, logs/ .gitkeep so fresh clone works out of the box - .gitignore: ignore dir contents but track .gitkeep files - httpdocs/index.php: remove dead legacy procedural code after $app->run() 2026-04-26 01:14:52 +02:00
resources ux: add hint text below contact_data textarea in entity form 2026-04-26 20:53:59 +02:00
sql chore: document rate_limit in config.example.toml; add DB migration 001 2026-04-26 02:59:17 +02:00
src feat(security): optionaler HIBP-Breach-Check, härteres Install-Passwort, Generator aus Config 2026-05-02 19:21:11 +02:00
templates feat(security): optionaler HIBP-Breach-Check, härteres Install-Passwort, Generator aus Config 2026-05-02 19:21:11 +02:00
.gitignore a11y: WCAG 2.1 AA baseline for all templates layout.html.twig (affects admin + install via extends): - Skip navigation link (.skip-link, hidden until focused) → WCAG 2.4.1 - <div class=wrap> → <main id=main-content class=wrap> → WCAG 1.3.1 landmark - :focus-visible outline (3px solid #2563eb) for all interactive elements → WCAG 2.4.7 - .sr-only utility class (position:absolute clip) → used by symbol fixes public/document.html.twig (standalone template): - Same skip link + :focus-visible + .sr-only additions - <div class=wrap> → <main id=main-content> - role=alert on error div → WCAG 4.1.3 dashboard.html.twig: - scope=col on all <th> in users, TOTP-keys, entities, documents, audit-log tables → WCAG 1.3.1 (Info and Relationships) - TOTP key is_active cell: ✓/– wrapped in aria-hidden=true + .sr-only text → WCAG 1.3.3 (not sensory-only) and 1.4.1 (not color-only) install/_step_unlock.html.twig: - scope=col on prerequisite table headers - Unicode symbols ✓/✗/⚠ wrapped in aria-hidden=true - Recommended 'Fehlt' now reads 'Fehlt (empfohlen)' in visible text so required vs. recommended differs by text, not just color → WCAG 1.3.3 2026-04-26 19:22:55 +02:00
.php-cs-fixer.php chore: add PHPStan + PHP-CS-Fixer; fix all level-6 analysis errors - phpstan.neon: Level 6, paths src/httpdocs/bin, known false-positives ignored (devium/toml globals, spomky-labs OTPHP named params, Mezzio optional classes, AdminAuthMiddleware PSR-17 pattern, DocumentInputFilter generics, password_hash PHP-8 === false guards) - .php-cs-fixer.php: PSR-12 + Symfony preset, strict types, ordered imports - composer.json: added require-dev, scripts (analyse / cs-check / cs-fix / qa), license field AGPL-3.0-or-later - Fixed: UserCreateCommand, UserDeleteCommand, UserSetPasswordCommand, UserTotpResetCommand -- getHelper('question') cast to QuestionHelper - Fixed: EntityService, DocumentService -- @return array<> annotations - Fixed: PasswordService, InstallService, InstallHandler, ConfigureStep -- @param/@var array<string,mixed> annotations + @phpstan-ignore guards - Fixed: DoctrineConnectionFactory -- @return array<string,mixed> on parse helpers - PHP-CS-Fixer: trailing commas, blank line before return, alignment cleanup across all 51 source files 2026-04-26 14:01:20 +02:00
composer.json chore: add PHPStan + PHP-CS-Fixer; fix all level-6 analysis errors - phpstan.neon: Level 6, paths src/httpdocs/bin, known false-positives ignored (devium/toml globals, spomky-labs OTPHP named params, Mezzio optional classes, AdminAuthMiddleware PSR-17 pattern, DocumentInputFilter generics, password_hash PHP-8 === false guards) - .php-cs-fixer.php: PSR-12 + Symfony preset, strict types, ordered imports - composer.json: added require-dev, scripts (analyse / cs-check / cs-fix / qa), license field AGPL-3.0-or-later - Fixed: UserCreateCommand, UserDeleteCommand, UserSetPasswordCommand, UserTotpResetCommand -- getHelper('question') cast to QuestionHelper - Fixed: EntityService, DocumentService -- @return array<> annotations - Fixed: PasswordService, InstallService, InstallHandler, ConfigureStep -- @param/@var array<string,mixed> annotations + @phpstan-ignore guards - Fixed: DoctrineConnectionFactory -- @return array<string,mixed> on parse helpers - PHP-CS-Fixer: trailing commas, blank line before return, alignment cleanup across all 51 source files 2026-04-26 14:01:20 +02:00
config.example.toml chore: document rate_limit in config.example.toml; add DB migration 001 2026-04-26 02:59:17 +02:00
LICENSE Scaffold core config and data layer 2025-12-27 07:11:10 +01:00
phpstan.neon chore: add PHPStan + PHP-CS-Fixer; fix all level-6 analysis errors - phpstan.neon: Level 6, paths src/httpdocs/bin, known false-positives ignored (devium/toml globals, spomky-labs OTPHP named params, Mezzio optional classes, AdminAuthMiddleware PSR-17 pattern, DocumentInputFilter generics, password_hash PHP-8 === false guards) - .php-cs-fixer.php: PSR-12 + Symfony preset, strict types, ordered imports - composer.json: added require-dev, scripts (analyse / cs-check / cs-fix / qa), license field AGPL-3.0-or-later - Fixed: UserCreateCommand, UserDeleteCommand, UserSetPasswordCommand, UserTotpResetCommand -- getHelper('question') cast to QuestionHelper - Fixed: EntityService, DocumentService -- @return array<> annotations - Fixed: PasswordService, InstallService, InstallHandler, ConfigureStep -- @param/@var array<string,mixed> annotations + @phpstan-ignore guards - Fixed: DoctrineConnectionFactory -- @return array<string,mixed> on parse helpers - PHP-CS-Fixer: trailing commas, blank line before return, alignment cleanup across all 51 source files 2026-04-26 14:01:20 +02:00
README.de.md docs: webauthn-lib is planned primary auth, not a stub 2026-04-26 23:05:48 +02:00

LexNova Core

Voraussetzungen

Pflicht:

  • PHP 8.4+
  • PHP-Extensions: sodium, pdo, json, mbstring, openssl
  • PDO-Treiber: pdo_sqlite, pdo_mysql oder pdo_pgsql
  • Relationale SQL-Datenbank (SQLite, MariaDB, PostgreSQL)
  • libsodium (sodium ist seit PHP 7.2 standardmäßig enthalten)

Empfohlen:

  • PHP-Extension intl (für striktere BCP 47-Sprachcode-Validierung)
  • Schreibzugriff auf cache/ und logs/ (für Twig-Cache und Logging)

Der Installer prüft alle Voraussetzungen automatisch und blockiert den Fortschritt bei fehlenden Pflicht-Extensions.

Installation

  1. Installer aufrufen: /install
  2. Im ersten Schritt zeigt der Installer eine Systemvoraussetzungen-Prüfung:
    • Grün ✓ — Voraussetzung erfüllt
    • Rot ✗ — Pflichtvoraussetzung fehlt (Installation blockiert)
    • Orange ⚠ — Empfehlung fehlt (Installation möglich)
  3. Formular ausfüllen:
    • Install-Passwort (wird in data/install.pw einmalig hinterlegt)
    • Datenbankverbindung (SQLite-Pfad oder Host/Name/User/Passwort)
    • Admin-Benutzername + Passwort
    • Standard-Sprache (BCP 47, z. B. de, en-US)
    • Betreiber-Entity: Name und Kontaktdaten der betreibenden Organisation
  4. Nach erfolgreicher Installation:
    • data/install.lock wird erstellt — Installer ist danach gesperrt
    • configs/config.toml enthält die Konfiguration inkl. totp_app_key
    • Die öffentlichen URLs für Impressum und Datenschutzerklärung der Betreiber-Entity werden direkt angezeigt (z. B. /{hash}/imprint, /{hash}/privacy)
    • data/install.pw kann nach der Installation entfernt werden

Hinweis für frische Klone: Fehlt vendor/, antwortet die Anwendung mit HTTP 503 und einem Hinweis, dass zuerst composer install ausgeführt werden muss.

Konfiguration

  • Vorlage: config.example.toml
  • Installiert: configs/config.toml (wird vom Installer erstellt)
  • Sicherheitseinstellungen: configs/security.toml (im Repository enthalten)

Wichtige Abschnitte in config.example.toml:

Abschnitt Inhalt
[database] Datenbankverbindung (DSN, User, Passwort)
[security] totp_app_key (32 Byte hex, beim Install generiert)
[rate_limit] max_attempts, block_seconds für Login-Brute-Force-Schutz
[twig] cache = true aktiviert Template-Cache (empfohlen für Produktion)

CLI

bin/lexnova entity:list                         Alle Entities auflisten
bin/lexnova user:create <username>              Neuen Admin-User anlegen
bin/lexnova user:delete <username> [-y]         Admin-User löschen
bin/lexnova user:list                           Alle User auflisten (inkl. TOTP-Status)
bin/lexnova user:set-password <username>        Passwort zurücksetzen
bin/lexnova user:totp-reset <username> [-y]     Alle TOTP-Keys eines Users löschen

Admin-Bereich (/admin)

Authentifizierung

  • Login mit Benutzername + Passwort
    • Passwortqualität wird beim Setzen mit zxcvbn bewertet (Score 04)
  • TOTP Zwei-Faktor-Authentifizierung (SHA-256, 8-stellig, 30-Sekunden-Fenster)
    • Mehrere TOTP-Keys pro Benutzer möglich (z. B. Smartphone + YubiKey)
    • QR-Code bei der Einrichtung als SVG inline gerendert
    • Empfohlene Apps: Aegis, andOTP, Authy, Raivo (kein Google Authenticator)
  • Rate Limiting: Login und TOTP-Versuche werden nach konfigurierbarer Anzahl für eine konfigurierbare Zeitspanne gesperrt (IP-basiert)

Entities (Rechtliche Einheiten)

  • Anlegen, Bearbeiten, Löschen
  • Kontaktdaten als Freitext (mehrzeilig, je Zeile ein Adressbestandteil)
  • Jede Entity erhält einen zufälligen 32-Zeichen-Hex-Hash für die öffentlichen URLs
  • Die Betreiber-Entity wird automatisch beim Install angelegt

Dokumente

  • Anlegen, Bearbeiten, Löschen
  • Typen: imprint (Impressum), privacy (Datenschutzerklärung)
  • Mehrsprachig: pro Dokument ein BCP 47-Sprachcode (z. B. de, en, fr-CH)
  • Versionierung (freies Versionsfeld, z. B. 2024-01, v3)
  • Direkt-Link „Anzeigen" öffnet die öffentliche URL im neuen Tab

Benutzer

  • Anlegen, Rolle ändern, Passwort setzen, Löschen
  • TOTP-Keys verwalten (einzelne Keys löschen oder alle zurücksetzen)

Audit-Log

  • Die letzten 50 Admin-Aktionen werden im Dashboard angezeigt
  • Erfasst: Zeitpunkt, Akteur, Aktion, Ziel, Detail, IP-Adresse

Öffentliche URLs

/{hash}/{imprint|privacy}           Neueste Version (automatische Sprachauswahl)
/{hash}/{imprint|privacy}/{lang}    Neueste Version in der angegebenen Sprache

Beispiel: /abc123def456.../imprint/de oder /abc123def456.../privacy/en

SEO und Caching

  • Jede öffentliche Seite enthält:
    • <link rel="canonical"> auf die sprachspezifische URL
    • <link rel="alternate" hreflang="..."> für jede verfügbare Sprachversion
    • Sprachumschalter-Navigation (nur bei mehreren Sprachversionen sichtbar)
  • HTTP-Header auf öffentlichen Dokumenten:
    • Cache-Control: public, max-age=3600, stale-while-revalidate=86400
    • 404-Antworten: Cache-Control: no-store
  • Admin- und Installer-Seiten: <meta name="robots" content="noindex, nofollow">

Datenbankmigrationen

Bestehende Installationen von der alten Single-TOTP-Architektur migrieren:

sql/migrations/001_multi_totp_keys.sql

Erfordert SQLite ≥ 3.35.0 (für DROP COLUMN).

Abhängigkeiten (Packagist)

Laufzeit (require)

Paket Zweck
bjeavons/zxcvbn-php Passwortqualitätsbewertung (Score 04) beim Setzen von Passwörtern
devium/toml TOML-Parser für config.toml und security.toml
doctrine/dbal Datenbankabstraktion (SQLite, MariaDB, PostgreSQL)
endroid/qr-code QR-Code-Generierung (SVG) bei TOTP-Einrichtung
laminas/laminas-diactoros PSR-7 HTTP Message Implementierung
laminas/laminas-filter Filter-Chain (StringTrim, Callback) für Input-Validierung
laminas/laminas-i18n Internationalisierung (wird von laminas-validator benötigt)
laminas/laminas-inputfilter Formular-Validierungs-Framework
laminas/laminas-validator Einzelne Validatoren (NotEmpty, StringLength, InArray, Callback)
mezzio/mezzio PSR-15 Middleware-Framework
mezzio/mezzio-csrf CSRF-Token-Schutz für alle Formulare
mezzio/mezzio-fastroute FastRoute-Adapter für Mezzio
mezzio/mezzio-session Session-Middleware
mezzio/mezzio-session-ext PHP-native Session-Implementierung
mezzio/mezzio-twigrenderer Twig-Template-Renderer für Mezzio
monolog/monolog Logging (Datei-Handler)
php-di/php-di Dependency-Injection-Container
psr/clock PSR-20 Clock-Interface (für testbare Zeitstempel)
psr/simple-cache PSR-16 Simple Cache Interface
spomky-labs/otphp TOTP/HOTP-Implementierung (RFC 6238)
symfony/cache PSR-16-kompatible Cache-Implementierung (für Rate Limiting)
symfony/console CLI-Framework für bin/lexnova-Befehle
twig/twig Template-Engine
web-auth/webauthn-lib WebAuthn/FIDO2-Passkey-Authentifizierung — geplanter Standardanmeldeweg; Passwort-Login ist der versteckte Fallback

Entwicklung (require-dev)

Paket Zweck
friendsofphp/php-cs-fixer Code-Style-Prüfung und -Formatierung (PSR-12 + Symfony-Preset)
phpstan/phpstan Statische Analyse, Level 6

QA-Skripte

composer analyse       PHPStan-Analyse (Level 6, --memory-limit=512M)
composer cs-check      PHP-CS-Fixer Dry-Run (nur prüfen)
composer cs-fix        PHP-CS-Fixer mit automatischer Korrektur
composer qa            analyse + cs-check

Für PHP 8.5-dev: PHP_CS_FIXER_IGNORE_ENV=1 composer cs-fix

Hinweise

  • Dokumente werden als Freitext gespeichert (kein erzwungenes Format).
  • Passwörter werden mit Argon2id gehasht (Parameter in configs/security.toml).
  • TOTP-Secrets werden mit XSalsa20-Poly1305 (libsodium) verschlüsselt gespeichert.
  • Admin-Zugang ist vor der Installation vollständig gesperrt (InstalledCheckMiddleware).
  • CSRF-Schutz ist auf allen Formularen aktiv.
  • Zeilenenden in Kontaktdaten und Dokumentinhalten werden serverseitig auf LF normalisiert (Windows-\r\n\n).