feat(calendar): Kalender an Nutzer und Gruppen freigeben

Nicht als eigene Tabelle, wie im Plan vorgesehen: acl_entries beantwortet
fuer Ordner und Dateien laengst dieselbe Frage -- wer darf was, direkt oder
ueber eine Gruppe. Ein zweites Rechtesystem daneben waere eine zweite
Antwort auf dieselbe Frage, mit eigener Gruppenaufloesung, eigenem
Widerruf, eigenem Pruefpfad; die Luecke dazwischen faellt erst auf, wenn
jemand sieht, was er nicht sehen soll. Migration 25 haengt deshalb nur eine
dritte Ressourcenspalte an.

Der Gewinn: Freigeben an eine **Gruppe** kostete keine Zeile extra.

Drei Entscheidungen, die nicht offensichtlich waren:

* Ein fremder Kalender steht immer unter seiner UUID, nie unter dem
  Pfadsegment des Eigentuemers -- das hat der sich ausgesucht, und zwei
  Leute duerfen denselben Namen waehlen. Sonst laegen im Kalenderbestand
  des Empfaengers zwei Kalender auf derselben Adresse.
* Alle Wege laufen durch RequireCalendar. CalDAV und Weboberflaeche sind
  zwei getrennte Zugaenge zu denselben Daten; ein Recht, das nur einer von
  beiden prueft, ist kein Recht. Im CalDAV-Teil genuegte, resolveCalendar
  umzustellen -- dort kommt jede Operation vorbei.
* Wer ein Recht geliehen hat, darf es nicht weiterverleihen: Umbenennen,
  Loeschen und Freigeben bleiben auch mit Schreibrecht beim Eigentuemer.

Oberflaeche: fremde Kalender tragen Eigentuemer und "(nur lesen)", das
Menue zeigt nur, was auch geht, und ein Termin laesst sich gar nicht erst
in einen Kalender ziehen, den der Server danach ablehnen wuerde.

scripts/kalender-teilen-selbsttest.py, 41 Pruefungen, jede ueber beide
Zugaenge. Er beginnt damit, dass der Empfaenger vorher nichts sieht --
ohne diesen Anfang beweist der Rest nichts.

Dabei hatte der Test selbst eine Falle: Mit festem Testdatum lief die
Terminliste aus ihrem Standardzeitraum, und die Pruefung "beim Eigentuemer
ist nichts angekommen" bestand **leer**.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
marcandClaude Opus 5 committed 2026-09-12 13:46:18 +02:00
1 parent 8fdde182b7
commit 5d90c0a2d2
21 files changed
+1031 -60

No files matched your search

+30
View File
@@ -202,6 +202,36 @@ Ruf ein `403` und sucht die Ursache bei den Rechten. Die Weboberfläche zieht
den neuen Wert an allen drei Stellen nach (`hooks/useAuth.ts`); ein Skript
muss es auch.
## Kalender teilen
python3 scripts/kalender-teilen-selbsttest.py
Eine Rechtefunktion ist so viel wert wie ihre engste Stelle. Aether hat **zwei**
Zugänge zu denselben Kalendern – die Weboberfläche und CalDAV –, und beide
prüfen getrennt. Ein Recht, das nur einer von beiden durchsetzt, ist keines;
deshalb geht hier jede Prüfung über beide Wege.
41 Prüfungen in sieben Gruppen, und die Reihenfolge ist der Punkt: **vorher
sieht der Empfänger nichts** (ohne diesen Anfang beweist der Rest nichts – ein
Kalender, den ohnehin jeder sieht, ist auch nach dem Freigeben sichtbar), dann
nur lesend (sehen ja, ändern nein – und beim Eigentümer kommt nachweislich
nichts an), schreibend, die **Grenzen** des Schreibrechts (fremden Kalender
umbenennen, löschen oder weiterverschenken geht auch dann nicht), Freigabe über
eine **Gruppe**, `sync-collection` auf einem fremden Kalender, und der
Widerruf.
Der Test legt zwei Konten, eine Gruppe und einen Kalender an und räumt am Ende
alles weg.
### Eine Falle, die der Test selbst hatte
Die Terminliste hat einen Standardzeitraum (ein Monat zurück bis ein Jahr
voraus). Mit einem festen Testdatum lieferte sie irgendwann eine leere Liste –
und die Prüfung „beim Eigentümer ist nichts angekommen" **bestand dann leer**.
Eine Prüfung, die ohne Daten besteht, prüft nichts. Jetzt liegen die Testtermine
relativ zum heutigen Tag, der Zeitraum wird ausdrücklich mitgegeben, und eine
Zeile davor stellt sicher, dass überhaupt etwas in der Liste steht.
## Notfall: Migration hängt fest ("Dirty database version")
Betrifft jede künftige Migration, nicht nur eine bestimmte. Nachgestellt und