feat(nextcloud-files): Teilen in Modul-Changelog, Anleitungen und Changelog

- Modul-Changelog: unveroeffentlichter Eintrag 1.0.0 um drei Teilen-Punkte erweitert (Version bleibt 1.0.0)
- CHANGELOG.md: Dateien, Teilen und Dateien, Uebersicht der Freigaben
- Anleitungen Anwender, Administration, Betrieb, Entwicklung beschreiben das Teilen
- Live-Test e2e-shares.sh: Abschnitt version
- Teilen-Dialog: Linkformular scrollt beim Oeffnen in den sichtbaren Bereich
- Mit mir geteilt: offene Freigaben zeigen kein irrefuehrendes Eigene Berechtigung

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-10-09 11:46:18 +02:00
parent c3adf851b8
commit 0585fd17c4
11 changed files with 129 additions and 5 deletions
+55
View File
@@ -796,3 +796,58 @@ Hintergrund- und Textfarbe über die CSS-Variablen laufen, nicht über `dark:`-U
Oberfläche wird also grundsätzlich dunkel, nur Feinheiten (Status-, Warn- und Fehlerfarben,
Hinweisboxen, Badges, wie im Projekt bereits an über 100 Stellen betroffen) bleiben falsch. Jede neue
`dark:`-Utility-Klasse im Projekt setzt voraus, dass diese Zeile in `globals.css` unverändert bleibt.
**Dateien (Nextcloud), Teilen (quick-261009-dkv):** Das Teilen läuft über eine eigene
OCS-Schicht, `apps/api/src/nextcloud-files/nextcloud-shares.ts`, und nicht über den Anmelde-Helfer
`ocsRequest` aus `nextcloud-auth-client.ts`. Der Anmeldecode wertet einen 403 als „App-Passwort
vorhanden“ und wirft den Text jeder Fehlerantwort weg; beim Teilen steht genau dort die Begründung
der Nextcloud (`ocs.meta.message`), und ein 403 heißt „nicht erlaubt“. Die Schicht liest den
Antwortkörper deshalb bei jedem Status (Erfolg bis 8 MiB, Fehler bis 64 KiB, Fähigkeiten bis
1 MiB), bleibt sonst aber bei den Regeln der Etappe 1: fester Pfadanfang `/ocs/v2.php/`, jedes
Segment einzeln codiert, keine Weiterleitungen, keine Cookies, und es wird nie eine Adresse aus
einer Nextcloud-Antwort aufgerufen (weder die `url` einer Freigabe noch `api.generate`). Die
Parser bauen kleine eigene Ansichten; Passwort (`hasPassword` statt Wert), Kennung und Adressen
fremder Links verlassen sie nie.
Der Browser schickt nie Bitmasken. Die Routen (`shares/policy`, `shares/by-path`, `sharees`,
`shares/mine`, `shares/received`, `POST shares`, `PUT/DELETE shares/:id`, `POST shares/:id/accept`)
nehmen nur die Aufzählungen `kind` (`user`, `group`, `link`) und `access` (`view`, `edit`,
`upload`); `permissionsFor` rechnet sie selbst in Bits um, Eintragsart und Schreibrecht kommen aus
einer eigenen PROPFIND- bzw. GET-Abfrage, das Teilen-Bit 16 wird nie gesendet. Die statischen
Routen stehen vor den `:id`-Routen (das Controller-Spec prüft die Reihenfolge). Die Freigaberegeln
(Links erlaubt, Passwortpflicht, Ablaufdatum mit Tagen, öffentliches Hochladen, Gruppen,
Mindestlänge) liest der Dienst bei **jedem** Schreibzugriff frisch aus `cloud/capabilities` des
jeweiligen Benutzers und prüft vorab; es gibt keinen Zwischenspeicher. Nextcloud selbst
beantwortet die Fähigkeiten nach einer `occ`-Änderung noch einige Sekunden aus dem Zwischenspeicher
(der Live-Test wartet darauf mit `policy_wait`).
Fehlerabbildung (`mapShareFailure`, immer nach Aufruf, Status und gesendeten Feldern, **nie nach
dem Text** der Nextcloud, der übersetzt sein kann): Nextcloud verschweigt bei `PUT` die Gründe, und
Ablauffehler kommen als **404**, nicht als 400 (gemessen: `PUT` mit Ablauf jenseits des Höchstwerts
ergibt 404, ein schwaches Passwort 400). Daraus folgt: 400 mit gesendetem Passwort ist
`sharePasswordRejected`, 404 oder (beim Ändern) 400 mit gesendetem Ablaufdatum ist
`shareExpiryInvalid`, 404 ohne beides ist beim Anlegen `shareRecipientInvalid` (Link: `notFound`),
beim Ändern `shareNotFound`; 401 setzt die Verbindung auf „abgelaufen“, 429 und ab 500 laufen
unverändert durch `mapNcFailure`, und ein 403 der Nextcloud kommt als `shareRejected` (422) beim
Browser an. 401 und 403 verlassen die API nie. Die Datums-Eingabe prüft der DTO streng
(`YYYY-MM-DD` plus echte Datumsprüfung `isRealDate`), weil Nextcloud Daten großzügig parst
(`31.12.2026x` würde angenommen).
Zwei Fallen beim Anlegen: Ein `POST` für einen Empfänger, der die Freigabe schon hat, liefert die
alte Freigabe zurück und **verschickt die Benachrichtigung erneut**. Der Dienst prüft deshalb vorab
über `shares/by-path`, ob es sie schon gibt (`shareAlreadyExists`, 409), und ändert Berechtigungen
immer per `PUT`. Außerdem begrenzt Tessera selbst auf 15 neue Freigaben je Benutzer in 10 Minuten
(`checkShareCreate`, im Arbeitsspeicher, zählt auch Versuche, die die Nextcloud ablehnt;
unmittelbar vor dem `POST`, nach allen Vorprüfungen). Das liegt absichtlich unter dem Limit der
Nextcloud (20 in 600 s): Deren 429 würde die Aufrufsperre des Ursprungs auslösen, und die hielte
die Anfragen aller Benutzer 15 Minuten an. Die Oberfläche legt immer nur eine Freigabe zugleich an.
Live-Test: `.planning/quick/261009-dkv-modul-dateien-etappe-2a-teilen-von-datei/e2e/e2e-shares.sh
[people|links|received|version|all]` gegen die Test-Nextcloud (`nc-test-setup.sh` der Etappe 1
vorher). Er legt Benutzer `ben` und Gruppe `tessera-team` an, schaltet die Ratenbegrenzung der
Nextcloud (`ratelimit.protection.enabled`) nur für den Lauf aus und stellt die Regeln über
`occ config:app:set core …` um (`shareapi_enforce_links_password`, `shareapi_default_expire_date`,
`shareapi_enforce_expire_date`, `shareapi_expire_after_n_days`; für die offenen Freigaben
`user:setting anna files_sharing default_accept`). Alles setzt der `trap` zurück. Der Zähler von
Tessera liegt im Prozess: `all` verbraucht 7 der 15 Plätze, nach höchstens zwei `all`-Läufen
`docker compose restart api`.