Hoofdstuk 6
Wie wat mag, en hoe vaak
Ingelogd is niet toegestaan
Hoofdstuk 5 maakte de server zeker van wie er vraagt. Zeker zijn van wie iemand is, zegt nog niets over wat die persoon mag aanraken. Deze les verandert geen code. Ze haalt twee vragen uit elkaar die de server tot nu toe beantwoordde alsof het er één was.
Twee vragen
Authenticatie vraagt “wie ben je?”. Een inloggegeven dat geverifieerd wordt, beantwoordt die vraag: een wachtwoord, een
Autorisatie vraagt “mag je dit doen, hiermee?”. Een regel beantwoordt die vraag, en de regel moet de aanroeper, de actie en het
“Wie ben je?” De server leest het token en controleert de tag. Geen token, of een token dat niet geverifieerd wordt: 401, en het verzoek stopt hier.
“Mag je dit doen, hiermee?” De server kijkt naar de aanroeper, de
en het boek. Geen regel staat het toe: 403 of 404, en het verzoek stopt hier.
Alleen een verzoek dat door beide poorten kwam, bereikt de code die een boek leest of verandert.
Vandaag kijkt de tweede poort naar de aanroeper en de methode. Ze kijkt nooit naar het boek.
Wat er nieuw is in het project van dit hoofdstuk
Het project dat je in de volgende les opent, is je server uit hoofdstuk 5 met drie veranderingen, die je geen van alle hoeft te schrijven.
GET /aboutis een nieuwe, openbare beschrijving van de dienst. Die houdt het gedrag vanETagenmax-agedat je in HTTP with Python bouwde, op een route die iedereen mag lezen.De toegangsbeslissingen van de boekroutes zitten nu in twee
, collection_refusalvoor/booksenitem_refusalvoor/books/{id}. Ze beslissen precies zoals je code uit hoofdstuk 5 deed.handleis nu twee regels die een nieuwe functie aanroepen,route, die alle routering bevat. Enrequest["user"]legt vast wie een verzoek bewees te zijn. Les 5 gebruikt beide.
Er veranderen ook twee feiten over de voorbeeldgebruikers. ben mag nu schrijven. En er is een derde gebruiker, cleo, met het testwachtwoord paper lantern, die alleen mag lezen.
De beslissing lezen
Dit is de meegeleverde item_refusal. Hij geeft None terug als het verzoek verder mag, of het antwoord dat het weigert. claims is de payload van een geverifieerd token, of 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")
Vraag bij elke claims is None is de eerste vraag. claims["can_write"] is een grove vorm van de tweede: mag dit account überhaupt iets schrijven. Geen enkele regel vraagt van wie het boek is, en dat zou ook niet kunnen, omdat een boek geen eigenaar heeft.
Kijk ook naar de vorm. De functie somt op wat geweigerd moet worden, en laat daarna door wat er overblijft.
Dit levert dat op, voor verzoeken aan de server zoals hij is. Beide voorbeeldboeken zijn bedoeld als die van ana.
| Verzoek | Gestuurd door | Antwoord nu | Klopt het? |
|---|---|---|---|
DELETE /books/1 | niemand, geen token | 401 | ja |
POST /books | cleo, die alleen mag lezen | 403 | ja |
DELETE /books/1 | ben, met zijn eigen geldige token | 204, en het boek is weg | nee |
PUT /books/2 | ben | 200, en het boek is vervangen | nee |
GET /books/1 | ben | 200, met het boek van ana | nee |
GET /books | niemand, geen token | 200, met elk boek | nee |
ben heeft een geldig token en mag schrijven. Op de server zoals hij is, stuurt hij DELETE /books/1, en boek 1 is van ana. Wat geeft item_refusal terug?
Een vreemde zonder token stuurt GET /books naar de server zoals hij is. Wat komt er terug?
De regel die ontbreekt
De oude regel kijkt alleen naar het token. De ontbrekende regel kijkt naar de gebruiker van het token en naar het boek. Voer beide uit voor ben en het boek van ana:
Het drukt af:
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
Een regel die het object noemt, heet autorisatie op objectniveau (object-level authorization). Die weglaten is een fout die je makkelijk mist, omdat alles nog werkt voor de eerlijke gebruiker: ana vraagt alleen ooit om haar eigen boeken, en haar verzoeken slagen hoe dan ook.
De eigenaarsregel hierboven controleert user is not None en book is not None voordat hij iets vergelijkt. De volgende vraag laat zien waarom.
Wat drukt print({}.get("owner") == None) af?
403 of 404 voor een boek dat niet van jou is
Als ben om het boek van ana vraagt, zou 403 een eerlijk antwoord zijn. Het zou ook zeggen: dat boek bestaat, en het is niet van jou. Boek-id’s tellen hier 1, 2, 3. Met die zin kan een vreemde omhoogtellen en in kaart brengen welke id’s er bij alle lezers bestaan.
Daarom antwoordt deze dienst 404, hetzelfde antwoord dat hij geeft voor een id die geen enkel boek heeft. Dat is een keuze van deze dienst, en andere diensten kiezen 403. De prijs is dat een 404 een eerlijke client minder vertelt over zijn fout.
De 403 die je in HTTP with Python bouwde, blijft voor het account dat helemaal niet mag schrijven. Dat antwoord hangt niet af van een boek, dus het verraadt er niets over.
Een id is geen geheim, en een id die moeilijk te raden is, vervangt de controle niet.
Het token van cleo wordt geverifieerd, en haar account mag alleen lezen. Ze stuurt POST /books. Welke vraag mislukt, en met welke status?
De volgende les voert het verzoek van ben uit tegen je server, en daarna schrijf je de regel.