Wie mag wat: elk endpoint apart
Een controle beschermt alleen het endpoint waar hij in staat. Het htmx-overzicht
heeft ook een knop Alles wissen, met een eigen
endpoint DELETE /berichten. Staat daar geen controle in, dan wist Alex daarmee
alsnog het bericht van Sara, en alle andere berichten erbij.
Loop daarom elk endpoint langs dat iets verandert of verwijdert, en stel bij elk dezelfde vraag: wie mag dit? In opdracht 1 doe je dat voor Alles wissen.
Opdrachten
Opdracht 1: Make - Alles wissen, maar alleen van jou
Voeg een endpoint DELETE /berichten toe dat alleen de berichten van de
bezoeker zelf wist. Test het met een scriptje waarin Alex alles wist, en kijk
of het bericht van Sara blijft staan.
Tip
Loop niet over alle sleutels in gastenboek.db, maar over de sleutels in de
sessie.
Antwoord
@app.delete("/berichten")
async def alles_wissen(sessie_id: str = Cookie(default="")):
with SqliteDict("sessies.db") as sessies:
mijn = sessies.get(sessie_id, {})
with SqliteDict("gastenboek.db") as db:
for sleutel in mijn.get("berichten", []):
if sleutel in db:
del db[sleutel]
db.commit()
return {"bericht": "Jouw berichten zijn gewist"}
Het testscript:
import httpx
adres = "http://127.0.0.1:8000"
sara = httpx.Client(base_url=adres)
alex = httpx.Client(base_url=adres)
sara.post("/bericht", data={"naam": "Sara", "bericht": "Hoi allemaal"})
alex.post("/bericht", data={"naam": "Alex", "bericht": "Leuk gastenboek"})
print(alex.delete("/berichten").json())
namen = [bericht["naam"] for bericht in alex.get("/berichten").json().values()]
print(f"Nog in het gastenboek: {namen}")
Uitvoer, met een lege database:
{'bericht': 'Jouw berichten zijn gewist'}
Nog in het gastenboek: ['Sara']
Hier is er geen 403 nodig: wie Alles wissen kiest, wist wat van hem is, en de
rest blijft staan.
Opdracht 2: Predict - Een endpoint om te wijzigen
Iemand voegt een endpoint POST /bericht/{sleutel}/wijzig toe, dat de tekst van
een bericht verandert. Het heeft geen controle; bericht_verwijderen wel.
Vraag: kan Alex het bericht van Sara nu veranderen?
Antwoord
Ja. De controle in bericht_verwijderen beschermt alleen het verwijderen. Het
nieuwe endpoint vraagt niet wie er komt, dus het verandert elk bericht waarvan
het de sleutel krijgt. Elk endpoint dat iets verandert, heeft zijn eigen
controle nodig.
Zo ziet je main.py er nu uit
import secrets
import time
from fastapi import Cookie, FastAPI, Form, HTTPException
from fastapi.responses import JSONResponse
from sqlitedict import SqliteDict
app = FastAPI()
@app.post("/bericht")
async def bericht_plaatsen(
naam: str = Form(...),
bericht: str = Form(...),
sessie_id: str = Cookie(default=""),
):
if not sessie_id:
sessie_id = secrets.token_hex(16)
sleutel = f"bericht_{time.time_ns()}"
with SqliteDict("gastenboek.db") as db:
db[sleutel] = {"naam": naam, "bericht": bericht}
db.commit()
with SqliteDict("sessies.db") as sessies:
mijn = sessies.get(sessie_id, {"naam": naam, "berichten": []})
mijn["naam"] = naam
mijn["berichten"].append(sleutel)
sessies[sessie_id] = mijn
sessies.commit()
antwoord = JSONResponse({"sleutel": sleutel})
antwoord.set_cookie(key="sessie_id", value=sessie_id, max_age=60 * 60 * 24 * 30)
return antwoord
@app.get("/berichten")
async def berichten():
with SqliteDict("gastenboek.db") as db:
return dict(db)
@app.delete("/bericht/{sleutel}")
async def bericht_verwijderen(sleutel: str, sessie_id: str = Cookie(default="")):
with SqliteDict("sessies.db") as sessies:
mijn = sessies.get(sessie_id, {})
if sleutel not in mijn.get("berichten", []):
raise HTTPException(status_code=403, detail="Dit is niet jouw bericht")
with SqliteDict("gastenboek.db") as db:
if sleutel in db:
del db[sleutel]
db.commit()
return {"bericht": "Verwijderd"}
@app.delete("/berichten")
async def alles_wissen(sessie_id: str = Cookie(default="")):
with SqliteDict("sessies.db") as sessies:
mijn = sessies.get(sessie_id, {})
with SqliteDict("gastenboek.db") as db:
for sleutel in mijn.get("berichten", []):
if sleutel in db:
del db[sleutel]
db.commit()
return {"bericht": "Jouw berichten zijn gewist"}
Elk endpoint dat iets verwijdert, controleert nu zelf.
Door naar les 5: in je eigen project.