Kapitel 6
Wer was darf, und wie oft
Angemeldet heißt nicht erlaubt
Kapitel 5 hat den Server sicher gemacht, wer fragt. Sicher zu sein, wer jemand ist, sagt noch nichts darüber, was er anfassen darf. Diese Lektion ändert keinen Code. Sie trennt zwei Fragen, die der Server bisher beantwortet hat, als wären sie eine.
Zwei Fragen
Authentifizierung (authentication) fragt „Wer bist du?“. Ein Berechtigungsnachweis, der die Prüfung besteht, beantwortet sie: ein Passwort, ein
Autorisierung (authorization) fragt „Darfst du das tun, mit diesem Ding?“. Eine Regel beantwortet sie, und die Regel muss den Aufrufer, die Aktion und das
„Wer bist du?“ Der Server liest das Token und prüft sein Tag. Kein Token oder eins, das die Prüfung nicht besteht: 401, und die Anfrage endet hier.
„Darfst du das tun, mit diesem Ding?“ Der Server sieht sich den Aufrufer, die
und das Buch an. Keine Regel erlaubt es: 403 oder 404, und die Anfrage endet hier.
Nur eine Anfrage, die beide Tore passiert hat, erreicht den Code, der ein Buch liest oder ändert.
Heute sieht sich das zweite Tor den Aufrufer und die Methode an. Das Buch sieht es sich nie an.
Was im Projekt dieses Kapitels neu ist
Das Projekt, das du in der nächsten Lektion öffnest, ist dein Server aus Kapitel 5 mit drei Änderungen, von denen du keine schreiben musst.
GET /aboutist eine neue, öffentliche Beschreibung des Dienstes. Sie behält das Verhalten mitETagundmax-age, das du in HTTP with Python gebaut hast, auf einer Route, die jeder lesen darf.Die Zugriffsentscheidungen der Bücherrouten stehen jetzt in zwei
, collection_refusalfür/booksunditem_refusalfür/books/{id}. Sie entscheiden genau so, wie dein Code aus Kapitel 5 es getan hat.handlebesteht jetzt aus zwei Zeilen, die eine neue Funktion aufrufen,route, die das ganze Routing enthält. Undrequest["user"]hält fest, als wer sich eine Anfrage ausgewiesen hat. Lektion 5 verwendet beides.
Auch zwei Tatsachen über die Beispielbenutzer ändern sich. ben darf jetzt schreiben. Und es gibt eine dritte Benutzerin, cleo, mit dem Testpasswort paper lantern, die nur lesen darf.
Lies die Entscheidung
Das ist das mitgelieferte item_refusal. Es gibt None zurück, wenn die Anfrage weitergehen darf, oder die Antwort, die sie ablehnt. claims ist die Payload eines verifizierten Tokens oder None.
def item_refusal(method, claims, book):
"""Return None when this method may go on for this book, or the refusal. Lesson 2 rewrites this."""
if method == "PUT" or method == "DELETE":
if claims is None:
return unauthorized("Bearer")
if not claims["can_write"]:
return json_error(403, "this token may read the list but not change it")
if book is None:
return json_error(404, "no book has that id")
if method == "GET" or method == "HEAD" or method == "PUT" or method == "DELETE":
return None
return not_allowed("GET, HEAD, PUT, DELETE")
Frag bei jedem claims is None ist die erste Frage. claims["can_write"] ist eine grobe Form der zweiten: Darf dieses Konto überhaupt etwas schreiben? Keine Zeile fragt, wessen Buch es ist, und keine könnte es, weil ein Buch keinen Eigentümer hat.
Sieh dir auch die Form an. Die Funktion listet auf, was abgelehnt wird, und lässt dann durch, was übrig bleibt.
Das ergibt sich daraus für Anfragen an den Server in seinem jetzigen Zustand. Beide Beispielbücher sollen ana gehören.
| Anfrage | Gesendet von | Antwort jetzt | Richtig? |
|---|---|---|---|
DELETE /books/1 | niemandem, kein Token | 401 | ja |
POST /books | cleo, die nur lesen darf | 403 | ja |
DELETE /books/1 | ben, mit seinem eigenen gültigen Token | 204, und das Buch ist weg | nein |
PUT /books/2 | ben | 200, und das Buch ist ersetzt | nein |
GET /books/1 | ben | 200, mit anas Buch | nein |
GET /books | niemandem, kein Token | 200, mit jedem Buch | nein |
ben hat ein gültiges Token und darf schreiben. Auf dem Server in seinem jetzigen Zustand sendet er DELETE /books/1, und Buch 1 gehört ana. Was gibt item_refusal zurück?
Ein Fremder ohne Token sendet GET /books an den Server in seinem jetzigen Zustand. Was kommt zurück?
Die fehlende Regel
Die alte Regel sieht sich nur das Token an. Die fehlende Regel sieht sich den Benutzer des Tokens und das Buch an. Führ beide für ben und anas Buch aus:
Es gibt aus:
old rule lets ben delete ana's book: True
owner rule lets ben delete ana's book: False
owner rule lets ana delete it: True
Eine Regel, die das Objekt nennt, heißt Autorisierung auf Objektebene (object-level authorization). Sie wegzulassen ist ein leicht übersehener Fehler, weil für den ehrlichen Benutzer alles weiter funktioniert: ana fragt immer nur nach ihren eigenen Büchern, und ihre Anfragen gelingen so oder so.
Die Eigentümerregel oben prüft user is not None und book is not None, bevor sie irgendetwas vergleicht. Die nächste Frage zeigt, warum.
Was gibt print({}.get("owner") == None) aus?
403 oder 404 für ein Buch, das nicht dir gehört
Wenn ben nach anas Buch fragt, wäre 403 eine ehrliche Antwort. Sie würde auch sagen: Dieses Buch existiert, und es gehört nicht dir. Buch-IDs zählen hier 1, 2, 3. Mit diesem Satz kann ein Fremder aufwärts zählen und kartieren, welche IDs es über alle Leser hinweg gibt.
Deshalb antwortet dieser Dienst mit 404, derselben Antwort, die er für eine ID gibt, die kein Buch hat. Das ist eine Entscheidung dieses Dienstes, und andere Dienste entscheiden sich für 403. Der Preis ist, dass eine 404 einem ehrlichen Client weniger über seinen Fehler verrät.
Die 403, die du in HTTP with Python gebaut hast, bleibt für das Konto, das überhaupt nicht schreiben darf. Diese Antwort hängt von keinem Buch ab, also verrät sie nichts über eines.
Eine ID ist kein Geheimnis, und eine ID, die schwer zu erraten ist, ersetzt die Prüfung nicht.
cleos Token besteht die Prüfung, und ihr Konto darf nur lesen. Sie sendet POST /books. Welche Frage scheitert, und mit welchem Status?
Die nächste Lektion führt bens Anfrage gegen deinen Server aus, und dann schreibst du die Regel.