Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib/user_interaction
diff options
context:
space:
mode:
authorDaniel Girtler <blackrabbit256@gmail.com>2022-05-27 05:48:29 +1000
committerGitHub <noreply@github.com>2022-05-26 21:48:29 +0200
commit870da403e79ab50350803b45f200e0b272334989 (patch)
tree9b203a054bd10cbc73a81b4fd5fe24ef8e6f141a /archinstall/lib/user_interaction
parent353c05318ce80b8eec32031c9e14b8471b458548 (diff)
Rework user management (#1220)
* Rework users * Update user installation * Fix config serialization * Update * Update schemas and documentation * Update * Fix flake8 * Make users mypy compatible * Fix minor copy Co-authored-by: Daniel Girtler <girtler.daniel@gmail.com> Co-authored-by: Anton Hvornum <anton@hvornum.se>
Diffstat (limited to 'archinstall/lib/user_interaction')
-rw-r--r--archinstall/lib/user_interaction/__init__.py2
-rw-r--r--archinstall/lib/user_interaction/manage_users_conf.py177
-rw-r--r--archinstall/lib/user_interaction/network_conf.py2
-rw-r--r--archinstall/lib/user_interaction/subvolume_config.py2
4 files changed, 64 insertions, 119 deletions
diff --git a/archinstall/lib/user_interaction/__init__.py b/archinstall/lib/user_interaction/__init__.py
index b0174d94..8aba4b4d 100644
--- a/archinstall/lib/user_interaction/__init__.py
+++ b/archinstall/lib/user_interaction/__init__.py
@@ -1,5 +1,5 @@
from .save_conf import save_config
-from .manage_users_conf import ask_for_superuser_account, ask_for_additional_users
+from .manage_users_conf import ask_for_additional_users
from .backwards_compatible_conf import generic_select, generic_multi_select
from .locale_conf import select_locale_lang, select_locale_enc
from .system_conf import select_kernel, select_harddrives, select_driver, ask_for_bootloader, ask_for_swap
diff --git a/archinstall/lib/user_interaction/manage_users_conf.py b/archinstall/lib/user_interaction/manage_users_conf.py
index ea909f35..567a2964 100644
--- a/archinstall/lib/user_interaction/manage_users_conf.py
+++ b/archinstall/lib/user_interaction/manage_users_conf.py
@@ -1,14 +1,12 @@
from __future__ import annotations
-import logging
import re
-from typing import Any, Dict, TYPE_CHECKING, List
+from typing import Any, Dict, TYPE_CHECKING, List, Optional
+from .utils import get_password
from ..menu import Menu
from ..menu.list_manager import ListManager
-from ..output import log
-from ..storage import storage
-from .utils import get_password
+from ..models.users import User
if TYPE_CHECKING:
_: Any
@@ -19,7 +17,7 @@ class UserList(ListManager):
subclass of ListManager for the managing of user accounts
"""
- def __init__(self, prompt: str, lusers: dict, sudo: bool = None):
+ def __init__(self, prompt: str, lusers: List[User]):
"""
param: prompt
type: str
@@ -27,140 +25,83 @@ class UserList(ListManager):
type: Dict
param: sudo. boolean to determine if we handle superusers or users. If None handles both types
"""
- self.sudo = sudo
- self.actions = [
+ self._actions = [
str(_('Add a user')),
str(_('Change password')),
str(_('Promote/Demote user')),
str(_('Delete User'))
]
- super().__init__(prompt, lusers, self.actions, self.actions[0])
-
- def reformat(self, data: List) -> Dict:
- def format_element(elem :str):
- # secret gives away the length of the password
- if data[elem].get('!password'):
- pwd = '*' * 16
- else:
- pwd = ''
- if data[elem].get('sudoer'):
- super_user = 'Superuser'
- else:
- super_user = ' '
- return f"{elem:16}: password {pwd:16} {super_user}"
+ super().__init__(prompt, lusers, self._actions, self._actions[0])
- return {format_element(e): e for e in data}
+ def reformat(self, data: List[User]) -> Dict[str, User]:
+ return {e.display(): e for e in data}
def action_list(self):
- if self.target:
- active_user = list(self.target.keys())[0]
- else:
- active_user = None
- sudoer = self.target[active_user].get('sudoer', False)
- if self.sudo is None:
- return self.actions
- if self.sudo and sudoer:
- return self.actions
- elif self.sudo and not sudoer:
- return [self.actions[2]]
- elif not self.sudo and sudoer:
- return [self.actions[2]]
+ active_user = self.target if self.target else None
+
+ if active_user is None:
+ return [self._actions[0]]
else:
- return self.actions
+ return self._actions[1:]
- def exec_action(self, data: Any):
+ def exec_action(self, data: List[User]) -> List[User]:
if self.target:
- active_user = list(self.target.keys())[0]
+ active_user = self.target
else:
active_user = None
- if self.action == self.actions[0]: # add
- new_user = self.add_user()
- # no unicity check, if exists will be replaced
- data.update(new_user)
- elif self.action == self.actions[1]: # change password
- data[active_user]['!password'] = get_password(
- prompt=str(_('Password for user "{}": ').format(active_user)))
- elif self.action == self.actions[2]: # promote/demote
- data[active_user]['sudoer'] = not data[active_user]['sudoer']
- elif self.action == self.actions[3]: # delete
- del data[active_user]
+ if self.action == self._actions[0]: # add
+ new_user = self._add_user()
+ if new_user is not None:
+ # in case a user with the same username as an existing user
+ # was created we'll replace the existing one
+ data = [d for d in data if d.username != new_user.username]
+ data += [new_user]
+ elif self.action == self._actions[1]: # change password
+ prompt = str(_('Password for user "{}": ').format(active_user.username))
+ new_password = get_password(prompt=prompt)
+ if new_password:
+ user = next(filter(lambda x: x == active_user, data), 1)
+ user.password = new_password
+ elif self.action == self._actions[2]: # promote/demote
+ user = next(filter(lambda x: x == active_user, data), 1)
+ user.sudo = False if user.sudo else True
+ elif self.action == self._actions[3]: # delete
+ data = [d for d in data if d != active_user]
+
+ return data
def _check_for_correct_username(self, username: str) -> bool:
if re.match(r'^[a-z_][a-z0-9_-]*\$?$', username) and len(username) <= 32:
return True
- log("The username you entered is invalid. Try again", level=logging.WARNING, fg='red')
return False
- def add_user(self):
+ def _add_user(self) -> Optional[User]:
print(_('\nDefine a new user\n'))
- prompt = str(_("User Name : "))
+ prompt = str(_('Enter username (leave blank to skip): '))
+
while True:
- userid = input(prompt).strip(' ')
- if not userid:
- return {} # end
- if not self._check_for_correct_username(userid):
- pass
+ username = input(prompt).strip(' ')
+ if not username:
+ return None
+ if not self._check_for_correct_username(username):
+ prompt = str(_("The username you entered is invalid. Try again")) + '\n' + prompt
else:
break
- if self.sudo:
- sudoer = True
- elif self.sudo is not None and not self.sudo:
- sudoer = False
- else:
- sudoer = False
- sudo_choice = Menu(str(_('Should {} be a superuser (sudoer)?')).format(userid), Menu.yes_no(),
- skip=False,
- preset_values=Menu.yes() if sudoer else Menu.no(),
- default_option=Menu.no()).run()
- sudoer = True if sudo_choice == Menu.yes() else False
-
- password = get_password(prompt=str(_('Password for user "{}": ').format(userid)))
-
- return {userid: {"!password": password, "sudoer": sudoer}}
-
-
-def manage_users(prompt: str, sudo: bool) -> tuple[dict, dict]:
- # TODO Filtering and some kind of simpler code
- lusers = {}
- if storage['arguments'].get('!superusers', {}):
- lusers.update({
- uid: {
- '!password': storage['arguments']['!superusers'][uid].get('!password'),
- 'sudoer': True
- }
- for uid in storage['arguments'].get('!superusers', {})
- })
- if storage['arguments'].get('!users', {}):
- lusers.update({
- uid: {
- '!password': storage['arguments']['!users'][uid].get('!password'),
- 'sudoer': False
- }
- for uid in storage['arguments'].get('!users', {})
- })
- # processing
- lusers = UserList(prompt, lusers, sudo).run()
- # return data
- superusers = {
- uid: {
- '!password': lusers[uid].get('!password')
- }
- for uid in lusers if lusers[uid].get('sudoer', False)
- }
- users = {uid: {'!password': lusers[uid].get('!password')} for uid in lusers if not lusers[uid].get('sudoer', False)}
- storage['arguments']['!superusers'] = superusers
- storage['arguments']['!users'] = users
- return superusers, users
-
-
-def ask_for_superuser_account(prompt: str) -> Dict[str, Dict[str, str]]:
- prompt = prompt if prompt else str(_('Define users with sudo privilege, by username: '))
- superusers, dummy = manage_users(prompt, sudo=True)
- return superusers
-
-
-def ask_for_additional_users(prompt: str = '') -> Dict[str, Dict[str, str | None]]:
- prompt = prompt if prompt else _('Any additional users to install (leave blank for no users): ')
- dummy, users = manage_users(prompt, sudo=False)
+
+ password = get_password(prompt=str(_('Password for user "{}": ').format(username)))
+
+ choice = Menu(
+ str(_('Should "{}" be a superuser (sudo)?')).format(username), Menu.yes_no(),
+ skip=False,
+ default_option=Menu.no()
+ ).run()
+
+ sudo = True if choice.value == Menu.yes() else False
+ return User(username, password, sudo)
+
+
+def ask_for_additional_users(prompt: str = '', defined_users: List[User] = []) -> List[User]:
+ prompt = prompt if prompt else _('Enter username (leave blank to skip): ')
+ users = UserList(prompt, defined_users).run()
return users
diff --git a/archinstall/lib/user_interaction/network_conf.py b/archinstall/lib/user_interaction/network_conf.py
index 25e9d4c6..5154d8b1 100644
--- a/archinstall/lib/user_interaction/network_conf.py
+++ b/archinstall/lib/user_interaction/network_conf.py
@@ -64,6 +64,8 @@ class ManualNetworkConfig(ListManager):
elif self.action == self._action_delete:
del data[iface_name]
+ return data
+
def _select_iface(self, existing_ifaces: List[str]) -> Optional[Any]:
all_ifaces = list_interfaces().values()
available = set(all_ifaces) - set(existing_ifaces)
diff --git a/archinstall/lib/user_interaction/subvolume_config.py b/archinstall/lib/user_interaction/subvolume_config.py
index 7383b13d..af783639 100644
--- a/archinstall/lib/user_interaction/subvolume_config.py
+++ b/archinstall/lib/user_interaction/subvolume_config.py
@@ -57,6 +57,8 @@ class SubvolumeList(ListManager):
data.update(self.target)
+ return data
+
class SubvolumeMenu(GeneralMenu):
def __init__(self,parameters,action=None):