Secured password handling #70

This commit is contained in:
Sven Heidemann 2022-10-18 18:33:03 +02:00
parent a51efa641d
commit a082b879ca
6 changed files with 35 additions and 15 deletions

View File

@ -56,7 +56,7 @@ 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(role=AuthRoleEnum.admin) @Route.authorize
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())

View File

@ -63,8 +63,8 @@ class AuthService(AuthServiceABC):
return mail return mail
@staticmethod @staticmethod
def _hash_sha256(password: str) -> str: def _hash_sha256(password: str, salt: str) -> str:
return hashlib.sha256(password.encode('utf-8')).hexdigest() return hashlib.sha256(f'{password}{salt}'.encode('utf-8')).hexdigest()
@staticmethod @staticmethod
def _is_email_valid(email: str) -> bool: def _is_email_valid(email: str) -> bool:
@ -188,8 +188,9 @@ class AuthService(AuthServiceABC):
if db_user is not None: if db_user is not None:
raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists') raise ServiceException(ServiceErrorCode.InvalidUser, 'User already exists')
user_dto.password = self._hash_sha256(user_dto.password)
user = AUT.to_db(user_dto) 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): if not self._is_email_valid(user.email):
raise ServiceException(ServiceErrorCode.InvalidData, 'Invalid E-Mail address') raise ServiceException(ServiceErrorCode.InvalidData, 'Invalid E-Mail address')
@ -244,17 +245,18 @@ class AuthService(AuthServiceABC):
# hash passwords in DTOs # hash passwords in DTOs
if update_user_dto.auth_user.password is not None and update_user_dto.auth_user.password != '': if update_user_dto.auth_user.password is not None and update_user_dto.auth_user.password != '':
is_existing_password_set = True 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: if update_user_dto.auth_user.password != user.password:
raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong 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 != '': if update_user_dto.new_auth_user.password is not None and update_user_dto.new_auth_user.password != '':
is_new_password_set = True 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 # 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: 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 user.password = update_user_dto.new_auth_user.password
self._auth_users.update_auth_user(user) self._auth_users.update_auth_user(user)
@ -301,7 +303,8 @@ class AuthService(AuthServiceABC):
# update password # update password
if update_user_dto.change_password and update_user_dto.auth_user.password != update_user_dto.new_auth_user.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 # 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: 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: if db_user is None:
raise ServiceException(ServiceErrorCode.InvalidUser, f'User not found') 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: if db_user.password != user_dto.password:
raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password') raise ServiceException(ServiceErrorCode.InvalidUser, 'Wrong password')
@ -432,5 +435,5 @@ class AuthService(AuthServiceABC):
if user.password is None or rp_dto.password == '': if user.password is None or rp_dto.password == '':
raise ServiceException(ServiceErrorCode.InvalidData, f'Password not set') 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() self._db.save_changes()

View File

@ -18,6 +18,7 @@ class AuthUserTransformer(TransformerABC):
None, None,
None, None,
None, None,
None,
datetime.now(tz=timezone.utc), datetime.now(tz=timezone.utc),
AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role), AuthRoleEnum.normal if dto.auth_role is None else AuthRoleEnum(dto.auth_role),
dto.user_id, dto.user_id,
@ -31,7 +32,7 @@ class AuthUserTransformer(TransformerABC):
db.first_name, db.first_name,
db.last_name, db.last_name,
db.email, db.email,
db.password, '',
db.confirmation_id, db.confirmation_id,
db.auth_role, db.auth_role,
db.user_id db.user_id

View File

@ -23,12 +23,13 @@ class ApiMigration(MigrationABC):
`LastName` VARCHAR(255), `LastName` VARCHAR(255),
`EMail` VARCHAR(255), `EMail` VARCHAR(255),
`Password` VARCHAR(255), `Password` VARCHAR(255),
`PasswordSalt` VARCHAR(255),
`RefreshToken` VARCHAR(255), `RefreshToken` VARCHAR(255),
`ConfirmationId` VARCHAR(255) DEFAULT NULL, `ConfirmationId` VARCHAR(255) DEFAULT NULL,
`ForgotPasswordId` VARCHAR(255) DEFAULT NULL, `ForgotPasswordId` VARCHAR(255) DEFAULT NULL,
`RefreshTokenExpiryTime` DATETIME(6) NOT NULL, `RefreshTokenExpiryTime` DATETIME(6) NOT NULL,
`AuthRole` INT NOT NULL DEFAULT '0', `AuthRole` INT NOT NULL DEFAULT '0',
`UserId` BIGINT NOT NULL DEFAULT '0', `UserId` BIGINT DEFAULT NULL,
`CreatedOn` DATETIME(6) NOT NULL, `CreatedOn` DATETIME(6) NOT NULL,
`LastModifiedOn` DATETIME(6) NOT NULL, `LastModifiedOn` DATETIME(6) NOT NULL,
PRIMARY KEY(`Id`), PRIMARY KEY(`Id`),

