Files
Aether/backend/db/migrations/000025_calendar_share.up.sql
T
marcandClaude Opus 5 5d90c0a2d2 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>
2026-09-12 13:46:18 +02:00

32 lines
1.7 KiB
SQL

-- Kalender an andere Konten freigeben.
--
-- **Keine zweite Rechtetabelle.** acl_entries beantwortet fuer Ordner und
-- Dateien bereits genau diese Frage -- wer darf was, direkt oder ueber eine
-- Gruppe. Ein eigenes cal_shares daneben waere ein zweites Rechtesystem mit
-- eigener Gruppenaufloesung, eigenem Widerrufen, eigenem Pruefpfad; zwei
-- Systeme fuer dieselbe Frage laufen auseinander, und die Luecke faellt erst
-- auf, wenn jemand sieht, was er nicht sehen soll.
--
-- Nebeneffekt, der die Entscheidung traegt: Das Freigeben an eine **Gruppe**
-- kommt damit ohne eine einzige Zeile zusaetzlichen Code.
ALTER TABLE acl_entries
ADD COLUMN IF NOT EXISTS resource_calendar_id UUID REFERENCES cal_calendars(id) ON DELETE CASCADE;
-- Genau eine Ressource je Eintrag -- jetzt zu dritt.
ALTER TABLE acl_entries DROP CONSTRAINT IF EXISTS acl_one_resource;
ALTER TABLE acl_entries ADD CONSTRAINT acl_one_resource
CHECK (num_nonnulls(resource_folder_id, resource_file_id, resource_calendar_id) = 1);
-- Zwei Freigaben derselben Ressource an denselben Empfaenger waeren
-- mehrdeutig: welches Recht gilt dann?
CREATE UNIQUE INDEX IF NOT EXISTS idx_acl_user_calendar
ON acl_entries (subject_user_id, resource_calendar_id)
WHERE subject_user_id IS NOT NULL AND resource_calendar_id IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS idx_acl_group_calendar
ON acl_entries (subject_group_id, resource_calendar_id)
WHERE subject_group_id IS NOT NULL AND resource_calendar_id IS NOT NULL;
-- Die Zugriffspruefung laeuft bei jedem CalDAV-Durchgang.
CREATE INDEX IF NOT EXISTS idx_acl_calendar
ON acl_entries (resource_calendar_id) WHERE resource_calendar_id IS NOT NULL;