Added role check to authorization #72
This commit is contained in:
parent
1857473ccc
commit
dfcc516389
@ -11,10 +11,12 @@ from flask import Flask
|
|||||||
from bot_api.abc.auth_service_abc import AuthServiceABC
|
from bot_api.abc.auth_service_abc import AuthServiceABC
|
||||||
from bot_api.api import Api
|
from bot_api.api import Api
|
||||||
from bot_api.api_thread import ApiThread
|
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.gui_controller import GuiController
|
||||||
from bot_api.controller.auth_controller import AuthController
|
from bot_api.controller.auth_controller import AuthController
|
||||||
from bot_api.event.bot_api_on_ready_event import BotApiOnReadyEvent
|
from bot_api.event.bot_api_on_ready_event import BotApiOnReadyEvent
|
||||||
from bot_api.service.auth_service import AuthService
|
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.abc.module_abc import ModuleABC
|
||||||
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
from bot_core.configuration.feature_flags_enum import FeatureFlagsEnum
|
||||||
|
|
||||||
@ -41,6 +43,8 @@ class ApiModule(ModuleABC):
|
|||||||
services.add_transient(AuthServiceABC, AuthService)
|
services.add_transient(AuthServiceABC, AuthService)
|
||||||
services.add_transient(AuthController)
|
services.add_transient(AuthController)
|
||||||
services.add_transient(GuiController)
|
services.add_transient(GuiController)
|
||||||
|
services.add_transient(DiscordService)
|
||||||
|
services.add_transient(ServerController)
|
||||||
|
|
||||||
# cpl-discord
|
# cpl-discord
|
||||||
self._dc.add_event(DiscordEventTypesEnum.on_ready.value, BotApiOnReadyEvent)
|
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.token_dto import TokenDTO
|
||||||
from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO
|
from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO
|
||||||
from bot_api.route.route import Route
|
from bot_api.route.route import Route
|
||||||
|
from bot_data.model.auth_role_enum import AuthRoleEnum
|
||||||
|
|
||||||
|
|
||||||
class AuthController:
|
class AuthController:
|
||||||
@ -41,13 +42,13 @@ class AuthController:
|
|||||||
self._auth_service = auth_service
|
self._auth_service = auth_service
|
||||||
|
|
||||||
@Route.get(f'{BasePath}/users')
|
@Route.get(f'{BasePath}/users')
|
||||||
@Route.authorize
|
@Route.authorize(role=AuthRoleEnum.admin)
|
||||||
async def get_all_users(self) -> Response:
|
async def get_all_users(self) -> Response:
|
||||||
result = await self._auth_service.get_all_auth_users_async()
|
result = await self._auth_service.get_all_auth_users_async()
|
||||||
return jsonify(result.select(lambda x: x.to_dict()))
|
return jsonify(result.select(lambda x: x.to_dict()))
|
||||||
|
|
||||||
@Route.post(f'{BasePath}/users/get/filtered')
|
@Route.post(f'{BasePath}/users/get/filtered')
|
||||||
@Route.authorize
|
@Route.authorize(role=AuthRoleEnum.admin)
|
||||||
async def get_filtered_users(self) -> Response:
|
async def get_filtered_users(self) -> Response:
|
||||||
dto: AuthUserSelectCriteria = JSONProcessor.process(AuthUserSelectCriteria, request.get_json(force=True, silent=True))
|
dto: AuthUserSelectCriteria = JSONProcessor.process(AuthUserSelectCriteria, request.get_json(force=True, silent=True))
|
||||||
result = await self._auth_service.get_filtered_auth_users_async(dto)
|
result = await self._auth_service.get_filtered_auth_users_async(dto)
|
||||||
@ -55,13 +56,13 @@ class AuthController:
|
|||||||
return jsonify(result.to_dict())
|
return jsonify(result.to_dict())
|
||||||
|
|
||||||
@Route.get(f'{BasePath}/users/get/<email>')
|
@Route.get(f'{BasePath}/users/get/<email>')
|
||||||
@Route.authorize
|
@Route.authorize(role=AuthRoleEnum.admin)
|
||||||
async def get_user_from_email(self, email: str) -> Response:
|
async def get_user_from_email(self, email: str) -> Response:
|
||||||
result = await self._auth_service.get_auth_user_by_email_async(email)
|
result = await self._auth_service.get_auth_user_by_email_async(email)
|
||||||
return jsonify(result.to_dict())
|
return jsonify(result.to_dict())
|
||||||
|
|
||||||
@Route.get(f'{BasePath}/users/find/<email>')
|
@Route.get(f'{BasePath}/users/find/<email>')
|
||||||
@Route.authorize
|
@Route.authorize(role=AuthRoleEnum.admin)
|
||||||
async def find_user_from_email(self, email: str) -> Response:
|
async def find_user_from_email(self, email: str) -> Response:
|
||||||
result = await self._auth_service.find_auth_user_by_email_async(email)
|
result = await self._auth_service.find_auth_user_by_email_async(email)
|
||||||
return jsonify(result.to_dict())
|
return jsonify(result.to_dict())
|
||||||
@ -109,7 +110,7 @@ class AuthController:
|
|||||||
return '', 200
|
return '', 200
|
||||||
|
|
||||||
@Route.post(f'{BasePath}/update-user-as-admin')
|
@Route.post(f'{BasePath}/update-user-as-admin')
|
||||||
@Route.authorize
|
@Route.authorize(role=AuthRoleEnum.admin)
|
||||||
async def update_user_as_admin(self):
|
async def update_user_as_admin(self):
|
||||||
dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True))
|
dto: UpdateAuthUserDTO = JSONProcessor.process(UpdateAuthUserDTO, request.get_json(force=True, silent=True))
|
||||||
await self._auth_service.update_user_as_admin_async(dto)
|
await self._auth_service.update_user_as_admin_async(dto)
|
||||||
@ -129,14 +130,14 @@ class AuthController:
|
|||||||
return '', 200
|
return '', 200
|
||||||
|
|
||||||
@Route.post(f'{BasePath}/delete-user')
|
@Route.post(f'{BasePath}/delete-user')
|
||||||
@Route.authorize
|
@Route.authorize(role=AuthRoleEnum.admin)
|
||||||
async def delete_user(self):
|
async def delete_user(self):
|
||||||
dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True))
|
dto: AuthUserDTO = JSONProcessor.process(AuthUserDTO, request.get_json(force=True, silent=True))
|
||||||
await self._auth_service.delete_auth_user_async(dto)
|
await self._auth_service.delete_auth_user_async(dto)
|
||||||
return '', 200
|
return '', 200
|
||||||
|
|
||||||
@Route.post(f'{BasePath}/delete-user-by-mail/<email>')
|
@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):
|
async def delete_user_by_mail(self, email: str):
|
||||||
await self._auth_service.delete_auth_user_by_email_async(email)
|
await self._auth_service.delete_auth_user_by_email_async(email)
|
||||||
return '', 200
|
return '', 200
|
||||||
|
@ -8,6 +8,7 @@ from bot_api.api import Api
|
|||||||
from bot_api.logging.api_logger import ApiLogger
|
from bot_api.logging.api_logger import ApiLogger
|
||||||
from bot_api.route.route import Route
|
from bot_api.route.route import Route
|
||||||
from bot_api.service.discord_service import DiscordService
|
from bot_api.service.discord_service import DiscordService
|
||||||
|
from bot_data.model.auth_role_enum import AuthRoleEnum
|
||||||
|
|
||||||
|
|
||||||
class ServerController:
|
class ServerController:
|
||||||
@ -34,6 +35,6 @@ class ServerController:
|
|||||||
self._discord_service = discord_service
|
self._discord_service = discord_service
|
||||||
|
|
||||||
@Route.get(f'{BasePath}/servers')
|
@Route.get(f'{BasePath}/servers')
|
||||||
@Route.authorize
|
@Route.authorize(role=AuthRoleEnum.admin)
|
||||||
async def get_all_servers(self) -> Response:
|
async def get_all_servers(self) -> Response:
|
||||||
return jsonify(self._discord_service.get_all_servers().select(lambda x: x.to_dict()))
|
return jsonify(self._discord_service.get_all_servers().select(lambda x: x.to_dict()))
|
||||||
|
@ -21,3 +21,4 @@ class ServiceErrorCode(Enum):
|
|||||||
MailError = 10
|
MailError = 10
|
||||||
|
|
||||||
Unauthorized = 11
|
Unauthorized = 11
|
||||||
|
Forbidden = 12
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
import functools
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Optional
|
from typing import Optional, Callable
|
||||||
|
|
||||||
from flask import request, jsonify
|
from flask import request, jsonify
|
||||||
from flask_cors import cross_origin
|
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.exception.service_exception import ServiceException
|
||||||
from bot_api.model.error_dto import ErrorDTO
|
from bot_api.model.error_dto import ErrorDTO
|
||||||
from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC
|
from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC
|
||||||
|
from bot_data.model.auth_role_enum import AuthRoleEnum
|
||||||
|
|
||||||
|
|
||||||
class Route:
|
class Route:
|
||||||
@ -23,7 +25,10 @@ class Route:
|
|||||||
cls._auth = auth
|
cls._auth = auth
|
||||||
|
|
||||||
@classmethod
|
@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)
|
@wraps(f)
|
||||||
async def decorator(*args, **kwargs):
|
async def decorator(*args, **kwargs):
|
||||||
token = None
|
token = None
|
||||||
@ -46,6 +51,23 @@ class Route:
|
|||||||
error = ErrorDTO(ex.error_code, ex.message)
|
error = ErrorDTO(ex.error_code, ex.message)
|
||||||
return jsonify(error.to_dict()), 401
|
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 await f(*args, **kwargs)
|
||||||
|
|
||||||
return decorator
|
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.abc.auth_user_repository_abc import AuthUserRepositoryABC
|
||||||
from bot_data.model.auth_user import AuthUser
|
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):
|
class AuthService(AuthServiceABC):
|
||||||
|
|
||||||
@ -65,7 +67,7 @@ class AuthService(AuthServiceABC):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _is_email_valid(email: str) -> bool:
|
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 True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
@ -19,7 +19,7 @@ class AuthUserTransformer(TransformerABC):
|
|||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
datetime.now(tz=timezone.utc),
|
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
|
id=0 if dto.id is None else dto.id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user