Added logic to create default api-key for frontend #162-3

This commit is contained in:
2023-02-09 22:09:02 +01:00
parent 7f14aff1bd
commit a6df06f13a
12 changed files with 112 additions and 19 deletions

View File

@@ -83,6 +83,10 @@ class AuthServiceABC(ABC):
async def verify_login(self, token_str: str) -> bool:
pass
@abstractmethod
async def verify_api_key(self, api_key: str) -> bool:
pass
@abstractmethod
async def login_async(self, user_dto: AuthUserDTO) -> TokenDTO:
pass

View File

@@ -13,7 +13,7 @@ from bot_api.api import Api
from bot_api.api_thread import ApiThread
from bot_api.controller.auth_controller import AuthController
from bot_api.controller.auth_discord_controller import AuthDiscordController
from bot_api.controller.grahpql_controller import GraphQLController
from bot_api.controller.graphql_controller import GraphQLController
from bot_api.controller.gui_controller import GuiController
from bot_api.event.bot_api_on_ready_event import BotApiOnReadyEvent
from bot_api.service.auth_service import AuthService

View File

@@ -33,7 +33,7 @@ class GraphQLController:
return PLAYGROUND_HTML, 200
@Route.post(f"{BasePath}")
@Route.authorize
@Route.authorize(by_api_key=True)
async def graphql(self):
data = request.get_json()

View File

@@ -30,15 +30,32 @@ class Route:
cls._env = env.environment_name
@classmethod
def authorize(cls, f: Callable = None, role: AuthRoleEnum = None, skip_in_dev=False):
def authorize(cls, f: Callable = None, role: AuthRoleEnum = None, skip_in_dev=False, by_api_key=False):
if f is None:
return functools.partial(cls.authorize, role=role, skip_in_dev=skip_in_dev)
return functools.partial(cls.authorize, role=role, skip_in_dev=skip_in_dev, by_api_key=by_api_key)
@wraps(f)
async def decorator(*args, **kwargs):
if skip_in_dev and cls._env == "development":
return await f(*args, **kwargs)
if "Authorization" not in request.headers and by_api_key and "API-Key" in request.headers:
valid = False
try:
valid = cls._auth.verify_api_key(request.headers["API-Key"])
except ServiceException as e:
error = ErrorDTO(e.error_code, e.message)
return jsonify(error.to_dict()), 403
except Exception as e:
return jsonify(e), 500
if not valid:
ex = ServiceException(ServiceErrorCode.Unauthorized, f"API-Key invalid")
error = ErrorDTO(ex.error_code, ex.message)
return jsonify(error.to_dict()), 401
return await f(*args, **kwargs)
token = None
if "Authorization" in request.headers:
bearer = request.headers.get("Authorization")

View File

@@ -31,9 +31,11 @@ from bot_api.model.reset_password_dto import ResetPasswordDTO
from bot_api.model.token_dto import TokenDTO
from bot_api.model.update_auth_user_dto import UpdateAuthUserDTO
from bot_api.transformer.auth_user_transformer import AuthUserTransformer as AUT
from bot_data.abc.api_key_repository_abc import ApiKeyRepositoryABC
from bot_data.abc.auth_user_repository_abc import AuthUserRepositoryABC
from bot_data.abc.server_repository_abc import ServerRepositoryABC
from bot_data.abc.user_repository_abc import UserRepositoryABC
from bot_data.model.api_key import ApiKey
from bot_data.model.auth_role_enum import AuthRoleEnum
from bot_data.model.auth_user import AuthUser
from bot_data.model.auth_user_users_relation import AuthUserUsersRelation
@@ -49,9 +51,9 @@ class AuthService(AuthServiceABC):
bot: DiscordBotServiceABC,
db: DatabaseContextABC,
auth_users: AuthUserRepositoryABC,
api_keys: ApiKeyRepositoryABC,
users: UserRepositoryABC,
servers: ServerRepositoryABC,
# mailer: MailThread,
mailer: EMailClientABC,
t: TranslatePipe,
auth_settings: AuthenticationSettings,
@@ -64,6 +66,7 @@ class AuthService(AuthServiceABC):
self._bot = bot
self._db = db
self._auth_users = auth_users
self._api_keys = api_keys
self._users = users
self._servers = servers
self._mailer = mailer
@@ -82,6 +85,11 @@ class AuthService(AuthServiceABC):
return False
def _get_api_key_str(self, api_key: ApiKey) -> str:
return hashlib.sha256(
f"{api_key.identifier}:{api_key.key}+{self._auth_settings.secret_key}".encode("utf-8")
).hexdigest()
def generate_token(self, user: AuthUser) -> str:
token = jwt.encode(
payload={
@@ -221,7 +229,12 @@ class AuthService(AuthServiceABC):
raise ServiceException(ServiceErrorCode.InvalidUser, "User already exists")
user = AUT.to_db(user_dto)
if self._auth_users.get_all_auth_users().count() == 0:
if (
self._auth_users.get_all_auth_users()
.where(lambda x: x.name != "internal" and x.email != "internal@localhost")
.count()
== 0
):
user.auth_role = AuthRoleEnum.admin
user.password_salt = uuid.uuid4()
@@ -478,6 +491,18 @@ class AuthService(AuthServiceABC):
return True
def verify_api_key(self, api_key: str) -> bool:
try:
keys = self._api_keys.get_api_keys().select(self._get_api_key_str)
if not keys.contains(api_key):
raise ServiceException(ServiceErrorCode.InvalidData, "API-Key invalid")
except Exception as e:
self._logger.error(__name__, f"Token invalid", e)
return False
return True
async def login_async(self, user_dto: AuthUser) -> TokenDTO:
if user_dto is None:
raise ServiceException(ServiceErrorCode.InvalidData, "User not set")