# ============================================================
# IT-Dokumentationssystem - Apache Konfiguration
# ============================================================

# Aktiviere Rewrite-Engine
RewriteEngine On

# Authorization-Header an PHP weitergeben (erforderlich für Bearer-Token-Authentifizierung)
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]

# SICHERHEIT: Verberge Verzeichnislisting
Options -Indexes

# SICHERHEIT: Verhindere Zugriff auf versteckte Dateien
<FilesMatch "^\.">
    Require all denied
</FilesMatch>

# SICHERHEIT: Im Webroot sind ausschließlich die beiden Einstiegsskripte
# ausführbar. Alles andere wird als reine Daten behandelt – damit bleibt eine
# Datei, die über ein Erweiterungspaket (draw.io, TinyMCE) hierher gelangt,
# ohne Wirkung, selbst wenn sie eine Skript-Endung trägt.
#
# HINWEIS: Greift nur unter Apache mit AllowOverride. Unter nginx/LiteSpeed
# gehört die entsprechende Regel in die Serverkonfiguration (siehe
# docs/INSTALLATION.md).
<FilesMatch "\.(php|phtml|php3|php4|php5|php7|php8|phps|phar|pht|cgi|pl|py|rb|sh|jsp|asp|aspx)$">
    Require all denied
</FilesMatch>
<FilesMatch "^(index|install)\.php$">
    Require all granted
</FilesMatch>

# SICHERHEIT: Verhindere Zugriff auf Backup-Dateien
<FilesMatch "\.(bak|config|dist|fla|inc|ini|log|psd|sh|sql|swp)$">
    Require all denied
</FilesMatch>

# SICHERHEIT: Installations-Script erst nach abgeschlossener Installation sperren.
# Gesperrt wird nur, wenn der Installations-Marker storage/.installed existiert –
# vorher muss install.php für die Erstinstallation erreichbar bleiben (sonst 403
# auf jeder Neuinstallation). Liegt storage/ außerhalb des Standardpfads, greift
# weiterhin die PHP-seitige Sperre in install.php (Marker-/APP_KEY-Prüfung).
# Unabhängig davon: install.php nach der Installation löschen (Pflicht).
RewriteCond %{DOCUMENT_ROOT}/../storage/.installed -f
RewriteRule ^install\.php$ - [F,L]

# SICHERHEIT: /storage/ komplett für direkten Web-Zugriff sperren (403)
# Dateien dort werden nur über PHP-Controller mit Auth-Prüfung ausgeliefert
RewriteRule ^storage(/.*)?$ - [F,L]

# SICHERHEIT: /uploads/ durch PHP-Authentifizierung leiten
# (verhindert direkten Dateizugriff ohne Login, auch wenn die Datei real existiert)
# Ausnahme: Öffentliche Login-Seiten-Assets (Logo, Favicon, Login-Hintergrund).
# Das Self-Service-Portal-Hintergrundbild wird bewusst NICHT freigegeben, sondern
# nur nach Login über /portal/branding-background gestreamt.
RewriteCond %{REQUEST_URI} ^/uploads/
RewriteCond %{REQUEST_URI} !^/uploads/logo/
RewriteCond %{REQUEST_URI} !^/uploads/favicon/
RewriteCond %{REQUEST_URI} !^/uploads/login_bg/
RewriteRule ^(.*)$ index.php [QSA,L]

# Front-Controller: Alle übrigen Anfragen an index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

# SICHERHEIT: PHP-Fehlerausgabe unterdrücken (Production)
php_flag display_errors Off
php_flag log_errors On

# SICHERHEIT: Limitiere Upload-Größe
php_value upload_max_filesize 50M
php_value post_max_size 52M
php_value max_execution_time 300

# SICHERHEIT: Session-Einstellungen
php_value session.cookie_httponly 1
php_value session.cookie_secure 1
php_value session.use_strict_mode 1
php_value session.cookie_samesite Lax

# SICHERHEIT: HTTP-Header
<IfModule mod_headers.c>
    # Verhindere MIME-Type-Sniffing
    Header always set X-Content-Type-Options "nosniff"

    # Verhindere Clickjacking (SAMEORIGIN erlaubt draw.io iframe von gleicher Domain)
    Header always set X-Frame-Options "SAMEORIGIN"

    # Hinweis: X-XSS-Protection wurde bewusst entfernt (in modernen Browsern
    # abgekündigt/deaktiviert). Maßgeblich ist die Content-Security-Policy.

    # Referrer-Policy
    Header always set Referrer-Policy "strict-origin-when-cross-origin"

    # Permissions-Policy
    Header always set Permissions-Policy "geolocation=(), microphone=(), camera=()"

    # HSTS (nur bei HTTPS)
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" env=HTTPS

    # WICHTIG (Cache-Strategie):
    # - Dynamische Seiten (HTML) werden NICHT gecacht. Das setzt PHP bereits pro
    #   Antwort auf "no-store" (src/Core/Response.php) -> Echtzeit. Tickets &
    #   andere Modul-Ansichten kommen damit nie aus dem Browser-Cache.
    # - NUR statische, mit ?v=<filemtime> versionierte Assets (CSS/JS/Fonts/Bilder)
    #   werden lange + immutable gecacht. Da sich die URL bei jeder Änderung ändert,
    #   ist das gefahrlos und spart bei jedem Klick ~190 KB CSS/JS-Re-Download.
    <FilesMatch "\.(css|js|mjs|map|woff2|woff|ttf|otf|eot|png|jpe?g|gif|svg|webp|ico)$">
        Header set Cache-Control "public, max-age=31536000, immutable"
    </FilesMatch>
    # Ausnahme: Service-Worker NIE immutable cachen (sonst bleiben Nutzer auf einer
    # alten Version hängen). Wird ohnehin bei jeder Update-Prüfung revalidiert.
    <Files "sw.js">
        Header set Cache-Control "no-cache, max-age=0, must-revalidate"
    </Files>
</IfModule>

# Komprimierung aktivieren
<IfModule mod_deflate.c>
    AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript application/json image/svg+xml application/wasm
</IfModule>

# Cache für statische Assets (Fallback ohne mod_headers; FilesMatch oben hat Vorrang)
<IfModule mod_expires.c>
    ExpiresActive On
    ExpiresByType image/jpeg "access plus 1 year"
    ExpiresByType image/png "access plus 1 year"
    ExpiresByType image/gif "access plus 1 year"
    ExpiresByType image/svg+xml "access plus 1 year"
    ExpiresByType image/webp "access plus 1 year"
    ExpiresByType font/woff2 "access plus 1 year"
    ExpiresByType text/css "access plus 1 year"
    ExpiresByType application/javascript "access plus 1 year"
</IfModule>

