From 8d29fbd07f9345c9942c4ace5a3036321f10109f Mon Sep 17 00:00:00 2001 From: Daniel Girtler Date: Fri, 18 Jul 2025 05:58:06 +0000 Subject: [PATCH] Revert "Move root password menu into authentication menu (#3650)" This reverts commit e0c3bb3869637d332b98e77665e46293cb69d5ae. --- archinstall/lib/args.py | 19 +++------- .../lib/authentication/authentication_menu.py | 25 ------------ archinstall/lib/global_menu.py | 38 ++++++++++++------- archinstall/lib/models/authentication.py | 5 --- archinstall/lib/models/users.py | 8 ++-- archinstall/scripts/guided.py | 4 +- tests/test_args.py | 11 ++---- 7 files changed, 39 insertions(+), 71 deletions(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index b255d1cc..4360879e 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -23,8 +23,6 @@ from archinstall.lib.models.network_configuration import NetworkConfiguration from archinstall.lib.models.packages import Repository from archinstall.lib.models.profile import ProfileConfiguration from archinstall.lib.models.users import Password, User -from archinstall.lib.models.profile_model import ProfileConfiguration -from archinstall.lib.models.users import Password, User, UserSerialization from archinstall.lib.output import debug, error, logger, warn from archinstall.lib.plugins import load_plugin from archinstall.lib.translationhandler import Language, tr, translation_handler @@ -80,15 +78,14 @@ class ArchConfig: # Special fields that should be handle with care due to security implications users: list[User] = field(default_factory=list) + root_enc_password: Password | None = None def unsafe_json(self) -> dict[str, Any]: - config: dict[str, list[UserSerialization] | str | None] = { + config = { 'users': [user.json() for user in self.users], + 'root_enc_password': self.root_enc_password.enc_password if self.root_enc_password else None, } - if self.auth_config and self.auth_config.root_enc_password: - config['root_enc_password'] = self.auth_config.root_enc_password.enc_password - if self.disk_config: disk_encryption = self.disk_config.disk_encryption if disk_encryption and disk_encryption.encryption_password: @@ -225,17 +222,11 @@ class ArchConfig: arch_config.services = services # DEPRECATED: backwards compatibility - root_password = None if root_password := args_config.get('!root-password', None): - root_password = Password(plaintext=root_password) + arch_config.root_enc_password = Password(plaintext=root_password) if enc_password := args_config.get('root_enc_password', None): - root_password = Password(enc_password=enc_password) - - if root_password is not None: - if arch_config.auth_config is None: - arch_config.auth_config = AuthenticationConfiguration() - arch_config.auth_config.root_enc_password = root_password + arch_config.root_enc_password = Password(enc_password=enc_password) if custom_commands := args_config.get('custom_commands', []): arch_config.custom_commands = custom_commands diff --git a/archinstall/lib/authentication/authentication_menu.py b/archinstall/lib/authentication/authentication_menu.py index 9180e82f..b88853e4 100644 --- a/archinstall/lib/authentication/authentication_menu.py +++ b/archinstall/lib/authentication/authentication_menu.py @@ -3,9 +3,7 @@ from typing import override from archinstall.lib.disk.fido import Fido2 from archinstall.lib.menu.abstract_menu import AbstractSubMenu from archinstall.lib.models.authentication import AuthenticationConfiguration, U2FLoginConfiguration, U2FLoginMethod -from archinstall.lib.models.users import Password from archinstall.lib.translationhandler import tr -from archinstall.lib.utils.util import get_password from archinstall.tui.curses_menu import SelectMenu from archinstall.tui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.result import ResultType @@ -35,12 +33,6 @@ class AuthenticationMenu(AbstractSubMenu[AuthenticationConfiguration]): def _define_menu_options(self) -> list[MenuItem]: return [ - MenuItem( - text=tr('Root password'), - action=select_root_password, - preview_action=self._prev_root_pwd, - key='root_enc_password', - ), MenuItem( text=tr('U2F login setup'), action=select_u2f_login, @@ -50,18 +42,6 @@ class AuthenticationMenu(AbstractSubMenu[AuthenticationConfiguration]): ), ] - def _prev_root_pwd(self, item: MenuItem) -> str | None: - if item.value is not None: - password: Password = item.value - return f'{tr("Root password")}: {password.hidden()}' - return None - - def _depends_on_u2f(self) -> bool: - devices = Fido2.get_fido2_devices() - if not devices: - return False - return True - def _prev_u2f_login(self, item: MenuItem) -> str | None: if item.value is not None: u2f_config: U2FLoginConfiguration = item.value @@ -81,11 +61,6 @@ class AuthenticationMenu(AbstractSubMenu[AuthenticationConfiguration]): return None -def select_root_password(preset: str | None = None) -> Password | None: - password = get_password(text=tr('Root password'), allow_skip=True) - return password - - def select_u2f_login(preset: U2FLoginConfiguration) -> U2FLoginConfiguration | None: devices = Fido2.get_fido2_devices() if not devices: diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index e86e537c..8014a643 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -34,11 +34,10 @@ from .models.network_configuration import NetworkConfiguration, NicType from .models.packages import Repository from .models.profile import ProfileConfiguration from .models.users import Password, User -from .models.profile_model import ProfileConfiguration -from .models.users import User from .output import FormattedOutput from .pacman.config import PacmanConfig from .translationhandler import Language, tr, translation_handler +from .utils.util import get_password class GlobalMenu(AbstractMenu[None]): @@ -111,9 +110,16 @@ class GlobalMenu(AbstractMenu[None]): preview_action=self._prev_hostname, key='hostname', ), + MenuItem( + text=tr('Root password'), + action=self._set_root_password, + preview_action=self._prev_root_pwd, + key='root_enc_password', + ), MenuItem( text=tr('Authentication'), action=self._select_authentication, + value=[], preview_action=self._prev_authentication, key='auth_config', ), @@ -224,16 +230,13 @@ class GlobalMenu(AbstractMenu[None]): missing = set() - item: MenuItem = self._item_group.find_by_key('auth_config') - auth_config: AuthenticationConfiguration | None = item.value - - if (auth_config is None or auth_config.root_enc_password is None) and not has_superuser(): - missing.add( - tr('Either root-password or at least 1 user with sudo privileges must be specified'), - ) - for item in self._item_group.items: - if item.mandatory: + if item.key in ['root_enc_password', 'users']: + if not check('root_enc_password') and not has_superuser(): + missing.add( + tr('Either root-password or at least 1 user with sudo privileges must be specified'), + ) + elif item.mandatory: assert item.key is not None if not check(item.key): missing.add(item.text) @@ -311,9 +314,6 @@ class GlobalMenu(AbstractMenu[None]): auth_config: AuthenticationConfiguration = item.value output = '' - if auth_config.root_enc_password: - output += f'{tr("Root password")}: {auth_config.root_enc_password.hidden()}\n' - if auth_config.u2f_config: u2f_config = auth_config.u2f_config login_method = u2f_config.u2f_login_method.display_value() @@ -400,6 +400,12 @@ class GlobalMenu(AbstractMenu[None]): return f'{tr("Hostname")}: {item.value}' return None + def _prev_root_pwd(self, item: MenuItem) -> str | None: + if item.value is not None: + password: Password = item.value + return f'{tr("Root password")}: {password.hidden()}' + return None + def _prev_parallel_dw(self, item: MenuItem) -> str | None: if item.value is not None: return f'{tr("Parallel Downloads")}: {item.value}' @@ -504,6 +510,10 @@ class GlobalMenu(AbstractMenu[None]): return None + def _set_root_password(self, preset: str | None = None) -> Password | None: + password = get_password(text=tr('Root password'), allow_skip=True) + return password + def _select_disk_config( self, preset: DiskLayoutConfiguration | None = None, diff --git a/archinstall/lib/models/authentication.py b/archinstall/lib/models/authentication.py index 74ead43c..66831813 100644 --- a/archinstall/lib/models/authentication.py +++ b/archinstall/lib/models/authentication.py @@ -2,7 +2,6 @@ from dataclasses import dataclass from enum import Enum from typing import Any, NotRequired, TypedDict -from archinstall.lib.models.users import Password from archinstall.lib.translationhandler import tr @@ -59,7 +58,6 @@ class U2FLoginConfiguration: @dataclass class AuthenticationConfiguration: - root_enc_password: Password | None = None u2f_config: U2FLoginConfiguration | None = None @staticmethod @@ -69,9 +67,6 @@ class AuthenticationConfiguration: if (u2f_config := args.get('u2f_config')) is not None: auth_config.u2f_config = U2FLoginConfiguration.parse_arg(u2f_config) - if enc_password := args.get('root_enc_password'): - auth_config.root_enc_password = Password(enc_password=enc_password) - return auth_config def json(self) -> AuthenticationSerialization: diff --git a/archinstall/lib/models/users.py b/archinstall/lib/models/users.py index 3f18f52d..e6a33841 100644 --- a/archinstall/lib/models/users.py +++ b/archinstall/lib/models/users.py @@ -100,8 +100,8 @@ class PasswordStrength(Enum): return PasswordStrength.VERY_WEAK -UserSerialization = TypedDict( - 'UserSerialization', +_UserSerialization = TypedDict( + '_UserSerialization', { 'username': str, '!password': NotRequired[str], @@ -173,7 +173,7 @@ class User: 'groups': self.groups, } - def json(self) -> UserSerialization: + def json(self) -> _UserSerialization: return { 'username': self.username, 'enc_password': self.password.enc_password, @@ -184,7 +184,7 @@ class User: @classmethod def parse_arguments( cls, - args: list[UserSerialization], + args: list[_UserSerialization], ) -> list['User']: users: list[User] = [] diff --git a/archinstall/scripts/guided.py b/archinstall/scripts/guided.py index 6de7fe09..88a92978 100644 --- a/archinstall/scripts/guided.py +++ b/archinstall/scripts/guided.py @@ -138,8 +138,8 @@ def perform_installation(mountpoint: Path) -> None: if accessibility_tools_in_use(): installation.enable_espeakup() - if config.auth_config and config.auth_config.root_enc_password: - root_user = User('root', config.auth_config.root_enc_password, False) + if root_pw := config.root_enc_password: + root_user = User('root', root_pw, False) installation.set_user_password(root_user) if (profile_config := config.profile_config) and profile_config.profile: diff --git a/tests/test_args.py b/tests/test_args.py index 3feb0f9f..a8666be7 100644 --- a/tests/test_args.py +++ b/tests/test_args.py @@ -134,7 +134,6 @@ def test_config_file_parsing( audio_config=AudioConfiguration(audio=Audio.PIPEWIRE), ), auth_config=AuthenticationConfiguration( - root_enc_password=Password(enc_password='password_hash'), u2f_config=U2FLoginConfiguration( u2f_login_method=U2FLoginMethod.Passwordless, passwordless_sudo=True, @@ -224,6 +223,7 @@ def test_config_file_parsing( ), ], services=['service_1', 'service_2'], + root_enc_password=Password(enc_password='password_hash'), custom_commands=["echo 'Hello, World!'"], ) @@ -280,8 +280,7 @@ def test_deprecated_creds_config_parsing( handler = ArchConfigHandler() arch_config = handler.config - assert arch_config.auth_config is not None - assert arch_config.auth_config.root_enc_password == Password(plaintext='rootPwd') + assert arch_config.root_enc_password == Password(plaintext='rootPwd') assert arch_config.users == [ User( @@ -332,8 +331,7 @@ def test_encrypted_creds_with_arg( handler = ArchConfigHandler() arch_config = handler.config - assert arch_config.auth_config is not None - assert arch_config.auth_config.root_enc_password == Password(enc_password='$y$j9T$FWCInXmSsS.8KV4i7O50H.$Hb6/g.Sw1ry888iXgkVgc93YNuVk/Rw94knDKdPVQw7') + assert arch_config.root_enc_password == Password(enc_password='$y$j9T$FWCInXmSsS.8KV4i7O50H.$Hb6/g.Sw1ry888iXgkVgc93YNuVk/Rw94knDKdPVQw7') assert arch_config.users == [ User( username='t', @@ -361,8 +359,7 @@ def test_encrypted_creds_with_env_var( handler = ArchConfigHandler() arch_config = handler.config - assert arch_config.auth_config is not None - assert arch_config.auth_config.root_enc_password == Password(enc_password='$y$j9T$FWCInXmSsS.8KV4i7O50H.$Hb6/g.Sw1ry888iXgkVgc93YNuVk/Rw94knDKdPVQw7') + assert arch_config.root_enc_password == Password(enc_password='$y$j9T$FWCInXmSsS.8KV4i7O50H.$Hb6/g.Sw1ry888iXgkVgc93YNuVk/Rw94knDKdPVQw7') assert arch_config.users == [ User( username='t',