From 587a70faf5cedab04fd963d30529c5c3faab3df8 Mon Sep 17 00:00:00 2001 From: Tim Van Baak Date: Mon, 28 Jun 2021 21:51:40 -0700 Subject: [PATCH] Update lexicon route decorators --- amanuensis/backend/membership.py | 9 ++ amanuensis/errors.py | 1 + amanuensis/server/helpers.py | 163 ++++++++++++++----------------- 3 files changed, 85 insertions(+), 88 deletions(-) diff --git a/amanuensis/backend/membership.py b/amanuensis/backend/membership.py index baddcdd..dff50e2 100644 --- a/amanuensis/backend/membership.py +++ b/amanuensis/backend/membership.py @@ -64,3 +64,12 @@ def create( db.session.add(new_membership) db.session.commit() return new_membership + + +def try_from_ids(db: DbContext, user_id: int, lexicon_id: int) -> Membership: + """Get a membership by the user and lexicon ids, or None if no such membership was found.""" + return db( + select(Membership) + .where(Membership.user_id == user_id) + .where(Membership.lexicon_id == lexicon_id) + ).scalar_one_or_none() diff --git a/amanuensis/errors.py b/amanuensis/errors.py index 7c35ee1..c44b48f 100644 --- a/amanuensis/errors.py +++ b/amanuensis/errors.py @@ -16,6 +16,7 @@ class BackendArgumentTypeError(ArgumentError): A call to a backend function was made with a value of an invalid type for the parameter. Specify the invalid parameter and value as a kwarg. """ + def __init__(self, obj_type, **kwarg): if not kwarg: raise ValueError("Missing kwarg") diff --git a/amanuensis/server/helpers.py b/amanuensis/server/helpers.py index f89f1fb..0af052f 100644 --- a/amanuensis/server/helpers.py +++ b/amanuensis/server/helpers.py @@ -1,107 +1,94 @@ -# Standard library imports -from datetime import datetime from functools import wraps +from typing import Optional -# Third party imports -from flask import g, flash, redirect, url_for, current_app +from flask import g, flash, redirect, url_for from flask_login import current_user -# Module imports -from amanuensis.parser import filesafe_title -from amanuensis.models import ModelFactory, UserModel, LexiconModel - - -def register_custom_filters(app): - """Adds custom filters to the Flask app""" - - @app.template_filter("user_attr") - def get_user_attr(uid, attr): - factory: ModelFactory = current_app.config['model_factory'] - user: UserModel = factory.user(uid) - val = getattr(user.cfg, attr) - return val - - @app.template_filter("articlelink") - def article_link(title): - return url_for( - 'lexicon.article', - name=g.lexicon.cfg.name, - title=filesafe_title(title)) - - @app.context_processor - def lexicon_status(): - return dict( - PREGAME=LexiconModel.PREGAME, - ONGOING=LexiconModel.ONGOING, - COMPLETE=LexiconModel.COMPLETE) +from amanuensis.backend import lexiq, memq +from amanuensis.db import DbContext, Lexicon, User, Membership def lexicon_param(route): - """Wrapper for loading a route's lexicon""" - @wraps(route) - def with_lexicon(**kwargs): - name = kwargs.get('name') - model_factory: ModelFactory = current_app.config['model_factory'] - g.lexicon = model_factory.lexicon(name) - if g.lexicon is None: - flash(f'Couldn\'t find a lexicon with the name "{name}"') - return redirect(url_for("home.home")) - return route(**kwargs) - return with_lexicon + """ + 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): + name: str = kwargs.get('name') + lexicon: Optional[Lexicon] = lexiq.try_from_name(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): - """ - Requires the user to be an admin to load this page - """ - @wraps(route) - def admin_route(*args, **kwargs): - if not current_user.cfg.is_admin: - flash("You must be an admin to view this page") - return redirect(url_for('home.home')) - return route(*args, **kwargs) - return admin_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): - """ - Requires the user to be a player in the lexicon to load this page - """ - @wraps(route) - def player_route(*args, **kwargs): - if current_user.uid not in g.lexicon.cfg.join.joined: - flash("You must be a player to view this page") - return (redirect(url_for('lexicon.contents', name=g.lexicon.cfg.name)) - if g.lexicon.cfg.join.public - else redirect(url_for('home.home'))) - return route(*args, **kwargs) - return player_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', name=lexicon.name)) + else: + return redirect(url_for('home.home')) + return route(*args, **kwargs) + return player_route def player_required_if_not_public(route): - """ - Requires the user to be a player in the lexicon to load this page if the - lexicon has join.public = false - """ - @wraps(route) - def player_route(*args, **kwargs): - if ((not g.lexicon.cfg.join.public) - and current_user.uid not in g.lexicon.cfg.join.joined): - flash("You must be a player to view this page") - return redirect(url_for('home.home')) - return route(*args, **kwargs) - return player_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): - """ - Requires the user to be the editor of the current lexicon to load this - page - """ - @wraps(route) - def editor_route(*args, **kwargs): - if current_user.uid != g.lexicon.cfg.editor: - flash("You must be the editor to view this page") - return redirect(url_for('lexicon.contents', name=g.lexicon.cfg.name)) - return route(*args, **kwargs) - return editor_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.is_editor: + flash("You must be the editor to view this page") + return redirect(url_for('lexicon.contents', name=lexicon.name)) + return route(*args, **kwargs) + return editor_route