Send patches - preferably formatted by git format-patch - to patches at archlinux32 dot org.
summaryrefslogtreecommitdiff
path: root/archinstall/lib/interactions/manage_users_conf.py
diff options
context:
space:
mode:
Diffstat (limited to 'archinstall/lib/interactions/manage_users_conf.py')
-rw-r--r--archinstall/lib/interactions/manage_users_conf.py106
1 files changed, 106 insertions, 0 deletions
diff --git a/archinstall/lib/interactions/manage_users_conf.py b/archinstall/lib/interactions/manage_users_conf.py
new file mode 100644
index 00000000..879578da
--- /dev/null
+++ b/archinstall/lib/interactions/manage_users_conf.py
@@ -0,0 +1,106 @@
+from __future__ import annotations
+
+import re
+from typing import Any, Dict, TYPE_CHECKING, List, Optional
+
+from .utils import get_password
+from ..menu import Menu, ListManager
+from ..models.users import User
+from ..output import FormattedOutput
+
+if TYPE_CHECKING:
+ _: Any
+
+
+class UserList(ListManager):
+ """
+ subclass of ListManager for the managing of user accounts
+ """
+
+ def __init__(self, prompt: str, lusers: List[User]):
+ self._actions = [
+ str(_('Add a user')),
+ str(_('Change password')),
+ str(_('Promote/Demote user')),
+ str(_('Delete User'))
+ ]
+ super().__init__(prompt, lusers, [self._actions[0]], self._actions[1:])
+
+ def reformat(self, data: List[User]) -> Dict[str, Any]:
+ table = FormattedOutput.as_table(data)
+ rows = table.split('\n')
+
+ # these are the header rows of the table and do not map to any User obviously
+ # we're adding 2 spaces as prefix because the menu selector '> ' will be put before
+ # the selectable rows so the header has to be aligned
+ display_data: Dict[str, Optional[User]] = {f' {rows[0]}': None, f' {rows[1]}': None}
+
+ for row, user in zip(rows[2:], data):
+ row = row.replace('|', '\\|')
+ display_data[row] = user
+
+ return display_data
+
+ def selected_action_display(self, user: User) -> str:
+ return user.username
+
+ def handle_action(self, action: str, entry: Optional[User], data: List[User]) -> List[User]:
+ if 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 action == self._actions[1] and entry: # change password
+ prompt = str(_('Password for user "{}": ').format(entry.username))
+ new_password = get_password(prompt=prompt)
+ if new_password:
+ user = next(filter(lambda x: x == entry, data))
+ user.password = new_password
+ elif action == self._actions[2] and entry: # promote/demote
+ user = next(filter(lambda x: x == entry, data))
+ user.sudo = False if user.sudo else True
+ elif action == self._actions[3] and entry: # delete
+ data = [d for d in data if d != entry]
+
+ 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
+ return False
+
+ def _add_user(self) -> Optional[User]:
+ prompt = '\n\n' + str(_('Enter username (leave blank to skip): '))
+
+ while True:
+ username = input(prompt).strip(' ')
+ if not username:
+ return None
+ if not self._check_for_correct_username(username):
+ error_prompt = str(_("The username you entered is invalid. Try again"))
+ print(error_prompt)
+ else:
+ break
+
+ password = get_password(prompt=str(_('Password for user "{}": ').format(username)))
+
+ if not password:
+ return None
+
+ choice = Menu(
+ str(_('Should "{}" be a superuser (sudo)?')).format(username), Menu.yes_no(),
+ skip=False,
+ default_option=Menu.yes(),
+ clear_screen=False,
+ show_search_hint=False
+ ).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]:
+ users = UserList(prompt, defined_users).run()
+ return users