feat(aether): initial commit – self-hosted Cloud, Phasen 1–7

Go-Backend (chi, pgx, golang-migrate) + React/TS-Frontend als PWA.
Enthalten: Auth inkl. OIDC, Dateien mit Versionen und Papierkorb,
Freigaben (Links + intern), WebDAV, Gruppenordner, Admin/Theming,
Volltextsuche mit OCR, eigener PDF-Betrachter und WOPI-Office
(Collabora/OnlyOffice).

Nicht im Repo: deploy/.env (Sitzungsschlüssel, DB- und Admin-Passwort)
sowie alle Daten- und Build-Verzeichnisse – siehe .gitignore.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
HoneyandClaude Opus 5 committed 2026-08-24 09:25:36 +02:00
commit 31a027c93a
225 files changed
+38042

No files matched your search

+55
View File
@@ -0,0 +1,55 @@
package middleware
import (
"net/http"
"net/url"
"strings"
)
// SecurityHeaders adds security headers that the app controls.
// HSTS is intentionally omitted — it belongs on the edge reverse proxy.
//
// frameSrc nennt zusätzliche Ursprünge, die in einem Rahmen erscheinen dürfen.
// Gebraucht für den Office-Server: Seine Bedienoberfläche läuft in einem
// <iframe>, und `default-src 'self'` deckt auch `frame-src` ab – ohne
// Ausnahme blockt der Browser sie, noch bevor eine Anfrage hinausgeht.
// Gemessen als „Framing 'http://…:9980/' violates … default-src 'self'".
//
// Bewusst **nur der eine konfigurierte Ursprung** und kein Platzhalter: Die
// Ausnahme soll so schmal sein wie der Anlass.
func SecurityHeaders(frameSrc ...string) func(http.Handler) http.Handler {
frame := "frame-src 'self'"
for _, raw := range frameSrc {
if origin := originOf(raw); origin != "" {
frame += " " + origin
}
}
csp := "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; " +
"img-src 'self' data: blob:; " + frame
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
h := w.Header()
h.Set("X-Content-Type-Options", "nosniff")
h.Set("X-Frame-Options", "DENY")
h.Set("Referrer-Policy", "strict-origin-when-cross-origin")
h.Set("Content-Security-Policy", csp)
next.ServeHTTP(w, r)
})
}
}
// originOf reduziert eine Adresse auf Schema und Host. Ein Pfad in einer
// CSP-Quelle wäre nicht falsch, aber enger als gemeint – und ein Tippfehler
// darin fiele erst auf, wenn der Rahmen leer bleibt.
func originOf(raw string) string {
raw = strings.TrimSpace(raw)
if raw == "" {
return ""
}
u, err := url.Parse(raw)
if err != nil || u.Scheme == "" || u.Host == "" {
return ""
}
return u.Scheme + "://" + u.Host
}