From 389aa1080b99b4b0a52a3940e9a344027b5cb9b6 Mon Sep 17 00:00:00 2001 From: Anton Hvornum Date: Tue, 9 Nov 2021 17:43:28 +0100 Subject: Adding in storage of user supplied credentials. This separates credentials from user_configuration.json into user_credentials.json. As well as the JSON serializer will omit the credentials from the user_configuration.json by detecting ! in the dictionary keys (which is why they are important). UNSAFE_JSON will leave those keys in there, good for storing credentials in a separate file." --- archinstall/lib/general.py | 39 ++++++++++++++++++++++++++++++++++++++- examples/guided.py | 21 +++++++++++++++------ 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/archinstall/lib/general.py b/archinstall/lib/general.py index 887a60d3..5ab2181c 100644 --- a/archinstall/lib/general.py +++ b/archinstall/lib/general.py @@ -77,6 +77,13 @@ def json_dumps(*args, **kwargs): class JsonEncoder: def _encode(obj): + """ + This JSON encoder function will try it's best to convert + any archinstall data structures, instances or variables into + something that's understandable by the json.parse()/json.loads() lib. + + _encode() will skip any dictionary key starting with an exclamation mark (!) + """ if isinstance(obj, dict): # We'll need to iterate not just the value that default() usually gets passed # But also iterate manually over each key: value pair in order to trap the keys. @@ -90,7 +97,7 @@ class JsonEncoder: val = JsonEncoder._encode(val) if type(key) == str and key[0] == '!': - copy[JsonEncoder._encode(key)] = '******' + pass else: copy[JsonEncoder._encode(key)] = val return copy @@ -105,14 +112,44 @@ class JsonEncoder: else: return obj + def _unsafe_encode(obj): + """ + Same as _encode() but it keeps dictionary keys starting with ! + """ + if isinstance(obj, dict): + copy = {} + for key, val in list(obj.items()): + if isinstance(val, dict): + # This, is a EXTREMELY ugly hack.. but it's the only quick way I can think of to trigger a encoding of sub-dictionaries. + val = json.loads(json.dumps(val, cls=JSON)) + else: + val = JsonEncoder._encode(val) + + copy[JsonEncoder._encode(key)] = val + return copy + else: + return JsonEncoder._encode(obj) class JSON(json.JSONEncoder, json.JSONDecoder): + """ + A safe JSON encoder that will omit private information in dicts (starting with !) + """ def _encode(self, obj): return JsonEncoder._encode(obj) def encode(self, obj): return super(JSON, self).encode(self._encode(obj)) +class UNSAFE_JSON(json.JSONEncoder, json.JSONDecoder): + """ + UNSAFE_JSON will call/encode and keep private information in dicts (starting with !) + """ + def _encode(self, obj): + return JsonEncoder._unsafe_encode(obj) + + def encode(self, obj): + return super(UNSAFE_JSON, self).encode(self._encode(obj)) + class SysCommandWorker: def __init__(self, cmd, callbacks=None, peak_output=False, environment_vars=None, logfile=None, working_directory='./'): if not callbacks: diff --git a/examples/guided.py b/examples/guided.py index b1c34464..a1f30f76 100644 --- a/examples/guided.py +++ b/examples/guided.py @@ -138,11 +138,11 @@ def ask_user_questions(): archinstall.arguments['!root-password'] = archinstall.get_password(prompt='Enter root password (leave blank to disable disabled & create superuser): ') # Ask for additional users (super-user if root pw was not set) - if not archinstall.arguments.get('!root-password', None) and not archinstall.arguments.get('superusers', None): - archinstall.arguments['superusers'] = archinstall.ask_for_superuser_account('Create a required super-user with sudo privileges: ', forced=True) + if not archinstall.arguments.get('!root-password', None) and not archinstall.arguments.get('!superusers', None): + archinstall.arguments['!superusers'] = archinstall.ask_for_superuser_account('Create a required super-user with sudo privileges: ', forced=True) users, superusers = archinstall.ask_for_additional_users('Enter a username to create an additional user (leave blank to skip & continue): ') - archinstall.arguments['users'] = users - archinstall.arguments['superusers'] = {**archinstall.arguments['superusers'], **superusers} + archinstall.arguments['!users'] = users + archinstall.arguments['!superusers'] = {**archinstall.arguments['!superusers'], **superusers} # Ask for archinstall-specific profiles (such as desktop environments etc) if not archinstall.arguments.get('profile', None): @@ -243,6 +243,15 @@ def perform_filesystem_operations(): fs.load_layout(archinstall.storage['disk_layouts'][drive.path]) def perform_installation(mountpoint): + user_credentials = json.dumps({ + "!users" : archinstall.arguments['!users'], + "!superusers" : archinstall.arguments['!users'], + "!root-password" : archinstall.arguments['!users'], + }, indent=4, sort_keys=True, cls=archinstall.UNSAFE_JSON) + + with open("/var/log/archinstall/user_credentials.json", "w") as config_file: + config_file.write(user_credentials) + """ Performs the installation steps on a block device. Only requirement is that the block devices are @@ -305,10 +314,10 @@ def perform_installation(mountpoint): if archinstall.arguments.get('profile', None): installation.install_profile(archinstall.arguments.get('profile', None)) - for user, user_info in archinstall.arguments.get('users', {}).items(): + for user, user_info in archinstall.arguments.get('!users', {}).items(): installation.user_create(user, user_info["!password"], sudo=False) - for superuser, user_info in archinstall.arguments.get('superusers', {}).items(): + for superuser, user_info in archinstall.arguments.get('!superusers', {}).items(): installation.user_create(superuser, user_info["!password"], sudo=True) if timezone := archinstall.arguments.get('timezone', None): -- cgit v1.2.3-70-g09d2