# The Mia! Accounting Project. # Author: imacat@mail.imacat.idv.tw (imacat), 2023/1/25 # Copyright (c) 2023 imacat. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """The permissions. This module should not import any other module from the application. """ from collections.abc import Callable from flask import abort, Blueprint, Response from accounting.utils.user import get_current_user, UserUtilityInterface def has_permission(rule: Callable[[], bool]) -> Callable: """The permission decorator to check whether the current user is allowed. :param rule: The permission rule. :return: The view decorator. """ def decorator(view: Callable) -> Callable: """The view decorator to decorate a view with permission tests. :param view: The view. :return: The decorated view. """ def decorated_view(*args, **kwargs): """The decorated view that tests against a permission rule. :param args: The arguments of the view. :param kwargs: The keyword arguments of the view. :return: The response of the view. :raise Forbidden: When the user is denied. """ if not rule(): if get_current_user() is None: response: Response | None = _unauthorized_func() if response is not None: return response abort(403) return view(*args, **kwargs) return decorated_view return decorator __can_view_func: Callable[[], bool] = lambda: True """The callback that returns whether the current user can view the accounting data.""" __can_edit_func: Callable[[], bool] = lambda: True """The callback that returns whether the current user can edit the accounting data.""" __can_admin_func: Callable[[], bool] = lambda: True """The callback that returns whether the current user can administrate the accounting settings.""" _unauthorized_func: Callable[[], Response | None] \ = lambda: Response(status=403) """The callback that returns the response to require the user to log in.""" def can_view() -> bool: """Returns whether the current user can view the accounting data. :return: True if the current user can view the accounting data, or False otherwise. """ return __can_view_func() def can_edit() -> bool: """Returns whether the current user can edit the accounting data. The user has to log in. :return: True if the current user can edit the accounting data, or False otherwise. """ if get_current_user() is None: return False return __can_edit_func() def can_admin() -> bool: """Returns whether the current user can administrate the accounting settings. The user has to log in. :return: True if the current user can administrate the accounting settings, or False otherwise. """ if get_current_user() is None: return False return __can_admin_func() def init_app(bp: Blueprint, user_utils: UserUtilityInterface) -> None: """Initializes the application. :param bp: The blueprint of the accounting application. :param user_utils: The user utilities. :return: None. """ global __can_view_func, __can_edit_func, __can_admin_func, \ _unauthorized_func __can_view_func = user_utils.can_view __can_edit_func = user_utils.can_edit __can_admin_func = user_utils.can_admin _unauthorized_func = user_utils.unauthorized bp.add_app_template_global(user_utils.can_view, "accounting_can_view") bp.add_app_template_global(user_utils.can_edit, "accounting_can_edit") bp.add_app_template_global(user_utils.can_admin, "accounting_can_admin")