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

+90
View File
@@ -0,0 +1,90 @@
package auth
import (
"context"
"os"
"testing"
"github.com/jackc/pgx/v5/pgxpool"
)
// Prüft den Fall, der beim Einschalten von OIDC über eine bestehende
// Installation entscheidet: Wird das vorhandene Konto verbunden – oder entsteht
// daneben ein zweites, leeres?
//
// Läuft nur mit TEST_DSN gegen eine echte Datenbank; ohne wird übersprungen,
// damit `go test ./...` überall grün bleibt.
func TestGetOrCreateByOIDC_Linking(t *testing.T) {
dsn := os.Getenv("TEST_DSN")
if dsn == "" {
t.Skip("TEST_DSN nicht gesetzt")
}
ctx := context.Background()
pool, err := pgxpool.New(ctx, dsn)
if err != nil {
t.Fatalf("verbinden: %v", err)
}
defer pool.Close()
s := NewStore(pool)
// Aufräumen, auch wenn der Test scheitert.
cleanup := func() {
_, _ = pool.Exec(ctx, `DELETE FROM users WHERE username LIKE 'linkprobe%'`)
}
cleanup()
defer cleanup()
hash, _ := HashPassword("irrelevant-aber-gueltig")
if _, err := pool.Exec(ctx,
`INSERT INTO users (username, display_name, password, is_admin)
VALUES ('linkprobe', 'Link Probe', $1, true)`, hash); err != nil {
t.Fatalf("konto anlegen: %v", err)
}
t.Run("ohne Verknüpfung entsteht ein zweites Konto", func(t *testing.T) {
u, err := s.GetOrCreateByOIDC(ctx, "sub-A", "linkprobe", "Link Probe", false)
if err != nil {
t.Fatalf("aufruf: %v", err)
}
if u.Username == "linkprobe" {
t.Fatal("erwartet: ein neues Konto mit abgewandeltem Namen")
}
if u.IsAdmin {
t.Error("ein neu angelegtes Konto darf keine Adminrechte erben")
}
_, _ = pool.Exec(ctx, `DELETE FROM users WHERE oidc_sub = 'sub-A'`)
})
t.Run("mit Verknüpfung bleibt das bestehende Konto", func(t *testing.T) {
u, err := s.GetOrCreateByOIDC(ctx, "sub-B", "linkprobe", "Link Probe", true)
if err != nil {
t.Fatalf("aufruf: %v", err)
}
if u.Username != "linkprobe" {
t.Fatalf("erwartet linkprobe, bekam %q", u.Username)
}
if !u.IsAdmin {
t.Error("Adminrecht des bestehenden Kontos ging verloren")
}
if !u.IsOIDC {
t.Error("Konto sollte jetzt als OIDC-Konto gelten")
}
})
t.Run("erneutes Anmelden findet dasselbe Konto", func(t *testing.T) {
u, err := s.GetOrCreateByOIDC(ctx, "sub-B", "linkprobe", "Link Probe", true)
if err != nil || u.Username != "linkprobe" {
t.Fatalf("wiederholte Anmeldung: %v / %+v", err, u)
}
})
t.Run("fremde Identität übernimmt ein gebundenes Konto nicht", func(t *testing.T) {
u, err := s.GetOrCreateByOIDC(ctx, "sub-FREMD", "linkprobe", "Angreifer", true)
if err != nil {
t.Fatalf("aufruf: %v", err)
}
if u.Username == "linkprobe" {
t.Fatal("ein bereits gebundenes Konto wurde übernommen – das darf nie passieren")
}
})
}