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.

This commit is contained in:
依瑪貓 2023-03-14 08:16:32 +08:00
parent cd8a480cd0
commit 8061a23fdc
4 changed files with 44 additions and 37 deletions

View File

@ -23,7 +23,7 @@ from pathlib import Path
from flask import Flask, Blueprint from flask import Flask, Blueprint
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from accounting.utils.user import AbstractUserUtils from accounting.utils.user import UserUtilityInterface
db: SQLAlchemy = SQLAlchemy() db: SQLAlchemy = SQLAlchemy()
"""The database instance.""" """The database instance."""
@ -31,19 +31,13 @@ data_dir: Path = Path(__file__).parent / "data"
"""The data directory.""" """The data directory."""
def init_app(app: Flask, user_utils: AbstractUserUtils, def init_app(app: Flask, user_utils: UserUtilityInterface,
url_prefix: str = "/accounting", url_prefix: str = "/accounting") -> None:
can_view_func: t.Callable[[], bool] | None = None,
can_edit_func: t.Callable[[], bool] | None = None) -> None:
"""Initialize the application. """Initialize the application.
:param app: The Flask application. :param app: The Flask application.
:param user_utils: The user utilities. :param user_utils: The user utilities.
:param url_prefix: The URL prefix of the accounting application. :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. :return: None.
""" """
# The database instance must be set before loading everything # 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) locale.init_app(app, bp)
from .utils import permission 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 from .utils import next_uri
next_uri.init_app(bp) next_uri.init_app(bp)

View File

@ -23,7 +23,7 @@ import typing as t
from flask import abort, Blueprint 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: def has_permission(rule: t.Callable[[], bool]) -> t.Callable:
@ -87,22 +87,15 @@ def can_edit() -> bool:
return __can_edit_func() return __can_edit_func()
def init_app(bp: Blueprint, def init_app(bp: Blueprint, user_utils: UserUtilityInterface) -> None:
can_view_func: t.Callable[[], bool] | None = None,
can_edit_func: t.Callable[[], bool] | None = None) -> None:
"""Initializes the application. """Initializes the application.
:param bp: The blueprint of the accounting application. :param bp: The blueprint of the accounting application.
:param can_view_func: A callback that returns whether the current user can :param user_utils: The user utilities.
view the accounting data.
:param can_edit_func: A callback that returns whether the current user can
edit the accounting data.
:return: None. :return: None.
""" """
global __can_view_func, __can_edit_func global __can_view_func, __can_edit_func
if can_view_func is not None: __can_view_func = user_utils.can_view
__can_view_func = can_view_func __can_edit_func = user_utils.can_edit
if can_edit_func is not None: bp.add_app_template_global(user_utils.can_view, "accounting_can_view")
__can_edit_func = can_edit_func bp.add_app_template_global(user_utils.can_edit, "accounting_can_edit")
bp.add_app_template_global(can_view, "accounting_can_view")
bp.add_app_template_global(can_edit, "accounting_can_edit")

View File

@ -29,8 +29,26 @@ from flask_sqlalchemy.model import Model
T = t.TypeVar("T", bound=Model) T = t.TypeVar("T", bound=Model)
class AbstractUserUtils(t.Generic[T], ABC): class UserUtilityInterface(t.Generic[T], ABC):
"""The abstract user utilities.""" """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 @property
@abstractmethod @abstractmethod
@ -72,7 +90,7 @@ class AbstractUserUtils(t.Generic[T], ABC):
""" """
__user_utils: AbstractUserUtils __user_utils: UserUtilityInterface
"""The user utilities.""" """The user utilities."""
user_cls: t.Type[Model] = Model user_cls: t.Type[Model] = Model
"""The user class.""" """The user class."""
@ -80,7 +98,7 @@ user_pk_column: sa.Column = sa.Column(sa.Integer)
"""The primary key column of the user class.""" """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. """Initializes the user utilities.
:param utils: The user utilities. :param utils: The user utilities.

View File

@ -29,8 +29,6 @@ from flask_sqlalchemy import SQLAlchemy
from flask_wtf import CSRFProtect from flask_wtf import CSRFProtect
from sqlalchemy import Column from sqlalchemy import Column
import accounting.utils.user
bp: Blueprint = Blueprint("home", __name__) bp: Blueprint = Blueprint("home", __name__)
babel_js: BabelJS = BabelJS() babel_js: BabelJS = BabelJS()
csrf: CSRFProtect = CSRFProtect() csrf: CSRFProtect = CSRFProtect()
@ -69,7 +67,16 @@ def create_app(is_testing: bool = False) -> Flask:
from . import auth from . import auth
auth.init_app(app) 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 @property
def cls(self) -> t.Type[auth.User]: 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: def get_pk(self, user: auth.User) -> int:
return user.id return user.id
can_view: t.Callable[[], bool] = lambda: auth.current_user() is not None \ accounting.init_app(app, user_utils=UserUtilities())
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)
return app return app