View File

@ -1,3 +1,4 @@
import uuid
from datetime import datetime from datetime import datetime
from typing import Optional from typing import Optional
from cpl_core.database import TableABC from cpl_core.database import TableABC
@ -14,6 +15,7 @@ class AuthUser(TableABC):
last_name: str, last_name: str,
email: str, email: str,
password: str, password: str,
password_salt: Optional[str],
refresh_token: Optional[str], refresh_token: Optional[str],
confirmation_id: Optional[str], confirmation_id: Optional[str],
forgot_password_id: Optional[str], forgot_password_id: Optional[str],
@ -29,6 +31,7 @@ class AuthUser(TableABC):
self._last_name = last_name self._last_name = last_name
self._email = email self._email = email
self._password = password self._password = password
self._password_salt = uuid.uuid4() if password_salt is None else password_salt
self._refresh_token = refresh_token self._refresh_token = refresh_token
self._confirmation_id = confirmation_id self._confirmation_id = confirmation_id
self._forgot_password_id = forgot_password_id self._forgot_password_id = forgot_password_id
@ -77,6 +80,14 @@ class AuthUser(TableABC):
def password(self, value: str): def password(self, value: str):
self._password = value 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 @property
def refresh_token(self) -> Optional[str]: def refresh_token(self) -> Optional[str]:
return self._refresh_token return self._refresh_token
@ -168,6 +179,7 @@ class AuthUser(TableABC):
`LastName`, `LastName`,
`EMail`, `EMail`,
`Password`, `Password`,
`PasswordSalt`,
`RefreshToken`, `RefreshToken`,
`ConfirmationId`, `ConfirmationId`,
`ForgotPasswordId`, `ForgotPasswordId`,
@ -182,12 +194,13 @@ class AuthUser(TableABC):
'{self._last_name}', '{self._last_name}',
'{self._email}', '{self._email}',
'{self._password}', '{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._confirmation_id is None else self._confirmation_id}',
'{"NULL" if self._forgot_password_id is None else self._forgot_password_id}', '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}',
'{self._refresh_token_expire_time}', '{self._refresh_token_expire_time}',
{self._auth_role_id.value}, {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._created_at}',
'{self._modified_at}' '{self._modified_at}'
) )
@ -201,6 +214,7 @@ class AuthUser(TableABC):
`LastName` = '{self._last_name}', `LastName` = '{self._last_name}',
`EMail` = '{self._email}', `EMail` = '{self._email}',
`Password` = '{self._password}', `Password` = '{self._password}',
`PasswordSalt` = '{self._password_salt}',
`RefreshToken` = '{self._refresh_token}', `RefreshToken` = '{self._refresh_token}',
`ConfirmationId` = '{"NULL" if self._confirmation_id is None else self._confirmation_id}', `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}', `ForgotPasswordId` = '{"NULL" if self._forgot_password_id is None else self._forgot_password_id}',

View File

@ -36,8 +36,9 @@ class AuthUserRepositoryService(AuthUserRepositoryABC):
self._get_value_from_result(result[6]), self._get_value_from_result(result[6]),
self._get_value_from_result(result[7]), self._get_value_from_result(result[7]),
self._get_value_from_result(result[8]), self._get_value_from_result(result[8]),
AuthRoleEnum(self._get_value_from_result(result[9])), self._get_value_from_result(result[9]),
self._get_value_from_result(result[10]), AuthRoleEnum(self._get_value_from_result(result[10])),
self._get_value_from_result(result[11]),
id=self._get_value_from_result(result[0]) id=self._get_value_from_result(result[0])
) )