add helpers to retrieve request data
This commit is contained in:
parent
c1b41d3882
commit
9bb433eb0c
1 changed files with 61 additions and 32 deletions
|
|
@ -2,7 +2,7 @@ import asyncio
|
||||||
import logging
|
import logging
|
||||||
import secrets
|
import secrets
|
||||||
from json.decoder import JSONDecodeError
|
from json.decoder import JSONDecodeError
|
||||||
from typing import Literal, Optional
|
from typing import Literal, Optional, overload
|
||||||
|
|
||||||
from starlette.applications import Starlette
|
from starlette.applications import Starlette
|
||||||
from starlette.authentication import (
|
from starlette.authentication import (
|
||||||
|
|
@ -103,11 +103,50 @@ def as_int(x, *, max: int = None, min: Optional[int] = 1, default: int = None):
|
||||||
|
|
||||||
def as_ulid(s: str) -> ULID:
|
def as_ulid(s: str) -> ULID:
|
||||||
try:
|
try:
|
||||||
|
if not s:
|
||||||
|
raise ValueError("Invalid ULID.")
|
||||||
|
|
||||||
return ULID(s)
|
return ULID(s)
|
||||||
|
|
||||||
except ValueError:
|
except ValueError:
|
||||||
raise HTTPException(422, "Not a valid ULID.")
|
raise HTTPException(422, "Not a valid ULID.")
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def json_from_body(request) -> dict:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@overload
|
||||||
|
async def json_from_body(request, keys: list[str]) -> list:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
async def json_from_body(request, keys: list[str] = None):
|
||||||
|
if not await request.body():
|
||||||
|
data = {}
|
||||||
|
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
data = await request.json()
|
||||||
|
except JSONDecodeError:
|
||||||
|
raise HTTPException(422, "Invalid JSON content.")
|
||||||
|
|
||||||
|
if not keys:
|
||||||
|
return data
|
||||||
|
|
||||||
|
try:
|
||||||
|
return [data[k] for k in keys]
|
||||||
|
except KeyError as err:
|
||||||
|
raise HTTPException(422, f"Missing data for key: {err.args[0]}")
|
||||||
|
|
||||||
|
|
||||||
|
def auth(request, secret: str = None):
|
||||||
|
is_admin = "admin" in request.auth.scopes
|
||||||
|
is_owner = secret and phc_compare(secret=request.user.token, phc_string=secret)
|
||||||
|
return is_admin, bool(is_owner)
|
||||||
|
|
||||||
|
|
||||||
_routes = []
|
_routes = []
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -125,6 +164,7 @@ route.registered = _routes
|
||||||
|
|
||||||
@route("/groups/{group_id}/ratings")
|
@route("/groups/{group_id}/ratings")
|
||||||
async def get_ratings_for_group(request):
|
async def get_ratings_for_group(request):
|
||||||
|
|
||||||
group_id = as_ulid(request.path_params["group_id"])
|
group_id = as_ulid(request.path_params["group_id"])
|
||||||
group = await db.get(Group, id=str(group_id))
|
group = await db.get(Group, id=str(group_id))
|
||||||
|
|
||||||
|
|
@ -180,6 +220,10 @@ def not_found(reason: str = "Not Found"):
|
||||||
return JSONResponse({"error": reason}, status_code=404)
|
return JSONResponse({"error": reason}, status_code=404)
|
||||||
|
|
||||||
|
|
||||||
|
def not_implemented():
|
||||||
|
raise HTTPException(404, "Not yet implemented.")
|
||||||
|
|
||||||
|
|
||||||
@route("/movies")
|
@route("/movies")
|
||||||
@requires(["private"])
|
@requires(["private"])
|
||||||
async def get_movies(request):
|
async def get_movies(request):
|
||||||
|
|
@ -194,7 +238,8 @@ async def get_movies(request):
|
||||||
@route("/movies", methods=["POST"])
|
@route("/movies", methods=["POST"])
|
||||||
@requires(["authenticated", "admin"])
|
@requires(["authenticated", "admin"])
|
||||||
async def add_movie(request):
|
async def add_movie(request):
|
||||||
pass
|
|
||||||
|
not_implemented()
|
||||||
|
|
||||||
|
|
||||||
import_lock = asyncio.Lock()
|
import_lock = asyncio.Lock()
|
||||||
|
|
@ -233,6 +278,7 @@ async def progress_for_load_imdb_movies(request):
|
||||||
@route("/movies/_reload_imdb", methods=["POST"])
|
@route("/movies/_reload_imdb", methods=["POST"])
|
||||||
@requires(["authenticated", "admin"])
|
@requires(["authenticated", "admin"])
|
||||||
async def load_imdb_movies(request):
|
async def load_imdb_movies(request):
|
||||||
|
|
||||||
async with import_lock:
|
async with import_lock:
|
||||||
progress = await db.get_import_progress()
|
progress = await db.get_import_progress()
|
||||||
if progress and not progress.stopped:
|
if progress and not progress.stopped:
|
||||||
|
|
@ -252,26 +298,31 @@ async def load_imdb_movies(request):
|
||||||
@route("/users")
|
@route("/users")
|
||||||
@requires(["authenticated", "admin"])
|
@requires(["authenticated", "admin"])
|
||||||
async def list_users(request):
|
async def list_users(request):
|
||||||
|
|
||||||
users = await db.get_all(User)
|
users = await db.get_all(User)
|
||||||
|
|
||||||
return JSONResponse([asplain(u) for u in users])
|
return JSONResponse([asplain(u) for u in users])
|
||||||
|
|
||||||
|
|
||||||
@route("/users", methods=["POST"])
|
@route("/users", methods=["POST"])
|
||||||
@requires(["authenticated", "admin"])
|
@requires(["authenticated", "admin"])
|
||||||
async def add_user(request):
|
async def add_user(request):
|
||||||
pass
|
|
||||||
|
not_implemented()
|
||||||
|
|
||||||
|
|
||||||
@route("/users/{user_id}/ratings")
|
@route("/users/{user_id}/ratings")
|
||||||
@requires(["private"])
|
@requires(["private"])
|
||||||
async def ratings_for_user(request):
|
async def ratings_for_user(request):
|
||||||
request.path_params["user_id"]
|
|
||||||
|
not_implemented()
|
||||||
|
|
||||||
|
|
||||||
@route("/users/{user_id}/ratings", methods=["PUT"])
|
@route("/users/{user_id}/ratings", methods=["PUT"])
|
||||||
@requires("authenticated")
|
@requires("authenticated")
|
||||||
async def set_rating_for_user(request):
|
async def set_rating_for_user(request):
|
||||||
request.path_params["user_id"]
|
|
||||||
|
not_implemented()
|
||||||
|
|
||||||
|
|
||||||
@route("/users/_reload_ratings", methods=["POST"])
|
@route("/users/_reload_ratings", methods=["POST"])
|
||||||
|
|
@ -286,25 +337,17 @@ async def load_imdb_user_ratings(request):
|
||||||
@route("/groups")
|
@route("/groups")
|
||||||
@requires(["authenticated", "admin"])
|
@requires(["authenticated", "admin"])
|
||||||
async def list_groups(request):
|
async def list_groups(request):
|
||||||
|
|
||||||
groups = await db.get_all(Group)
|
groups = await db.get_all(Group)
|
||||||
|
|
||||||
return JSONResponse([asplain(g) for g in groups])
|
return JSONResponse([asplain(g) for g in groups])
|
||||||
|
|
||||||
|
|
||||||
@route("/groups", methods=["POST"])
|
@route("/groups", methods=["POST"])
|
||||||
@requires(["authenticated", "admin"])
|
@requires(["authenticated", "admin"])
|
||||||
async def add_group(request):
|
async def add_group(request):
|
||||||
if not await request.body():
|
|
||||||
data = {}
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
data = await request.json()
|
|
||||||
except JSONDecodeError:
|
|
||||||
raise HTTPException(422, "Invalid JSON content.")
|
|
||||||
|
|
||||||
try:
|
(name,) = await json_from_body(request, ["name"])
|
||||||
name = data["name"]
|
|
||||||
except KeyError as err:
|
|
||||||
raise HTTPException(422, f"Missing data for key: {err.args[0]}")
|
|
||||||
|
|
||||||
# XXX restrict name
|
# XXX restrict name
|
||||||
|
|
||||||
|
|
@ -330,25 +373,11 @@ async def add_user_to_group(request):
|
||||||
if not group:
|
if not group:
|
||||||
return not_found()
|
return not_found()
|
||||||
|
|
||||||
is_allowed = "admin" in request.auth.scopes or phc_compare(
|
is_allowed = any(auth(request, group.secret))
|
||||||
secret=request.user.token, phc_string=group.secret
|
|
||||||
)
|
|
||||||
if not is_allowed:
|
if not is_allowed:
|
||||||
return forbidden()
|
return forbidden()
|
||||||
|
|
||||||
if not await request.body():
|
name, user_id = await json_from_body(request, ["name", "id"])
|
||||||
data = {}
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
data = await request.json()
|
|
||||||
except JSONDecodeError:
|
|
||||||
raise HTTPException(422, "Invalid JSON content.")
|
|
||||||
|
|
||||||
try:
|
|
||||||
name = data["name"]
|
|
||||||
user_id = data["id"]
|
|
||||||
except KeyError as err:
|
|
||||||
raise HTTPException(422, f"Missing data for key: {err.args[0]}")
|
|
||||||
|
|
||||||
# XXX check if user exists
|
# XXX check if user exists
|
||||||
# XXX restrict name
|
# XXX restrict name
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue