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 }