Files
Aether/backend/internal/files/access.go
T
HoneyandClaude Opus 5 31a027c93a 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>
2026-08-24 09:25:36 +02:00

64 lines
2.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package files
import (
"errors"
"net/http"
"github.com/google/uuid"
"aether/internal/acl"
"aether/internal/auth"
"aether/pkg/response"
)
// Zugriffsprüfung für Dateien und Ordner, die dem Anfragenden nicht selbst
// gehören müssen.
//
// Die Datenbankschicht arbeitet durchgehend eigentümerbezogen (`WHERE owner_id
// = $x`) – daran ändert sich nichts. Stattdessen klären diese Helfer vorab, in
// wessen Namen die Operation läuft: bei eigenen Daten der Anfragende selbst,
// bei geliehenen der Eigentümer. Danach sieht der Rest des Handlers aus wie
// vorher.
// resolveFile liefert den Zugriffskontext für eine Datei. Ist der Zugriff nicht
// erlaubt, ist die Antwort bereits geschrieben und der zweite Rückgabewert
// false – der Handler bricht dann einfach ab.
func (h *Handler) resolveFile(w http.ResponseWriter, r *http.Request, id uuid.UUID, needWrite bool) (acl.Access, bool) {
user := auth.UserFromContext(r.Context())
if h.acl == nil {
return acl.Access{OwnerID: user.ID, OwnerUsername: user.Username, OwnerQuota: user.QuotaBytes, Permission: acl.PermWrite}, true
}
a, err := h.acl.ResolveFile(r.Context(), user.ID, id)
return finish(w, a, err, needWrite, "file")
}
// resolveFolder ist dasselbe für Ordner. folderID darf nil sein – das ist die
// eigene Wurzel, die niemandem sonst gehört.
func (h *Handler) resolveFolder(w http.ResponseWriter, r *http.Request, id *uuid.UUID, needWrite bool) (acl.Access, bool) {
user := auth.UserFromContext(r.Context())
own := acl.Access{OwnerID: user.ID, OwnerUsername: user.Username, OwnerQuota: user.QuotaBytes, Permission: acl.PermWrite}
if h.acl == nil || id == nil {
return own, true
}
a, err := h.acl.ResolveFolder(r.Context(), user.ID, *id)
return finish(w, a, err, needWrite, "folder")
}
func finish(w http.ResponseWriter, a acl.Access, err error, needWrite bool, kind string) (acl.Access, bool) {
switch {
case errors.Is(err, acl.ErrNotFound), errors.Is(err, acl.ErrNoAccess):
// Absichtlich 404 statt 403: Ein „verboten" verriete, dass es die ID
// gibt. Wer keinen Zugriff hat, soll nicht einmal das erfahren.
response.Error(w, http.StatusNotFound, kind+" not found")
return acl.Access{}, false
case err != nil:
response.Error(w, http.StatusInternalServerError, "database error")
return acl.Access{}, false
}
if needWrite && !a.CanWrite() {
response.Error(w, http.StatusForbidden, "you only have read access to this "+kind)
return acl.Access{}, false
}
return a, true
}