From 8061a23fdc3e76e1266ae4fa5c8017e2b27d4f54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BE=9D=E7=91=AA=E8=B2=93?= Date: Tue, 14 Mar 2023 08:16:32 +0800 Subject: [PATCH] Renamed the AbstractUserUtils class to UserUtilityInterface, and added the can_view and can_edit functions to the UserUtilityInterface interface. There is no need to separately supply two additional can_view and can_edit callbacks. --- src/accounting/__init__.py | 14 ++++---------- src/accounting/utils/permission.py | 21 +++++++-------------- src/accounting/utils/user.py | 26 ++++++++++++++++++++++---- tests/test_site/__init__.py | 20 +++++++++++--------- 4 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/accounting/__init__.py b/src/accounting/__init__.py index 716d0cd..06282f5 100644 --- a/src/accounting/__init__.py +++ b/src/accounting/__init__.py @@ -23,7 +23,7 @@ from pathlib import Path from flask import Flask, Blueprint from flask_sqlalchemy import SQLAlchemy -from accounting.utils.user import AbstractUserUtils +from accounting.utils.user import UserUtilityInterface db: SQLAlchemy = SQLAlchemy() """The database instance.""" @@ -31,19 +31,13 @@ data_dir: Path = Path(__file__).parent / "data" """The data directory.""" -def init_app(app: Flask, user_utils: AbstractUserUtils, - url_prefix: str = "/accounting", - can_view_func: t.Callable[[], bool] | None = None, - can_edit_func: t.Callable[[], bool] | None = None) -> None: +def init_app(app: Flask, user_utils: UserUtilityInterface, + url_prefix: str = "/accounting") -> None: """Initialize the application. :param app: The Flask application. :param user_utils: The user utilities. :param url_prefix: The URL prefix of the accounting application. - :param can_view_func: A callback that returns whether the current user can - view the accounting data. - :param can_edit_func: A callback that returns whether the current user can - edit the accounting data. :return: None. """ # The database instance must be set before loading everything @@ -73,7 +67,7 @@ def init_app(app: Flask, user_utils: AbstractUserUtils, locale.init_app(app, bp) from .utils import permission - permission.init_app(bp, can_view_func, can_edit_func) + permission.init_app(bp, user_utils) from .utils import next_uri next_uri.init_app(bp) diff --git a/src/accounting/utils/permission.py b/src/accounting/utils/permission.py index 12394fe..39f13c5 100644 --- a/src/accounting/utils/permission.py +++ b/src/accounting/utils/permission.py @@ -23,7 +23,7 @@ import typing as t from flask import abort, Blueprint -from accounting.utils.user import get_current_user +from accounting.utils.user import get_current_user, UserUtilityInterface def has_permission(rule: t.Callable[[], bool]) -> t.Callable: @@ -87,22 +87,15 @@ def can_edit() -> bool: return __can_edit_func() -def init_app(bp: Blueprint, - can_view_func: t.Callable[[], bool] | None = None, - can_edit_func: t.Callable[[], bool] | None = None) -> None: +def init_app(bp: Blueprint, user_utils: UserUtilityInterface) -> None: """Initializes the application. :param bp: The blueprint of the accounting application. - :param can_view_func: A callback that returns whether the current user can - view the accounting data. - :param can_edit_func: A callback that returns whether the current user can - edit the accounting data. + :param user_utils: The user utilities. :return: None. """ global __can_view_func, __can_edit_func - if can_view_func is not None: - __can_view_func = can_view_func - if can_edit_func is not None: - __can_edit_func = can_edit_func - bp.add_app_template_global(can_view, "accounting_can_view") - bp.add_app_template_global(can_edit, "accounting_can_edit") + __can_view_func = user_utils.can_view + __can_edit_func = user_utils.can_edit + bp.add_app_template_global(user_utils.can_view, "accounting_can_view") + bp.add_app_template_global(user_utils.can_edit, "accounting_can_edit") diff --git a/src/accounting/utils/user.py b/src/accounting/utils/user.py index 16b6ddd..a99c487 100644 --- a/src/accounting/utils/user.py +++ b/src/accounting/utils/user.py @@ -29,8 +29,26 @@ from flask_sqlalchemy.model import Model T = t.TypeVar("T", bound=Model) -class AbstractUserUtils(t.Generic[T], ABC): - """The abstract user utilities.""" +class UserUtilityInterface(t.Generic[T], ABC): + """The interface for the user utilities.""" + + @abstractmethod + def can_view(self) -> bool: + """Returns whether the currently logged-in user can view the accounting + data. + + :return: True if the currently logged-in user can view the accounting + data, or False otherwise. + """ + + @abstractmethod + def can_edit(self) -> bool: + """Returns whether the currently logged-in user can edit the accounting + data. + + :return: True if the currently logged-in user can edit the accounting + data, or False otherwise. + """ @property @abstractmethod @@ -72,7 +90,7 @@ class AbstractUserUtils(t.Generic[T], ABC): """ -__user_utils: AbstractUserUtils +__user_utils: UserUtilityInterface """The user utilities.""" user_cls: t.Type[Model] = Model """The user class.""" @@ -80,7 +98,7 @@ user_pk_column: sa.Column = sa.Column(sa.Integer) """The primary key column of the user class.""" -def init_user_utils(utils: AbstractUserUtils) -> None: +def init_user_utils(utils: UserUtilityInterface) -> None: """Initializes the user utilities. :param utils: The user utilities. diff --git a/tests/test_site/__init__.py b/tests/test_site/__init__.py index 7455172..182d312 100644 --- a/tests/test_site/__init__.py +++ b/tests/test_site/__init__.py @@ -29,8 +29,6 @@ from flask_sqlalchemy import SQLAlchemy from flask_wtf import CSRFProtect from sqlalchemy import Column -import accounting.utils.user - bp: Blueprint = Blueprint("home", __name__) babel_js: BabelJS = BabelJS() csrf: CSRFProtect = CSRFProtect() @@ -69,7 +67,16 @@ def create_app(is_testing: bool = False) -> Flask: from . import auth auth.init_app(app) - class UserUtils(accounting.utils.user.AbstractUserUtils[auth.User]): + class UserUtilities(accounting.UserUtilityInterface[auth.User]): + + def can_view(self) -> bool: + return auth.current_user() is not None \ + and auth.current_user().username in ["viewer", "editor", + "editor2"] + + def can_edit(self) -> bool: + return auth.current_user() is not None \ + and auth.current_user().username in ["editor", "editor2"] @property def cls(self) -> t.Type[auth.User]: @@ -90,12 +97,7 @@ def create_app(is_testing: bool = False) -> Flask: def get_pk(self, user: auth.User) -> int: return user.id - can_view: t.Callable[[], bool] = lambda: auth.current_user() is not None \ - and auth.current_user().username in ["viewer", "editor", "editor2"] - can_edit: t.Callable[[], bool] = lambda: auth.current_user() is not None \ - and auth.current_user().username in ["editor", "editor2"] - accounting.init_app(app, user_utils=UserUtils(), - can_view_func=can_view, can_edit_func=can_edit) + accounting.init_app(app, user_utils=UserUtilities()) return app