amanuensis/amanuensis/server/helpers.py
Tim Van Baak ccb285cbf0 Rename route parameter to lexicon_name
This disambiguates and future-proofs the route parameters so they can be more
easily referenced in before_request contexts shared by all routes.
2021-09-09 19:34:24 -07:00

114 lines
3.7 KiB
Python

from functools import wraps
from typing import Optional, Any
from uuid import UUID
from flask import g, flash, redirect, url_for
from flask_login import current_user
from werkzeug.routing import BaseConverter, ValidationError
from amanuensis.backend import lexiq, memq
from amanuensis.db import DbContext, Lexicon, User, Membership
class UuidConverter(BaseConverter):
"""Converter that matches version 4 UUIDs"""
regex = r"[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89aAbB][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}"
def to_python(self, value: str) -> Any:
try:
return UUID(value)
except:
return ValidationError(f"Invalid UUID: {value}")
def to_url(self, value: Any) -> str:
if not isinstance(value, UUID):
raise ValueError(f"Expected UUID, got {type(value)}: {value}")
return str(value)
def lexicon_param(route):
"""
Wrapper for loading a route's lexicon to `g`.
This decorator should be applied above any other decorators that reference `g.lexicon`.
"""
@wraps(route)
def with_lexicon(*args, **kwargs):
db: DbContext = g.db
name: str = kwargs.get('lexicon_name')
lexicon: Optional[Lexicon] = lexiq.try_from_name(db, name)
if lexicon is None:
flash(f"Couldn't find a lexicon with the name \"{name}\"")
return redirect(url_for("home.home"))
g.lexicon = lexicon
return route(*args, **kwargs)
return with_lexicon
def admin_required(route):
"""
Restricts a route to users who are site admins.
"""
@wraps(route)
def admin_route(*args, **kwargs):
user: User = current_user
if not user.is_site_admin:
flash("You must be an admin to view this page")
return redirect(url_for('home.home'))
return route(*args, **kwargs)
return admin_route
def player_required(route):
"""
Restricts a route to users who are players in the current lexicon.
"""
@wraps(route)
def player_route(*args, **kwargs):
db: DbContext = g.db
user: User = current_user
lexicon: Lexicon = g.lexicon
mem: Optional[Membership] = memq.try_from_ids(db, user.id, lexicon.id)
if not mem:
flash("You must be a player to view this page")
if lexicon.public:
return redirect(url_for('lexicon.contents', lexicon_name=lexicon.name))
else:
return redirect(url_for('home.home'))
return route(*args, **kwargs)
return player_route
def player_required_if_not_public(route):
"""
Restricts a route to users who are players in the current lexicon if the lexicon is nonpublic.
"""
@wraps(route)
def player_route(*args, **kwargs):
db: DbContext = g.db
user: User = current_user
lexicon: Lexicon = g.lexicon
if not lexicon.public:
mem: Optional[Membership] = memq.try_from_ids(db, user.id, lexicon.id)
if not mem:
flash("You must be a player to view this page")
return redirect(url_for('home.home'))
return route(*args, **kwargs)
return player_route
def editor_required(route):
"""
Restricts a route to users who are editors of the current lexicon.
"""
@wraps(route)
def editor_route(*args, **kwargs):
db: DbContext = g.db
user: User = current_user
lexicon: Lexicon = g.lexicon
mem: Optional[Membership] = memq.try_from_ids(db, user.id, lexicon.id)
if not mem or not mem.is_editor:
flash("You must be the editor to view this page")
return redirect(url_for('lexicon.contents', lexicon_name=lexicon.name))
return route(*args, **kwargs)
return editor_route