forked from sh-edraft.de/sh_discord_bot
		
	Added role check to authorization #72
This commit is contained in:
		| @@ -11,10 +11,12 @@ from flask import Flask | ||||
| from bot_api.abc.auth_service_abc import AuthServiceABC | ||||
| from bot_api.api import Api | ||||
| from bot_api.api_thread import ApiThread | ||||
| from bot_api.controller.discord.server_controller import ServerController | ||||
| from bot_api.controller.gui_controller import GuiController | ||||
| from bot_api.controller.auth_controller import AuthController | ||||
| from bot_api.event.bot_api_on_ready_event import BotApiOnReadyEvent | ||||
| from bot_api.service.auth_service import AuthService | ||||
| from bot_api.service.discord_service import DiscordService | ||||
| from bot_core.abc.module_abc import ModuleABC | ||||
| from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum | ||||
|  | ||||
| @@ -41,6 +43,8 @@ class ApiModule(ModuleABC): | ||||
|         services.add_transient(AuthServiceABC, AuthService) | ||||
|         services.add_transient(AuthController) | ||||
|         services.add_transient(GuiController) | ||||
|         services.add_transient(DiscordService) | ||||
|         services.add_transient(ServerController) | ||||
|  | ||||
|         # cpl-discord | ||||
|         self._dc.add_event(DiscordEventTypesEnum.on_ready.value, BotApiOnReadyEvent) | ||||
|   | ||||
| @@ -15,6 +15,7 @@ from bot_api.model.auth_user_dto import AuthUserDTO | ||||
| from bot_api.model.token_dto import TokenDTO | ||||
| from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO | ||||
| from bot_api.route.route import Route | ||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | ||||
|  | ||||
|  | ||||
| class AuthController: | ||||
| @@ -41,13 +42,13 @@ class AuthController: | ||||
|         self._auth_service = auth_service | ||||
|  | ||||
|     @Route.get(f'{BasePath}/users') | ||||
|     @Route.authorize | ||||
|     @Route.authorize(role=AuthRoleEnum.admin) | ||||
|     async def get_all_users(self) -> Response: | ||||
|         result = await self._auth_service.get_all_auth_users_async() | ||||
|         return jsonify(result.select(lambda x: x.to_dict())) | ||||
|  | ||||
|     @Route.post(f'{BasePath}/users/get/filtered') | ||||
|     @Route.authorize | ||||
|     @Route.authorize(role=AuthRoleEnum.admin) | ||||
|     async def get_filtered_users(self) -> Response: | ||||
|         dto: AuthUserSelectCriteria = JSONProcessor.process(AuthUserSelectCriteria, request.get_json(force=True, silent=True)) | ||||
|         result = await self._auth_service.get_filtered_auth_users_async(dto) | ||||
| @@ -55,13 +56,13 @@ class AuthController: | ||||
|         return jsonify(result.to_dict()) | ||||
|  | ||||
|     @Route.get(f'{BasePath}/users/get/<email>') | ||||
|     @Route.authorize | ||||
|     @Route.authorize(role=AuthRoleEnum.admin) | ||||
|     async def get_user_from_email(self, email: str) -> Response: | ||||
|         result = await self._auth_service.get_auth_user_by_email_async(email) | ||||
|         return jsonify(result.to_dict()) | ||||
|  | ||||
|     @Route.get(f'{BasePath}/users/find/<email>') | ||||
|     @Route.authorize | ||||
|     @Route.authorize(role=AuthRoleEnum.admin) | ||||
|     async def find_user_from_email(self, email: str) -> Response: | ||||
|         result = await self._auth_service.find_auth_user_by_email_async(email) | ||||
|         return jsonify(result.to_dict()) | ||||
| @@ -109,7 +110,7 @@ class AuthController: | ||||
|         return '', 200 | ||||
|  | ||||
|     @Route.post(f'{BasePath}/update-user-as-admin') | ||||
|     @Route.authorize | ||||
|     @Route.authorize(role=AuthRoleEnum.admin) | ||||
|     async def update_user_as_admin(self): | ||||
|         dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True)) | ||||
|         await self._auth_service.update_user_as_admin_async(dto) | ||||
| @@ -129,14 +130,14 @@ class AuthController: | ||||
|         return '', 200 | ||||
|  | ||||
|     @Route.post(f'{BasePath}/delete-user') | ||||
|     @Route.authorize | ||||
|     @Route.authorize(role=AuthRoleEnum.admin) | ||||
|     async def delete_user(self): | ||||
|         dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True)) | ||||
|         await self._auth_service.delete_auth_user_async(dto) | ||||
|         return '', 200 | ||||
|  | ||||
|     @Route.post(f'{BasePath}/delete-user-by-mail/<email>') | ||||
|     @Route.authorize | ||||
|     @Route.authorize(role=AuthRoleEnum.admin) | ||||
|     async def delete_user_by_mail(self, email: str): | ||||
|         await self._auth_service.delete_auth_user_by_email_async(email) | ||||
|         return '', 200 | ||||
|   | ||||
| @@ -8,6 +8,7 @@ from bot_api.api import Api | ||||
| from bot_api.logging.api_logger import ApiLogger | ||||
| from bot_api.route.route import Route | ||||
| from bot_api.service.discord_service import DiscordService | ||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | ||||
|  | ||||
|  | ||||
| class ServerController: | ||||
| @@ -34,6 +35,6 @@ class ServerController: | ||||
|         self._discord_service = discord_service | ||||
|  | ||||
|     @Route.get(f'{BasePath}/servers') | ||||
|     @Route.authorize | ||||
|     @Route.authorize(role=AuthRoleEnum.admin) | ||||
|     async def get_all_servers(self) -> Response: | ||||
|         return jsonify(self._discord_service.get_all_servers().select(lambda x: x.to_dict())) | ||||
|   | ||||
| @@ -21,3 +21,4 @@ class ServiceErrorCode(Enum): | ||||
|     MailError = 10 | ||||
|  | ||||
|     Unauthorized = 11 | ||||
|     Forbidden = 12 | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| import functools | ||||
| from functools import wraps | ||||
| from typing import Optional | ||||
| from typing import Optional, Callable | ||||
|  | ||||
| from flask import request, jsonify | ||||
| from flask_cors import cross_origin | ||||
| @@ -9,6 +10,7 @@ from bot_api.exception.service_error_code_enum import ServiceErrorCode | ||||
| from bot_api.exception.service_exception import ServiceException | ||||
| from bot_api.model.error_dto import ErrorDTO | ||||
| from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC | ||||
| from bot_data.model.auth_role_enum import AuthRoleEnum | ||||
|  | ||||
|  | ||||
| class Route: | ||||
| @@ -23,7 +25,10 @@ class Route: | ||||
|         cls._auth = auth | ||||
|  | ||||
|     @classmethod | ||||
|     def authorize(cls, f): | ||||
|     def authorize(cls, f: Callable = None, role: AuthRoleEnum = None): | ||||
|         if f is None: | ||||
|             return functools.partial(cls.authorize, role=role) | ||||
|  | ||||
|         @wraps(f) | ||||
|         async def decorator(*args, **kwargs): | ||||
|             token = None | ||||
| @@ -46,6 +51,23 @@ class Route: | ||||
|                 error = ErrorDTO(ex.error_code, ex.message) | ||||
|                 return jsonify(error.to_dict()), 401 | ||||
|  | ||||
|             token = cls._auth.decode_token(token) | ||||
|             if token is None or 'email' not in token: | ||||
|                 ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token invalid') | ||||
|                 error = ErrorDTO(ex.error_code, ex.message) | ||||
|                 return jsonify(error.to_dict()), 401 | ||||
|  | ||||
|             user = cls._auth_users.get_auth_user_by_email(token['email']) | ||||
|             if user is None: | ||||
|                 ex = ServiceException(ServiceErrorCode.Unauthorized, f'Token invalid') | ||||
|                 error = ErrorDTO(ex.error_code, ex.message) | ||||
|                 return jsonify(error.to_dict()), 401 | ||||
|  | ||||
|             if role is not None and user.auth_role.value < role.value: | ||||
|                 ex = ServiceException(ServiceErrorCode.Unauthorized, f'Role {role} required') | ||||
|                 error = ErrorDTO(ex.error_code, ex.message) | ||||
|                 return jsonify(error.to_dict()), 403 | ||||
|  | ||||
|             return await f(*args, **kwargs) | ||||
|  | ||||
|         return decorator | ||||
|   | ||||
| @@ -27,6 +27,8 @@ from bot_api.transformer.auth_user_transformer import AuthUserTransformer as AUT | ||||
| from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC | ||||
| from bot_data.model.auth_user import AuthUser | ||||
|  | ||||
| _email_regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' | ||||
|  | ||||
|  | ||||
| class AuthService(AuthServiceABC): | ||||
|  | ||||
| @@ -65,7 +67,7 @@ class AuthService(AuthServiceABC): | ||||
|  | ||||
|     @staticmethod | ||||
|     def _is_email_valid(email: str) -> bool: | ||||
|         if re.match(re.compile(r'^[a-z0-9]+[\._]?[a-z0-9]+[@]\w+[.]\w{2,3}$'), email) is not None: | ||||
|         if re.fullmatch(_email_regex, email) is not None: | ||||
|             return True | ||||
|  | ||||
|         return False | ||||
|   | ||||
| @@ -19,7 +19,7 @@ class AuthUserTransformer(TransformerABC): | ||||
|             None, | ||||
|             None, | ||||
|             datetime.now(tz=timezone.utc), | ||||
|             AuthRoleEnum.normal if dto.auth_role is None else dto.auth_role, | ||||
|             AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role), | ||||
|             id=0 if dto.id is None else dto.id | ||||
|         ) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user