From a082b879ca08190715b379ee07962e9ccfedaba2 Mon Sep 17 00:00:00 2001 From: Sven Heidemann Date: Tue, 18 Oct 2022 18:33:03 +0200 Subject: [PATCH] Secured password handling #70 --- .../src/bot_api/controller/auth_controller.py | 2 +- kdb-bot/src/bot_api/service/auth_service.py | 19 +++++++++++-------- .../transformer/auth_user_transformer.py | 3 ++- .../src/bot_data/migration/api_migration.py | 3 ++- kdb-bot/src/bot_data/model/auth_user.py | 18 ++++++++++++++++-- .../service/auth_user_repository_service.py | 5 +++-- 6 files changed, 35 insertions(+), 15 deletions(-) diff --git a/kdb-bot/src/bot_api/controller/auth_controller.py b/kdb-bot/src/bot_api/controller/auth_controller.py index 78a17e79e6..b74c951855 100644 --- a/kdb-bot/src/bot_api/controller/auth_controller.py +++ b/kdb-bot/src/bot_api/controller/auth_controller.py @@ -56,7 +56,7 @@ class AuthController: return jsonify(result.to_dict()) @Route.get(f'{BasePath}/users/get/') - @Route.authorize(role=AuthRoleEnum.admin) + @Route.authorize 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()) diff --git a/kdb-bot/src/bot_api/service/auth_service.py b/kdb-bot/src/bot_api/service/auth_service.py index b2056530d7..5887bb9a30 100644 --- a/kdb-bot/src/bot_api/service/auth_service.py +++ b/kdb-bot/src/bot_api/service/auth_service.py @@ -63,8 +63,8 @@ class AuthService(AuthServiceABC): return mail @staticmethod - def _hash_sha256(password: str) -> str: - return hashlib.sha256(password.encode('utf-8')).hexdigest() + def _hash_sha256(password: str, salt: str) -> str: + return hashlib.sha256(f'{password}{salt}'.encode('utf-8')).hexdigest() @staticmethod def _is_email_valid(email: str) -> bool: @@ -188,8 +188,9 @@ class AuthService(AuthServiceABC): if db_user is not None: raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') - user_dto.password = self._hash_sha256(user_dto.password) user = AUT.to_db(user_dto) + user.password_salt = uuid.uuid4() + user.password = self._hash_sha256(user_dto.password, user.password_salt) if not self._is_email_valid(user.email): raise ServiceException(ServiceErrorCode.InvalidData, 'Invalid E-Mail address') @@ -244,17 +245,18 @@ class AuthService(AuthServiceABC): # hash passwords in DTOs if update_user_dto.auth_user.password is not None and update_user_dto.auth_user.password != '': is_existing_password_set = True - update_user_dto.auth_user.password = self._hash_sha256(update_user_dto.auth_user.password) + update_user_dto.auth_user.password = self._hash_sha256(update_user_dto.auth_user.password, user.password_salt) if update_user_dto.auth_user.password != user.password: raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') if update_user_dto.new_auth_user.password is not None and update_user_dto.new_auth_user.password != '': is_new_password_set = True - update_user_dto.new_auth_user.password = self._hash_sha256(update_user_dto.new_auth_user.password) + update_user_dto.new_auth_user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) # update password if is_existing_password_set and is_new_password_set and update_user_dto.auth_user.password != update_user_dto.new_auth_user.password: + user.password_salt = uuid.uuid4() user.password = update_user_dto.new_auth_user.password self._auth_users.update_auth_user(user) @@ -301,7 +303,8 @@ class AuthService(AuthServiceABC): # update password if update_user_dto.change_password and update_user_dto.auth_user.password != update_user_dto.new_auth_user.password: - user.password = self._hash_sha256(update_user_dto.new_auth_user.password) + user.password_salt = uuid.uuid4() + user.password = self._hash_sha256(update_user_dto.new_auth_user.password, user.password_salt) # update role if user.auth_role == update_user_dto.auth_user.auth_role and user.auth_role != update_user_dto.new_auth_user.auth_role: @@ -350,7 +353,7 @@ class AuthService(AuthServiceABC): if db_user is None: raise ServiceException(ServiceErrorCode.InvalidUser, f'User not found') - user_dto.password = self._hash_sha256(user_dto.password) + user_dto.password = self._hash_sha256(user_dto.password, db_user.password_salt) if db_user.password != user_dto.password: raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') @@ -432,5 +435,5 @@ class AuthService(AuthServiceABC): if user.password is None or rp_dto.password == '': raise ServiceException(ServiceErrorCode.InvalidData, f'Password not set') - user.password = self._hash_sha256(rp_dto.password) + user.password = self._hash_sha256(rp_dto.password, user.password_salt) self._db.save_changes() 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 b5608e2376..768cc7ef0f 100644 --- a/kdb-bot/src/bot_api/transformer/auth_user_transformer.py +++ b/kdb-bot/src/bot_api/transformer/auth_user_transformer.py @@ -18,6 +18,7 @@ class AuthUserTransformer(TransformerABC): None, None, None, + None, datetime.now(tz=timezone.utc), AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role), dto.user_id, @@ -31,7 +32,7 @@ class AuthUserTransformer(TransformerABC): db.first_name, db.last_name, db.email, - db.password, + '', db.confirmation_id, db.auth_role, db.user_id diff --git a/kdb-bot/src/bot_data/migration/api_migration.py b/kdb-bot/src/bot_data/migration/api_migration.py index 6860ae1440..8466ec463b 100644 --- a/kdb-bot/src/bot_data/migration/api_migration.py +++ b/kdb-bot/src/bot_data/migration/api_migration.py @@ -23,12 +23,13 @@ class ApiMigration(MigrationABC): `LastName` VARCHAR(255), `EMail` VARCHAR(255), `Password` VARCHAR(255), + `PasswordSalt` VARCHAR(255), `RefreshToken` VARCHAR(255), `ConfirmationId` VARCHAR(255) DEFAULT NULL, `ForgotPasswordId` VARCHAR(255) DEFAULT NULL, `RefreshTokenExpiryTime` DATETIME(6) NOT NULL, `AuthRole` INT NOT NULL DEFAULT '0', - `UserId` BIGINT NOT NULL DEFAULT '0', + `UserId` BIGINT DEFAULT NULL, `CreatedOn` DATETIME(6) NOT NULL, `LastModifiedOn` DATETIME(6) NOT NULL, PRIMARY KEY(`Id`), diff --git a/kdb-bot/src/bot_data/model/auth_user.py b/kdb-bot/src/bot_data/model/auth_user.py index fe92ad1eb9..58c9f982e5 100644 --- a/kdb-bot/src/bot_data/model/auth_user.py +++ b/kdb-bot/src/bot_data/model/auth_user.py @@ -1,3 +1,4 @@ +import uuid from datetime import datetime from typing import Optional from cpl_core.database import TableABC @@ -14,6 +15,7 @@ class AuthUser(TableABC): last_name: str, email: str, password: str, + password_salt: Optional[str], refresh_token: Optional[str], confirmation_id: Optional[str], forgot_password_id: Optional[str], @@ -29,6 +31,7 @@ class AuthUser(TableABC): self._last_name = last_name self._email = email self._password = password + self._password_salt = uuid.uuid4() if password_salt is None else password_salt self._refresh_token = refresh_token self._confirmation_id = confirmation_id self._forgot_password_id = forgot_password_id @@ -77,6 +80,14 @@ class AuthUser(TableABC): def password(self, value: str): self._password = value + @property + def password_salt(self) -> str: + return self._password_salt + + @password_salt.setter + def password_salt(self, value: str): + self._password_salt = value + @property def refresh_token(self) -> Optional[str]: return self._refresh_token @@ -168,6 +179,7 @@ class AuthUser(TableABC): `LastName`, `EMail`, `Password`, + `PasswordSalt`, `RefreshToken`, `ConfirmationId`, `ForgotPasswordId`, @@ -182,12 +194,13 @@ class AuthUser(TableABC): '{self._last_name}', '{self._email}', '{self._password}', - '{self._refresh_token}', + '{self._password_salt}', + '{"NULL" if self._refresh_token is None else self._refresh_token}', '{"NULL" if self._confirmation_id is None else self._confirmation_id}', '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', '{self._refresh_token_expire_time}', {self._auth_role_id.value}, - {"NULL" if self._user_id is None else self._user_id} + {"NULL" if self._user_id is None else self._user_id}, '{self._created_at}', '{self._modified_at}' ) @@ -201,6 +214,7 @@ class AuthUser(TableABC): `LastName` = '{self._last_name}', `EMail` = '{self._email}', `Password` = '{self._password}', + `PasswordSalt` = '{self._password_salt}', `RefreshToken` = '{self._refresh_token}', `ConfirmationId` = '{"NULL" if self._confirmation_id is None else self._confirmation_id}', `ForgotPasswordId` = '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', diff --git a/kdb-bot/src/bot_data/service/auth_user_repository_service.py b/kdb-bot/src/bot_data/service/auth_user_repository_service.py index c8fee7b4aa..ce8e3e4bd7 100644 --- a/kdb-bot/src/bot_data/service/auth_user_repository_service.py +++ b/kdb-bot/src/bot_data/service/auth_user_repository_service.py @@ -36,8 +36,9 @@ class AuthUserRepositoryService(AuthUserRepositoryABC): self._get_value_from_result(result[6]), self._get_value_from_result(result[7]), self._get_value_from_result(result[8]), - AuthRoleEnum(self._get_value_from_result(result[9])), - self._get_value_from_result(result[10]), + self._get_value_from_result(result[9]), + AuthRoleEnum(self._get_value_from_result(result[10])), + self._get_value_from_result(result[11]), id=self._get_value_from_result(result[0]) )