#!/usr/bin/env -S uv run --script # /// script # requires-python = ">=3.12" # dependencies = [ # "fastapi", # "uvicorn[standard]", # ] # /// from fastapi import FastAPI, Request, Response, HTTPException, Depends from fastapi.responses import RedirectResponse, PlainTextResponse from fastapi.staticfiles import StaticFiles from fastapi.security import HTTPBasic, HTTPBasicCredentials import secrets app = FastAPI() security = HTTPBasic() USERS = { "admin": {"password": "admin", "role": "admin"}, "reader": {"password": "reader", "role": "reader"}, } # Cookie format: session=username def get_current_user(request: Request): session = request.cookies.get("session") if session and session in USERS: return session return None def get_current_role(user: str): return USERS[user]["role"] @app.post("/login") async def login(credentials: HTTPBasicCredentials = Depends(security)): user = credentials.username pwd = credentials.password if user in USERS and secrets.compare_digest(USERS[user]['password'], pwd): resp = Response("OK", status_code=200) resp.set_cookie("session", user, httponly=True, samesite='lax', path="/") # Ensure login response isn't cached resp.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" resp.headers["Pragma"] = "no-cache" return resp raise HTTPException(status_code=401, detail="Invalid credentials") @app.get("/logout") def logout(): resp = RedirectResponse("/") resp.delete_cookie("session") # Ensure logout response isn't cached resp.headers["Cache-Control"] = "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0" resp.headers["Pragma"] = "no-cache" resp.headers["Expires"] = "Thu, 01 Jan 1970 00:00:00 GMT" return resp @app.get("/authz") def authz(request: Request): session = request.cookies.get("session") path = request.headers.get("X-Original-URI") if not session or session not in USERS: return Response("Not authenticated", status_code=401) user_role = USERS[session]['role'] # Only admin may access /admin if path and path.startswith("/admin") and user_role != 'admin': return Response("Forbidden", status_code=403) # Everything else: allowed return Response("OK", status_code=200) if __name__ == "__main__": import uvicorn app.mount("/static", StaticFiles(directory="static"), name="static") uvicorn.run(app, host="localhost", port=5115)