From 86d991f4422d920ca714f459be3be4352d1c40a1 Mon Sep 17 00:00:00 2001 From: Werner Llácer Date: Mon, 28 Feb 2022 23:11:25 +0100 Subject: User Management via lists (#1008) * Fix user/superuser config * Fix flake8 * Remove timezone check since we have a default value now * Remove unused * add new widget ListManager * flake8 complains * Null_action appears now in the main list (to simplify additions to the list) Formatted data are now at the from to the actions submenu * Manage users thru a ListManagers * Define a default action in the menu, potentially independent of a null_action Both default and null actions don't have to be part of the element's action list Some cleanup Co-authored-by: Daniel Girtler Co-authored-by: Anton Hvornum Co-authored-by: Anton Hvornum --- archinstall/lib/menu/selection_menu.py | 11 ++- archinstall/lib/user_interaction.py | 139 +++++++++++++++++++++++++++++---- 2 files changed, 132 insertions(+), 18 deletions(-) diff --git a/archinstall/lib/menu/selection_menu.py b/archinstall/lib/menu/selection_menu.py index 99015fad..8ac7dc99 100644 --- a/archinstall/lib/menu/selection_menu.py +++ b/archinstall/lib/menu/selection_menu.py @@ -497,6 +497,7 @@ class GlobalMenu(GeneralMenu): Selector( _('Specify superuser account'), lambda preset: self._create_superuser_account(), + exec_func=lambda n,v:self._users_resynch(), dependencies_not=['!root-password'], display_func=lambda x: self._display_superusers()) self._menu_options['!users'] = \ @@ -504,6 +505,7 @@ class GlobalMenu(GeneralMenu): _('Specify user account'), lambda x: self._create_user_account(), default={}, + exec_func=lambda n,v:self._users_resynch(), display_func=lambda x: list(x.keys()) if x else '[]') self._menu_options['profile'] = \ Selector( @@ -668,11 +670,11 @@ class GlobalMenu(GeneralMenu): return profile def _create_superuser_account(self): - superusers = ask_for_superuser_account(str(_('Enter a username to create an additional superuser (leave blank to skip): '))) + superusers = ask_for_superuser_account(str(_('Manage superuser accounts: '))) return superusers if superusers else None def _create_user_account(self): - users = ask_for_additional_users(str(_('Enter a username to create an additional user (leave blank to skip): '))) + users = ask_for_additional_users(str(_('Manage ordinary user accounts: '))) return users def _display_superusers(self): @@ -682,3 +684,8 @@ class GlobalMenu(GeneralMenu): return list(superusers.keys()) if superusers else '[]' else: return list(superusers.keys()) if superusers else '' + + def _users_resynch(self): + self.synch('!superusers') + self.synch('!users') + return False \ No newline at end of file diff --git a/archinstall/lib/user_interaction.py b/archinstall/lib/user_interaction.py index f0260488..c6f3eef7 100644 --- a/archinstall/lib/user_interaction.py +++ b/archinstall/lib/user_interaction.py @@ -28,6 +28,7 @@ from .hardware import AVAILABLE_GFX_DRIVERS, has_uefi, has_amd_graphics, has_int from .locale_helpers import list_keyboard_languages, list_timezones, list_locales from .networking import list_interfaces from .menu import Menu +from .menu.list_manager import ListManager from .output import log from .profiles import Profile, list_profiles from .storage import storage @@ -336,26 +337,14 @@ def ask_hostname(preset :str = None) -> str : def ask_for_superuser_account(prompt: str) -> Dict[str, Dict[str, str]]: - prompt = prompt if prompt else str(_('Enter username for superuser with sudo privileges (leave blank for no superusers): ')) - superusers = ask_for_additional_users(prompt) + prompt = prompt if prompt else str(_('Define users with sudo privilege: ')) + 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): ') - users = {} - - while 1: - new_user = input(prompt).strip(' ') - if not new_user: - break - if not check_for_correct_username(new_user): - continue - - password_prompt = str(_('Password for user "{}": ').format(new_user)) - password = get_password(prompt=password_prompt) - users[new_user] = {"!password": password} - + dummy,users = manage_users(prompt,sudo=False) return users @@ -1174,6 +1163,124 @@ def generic_multi_select(p_options :Union[list,dict], default=default) +class UserList(ListManager): + """ + subclass of ListManager for the managing of user accounts + """ + def __init__(self,prompt :str, lusers :dict, sudo :bool = None): + """ + param: prompt + type: str + param: lusers dict with the users already defined for the system + type: Dict + param: sudo. boolean to determine if we handle superusers or users. If None handles both types + """ + self.sudo = sudo + self.actions = [ + str(_('Add an user')), + str(_('Change password')), + str(_('Promote/Demote user')), + str(_('Delete User')) + ] + self.default_action = self.actions[0] + super().__init__(prompt,lusers,self.actions,self.default_action) + + def reformat(self): + def format_element(elem): + # secret gives away the length of the password + if self.data[elem].get('!password'): + pwd = '*' * 16 + # pwd = archinstall.secret(self.data[elem]['!password']) + else: + pwd = '' + if self.data[elem].get('sudoer'): + super = 'Superuser' + else: + super = ' ' + return f"{elem:16}: password {pwd:16} {super}" + return list(map(lambda x:format_element(x),self.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]] + else: + return self.actions + + def exec_action(self): + if self.target: + active_user = list(self.target.keys())[0] + else: + active_user = None + + if self.action == self.actions[0]: # add + new_user = self.add_user() + # no unicity check, if exists will be replaced + self.data.update(new_user) + elif self.action == self.actions[1]: # change password + self.data[active_user]['!password'] = get_password(prompt=str(_('Password for user "{}": ').format(active_user))) + elif self.action == self.actions[2]: # promote/demote + self.data[active_user]['sudoer'] = not self.data[active_user]['sudoer'] + elif self.action == self.actions[3]: # delete + del self.data[active_user] + + def add_user(self): + print(_('\nDefine a new user\n')) + prompt = str(_("User Name : ")) + while True: + userid = input(prompt).strip(' ') + if not userid: + return {} # end + if not check_for_correct_username(userid): + pass + 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), + ['yes', 'no'], + skip=False, + preset_values='yes' if sudoer else 'no', + default_option='no' + ).run() + sudoer = True if sudo_choice == '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 save_config(config: Dict): def preview(selection: str): if options['user_config'] == selection: @@ -1235,4 +1342,4 @@ def save_config(config: Dict): elif options['all'] == selection: config_output.save_user_config(dest_path) config_output.save_user_creds(dest_path) - config_output.save_disk_layout(dest_path) + config_output.save_disk_layout -- cgit v1.2.3-54-g00ecf