From 78e6b48c243ff948620d4c8221e0a429375f8ddc Mon Sep 17 00:00:00 2001 From: Evgeniy Alekseev Date: Tue, 22 Nov 2022 02:19:37 +0200 Subject: [PATCH] ask users to repeat password In case if password is asked via getpass, it is possible to make typo and user will not see the mistake. In order to avoid it, additional confirmation has been added --- src/ahriman/application/handlers/users.py | 11 +++++++++- src/ahriman/core/exceptions.py | 15 ++++++++++++++ .../handlers/test_handler_users.py | 20 +++++++++++++++---- 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/ahriman/application/handlers/users.py b/src/ahriman/application/handlers/users.py index 9ebf243e..bf8a5b4b 100644 --- a/src/ahriman/application/handlers/users.py +++ b/src/ahriman/application/handlers/users.py @@ -26,6 +26,7 @@ from typing import Type from ahriman.application.handlers import Handler from ahriman.core.configuration import Configuration from ahriman.core.database import SQLite +from ahriman.core.exceptions import PasswordError from ahriman.core.formatters import UserPrinter from ahriman.models.action import Action from ahriman.models.user import User @@ -149,7 +150,15 @@ class Users(Handler): Returns: User: built user descriptor """ + def read_password() -> str: + first_password = getpass.getpass() + second_password = getpass.getpass("Repeat password: ") + if first_password != second_password: + raise PasswordError("passwords don't match") + return first_password + password = args.password if password is None: - password = getpass.getpass() + password = read_password() + return User(username=args.username, password=password, access=args.role) diff --git a/src/ahriman/core/exceptions.py b/src/ahriman/core/exceptions.py index 710e5f08..e61cf8f0 100644 --- a/src/ahriman/core/exceptions.py +++ b/src/ahriman/core/exceptions.py @@ -179,6 +179,21 @@ class PathError(ValueError): ValueError.__init__(self, f"Path `{path}` does not belong to repository root `{root}`") +class PasswordError(ValueError): + """ + exception which will be raised in case of password related errors + """ + + def __init__(self, details: Any) -> None: + """ + default constructor + + Args: + details(Any); error details + """ + ValueError.__init__(self, f"Password error: {details}") + + class ReportError(RuntimeError): """ report generation exception diff --git a/tests/ahriman/application/handlers/test_handler_users.py b/tests/ahriman/application/handlers/test_handler_users.py index d7139a17..0990b6c0 100644 --- a/tests/ahriman/application/handlers/test_handler_users.py +++ b/tests/ahriman/application/handlers/test_handler_users.py @@ -3,11 +3,12 @@ import pytest from pathlib import Path from pytest_mock import MockerFixture +from unittest.mock import call as MockCall from ahriman.application.handlers import Users from ahriman.core.configuration import Configuration from ahriman.core.database import SQLite -from ahriman.core.exceptions import InitializeError +from ahriman.core.exceptions import InitializeError, PasswordError from ahriman.models.action import Action from ahriman.models.user import User from ahriman.models.user_access import UserAccess @@ -214,14 +215,25 @@ def test_user_create_getpass(args: argparse.Namespace, mocker: MockerFixture) -> """ args = _default_args(args) args.password = None - getpass_mock = mocker.patch("getpass.getpass", return_value="password") - generated = Users.user_create(args) - getpass_mock.assert_called_once_with() + generated = Users.user_create(args) + getpass_mock.assert_has_calls([MockCall(), MockCall("Repeat password: ")]) assert generated.password == "password" +def test_user_create_getpass_exception(args: argparse.Namespace, mocker: MockerFixture) -> None: + """ + must raise password error in case if password doesn't match + """ + args = _default_args(args) + args.password = None + mocker.patch("getpass.getpass", side_effect=lambda *_: User.generate_password(10)) + + with pytest.raises(PasswordError): + Users.user_create(args) + + def test_disallow_auto_architecture_run() -> None: """ must not allow multi architecture run