diff --git a/kdb-bot/src/bot_api/api_module.py b/kdb-bot/src/bot_api/api_module.py index d6d659ec..0a9446ad 100644 --- a/kdb-bot/src/bot_api/api_module.py +++ b/kdb-bot/src/bot_api/api_module.py @@ -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) diff --git a/kdb-bot/src/bot_api/controller/auth_controller.py b/kdb-bot/src/bot_api/controller/auth_controller.py index a23d490d..78a17e79 100644 --- a/kdb-bot/src/bot_api/controller/auth_controller.py +++ b/kdb-bot/src/bot_api/controller/auth_controller.py @@ -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/') - @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/') - @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/') - @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 diff --git a/kdb-bot/src/bot_api/controller/discord/server_controller.py b/kdb-bot/src/bot_api/controller/discord/server_controller.py index 957624a4..59b7f531 100644 --- a/kdb-bot/src/bot_api/controller/discord/server_controller.py +++ b/kdb-bot/src/bot_api/controller/discord/server_controller.py @@ -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())) diff --git a/kdb-bot/src/bot_api/exception/service_error_code_enum.py b/kdb-bot/src/bot_api/exception/service_error_code_enum.py index 7b19aeb1..5848bf33 100644 --- a/kdb-bot/src/bot_api/exception/service_error_code_enum.py +++ b/kdb-bot/src/bot_api/exception/service_error_code_enum.py @@ -21,3 +21,4 @@ class ServiceErrorCode(Enum): MailError = 10 Unauthorized = 11 + Forbidden = 12 diff --git a/kdb-bot/src/bot_api/route/route.py b/kdb-bot/src/bot_api/route/route.py index ce8826bc..26bdad2b 100644 --- a/kdb-bot/src/bot_api/route/route.py +++ b/kdb-bot/src/bot_api/route/route.py @@ -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 diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index e1e4a2b9..540f5d14 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -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 diff --git a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py index 3f84a939..3506611a 100644 --- a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py +++ b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py @@ -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 )