Commit Graph

5 Commits (1d0bacc65070c34f5780c92760793dbf7cd2849d)

Author SHA1 Message Date
nexxo 1fd5d5f8d2 Was der Auftrag ausfuehrt, ist die Absicht von jetzt, nicht die von vorhin
Drei Befunde aus dem Gesamt-Review, alle dieselbe Klasse: die Zeile im Portal
behauptet etwas, das in der Cloud des Kunden nicht gilt.

K1 — Entziehen, waehrend die Einladung noch in der Warteschlange steht. Der
Arbeiter teilt sich die Warteschlange mit der bezahlten Bereitstellung; das
Fenster ist Minuten lang. queueSync() stieg bei nc_synced_at === null aus und
kannte damit den dritten Waechter-Fall nicht: bei `pending` entsteht dort
gerade etwas, das gesperrt werden muss. Und der invite-Auftrag liest den Status
jetzt am Ende frisch aus der Datenbank nach und schiebt bei revoked/suspended
ein disable hinterher — dieselbe Begruendung wie beim schon gebauten "ein
angelegtes Konto sofort vermerken". Vorher endete der Ablauf mit
status=revoked, nc_state=synced und einem aktiven Konto in der Nextcloud, ohne
jeden Knopf, es nachzuholen.

K2 — retry() konnte ein gescheitertes Entsperren nie wiederholen: die Ableitung
kannte disable, invite und role, aber kein enable, und waehlte deshalb role.
Der Auftrag fuhr Gruppen und Quota, gelang, die Zeile sprang auf "Aktiv" — und
user:enable war nie geschickt. Woran das zu erkennen waere, steht nirgends am
Sitz; ein Feld dafuer waere die naechste Behauptung ueber die Cloud, die
irgendwann nicht mehr stimmt. Deshalb raet retry() nicht, sondern schickt an
einer offenen Zeile beides: der neue Auftrag `restore` sperrt auf UND setzt die
Rolle. suspend() bleibt bei enable, denn dort ist bekannt, was fehlt.

W2 — der Inhaber konnte sich selbst aussperren. setRole() nahm jede Rolle aus
Seat::ROLES an, also auch owner; das Auswahlfeld bietet sie nicht an, die
Livewire-Methode ist trotzdem oeffentlich erreichbar. Mit zwei Inhaber-Sitzen
griff die Zaehlung in revoke() nicht mehr, und der echte Inhaber bekam
user:disable admin samt user:auth-tokens:delete admin in seine eigene Cloud.
revoke() und setRole() weisen owner jetzt genauso ab wie suspend(), und owner
ist keine zulaessige Zielrolle mehr. Damit faellt die Zaehlung selbst weg —
eine Sperre, die man sich erst erarbeiten muss, ist keine — und mit ihr die
Meldung users.last_owner.

Sechs Pruefungen, jede einzeln gegen den zurueckgedrehten Fix rot gesehen.
Umlaute in den beruehrten Dateien nachgezogen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 23:56:48 +02:00
nexxo 3704620bb5 Ein Knopf, der nur wieder rot zurueckkommt, und eine Zeile, die niemand einlud
Zwei Reste aus dem letzten Fix, beide dieselbe Klasse: die Zeile behauptet etwas
ueber den Gast, das dort nicht gilt.

Der Wiederholen-Knopf an einer entzogenen, nie gespiegelten Zeile lief auf
user:disable gegen einen Benutzer, den es nie gab — scheitert, dieselbe rote
Zeile, beliebig oft. retry() traegt jetzt denselben nc_synced_at-Vorbehalt wie
queueSync(), mit invite als Ausnahme, denn genau dafuer ist der Knopf da. Und
gezeichnet wird er nur noch, wo der Server ihn auch annimmt.

Nach einer Wiederaufnahme stand nc_state auf synced und status auf invited, also
zeichnete die Spalte "Eingeladen" — fuer jemanden, den niemand eingeladen hat
und dessen Konto im Gast weiter gesperrt ist. nc_state geht beim Wiederaufnehmen
zurueck auf none; nc_username und nc_synced_at bleiben, denn an letzterem haengt
das Entsperren beim folgenden Einladen. Eine Pruefung geht den ganzen Weg.
2026-08-03 22:46:32 +02:00
nexxo 02a5120b9c Der Weg zurueck geht durch die Vordertuer, und ein Fehlschlag bleibt sichtbar
Der letzte Fix hat zwei eigene Loecher gerissen, beide dieselbe Klasse wie das,
was er schliessen sollte: die Oberflaeche versicherte etwas, das im Gast nicht
eingetreten war.

Entzogen stand VOR dem Fehlschlag, also verschwanden Grund und
Wiederholen-Knopf genau dort, wo sie am noetigsten sind: scheitert das disable,
ist das Konto weiter offen, waehrend die Zeile "entzogen" sagt. Es gibt keinen
Wiederholungslauf und keinen Abgleich, und diese Seite ist die einzige, die
nc_state anzeigt. Beides steht jetzt nebeneinander.

Und die Meldung empfahl einen neuen Sitz, den die Eindeutigkeit von
(customer_id, email) unmoeglich machte. addSeat() nimmt einen entzogenen Sitz
derselben Adresse wieder auf — durch dieselbe Platzpruefung wie jeder neue,
denn das ist der Grund, warum es die Vordertuer sein muss und kein Knopf an der
Zeile. Der Auftrag entsperrt dabei, was revoke() gesperrt hat; user:welcome tut
das nicht.

retry() bekommt die owner-Wache nach, die bisher nur am Knopf davor hing. Und
der Kommentar in mount() behauptet keine Heilung mehr, die nicht stattfindet.
2026-08-03 22:32:45 +02:00
nexxo a569d2215b Entzogen bleibt entzogen, und der Inhaber laedt sich nicht selbst ein
Dass revoke() nicht mehr loescht, hat eine Kehrseite, die nirgends stand: der
Umschalter in suspend() kannte nur zwei Zustaende und machte aus einem
entzogenen Sitz beim zweiten Klick wieder einen aktiven — samt user:enable und
vorbei an der Platzgrenze, die nur beim Anlegen geprueft wird. sendInvite() bot
denselben Weg. Beide weisen 'revoked' jetzt ab, die Zeile traegt keinen
Handlungsknopf mehr, und wieder aufmachen kann man sie gar nicht: der Weg
zurueck ist ein neuer Sitz.

Der Einladen-Knopf stand auch an der Inhaber-Zeile. Ein Klick vor der fertigen
Bereitstellung haette spaeter ein zweites Konto in der Gruppe admin angelegt,
neben dem echten — und mount() haette den Sitz danach nie wieder verknuepft,
weil die Bedingung am Zustand hing statt am Anmeldenamen.

queueSync() entscheidet ueber nc_synced_at statt ueber nc_state: ein Sitz,
dessen Einladung an einem unerreichbaren Gast scheiterte, hat dort nichts, was
man sperren koennte. Damit das keine Luecke reisst, vermerkt der Auftrag ein
angelegtes Konto sofort, auch wenn die Rolle danach scheitert.
2026-08-03 22:14:19 +02:00
nexxo 0e3a76a7cb Aus der Attrappe wird Verwaltung
Anlegen und Einladen sind zwei Vorgaenge. Einladen schickt einen Auftrag auf
die Bereitstellungs-Warteschlange — die einzige, die einen Gast erreicht — und
der Sitz zeigt danach, was WIRKLICH passiert ist, samt Grund und
Wiederholen-Knopf. Ohne das drueckt der Inhaber wieder und wieder.

Entziehen loescht nichts mehr. Ratelimit 10 je Kunde und 3 je Sitz pro Stunde,
mit echter Restzeit in der Meldung statt stummer Verweigerung.

Eine Pruefung verbietet user:delete im ganzen app/-Verzeichnis.
2026-08-03 21:52:59 +02:00