5 · Anmeldung und Rechte
Es gibt drei Ebenen, und sie greifen unabhängig voneinander:
- Wer darf die Seite sehen? —
öffentlich,angemeldet,nur … - Wer darf den Datensatz sehen? —
sichtbar …im Model - Wer darf ihn ändern oder löschen? — die Eigentümer-Regel und
aenderbar …
Anmeldung
Sobald es ein Model Nutzer (oder User) gibt, kann man sich anmelden. Mehr ist nicht nötig — kein zusätzlicher Schalter.
modell Nutzer
name text
rolle text = "kunde"Damit gibt es:
- eine Anmeldeseite, die Fibel selbst mitbringt
ichals Wert im ganzen Programmknopf "Abmelden" -> abmeldenformular anmeldung, falls du die Anmeldung irgendwo einbauen willst
Wie angemeldet wird, sagt zugang:
zugang passwort # Vorgabe: Name und Passwort, mindestens 8 Zeichen
zugang offen # es reicht ein Name — für Kurse und VorführungenPasswort vergessen
Jeder bekommt beim Anlegen des Kontos einmalig einen Wiederherstellungscode zu sehen:
K7M4-P2XR-9WTD-4FQHDer wird ebenfalls nur als Hash gespeichert; Fibel selbst kann ihn nicht mehr lesen. Wer ihn benutzt, setzt damit ein neues Passwort, alle offenen Sitzungen enden, und es gibt einen neuen Code. Der alte gilt dann nicht mehr.
Der Rückweg per Post
Ein Code, den man genau einmal sieht, wird von vielen Menschen nicht aufgeschrieben. Deshalb gibt es einen zweiten Weg — er kostet ein Feld:
modell Nutzer
name text
email emailSteht am Nutzer-Model ein Feld der Art email, dann fragt das Anmeldeformular nach der Adresse, und unter „Passwort vergessen" steht „Code schicken". Der zugeschickte Code gilt 30 Minuten und genau einmal; danach ist er verbraucht, ob er benutzt wurde oder nicht. Eingetragen wird er in dasselbe Feld wie der aufgeschriebene — die Seite prüft beide.
Verschickt wird über denselben Weg wie jede andere Post, also über FIBEL_POST (siehe 14 · Post). Ohne Zugangsdaten in der Umgebung geht auch dieser Code nicht hinaus, sondern in die Ablage <app>.post/. Beim Entwickeln ist das praktisch; wer eine App ins Netz stellt und den Rückweg anbietet, muss den Versand eingerichtet haben — sonst wartet jemand auf eine Nachricht, die in einem Ordner liegt.
Zwei Dinge sind mit Absicht so und nicht anders:
- Die Antwort ist immer dieselbe, ob es zu der Adresse ein Konto gibt oder nicht. „Kenne ich nicht" wäre eine Auskunft darüber, wer hier ein Konto hat — und die hat niemand geben wollen.
- Die Adresse wird nicht bestätigt. Wer sich vertippt, bekommt keinen Code und merkt es erst, wenn er ihn braucht. Eine Bestätigung wäre ein zweiter Weg mit eigener Frist und eigener Seite; solange es sie nicht gibt, steht das hier.
Wenn Passwort, Code und Postfach weg sind, hilft nur noch die Kommandozeile:
fibel zugang meine-app AnkeDas setzt ein neues Passwort für ein vorhandenes Konto — auf dem Rechner, auf dem die App liegt.
Ebene 1 · Wer darf die Seite sehen
Als erste Zeile in der Seite:
seite / als "Start"
oeffentlich
titel "Willkommen"
seite /profil als "Mein Profil"
angemeldet
titel ich.name
seite /verwaltung als "Verwaltung"
nur ich.rolle == "verwalter"
titel "Alle Bestellungen"Ohne Angabe gilt angemeldet, sobald es ein Nutzer-Model gibt — sonst öffentlich. Die Vorgabe ist absichtlich die vorsichtige: wer eine Seite öffentlich haben will, muss es hinschreiben.
Ein verweis, der den Betrachter in eine Absage führen würde, wird gar nicht erst gezeigt — auch nicht im Menü. Einer, hinter dem nur die Anmeldung wartet, bleibt dagegen auch für Gäste sichtbar; Seiten mit nur … bleiben ihnen verborgen.
Ebene 2 · Wer darf den Datensatz sehen
modell Nachricht
an Nutzer
von Nutzer = ich
text langtext
am zeit = jetzt
sichtbar eintrag.an == ich oder eintrag.von == ichDer Datensatz heißt in der Bedingung eintrag. Wer die Bedingung nicht erfüllt, sieht ihn nirgends:
- nicht in
fuer … in Nachricht - nicht über
anzahl Nachricht - nicht über einen Platzhalter im Pfad (
/nachricht/:nachrichtgibt 404) - und kann ihn auch nicht löschen — die Aktion prüft dieselbe Regel
Das ist wichtig: Das Leserecht wird an einer Stelle durchgesetzt, nämlich an einer Hülle um den Datenspeicher. Es gibt keinen Weg an ihr vorbei, weil jeder Zugriff durch dieselbe Tür geht.
Und die Dateien?
Eine hochgeladene Datei ist so sichtbar wie der Datensatz, der auf sie zeigt. Dafür muss man nichts hinschreiben, und man kann es nicht vergessen:
modell Stein
bild bild optional # kein "sichtbar" -> das Foto sieht jeder
modell Zustellung
unterschrift unterschrift optional
fahrer Nutzer = ich
sichtbar eintrag.fahrer == ich # -> die Unterschrift auch nur der FahrerEin Produktfoto ist damit öffentlich, eine Unterschrift nicht — ohne dass irgendwo eine zweite Regel steht, die man mit der ersten in Einklang halten müsste.
Zwei Sonderfälle:
- Gerade hochgeladen, Formular noch offen. Dann zeigt noch kein Datensatz auf die Datei. Für eine Weile sieht sie deshalb, wer sie hochgeladen hat — sonst wäre die Vorschau im Formular leer.
- Verwaist. Zeigt niemand mehr darauf, ist sie für niemanden mehr da.
fibel aufräumenentfernt sie.
In beiden Fällen kommt eine 404 zurück, keine 403. Ob es eine Datei gibt, ist selbst schon eine Auskunft.
Ebene 3 · Wer darf ändern und löschen
Hier gibt es keine eigene Schreibweise, sondern eine Regel:
Wer einen Datensatz angelegt hat, darf ihn ändern und löschen. Sonst niemand.
Erkannt wird das an einem Feld, das auf = ich steht:
modell Beitrag
inhalt langtext
autor Nutzer = ich # ← hieran hängt die RegelHat ein Model kein solches Feld, darf jeder Angemeldete ändern und löschen. Das ist manchmal richtig (ein gemeinsamer Einkaufszettel) und oft nicht — also: wenn es einen Eigentümer geben soll, gib dem Model ein = ich-Feld.
Ein festgeschrieben im Model hebt das Ändern für alle auf, auch für den Eigentümer.
Wenn noch jemand anders ändern darf
In einem Laden muss die Verwaltung eine Bestellung weiterstellen können, ohne sie zu besitzen. Die Eigentümer-Regel allein reicht dafür nicht — sie kennt nur den, der etwas angelegt hat. Dafür gibt es ein zweites Schreibrecht:
modell Bestellung
stein Stein
besteller Nutzer = ich
stand text = "eingegangen"
sichtbar eintrag.besteller == ich oder ich.rolle == "verwalter"
aenderbar ich.rolle == "verwalter"aenderbar erlaubt zusätzlich, es nimmt nie etwas weg: Wem der Eintrag gehört, der darf ihn weiterhin ändern. Und festgeschrieben schlägt beides — ein Nachweis bleibt ein Nachweis.
Wie sichtbar heißt der Eintrag in der Bedingung eintrag, und wie sichtbar wird die Regel an genau einer Stelle durchgesetzt: Ein Ändern-Formular oder ein Löschen-Knopf, den jemand nicht benutzen darf, erscheint gar nicht erst — und wer es trotzdem versucht, bekommt eine Absage.
Wenn ein Feld nicht jedem gehört
Wem ein Datensatz gehört, der darf ihn ändern — und zwar jedes Feld darin. Das ist meistens richtig und manchmal gefährlich. Ein Formular bietet vielleicht nur Name und Anschrift an, aber ein Formular ist nur ein Vorschlag. Wer die Anfrage von Hand baut, schickt mit, was er will — etwa rolle=verwalter an den eigenen Nutzer-Datensatz. Dann ist jeder, der sich registriert, Verwalter.
Dagegen steht ein Schreibrecht am einzelnen Feld:
modell Nutzer
name text
rolle auswahl "kunde" "verwalter" = "kunde" schreibbar ich.rolle == "verwalter"
modell Bestellung
besteller Nutzer = ich
stand text = "eingegangen" schreibbar ich.rolle == "verwalter"schreibbar nimmt weg, anders als aenderbar: Wer die Bedingung nicht erfüllt, kann dieses eine Feld nicht setzen — auch nicht am eigenen Datensatz.
| was geschieht | |
|---|---|
| beim Anlegen | das Feld nimmt nichts aus dem Formular, es gilt die Vorgabe |
| beim Ändern | die ganze Änderung wird abgewiesen |
| im Formular | das Feld erscheint gar nicht erst |
Beim Ändern wird mit Absicht abgewiesen statt still übergangen: Wer ein Feld mitschickt, das ihm kein Formular angeboten hat, versucht etwas. Das gehört beantwortet, nicht heimlich verworfen.
Wie bei aenderbar heißt der Datensatz in der Bedingung eintrag. Beim Anlegen gibt es ihn noch nicht; dann ist er leer.
fibel prüfe hilft dabei: Taucht ein Feld in einer Rechte-Bedingung auf (nur ich.rolle == …), trägt es am Nutzer-Model aber kein schreibbar, kommt eine Warnung. Das ist genau der Fall, in dem sich jeder selbst hochstufen kann.
Der erste Verwalter
Mit dieser Sperre kann niemand mehr über ein Formular Verwalter werden — auch nicht der erste. Das geht am Rechner, auf dem die App liegt:
fibel rolle meine-app Anke verwalterDer Befehl setzt nur das Feld rolle am Nutzer-Model und prüft den Wert gegen die auswahl. Er ist kein Weg um die Regeln herum, sondern der Weg für den, der die Maschine betreibt — wie fibel zugang fürs Passwort.
Wer anlegen darf
Anlegen darf, wer eine Seite sehen darf, auf der ein formular neu <Model> steht. Mehr muss man dafür nicht hinschreiben: Das Formular ist die Zusage, und die Rechte an der Seite sichern sie ab.
seite /verwaltung als "Verwaltung"
nur ich.rolle == "verwalter"
formular neu Stein
feld name "Bezeichnung"
absenden "Stein aufnehmen"Einen Stein legt damit nur an, wer /verwaltung sehen darf — auch wenn jemand die Anfrage von Hand baut. Steht für ein Model nirgends ein Formular, lässt es sich über eine Anfrage gar nicht anlegen. Regeln dürfen es trotzdem; sie handeln mit der Vollmacht des Programms.
Eine Grenze: Ein wenn um das Formular zählt dabei nicht. Steht das Formular auf einer Seite, die jemand sehen darf, darf er anlegen — auch wenn die Bedingung ihm das Formular nicht zeigt. Wer ein Formular nur Verwaltern zeigen will, setzt es auf eine Seite mit nur, nicht hinter ein wenn.
Mit Konto oder ohne — eine Entscheidung je Projekt
Beides geht, und beides ist je nach Vorhaben das Richtige: Ein Laden verkauft oft ohne Konto, eine Vereinsseite nimmt Anmeldungen oft nur mit. Die Wahl steht in einer Zeile — der Seite, auf der das Formular steht:
seite /bestellen als "Bestellen"
oeffentlich # Gäste dürfen bestellen
formular neu Bestellung
feld was "Was?"
absenden "Bestellen"seite /bestellen als "Bestellen"
angemeldet # nur mit Konto
formular neu Bestellung
feld was "Was?"
absenden "Bestellen"Sobald es ein Model Nutzer gibt, ist angemeldet ohnehin die Vorgabe. Ohne eine dieser Zeilen verlangt Fibel also ein Konto — wer Gäste will, schreibt es hin.
Was die Gastbestellung kostet. Ein Datensatz ohne Konto hat keinen Eigentümer. Ein Feld mit = ich bleibt leer, und eine Sichtregel wie sichtbar eintrag.kunde == ich schließt den Gast von seiner eigenen Bestellung aus: Er kann sie anlegen, aber nicht wiederfinden.
Das ist kein Versehen, sondern die Folge derselben Regel, die fremde Bestellungen schützt. Wer dem Gast seine Bestellung trotzdem zeigen will, braucht einen zweiten Weg zu diesem einen Datensatz — eine Bestellnummer zum Nachschlagen oder einen Verweis per post. Ein fertiger Baustein dafür fehlt noch; er steht in OFFEN.md.
Warum = ich nicht aus dem Formular kommt
Ein Feld mit = ich wird beim Speichern immer aus der laufenden Sitzung gefüllt. Schickt jemand es trotzdem mit, wird es verworfen. Sonst könnte man sich fremde Datensätze zuschreiben und über die Eigentümer-Regel Rechte daran bekommen.
Was Fibel sonst noch tut, ohne dass man es hinschreibt
| Sitzungen | ein zufälliger Wert im Keks, HttpOnly und SameSite; die eigentliche Sitzung liegt auf dem Server |
| Passwörter | scrypt, eigenes Salz je Konto, nur als Hash gespeichert |
| Fremde Formulare | jedes Formular trägt eine Marke; ohne die passende wird nichts angenommen |
| Zu viele Versuche | die Anmeldung bremst nach mehreren Fehlversuchen |
| Zu schnell | Ändern und Hochladen sind gebremst — 120 Änderungen je Minute, 60 Uploads je Stunde |
| Falscher Name | „Name oder Passwort stimmt nicht" — die Meldung verrät nicht, ob es den Namen gibt |
| Uploads | geprüft an den ersten Bytes, mit nosniff und einer engen Sicherheitsregel ausgeliefert |
Was Fibel nicht tut
Ehrlicherweise, damit niemand sich täuscht:
- Die Bremse liegt im Arbeitsspeicher eines Prozesses. Nach einem Neustart ist sie leer, und gegen viele verschiedene Adressen hilft sie nicht — dafür ist der Webserver davor zuständig.
- Gebremst wird Schreiben und Hochladen, nicht Lesen. Eine Seite aufzurufen kostet zwar auch etwas; das gehört am Speicher behoben und nicht mit einer Bremse verdeckt.
- Es gibt kein Protokoll, wer was wann geändert hat.
Diese Punkte stehen so auch in OFFEN.md. Wenn eine davon für dein Vorhaben zählt, weißt du es jetzt vorher.
Weiter: Formulare