Toestand en identiteit · eindproject
Eindproject: Project: wie mag de lijst wijzigen
Je leeslijst weet nu wie er vraagt. Een login controleert een wachtwoord en start een sessie in een HttpOnly-/me meldt de gebruiker van de sessie, en elke schrijfactie heeft een token nodig dat mag schrijven. Eén ding ontbreekt: een sessie kan beginnen, maar nooit eindigen. Dit project voegt POST /logout toe en controleert daarna het hele hoofdstuk tegen het contract.
Wat het contract vraagt
Open SERVICE_CONTRACT.md en lees de sectie “Who you are” als checklist:
POST /loginmet de Basic-inloggegevens van een bekende gebruiker: 200,{"user": name}, enset-cookie: session=<id>; HttpOnly; Max-Age=3600; Path=/. Al het andere: 401 met de Basic-challenge, geen cookie en geen sessie.GET /me: 200, met de gebruiker van de sessie ofnull.POST /logout: 204 zonder body en zondercontent-length; de sessie eindigt, enset-cookielaat de cookie verlopen.POST,PUTenDELETEop boeken: 401 metwww-authenticate: Bearervoor een ontbrekend, misvormd of onbekend token, 403 voor een token dat alleen mag lezen, gecontroleerd vóór de boek-id en de body.405 met
allow: POSTop/loginen/logout, enallow: GET, HEADop/me.Geen enkele body van een antwoord bevat een wachtwoord, een token of een sessie-id.
Elke rij behalve uitloggen zou al moeten kloppen. Voer het scenario who van het hulpprogramma uit om te zien waar je staat:
python pl_http.py who
Het logt in als ana met Basic-inloggegevens, leest /me met de cookie die het kreeg, stuurt POST /logout met die cookie en Content-Length: 0, leest /me opnieuw met dezelfde oude cookie, en stuurt als laatste POST /books met het leestoken. Vandaag antwoordt het uitloggen met HTTP/1.0 404 Not Found, en de tweede /me antwoordt nog steeds {"user": "ana"}: de sessie leeft.
Een sessie beëindigen
Uitloggen kost twee stappen, en ze zijn niet even belangrijk.
Eerst verwijdert de server de sessie uit state["sessions"]. Dit is de stap die telt. Zodra de id weg is, is de cookie waardeloos: elk verzoek dat hem draagt, van deze client of van iemand die hem heeft gekopieerd, krijgt {"user": null}.
Daarna vraagt het antwoord de client de cookie te vergeten, met een set-cookie waarvan Max-Age 0 is. Je session_cookie schrijft dat al: session_cookie("", max_age=0) geeft session=""; HttpOnly; Max-Age=0; Path=/ als hij SimpleCookie gebruikt, dat de lege "" schrijft. Een session=; HttpOnly; Max-Age=0; Path=/. Beide regels laten de cookie verlopen. Een jar die deze regel leest, laat zijn cookie session meteen vallen. Maar les 1 zei het al: elk attribuut is een verzoek dat de client mag negeren. Een server die alleen deze regel stuurde en de sessie hield, zou de oude cookie laten werken voor iedereen die een kopie had bewaard.
Om de sessie te vinden, lees je de cookie session op dezelfde manier als session_user doet, en verwijder je hem met state["sessions"].pop(value, None). Met een standaardwaarde geeft pop die terug in plaats van
Mijlpalen
Voeg in
handleeen tak toe voor/logout. EenPOSTbeëindigt de sessie die zijn cookie aanduidt, als die er is, en antwoordt met 204 met een lege body, geencontent-length, en een headerset-cookievansession_cookie("", max_age=0). Elke anderekrijgt not_allowed("POST").Voer
python pl_http.py whoopnieuw uit. Het uitloggen antwoordt nu metHTTP/1.0 204 No Content, en de volgende/memet de oude cookie antwoordt{"user": null}. DePOST /booksmet het leestoken krijgt nog steedsHTTP/1.0 403 Forbidden. Onder de 204 drukt het hulpprogramma de regel af die de cookie laat verlopen, hier zoalsSimpleCookiehem schrijft:
set-cookie: session=""; HttpOnly; Max-Age=0; Path=/
Je kunt een test voor uitloggen toevoegen aan
tests/test_server.py.make_requestneemt eenmet headers, dus een test kan een cookie sturen, bijvoorbeeld:
def test_logout_ends_the_session():
state = reading_server.sample_state()
session_id = reading_server.start_session(state, "ana")
cookie = {"cookie": "session=" + session_id}
response = reading_server.handle(make_request("POST", "/logout", headers=cookie), state)
assert response["status"] == 204
me = reading_server.handle(make_request("GET", "/me", headers=cookie), state)
assert json.loads(me["body"].decode("utf-8")) == {"user": None}
Voer
python -m pytest -quit. Elke test zou moeten slagen, ook de tests die bij het hoofdstuk hoorden.
python -m pytest -q
Wat er wordt gecontroleerd
Check my work controleert het hele hoofdstuk opnieuw met gebruikers, wachtwoorden, tokens en boeken die hij je niet heeft laten zien, en met sessie-id’s die hij zelf kiest, zoals de controle van les 2 deed. Hij roept handle en de hulpfuncties rechtstreeks aan voor logins, /me, uitloggen en elke soort schrijfactie, vergelijkt de state voor en na elke weigering, en doorzoekt elk antwoord op de inloggegevens die hij stuurde. Hij bouwt de handler-klasse van je server met make_server zonder een set-cookie en www-authenticate die terugkomen. Als laatste voert hij je tests één keer uit, zoals ze zijn; ze moeten allemaal slagen, en geen ervan mag socket importeren. Het scenario who in je
Klik daarna op Check my work. Je
Opdracht
Voeg POST /logout toe aan reading_server.py, en laat elke regel van “Who you are” in SERVICE_CONTRACT.md kloppen.
POST /logoutverwijdert de sessie die zijnsessionaanduidt uitstate["sessions"], als die er is, en antwoordt met 204 met de bodyb"", geencontent-length, en een headerset-cookievansession_cookie("", max_age=0). Zonder cookie, of voor een sessie die al beëindigd is, antwoordt hij nog steeds met 204. Elke andereop /logoutkrijgt 405 metallow: POST.De
POST /logoutvan een client stuurt geen body, dus stuurt hij, zoals elkePOSTnaar deze server,content-length: 0; zonder die header antwoordtread_bodymet 411.Alles uit les 2, 5 en 6 klopt nog steeds: Basic-login met een sessie in een
HttpOnly-cookie of 401 met de Basic-challenge,/memet de gebruiker ofnull, met bearer-tokens beschermde schrijfacties met 401 of 403 naar oorzaak vóór de boek-id en de body, de 405’s, en geen wachtwoord, token of sessie-id in enig antwoord.
Voer python pl_http.py who uit in de python -m pytest -q om je tests uit te voeren.
Klik daarna op Check my work. Met gebruikers, tokens, boeken en sessie-id’s die je nog niet hebt gezien, roept hij handle en de hulpfuncties rechtstreeks aan voor logins, /me, uitloggen en elke soort schrijfactie, vergelijkt hij de state voor en na elke weigering, en doorzoekt hij elk antwoord op inloggegevens. Hij bouwt de handler-klasse van je server met make_server zonder een socket importeren. Niets uit je terminal wordt beoordeeld.