70 lines
2.8 KiB
Python
70 lines
2.8 KiB
Python
from fastapi import Depends, HTTPException, Request
|
|
from .auth import get_current_user, get_current_user_event_stream
|
|
from .db import get_portal_item
|
|
|
|
|
|
def check(user: dict, *features: str) -> None:
|
|
access = user.get("features") or {}
|
|
if user.get("role") == "admin":
|
|
return
|
|
if not any(access.get(feature, False) for feature in features):
|
|
raise HTTPException(status_code=403, detail="This feature is disabled for your account")
|
|
|
|
|
|
def require_stats(user: dict = Depends(get_current_user)) -> dict:
|
|
check(user, "stats")
|
|
return user
|
|
|
|
|
|
def require_invites(user: dict = Depends(get_current_user)) -> dict:
|
|
check(user, "invites")
|
|
return user
|
|
|
|
|
|
def require_request_access(request: Request, user: dict = Depends(get_current_user)) -> None:
|
|
path = request.url.path.rstrip("/")
|
|
if path.endswith("/search") and "/actions/" not in path:
|
|
# The issue picker uses the same media search; creation is checked separately.
|
|
check(user, "new_requests", "issues")
|
|
elif path.endswith(("/create", "/request-options")):
|
|
check(user, "new_requests")
|
|
elif path.endswith(("/issue-options", "/replacement-options", "/actions/replace", "/actions/search-missing", "/actions/repair-subtitles")):
|
|
check(user, "issues")
|
|
else:
|
|
check(user, "requests")
|
|
|
|
|
|
async def require_portal_access(request: Request, user: dict = Depends(get_current_user)) -> None:
|
|
if user.get("role") == "admin":
|
|
return
|
|
path = request.url.path.rstrip("/")
|
|
access = user.get("features", {})
|
|
if access.get("requests") and access.get("issues") and access.get("new_requests"):
|
|
return
|
|
if "/issues" in path:
|
|
check(user, "issues")
|
|
elif path.endswith("/requests") or path.endswith("/pipeline"):
|
|
check(user, "requests")
|
|
elif "item_id" in request.path_params:
|
|
try:
|
|
item = get_portal_item(int(request.path_params["item_id"]))
|
|
except (ValueError, TypeError):
|
|
item = None
|
|
if not item:
|
|
raise HTTPException(status_code=404, detail="Item not found")
|
|
check(user, "requests" if item.get("kind") == "request" else "issues")
|
|
elif path.endswith("/items") and request.method == "POST":
|
|
payload = await request.json()
|
|
check(user, "new_requests" if isinstance(payload, dict) and payload.get("kind", "request") == "request" else "issues")
|
|
elif path.endswith(("/items", "/overview")) and request.query_params.get("kind"):
|
|
check(user, "requests" if request.query_params["kind"] == "request" else "issues")
|
|
else:
|
|
# Unfiltered lists/overview can include both kinds.
|
|
check(user, "requests")
|
|
check(user, "issues")
|
|
|
|
|
|
def require_request_stream(user: dict = Depends(get_current_user_event_stream)) -> dict:
|
|
check(user, "requests")
|
|
return user
|