New encryption menu (#1520)
* New encryption menu Co-authored-by: Daniel Girtler <girtler.daniel@gmail.com> Co-authored-by: Anton Hvornum <anton@hvornum.se>
This commit is contained in:
parent
ee1eea2130
commit
c3862c5779
|
|
@ -15,4 +15,8 @@ jobs:
|
|||
# one day this will be enabled
|
||||
# run: mypy --strict --module archinstall || exit 0
|
||||
- name: run mypy
|
||||
run: mypy --follow-imports=silent archinstall/lib/menu/selection_menu.py archinstall/lib/menu/global_menu.py archinstall/lib/models/network_configuration.py archinstall/lib/menu/list_manager.py archinstall/lib/user_interaction/network_conf.py archinstall/lib/models/users.py archinstall/lib/disk/blockdevice.py archinstall/lib/user_interaction/subvolume_config.py archinstall/lib/disk/btrfs/btrfs_helpers.py archinstall/lib/translationhandler.py archinstall/lib/disk/diskinfo.py
|
||||
run: mypy --follow-imports=silent archinstall/lib/menu/abstract_menu.py archinstall/lib/menu/global_menu.py
|
||||
archinstall/lib/models/network_configuration.py archinstall/lib/menu/list_manager.py archinstall/lib/user_interaction/network_conf.py archinstall/lib/models/users.py
|
||||
archinstall/lib/disk/blockdevice.py archinstall/lib/user_interaction/subvolume_config.py archinstall/lib/disk/btrfs/btrfs_helpers.py
|
||||
archinstall/lib/translationhandler.py archinstall/lib/disk/diskinfo.py archinstall/lib/menu/table_selection_menu.py archinstall/lib/hsm
|
||||
archinstall/lib/disk/encryption.py archinstall/lib/models/disk_encryption.py
|
||||
|
|
|
|||
|
|
@ -37,18 +37,14 @@ from .lib.menu import Menu
|
|||
from .lib.menu.list_manager import ListManager
|
||||
from .lib.menu.text_input import TextInput
|
||||
from .lib.menu.global_menu import GlobalMenu
|
||||
from .lib.menu.selection_menu import (
|
||||
from .lib.menu.abstract_menu import (
|
||||
Selector,
|
||||
GeneralMenu
|
||||
AbstractMenu
|
||||
)
|
||||
from .lib.translationhandler import TranslationHandler, DeferredTranslation
|
||||
from .lib.plugins import plugins, load_plugin # This initiates the plugin loading ceremony
|
||||
from .lib.configuration import *
|
||||
from .lib.udev import udevadm_info
|
||||
from .lib.hsm import (
|
||||
get_fido2_devices,
|
||||
fido2_enroll
|
||||
)
|
||||
parser = ArgumentParser()
|
||||
|
||||
__version__ = "2.5.2"
|
||||
|
|
|
|||
|
|
@ -5,15 +5,18 @@ import logging
|
|||
import pathlib
|
||||
from typing import Optional, Dict
|
||||
|
||||
from .hsm.fido import Fido2
|
||||
from .models.disk_encryption import DiskEncryption
|
||||
from .storage import storage
|
||||
from .general import JSON, UNSAFE_JSON
|
||||
from .output import log
|
||||
from .exceptions import RequirementError
|
||||
from .hsm import get_fido2_devices
|
||||
|
||||
|
||||
def configuration_sanity_check():
|
||||
if storage['arguments'].get('HSM'):
|
||||
if not get_fido2_devices():
|
||||
disk_encryption: DiskEncryption = storage['arguments'].get('disk_encryption')
|
||||
if disk_encryption.hsm_device:
|
||||
if not Fido2.get_fido2_devices():
|
||||
raise RequirementError(
|
||||
f"In order to use HSM to pair with the disk encryption,"
|
||||
+ f" one needs to be accessible through /dev/hidraw* and support"
|
||||
|
|
@ -21,6 +24,7 @@ def configuration_sanity_check():
|
|||
+ f" 'systemd-cryptenroll --fido2-device=list'."
|
||||
)
|
||||
|
||||
|
||||
class ConfigurationOutput:
|
||||
def __init__(self, config: Dict):
|
||||
"""
|
||||
|
|
@ -39,8 +43,8 @@ class ConfigurationOutput:
|
|||
self._user_creds_file = "user_credentials.json"
|
||||
self._disk_layout_file = "user_disk_layout.json"
|
||||
|
||||
self._sensitive = ['!users', '!encryption-password']
|
||||
self._ignore = ['abort', 'install', 'config', 'creds', 'dry_run']
|
||||
self._sensitive = ['!users']
|
||||
self._ignore = ['abort', 'install', 'config', 'creds', 'dry_run', 'disk_encryption']
|
||||
|
||||
self._process_config()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,162 @@
|
|||
from typing import Dict, Optional, Any, TYPE_CHECKING, List
|
||||
|
||||
from ..menu.abstract_menu import Selector, AbstractSubMenu
|
||||
from ..menu.menu import MenuSelectionType
|
||||
from ..menu.table_selection_menu import TableMenu
|
||||
from ..models.disk_encryption import EncryptionType, DiskEncryption
|
||||
from ..user_interaction.partitioning_conf import current_partition_layout
|
||||
from ..user_interaction.utils import get_password
|
||||
from ..menu import Menu
|
||||
from ..general import secret
|
||||
from ..hsm.fido import Fido2Device, Fido2
|
||||
|
||||
if TYPE_CHECKING:
|
||||
_: Any
|
||||
|
||||
|
||||
class DiskEncryptionMenu(AbstractSubMenu):
|
||||
def __init__(self, data_store: Dict[str, Any], preset: Optional[DiskEncryption], disk_layouts: Dict[str, Any]):
|
||||
if preset:
|
||||
self._preset = preset
|
||||
else:
|
||||
self._preset = DiskEncryption()
|
||||
|
||||
self._disk_layouts = disk_layouts
|
||||
super().__init__(data_store=data_store)
|
||||
|
||||
def _setup_selection_menu_options(self):
|
||||
self._menu_options['encryption_password'] = \
|
||||
Selector(
|
||||
_('Encryption password'),
|
||||
lambda x: select_encrypted_password(),
|
||||
display_func=lambda x: secret(x) if x else '',
|
||||
default=self._preset.encryption_password,
|
||||
enabled=True
|
||||
)
|
||||
self._menu_options['encryption_type'] = \
|
||||
Selector(
|
||||
_('Encryption type'),
|
||||
func=lambda preset: select_encryption_type(preset),
|
||||
display_func=lambda x: EncryptionType.type_to_text(x) if x else None,
|
||||
dependencies=['encryption_password'],
|
||||
default=self._preset.encryption_type,
|
||||
enabled=True
|
||||
)
|
||||
self._menu_options['partitions'] = \
|
||||
Selector(
|
||||
_('Partitions'),
|
||||
func=lambda preset: select_partitions_to_encrypt(self._disk_layouts, preset),
|
||||
display_func=lambda x: f'{len(x)} {_("Partitions")}' if x else None,
|
||||
dependencies=['encryption_password'],
|
||||
default=self._preset.partitions,
|
||||
preview_func=self._prev_disk_layouts,
|
||||
enabled=True
|
||||
)
|
||||
self._menu_options['HSM'] = \
|
||||
Selector(
|
||||
description=_('Use HSM to unlock encrypted drive'),
|
||||
func=lambda preset: select_hsm(preset),
|
||||
display_func=lambda x: self._display_hsm(x),
|
||||
dependencies=['encryption_password'],
|
||||
default=self._preset.hsm_device,
|
||||
enabled=True
|
||||
)
|
||||
|
||||
def run(self, allow_reset: bool = True) -> Optional[DiskEncryption]:
|
||||
super().run(allow_reset=allow_reset)
|
||||
|
||||
if self._data_store.get('encryption_password', None):
|
||||
return DiskEncryption(
|
||||
encryption_password=self._data_store.get('encryption_password', None),
|
||||
encryption_type=self._data_store['encryption_type'],
|
||||
partitions=self._data_store.get('partitions', None),
|
||||
hsm_device=self._data_store.get('HSM', None)
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
def _display_hsm(self, device: Optional[Fido2Device]) -> Optional[str]:
|
||||
if device:
|
||||
return device.manufacturer
|
||||
|
||||
if not Fido2.get_fido2_devices():
|
||||
return str(_('No HSM devices available'))
|
||||
return None
|
||||
|
||||
def _prev_disk_layouts(self) -> Optional[str]:
|
||||
selector = self._menu_options['partitions']
|
||||
if selector.has_selection():
|
||||
partitions: List[Any] = selector.current_selection
|
||||
output = str(_('Partitions to be encrypted')) + '\n'
|
||||
output += current_partition_layout(partitions, with_title=False)
|
||||
return output.rstrip()
|
||||
return None
|
||||
|
||||
|
||||
def select_encryption_type(preset: EncryptionType) -> Optional[EncryptionType]:
|
||||
title = str(_('Select disk encryption option'))
|
||||
options = [
|
||||
# _type_to_text(EncryptionType.FullDiskEncryption),
|
||||
EncryptionType.type_to_text(EncryptionType.Partition)
|
||||
]
|
||||
|
||||
preset_value = EncryptionType.type_to_text(preset)
|
||||
choice = Menu(title, options, preset_values=preset_value).run()
|
||||
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Reset: return None
|
||||
case MenuSelectionType.Skip: return preset
|
||||
case MenuSelectionType.Selection: return EncryptionType.text_to_type(choice.value) # type: ignore
|
||||
|
||||
|
||||
def select_encrypted_password() -> Optional[str]:
|
||||
if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))):
|
||||
return passwd
|
||||
return None
|
||||
|
||||
|
||||
def select_hsm(preset: Optional[Fido2Device] = None) -> Optional[Fido2Device]:
|
||||
title = _('Select a FIDO2 device to use for HSM')
|
||||
fido_devices = Fido2.get_fido2_devices()
|
||||
|
||||
if fido_devices:
|
||||
choice = TableMenu(title, data=fido_devices).run()
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Reset:
|
||||
return None
|
||||
case MenuSelectionType.Skip:
|
||||
return preset
|
||||
case MenuSelectionType.Selection:
|
||||
return choice.value # type: ignore
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def select_partitions_to_encrypt(disk_layouts: Dict[str, Any], preset: List[Any]) -> List[Any]:
|
||||
# If no partitions was marked as encrypted, but a password was supplied and we have some disks to format..
|
||||
# Then we need to identify which partitions to encrypt. This will default to / (root).
|
||||
all_partitions = []
|
||||
for blockdevice in disk_layouts.values():
|
||||
if partitions := blockdevice.get('partitions'):
|
||||
partitions = [p for p in partitions if p['mountpoint'] != '/boot']
|
||||
all_partitions += partitions
|
||||
|
||||
if all_partitions:
|
||||
title = str(_('Select which partitions to encrypt'))
|
||||
partition_table = current_partition_layout(all_partitions, with_title=False).strip()
|
||||
|
||||
choice = TableMenu(
|
||||
title,
|
||||
table_data=(all_partitions, partition_table),
|
||||
multi=True
|
||||
).run()
|
||||
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Reset:
|
||||
return []
|
||||
case MenuSelectionType.Skip:
|
||||
return preset
|
||||
case MenuSelectionType.Selection:
|
||||
return choice.value # type: ignore
|
||||
|
||||
return []
|
||||
|
|
@ -5,6 +5,8 @@ import json
|
|||
import pathlib
|
||||
from typing import Optional, Dict, Any, TYPE_CHECKING
|
||||
# https://stackoverflow.com/a/39757388/929999
|
||||
from ..models.disk_encryption import DiskEncryption
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .blockdevice import BlockDevice
|
||||
_: Any
|
||||
|
|
@ -107,33 +109,22 @@ class Filesystem:
|
|||
continue
|
||||
|
||||
if partition.get('filesystem', {}).get('format', False):
|
||||
|
||||
# needed for backward compatibility with the introduction of the new "format_options"
|
||||
format_options = partition.get('options',[]) + partition.get('filesystem',{}).get('format_options',[])
|
||||
if partition.get('encrypted', False):
|
||||
disk_encryption: DiskEncryption = storage['arguments'].get('disk_encryption')
|
||||
|
||||
if partition in disk_encryption.partitions:
|
||||
if not partition['device_instance']:
|
||||
raise DiskError(f"Internal error caused us to loose the partition. Please report this issue upstream!")
|
||||
|
||||
if not partition.get('!password'):
|
||||
if not storage['arguments'].get('!encryption-password'):
|
||||
if storage['arguments'] == 'silent':
|
||||
raise ValueError(f"Missing encryption password for {partition['device_instance']}")
|
||||
|
||||
from ..user_interaction import get_password
|
||||
|
||||
prompt = str(_('Enter a encryption password for {}').format(partition['device_instance']))
|
||||
storage['arguments']['!encryption-password'] = get_password(prompt)
|
||||
|
||||
partition['!password'] = storage['arguments']['!encryption-password']
|
||||
|
||||
if partition.get('mountpoint',None):
|
||||
loopdev = f"{storage.get('ENC_IDENTIFIER', 'ai')}{pathlib.Path(partition['mountpoint']).name}loop"
|
||||
else:
|
||||
loopdev = f"{storage.get('ENC_IDENTIFIER', 'ai')}{pathlib.Path(partition['device_instance'].path).name}"
|
||||
|
||||
partition['device_instance'].encrypt(password=partition['!password'])
|
||||
partition['device_instance'].encrypt(password=disk_encryption.encryption_password)
|
||||
# Immediately unlock the encrypted device to format the inner volume
|
||||
with luks2(partition['device_instance'], loopdev, partition['!password'], auto_unmount=True) as unlocked_device:
|
||||
with luks2(partition['device_instance'], loopdev, disk_encryption.encryption_password, auto_unmount=True) as unlocked_device:
|
||||
if not partition.get('wipe'):
|
||||
if storage['arguments'] == 'silent':
|
||||
raise ValueError(f"Missing fs-type to format on newly created encrypted partition {partition['device_instance']}")
|
||||
|
|
|
|||
|
|
@ -469,12 +469,6 @@ def disk_layouts() -> Optional[Dict[str, Any]]:
|
|||
return None
|
||||
|
||||
|
||||
def encrypted_partitions(blockdevices :Dict[str, Any]) -> bool:
|
||||
for blockdevice in blockdevices.values():
|
||||
for partition in blockdevice.get('partitions', []):
|
||||
if partition.get('encrypted', False):
|
||||
yield partition
|
||||
|
||||
def find_partition_by_mountpoint(block_devices :List[BlockDevice], relative_mountpoint :str) -> Partition:
|
||||
for device in block_devices:
|
||||
for partition in block_devices[device]['partitions']:
|
||||
|
|
|
|||
|
|
@ -91,7 +91,6 @@ class Partition:
|
|||
self._path = path
|
||||
self._part_id = part_id
|
||||
self._target_mountpoint = mountpoint
|
||||
self._encrypted = None
|
||||
self._encrypted = encrypted
|
||||
self._wipe = False
|
||||
self._type = 'primary'
|
||||
|
|
|
|||
|
|
@ -1,4 +1 @@
|
|||
from .fido import (
|
||||
get_fido2_devices,
|
||||
fido2_enroll
|
||||
)
|
||||
from .fido import Fido2
|
||||
|
|
|
|||
|
|
@ -1,57 +1,92 @@
|
|||
import typing
|
||||
import pathlib
|
||||
import getpass
|
||||
import logging
|
||||
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from ..general import SysCommand, SysCommandWorker, clear_vt100_escape_codes
|
||||
from ..disk.partition import Partition
|
||||
from ..general import log
|
||||
|
||||
def get_fido2_devices() -> typing.Dict[str, typing.Dict[str, str]]:
|
||||
"""
|
||||
Uses systemd-cryptenroll to list the FIDO2 devices
|
||||
connected that supports FIDO2.
|
||||
Some devices might show up in udevadm as FIDO2 compliant
|
||||
when they are in fact not.
|
||||
|
||||
The drawback of systemd-cryptenroll is that it uses human readable format.
|
||||
That means we get this weird table like structure that is of no use.
|
||||
@dataclass
|
||||
class Fido2Device:
|
||||
path: Path
|
||||
manufacturer: str
|
||||
product: str
|
||||
|
||||
So we'll look for `MANUFACTURER` and `PRODUCT`, we take their index
|
||||
and we split each line based on those positions.
|
||||
"""
|
||||
worker = clear_vt100_escape_codes(SysCommand(f"systemd-cryptenroll --fido2-device=list").decode('UTF-8'))
|
||||
|
||||
MANUFACTURER_POS = 0
|
||||
PRODUCT_POS = 0
|
||||
devices = {}
|
||||
for line in worker.split('\r\n'):
|
||||
if '/dev' not in line:
|
||||
MANUFACTURER_POS = line.find('MANUFACTURER')
|
||||
PRODUCT_POS = line.find('PRODUCT')
|
||||
continue
|
||||
class Fido2:
|
||||
_loaded: bool = False
|
||||
_fido2_devices: List[Fido2Device] = []
|
||||
|
||||
path = line[:MANUFACTURER_POS].rstrip()
|
||||
manufacturer = line[MANUFACTURER_POS:PRODUCT_POS].rstrip()
|
||||
product = line[PRODUCT_POS:]
|
||||
@classmethod
|
||||
def get_fido2_devices(cls, reload: bool = False) -> List[Fido2Device]:
|
||||
"""
|
||||
Uses systemd-cryptenroll to list the FIDO2 devices
|
||||
connected that supports FIDO2.
|
||||
Some devices might show up in udevadm as FIDO2 compliant
|
||||
when they are in fact not.
|
||||
|
||||
devices[path] = {
|
||||
'manufacturer' : manufacturer,
|
||||
'product' : product
|
||||
}
|
||||
The drawback of systemd-cryptenroll is that it uses human readable format.
|
||||
That means we get this weird table like structure that is of no use.
|
||||
|
||||
return devices
|
||||
So we'll look for `MANUFACTURER` and `PRODUCT`, we take their index
|
||||
and we split each line based on those positions.
|
||||
|
||||
def fido2_enroll(hsm_device_path :pathlib.Path, partition :Partition, password :str) -> bool:
|
||||
worker = SysCommandWorker(f"systemd-cryptenroll --fido2-device={hsm_device_path} {partition.real_device}", peak_output=True)
|
||||
pw_inputted = False
|
||||
pin_inputted = False
|
||||
while worker.is_alive():
|
||||
if pw_inputted is False and bytes(f"please enter current passphrase for disk {partition.real_device}", 'UTF-8') in worker._trace_log.lower():
|
||||
worker.write(bytes(password, 'UTF-8'))
|
||||
pw_inputted = True
|
||||
Output example:
|
||||
|
||||
elif pin_inputted is False and bytes(f"please enter security token pin", 'UTF-8') in worker._trace_log.lower():
|
||||
worker.write(bytes(getpass.getpass(" "), 'UTF-8'))
|
||||
pin_inputted = True
|
||||
PATH MANUFACTURER PRODUCT
|
||||
/dev/hidraw1 Yubico YubiKey OTP+FIDO+CCID
|
||||
"""
|
||||
|
||||
log(f"You might need to touch the FIDO2 device to unlock it if no prompt comes up after 3 seconds.", level=logging.INFO, fg="yellow")
|
||||
# to prevent continous reloading which will slow
|
||||
# down moving the cursor in the menu
|
||||
if not cls._loaded or reload:
|
||||
ret = SysCommand(f"systemd-cryptenroll --fido2-device=list").decode('UTF-8')
|
||||
if not ret:
|
||||
log('Unable to retrieve fido2 devices', level=logging.ERROR)
|
||||
return []
|
||||
|
||||
fido_devices = clear_vt100_escape_codes(ret)
|
||||
|
||||
manufacturer_pos = 0
|
||||
product_pos = 0
|
||||
devices = []
|
||||
|
||||
for line in fido_devices.split('\r\n'):
|
||||
if '/dev' not in line:
|
||||
manufacturer_pos = line.find('MANUFACTURER')
|
||||
product_pos = line.find('PRODUCT')
|
||||
continue
|
||||
|
||||
path = line[:manufacturer_pos].rstrip()
|
||||
manufacturer = line[manufacturer_pos:product_pos].rstrip()
|
||||
product = line[product_pos:]
|
||||
|
||||
devices.append(
|
||||
Fido2Device(path, manufacturer, product)
|
||||
)
|
||||
|
||||
cls._loaded = True
|
||||
cls._fido2_devices = devices
|
||||
|
||||
return cls._fido2_devices
|
||||
|
||||
@classmethod
|
||||
def fido2_enroll(cls, hsm_device: Fido2Device, partition :Partition, password :str):
|
||||
worker = SysCommandWorker(f"systemd-cryptenroll --fido2-device={hsm_device.path} {partition.real_device}", peak_output=True)
|
||||
pw_inputted = False
|
||||
pin_inputted = False
|
||||
|
||||
while worker.is_alive():
|
||||
if pw_inputted is False and bytes(f"please enter current passphrase for disk {partition.real_device}", 'UTF-8') in worker._trace_log.lower():
|
||||
worker.write(bytes(password, 'UTF-8'))
|
||||
pw_inputted = True
|
||||
|
||||
elif pin_inputted is False and bytes(f"please enter security token pin", 'UTF-8') in worker._trace_log.lower():
|
||||
worker.write(bytes(getpass.getpass(" "), 'UTF-8'))
|
||||
pin_inputted = True
|
||||
|
||||
log(f"You might need to touch the FIDO2 device to unlock it if no prompt comes up after 3 seconds.", level=logging.INFO, fg="yellow")
|
||||
|
|
|
|||
|
|
@ -15,16 +15,16 @@ from .hardware import has_uefi, is_vm, cpu_vendor
|
|||
from .locale_helpers import verify_keyboard_layout, verify_x11_keyboard_layout
|
||||
from .disk.helpers import findmnt
|
||||
from .mirrors import use_mirrors
|
||||
from .models.disk_encryption import DiskEncryption
|
||||
from .plugins import plugins
|
||||
from .storage import storage
|
||||
# from .user_interaction import *
|
||||
from .output import log
|
||||
from .profiles import Profile
|
||||
from .disk.partition import get_mount_fs_type
|
||||
from .exceptions import DiskError, ServiceException, RequirementError, HardwareIncompatibilityError, SysCallError
|
||||
from .hsm import fido2_enroll
|
||||
from .models.users import User
|
||||
from .models.subvolume import Subvolume
|
||||
from .hsm import Fido2
|
||||
|
||||
if TYPE_CHECKING:
|
||||
_: Any
|
||||
|
|
@ -135,6 +135,8 @@ class Installer:
|
|||
|
||||
self._zram_enabled = False
|
||||
|
||||
self._disk_encryption: DiskEncryption = storage['arguments'].get('disk_encryption')
|
||||
|
||||
def log(self, *args :str, level :int = logging.DEBUG, **kwargs :str):
|
||||
"""
|
||||
installer.log() wraps output.log() mainly to set a default log-level for this install session.
|
||||
|
|
@ -196,7 +198,7 @@ class Installer:
|
|||
def _create_keyfile(self,luks_handle , partition :dict, password :str):
|
||||
""" roiutine to create keyfiles, so it can be moved elsewhere
|
||||
"""
|
||||
if partition.get('generate-encryption-key-file'):
|
||||
if self._disk_encryption.generate_encryption_file(partition):
|
||||
if not (cryptkey_dir := pathlib.Path(f"{self.target}/etc/cryptsetup-keys.d")).exists():
|
||||
cryptkey_dir.mkdir(parents=True)
|
||||
# Once we store the key as ../xyzloop.key systemd-cryptsetup can automatically load this key
|
||||
|
|
@ -244,26 +246,20 @@ class Installer:
|
|||
mount_queue = {}
|
||||
|
||||
# we manage the encrypted partititons
|
||||
for partition in [entry for entry in list_part if entry.get('encrypted', False)]:
|
||||
for partition in self._disk_encryption.partitions:
|
||||
# open the luks device and all associate stuff
|
||||
if not (password := partition.get('!password', None)) and storage['arguments'].get('!encryption-password'):
|
||||
password = storage['arguments'].get('!encryption-password')
|
||||
elif not password:
|
||||
raise RequirementError(f"Missing partition encryption password in layout: {partition}")
|
||||
|
||||
loopdev = f"{storage.get('ENC_IDENTIFIER', 'ai')}{pathlib.Path(partition['device_instance'].path).name}"
|
||||
|
||||
# note that we DON'T auto_unmount (i.e. close the encrypted device so it can be used
|
||||
with (luks_handle := luks2(partition['device_instance'], loopdev, password, auto_unmount=False)) as unlocked_device:
|
||||
if partition.get('generate-encryption-key-file', False) and not self._has_root(partition):
|
||||
list_luks_handles.append([luks_handle, partition, password])
|
||||
with (luks_handle := luks2(partition['device_instance'], loopdev, self._disk_encryption.encryption_password, auto_unmount=False)) as unlocked_device:
|
||||
if self._disk_encryption.generate_encryption_file(partition) and not self._has_root(partition):
|
||||
list_luks_handles.append([luks_handle, partition, self._disk_encryption.encryption_password])
|
||||
# this way all the requesrs will be to the dm_crypt device and not to the physical partition
|
||||
partition['device_instance'] = unlocked_device
|
||||
|
||||
if self._has_root(partition) and partition.get('generate-encryption-key-file', False) is False:
|
||||
if storage['arguments'].get('HSM'):
|
||||
hsm_device_path = storage['arguments']['HSM']
|
||||
fido2_enroll(hsm_device_path, partition['device_instance'], password)
|
||||
if self._has_root(partition) and self._disk_encryption.generate_encryption_file(partition) is False:
|
||||
if self._disk_encryption.hsm_device:
|
||||
Fido2.fido2_enroll(self._disk_encryption.hsm_device, partition['device_instance'], self._disk_encryption.encryption_password)
|
||||
|
||||
btrfs_subvolumes = [entry for entry in list_part if entry.get('btrfs', {}).get('subvolumes', [])]
|
||||
|
||||
|
|
@ -650,7 +646,7 @@ class Installer:
|
|||
mkinit.write(f"BINARIES=({' '.join(self.BINARIES)})\n")
|
||||
mkinit.write(f"FILES=({' '.join(self.FILES)})\n")
|
||||
|
||||
if not storage['arguments'].get('HSM'):
|
||||
if not self._disk_encryption.hsm_device:
|
||||
# For now, if we don't use HSM we revert to the old
|
||||
# way of setting up encryption hooks for mkinitcpio.
|
||||
# This is purely for stability reasons, we're going away from this.
|
||||
|
|
@ -694,7 +690,7 @@ class Installer:
|
|||
self.HOOKS.remove('fsck')
|
||||
|
||||
if self.detect_encryption(partition):
|
||||
if storage['arguments'].get('HSM'):
|
||||
if self._disk_encryption.hsm_device:
|
||||
# Required bby mkinitcpio to add support for fido2-device options
|
||||
self.pacstrap('libfido2')
|
||||
|
||||
|
|
@ -758,7 +754,7 @@ class Installer:
|
|||
# TODO: Use python functions for this
|
||||
SysCommand(f'/usr/bin/arch-chroot {self.target} chmod 700 /root')
|
||||
|
||||
if storage['arguments'].get('HSM'):
|
||||
if self._disk_encryption.hsm_device:
|
||||
# TODO:
|
||||
# A bit of a hack, but we need to get vconsole.conf in there
|
||||
# before running `mkinitcpio` because it expects it in HSM mode.
|
||||
|
|
@ -886,7 +882,7 @@ class Installer:
|
|||
|
||||
kernel_options = f"options"
|
||||
|
||||
if storage['arguments'].get('HSM'):
|
||||
if self._disk_encryption.hsm_device:
|
||||
# Note: lsblk UUID must be used, not PARTUUID for sd-encrypt to work
|
||||
kernel_options += f" rd.luks.name={real_device.uuid}=luksdev"
|
||||
# Note: tpm2-device and fido2-device don't play along very well:
|
||||
|
|
|
|||
|
|
@ -2,15 +2,12 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
import sys
|
||||
import pathlib
|
||||
from typing import Callable, Any, List, Iterator, Tuple, Optional, Dict, TYPE_CHECKING
|
||||
|
||||
from .menu import Menu, MenuSelectionType
|
||||
from ..locale_helpers import set_keyboard_language
|
||||
from ..output import log
|
||||
from ..translationhandler import TranslationHandler, Language
|
||||
from ..hsm.fido import get_fido2_devices
|
||||
|
||||
from ..user_interaction.general_conf import select_archinstall_language
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
|
@ -167,8 +164,9 @@ class Selector:
|
|||
if status and not self.is_enabled():
|
||||
self.set_enabled(True)
|
||||
|
||||
class GeneralMenu:
|
||||
def __init__(self, data_store :dict = None, auto_cursor=False, preview_size :float = 0.2):
|
||||
|
||||
class AbstractMenu:
|
||||
def __init__(self, data_store: Dict[str, Any] = None, auto_cursor=False, preview_size :float = 0.2):
|
||||
"""
|
||||
Create a new selection menu.
|
||||
|
||||
|
|
@ -196,7 +194,7 @@ class GeneralMenu:
|
|||
def last_choice(self):
|
||||
return self._last_choice
|
||||
|
||||
def __enter__(self, *args :Any, **kwargs :Any) -> GeneralMenu:
|
||||
def __enter__(self, *args :Any, **kwargs :Any) -> AbstractMenu:
|
||||
self.is_context_mgr = True
|
||||
return self
|
||||
|
||||
|
|
@ -209,9 +207,9 @@ class GeneralMenu:
|
|||
raise args[1]
|
||||
|
||||
for key in self._menu_options:
|
||||
sel = self._menu_options[key]
|
||||
selector = self._menu_options[key]
|
||||
if key and key not in self._data_store:
|
||||
self._data_store[key] = sel._current_selection
|
||||
self._data_store[key] = selector.current_selection
|
||||
|
||||
self.exit_callback()
|
||||
|
||||
|
|
@ -286,7 +284,7 @@ class GeneralMenu:
|
|||
selector = option[0][1]
|
||||
return config_name, selector
|
||||
|
||||
def run(self):
|
||||
def run(self, allow_reset: bool = False):
|
||||
""" Calls the Menu framework"""
|
||||
# we synch all the options just in case
|
||||
for item in self.list_options():
|
||||
|
|
@ -304,6 +302,8 @@ class GeneralMenu:
|
|||
padding = self._get_menu_text_padding(list(enabled_menus.values()))
|
||||
menu_options = [m.menu_text(padding) for m in enabled_menus.values()]
|
||||
|
||||
warning_msg = str(_('All settings will be reset, are you sure?'))
|
||||
|
||||
selection = Menu(
|
||||
_('Set/Modify the below options'),
|
||||
menu_options,
|
||||
|
|
@ -312,33 +312,39 @@ class GeneralMenu:
|
|||
preview_command=self._preview_display,
|
||||
preview_size=self.preview_size,
|
||||
skip_empty_entries=True,
|
||||
skip=False
|
||||
skip=False,
|
||||
allow_reset=allow_reset,
|
||||
allow_reset_warning_msg=warning_msg
|
||||
).run()
|
||||
|
||||
if selection.type_ == MenuSelectionType.Selection:
|
||||
value = selection.value
|
||||
match selection.type_:
|
||||
case MenuSelectionType.Reset:
|
||||
self._data_store = {}
|
||||
return
|
||||
case MenuSelectionType.Selection:
|
||||
value: str = selection.value # type: ignore
|
||||
|
||||
if self.auto_cursor:
|
||||
cursor_pos = menu_options.index(value) + 1 # before the strip otherwise fails
|
||||
if self.auto_cursor:
|
||||
cursor_pos = menu_options.index(value) + 1 # before the strip otherwise fails
|
||||
|
||||
# in case the new position lands on a "placeholder" we'll skip them as well
|
||||
while True:
|
||||
if cursor_pos >= len(menu_options):
|
||||
cursor_pos = 0
|
||||
if len(menu_options[cursor_pos]) > 0:
|
||||
break
|
||||
cursor_pos += 1
|
||||
# in case the new position lands on a "placeholder" we'll skip them as well
|
||||
while True:
|
||||
if cursor_pos >= len(menu_options):
|
||||
cursor_pos = 0
|
||||
if len(menu_options[cursor_pos]) > 0:
|
||||
break
|
||||
cursor_pos += 1
|
||||
|
||||
value = value.strip()
|
||||
value = value.strip()
|
||||
|
||||
# if this calls returns false, we exit the menu
|
||||
# we allow for an callback for special processing on realeasing control
|
||||
if not self._process_selection(value):
|
||||
break
|
||||
# if this calls returns false, we exit the menu
|
||||
# we allow for an callback for special processing on realeasing control
|
||||
if not self._process_selection(value):
|
||||
break
|
||||
|
||||
# we get the last action key
|
||||
actions = {str(v.description):k for k,v in self._menu_options.items()}
|
||||
self._last_choice = actions[selection.value.strip()]
|
||||
self._last_choice = actions[selection.value.strip()] # type: ignore
|
||||
|
||||
if not self.is_context_mgr:
|
||||
self.__exit__()
|
||||
|
|
@ -472,26 +478,16 @@ class GeneralMenu:
|
|||
self._translation_handler.activate(language)
|
||||
return language
|
||||
|
||||
def _select_hsm(self, preset :Optional[pathlib.Path] = None) -> Optional[pathlib.Path]:
|
||||
title = _('Select which partitions to mark for formatting:')
|
||||
title += '\n'
|
||||
|
||||
fido_devices = get_fido2_devices()
|
||||
class AbstractSubMenu(AbstractMenu):
|
||||
def __init__(self, data_store: Dict[str, Any] = None):
|
||||
super().__init__(data_store=data_store)
|
||||
|
||||
indexes = []
|
||||
for index, path in enumerate(fido_devices.keys()):
|
||||
title += f"{index}: {path} ({fido_devices[path]['manufacturer']} - {fido_devices[path]['product']})"
|
||||
indexes.append(f"{index}|{fido_devices[path]['product']}")
|
||||
|
||||
title += '\n'
|
||||
|
||||
choice = Menu(title, indexes, multi=False).run()
|
||||
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Esc: return preset
|
||||
case MenuSelectionType.Selection:
|
||||
selection: Any = choice.value
|
||||
index = int(selection.split('|',1)[0])
|
||||
return pathlib.Path(list(fido_devices.keys())[index])
|
||||
|
||||
return None
|
||||
self._menu_options['__separator__'] = Selector('')
|
||||
self._menu_options['back'] = \
|
||||
Selector(
|
||||
_('Back'),
|
||||
no_store=True,
|
||||
enabled=True,
|
||||
exec_func=lambda n, v: True,
|
||||
)
|
||||
|
|
@ -3,12 +3,13 @@ from __future__ import annotations
|
|||
from typing import Any, List, Optional, Union, Dict, TYPE_CHECKING
|
||||
|
||||
import archinstall
|
||||
from ..disk import encrypted_partitions
|
||||
from ..disk.encryption import DiskEncryptionMenu
|
||||
from ..general import SysCommand, secret
|
||||
from ..hardware import has_uefi
|
||||
from ..menu import Menu
|
||||
from ..menu.selection_menu import Selector, GeneralMenu
|
||||
from ..menu.abstract_menu import Selector, AbstractMenu
|
||||
from ..models import NetworkConfiguration
|
||||
from ..models.disk_encryption import DiskEncryption, EncryptionType
|
||||
from ..models.users import User
|
||||
from ..output import FormattedOutput
|
||||
from ..profiles import is_desktop_profile, Profile
|
||||
|
|
@ -25,7 +26,6 @@ from ..user_interaction import ask_to_configure_network
|
|||
from ..user_interaction import get_password, ask_for_a_timezone, save_config
|
||||
from ..user_interaction import select_additional_repositories
|
||||
from ..user_interaction import select_disk_layout
|
||||
from ..user_interaction import select_encrypted_partitions
|
||||
from ..user_interaction import select_harddrives
|
||||
from ..user_interaction import select_kernel
|
||||
from ..user_interaction import select_language
|
||||
|
|
@ -39,7 +39,7 @@ if TYPE_CHECKING:
|
|||
_: Any
|
||||
|
||||
|
||||
class GlobalMenu(GeneralMenu):
|
||||
class GlobalMenu(AbstractMenu):
|
||||
def __init__(self,data_store):
|
||||
self._disk_check = True
|
||||
super().__init__(data_store=data_store, auto_cursor=True, preview_size=0.3)
|
||||
|
|
@ -91,18 +91,13 @@ class GlobalMenu(GeneralMenu):
|
|||
preview_func=self._prev_disk_layouts,
|
||||
display_func=lambda x: self._display_disk_layout(x),
|
||||
dependencies=['harddrives'])
|
||||
self._menu_options['!encryption-password'] = \
|
||||
self._menu_options['disk_encryption'] = \
|
||||
Selector(
|
||||
_('Encryption password'),
|
||||
lambda x: self._select_encrypted_password(),
|
||||
display_func=lambda x: secret(x) if x else 'None',
|
||||
dependencies=['harddrives'])
|
||||
self._menu_options['HSM'] = Selector(
|
||||
description=_('Use HSM to unlock encrypted drive'),
|
||||
func=lambda preset: self._select_hsm(preset),
|
||||
dependencies=['!encryption-password'],
|
||||
default=None
|
||||
)
|
||||
_('Disk encryption'),
|
||||
lambda preset: self._disk_encryption(preset),
|
||||
preview_func=self._prev_disk_encryption,
|
||||
display_func=lambda x: self._display_disk_encryption(x),
|
||||
dependencies=['disk_layouts'])
|
||||
self._menu_options['swap'] = \
|
||||
Selector(
|
||||
_('Swap'),
|
||||
|
|
@ -209,28 +204,6 @@ class GlobalMenu(GeneralMenu):
|
|||
def post_callback(self,name :str = None ,result :Any = None):
|
||||
self._update_install_text(name, result)
|
||||
|
||||
def exit_callback(self):
|
||||
if self._data_store.get('harddrives', None) and self._data_store.get('!encryption-password', None):
|
||||
# If no partitions was marked as encrypted, but a password was supplied and we have some disks to format..
|
||||
# Then we need to identify which partitions to encrypt. This will default to / (root).
|
||||
if len(list(encrypted_partitions(storage['arguments'].get('disk_layouts', [])))) == 0:
|
||||
for blockdevice in storage['arguments']['disk_layouts']:
|
||||
if storage['arguments']['disk_layouts'][blockdevice].get('partitions'):
|
||||
for partition_index in select_encrypted_partitions(
|
||||
title=_('Select which partitions to encrypt:'),
|
||||
partitions=storage['arguments']['disk_layouts'][blockdevice]['partitions'],
|
||||
filter_=(lambda p: p['mountpoint'] != '/boot')
|
||||
):
|
||||
|
||||
partition = storage['arguments']['disk_layouts'][blockdevice]['partitions'][partition_index]
|
||||
partition['encrypted'] = True
|
||||
partition['!password'] = storage['arguments']['!encryption-password']
|
||||
|
||||
# We make sure generate-encryption-key-file is set on additional partitions
|
||||
# other than the root partition. Otherwise they won't unlock properly #1279
|
||||
if partition['mountpoint'] != '/':
|
||||
partition['generate-encryption-key-file'] = True
|
||||
|
||||
def _install_text(self):
|
||||
missing = len(self._missing_configs())
|
||||
if missing > 0:
|
||||
|
|
@ -246,6 +219,20 @@ class GlobalMenu(GeneralMenu):
|
|||
else:
|
||||
return str(cur_value)
|
||||
|
||||
def _disk_encryption(self, preset: Optional[DiskEncryption]) -> Optional[DiskEncryption]:
|
||||
data_store: Dict[str, Any] = {}
|
||||
|
||||
selector = self._menu_options['disk_layouts']
|
||||
|
||||
if selector.has_selection():
|
||||
layouts: Dict[str, Dict[str, Any]] = selector.current_selection
|
||||
else:
|
||||
# this should not happen as the encryption menu has the disk layout as dependency
|
||||
raise ValueError('No disk layout specified')
|
||||
|
||||
disk_encryption = DiskEncryptionMenu(data_store, preset, layouts).run()
|
||||
return disk_encryption
|
||||
|
||||
def _prev_network_config(self) -> Optional[str]:
|
||||
selector = self._menu_options['nic']
|
||||
if selector.has_selection():
|
||||
|
|
@ -283,6 +270,30 @@ class GlobalMenu(GeneralMenu):
|
|||
return f'{total_nr} {_("Partitions")}'
|
||||
return ''
|
||||
|
||||
def _prev_disk_encryption(self) -> Optional[str]:
|
||||
selector = self._menu_options['disk_encryption']
|
||||
if selector.has_selection():
|
||||
encryption: DiskEncryption = selector.current_selection
|
||||
|
||||
enc_type = EncryptionType.type_to_text(encryption.encryption_type)
|
||||
output = str(_('Encryption type')) + f': {enc_type}\n'
|
||||
output += str(_('Password')) + f': {secret(encryption.encryption_password)}\n'
|
||||
|
||||
if encryption.partitions:
|
||||
output += 'Partitions: {} selected'.format(len(encryption.partitions)) + '\n'
|
||||
|
||||
if encryption.hsm_device:
|
||||
output += f'HSM: {encryption.hsm_device.manufacturer}'
|
||||
|
||||
return output
|
||||
|
||||
return None
|
||||
|
||||
def _display_disk_encryption(self, current_value: Optional[DiskEncryption]) -> str:
|
||||
if current_value:
|
||||
return EncryptionType.type_to_text(current_value.encryption_type)
|
||||
return ''
|
||||
|
||||
def _prev_install_missing_config(self) -> Optional[str]:
|
||||
if missing := self._missing_configs():
|
||||
text = str(_('Missing configurations:\n'))
|
||||
|
|
@ -327,11 +338,10 @@ class GlobalMenu(GeneralMenu):
|
|||
password = get_password(prompt=prompt)
|
||||
return password
|
||||
|
||||
def _select_encrypted_password(self) -> Optional[str]:
|
||||
if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))):
|
||||
return passwd
|
||||
else:
|
||||
return None
|
||||
# def _select_encrypted_password(self) -> Optional[str]:
|
||||
# if passwd := get_password(prompt=str(_('Enter disk encryption password (leave blank for no encryption): '))):
|
||||
# return passwd
|
||||
# return None
|
||||
|
||||
def _select_ntp(self, preset :bool = True) -> bool:
|
||||
ntp = ask_ntp(preset)
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ class ListManager:
|
|||
return options, header
|
||||
|
||||
def _run_actions_on_entry(self, entry: Any):
|
||||
options = self.filter_options(entry,self._sub_menu_actions) + [self._cancel_action]
|
||||
options = self.filter_options(entry, self._sub_menu_actions) + [self._cancel_action]
|
||||
display_value = self.selected_action_display(entry)
|
||||
|
||||
prompt = _("Select an action for '{}'").format(display_value)
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ if TYPE_CHECKING:
|
|||
|
||||
class MenuSelectionType(Enum):
|
||||
Selection = auto()
|
||||
Esc = auto()
|
||||
Ctrl_c = auto()
|
||||
Skip = auto()
|
||||
Reset = auto()
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -56,8 +56,8 @@ class Menu(TerminalMenu):
|
|||
preview_size: float = 0.0,
|
||||
preview_title: str = 'Info',
|
||||
header :Union[List[str],str] = None,
|
||||
raise_error_on_interrupt :bool = False,
|
||||
raise_error_warning_msg :str = '',
|
||||
allow_reset :bool = False,
|
||||
allow_reset_warning_msg :str = '',
|
||||
clear_screen: bool = True,
|
||||
show_search_hint: bool = True,
|
||||
cycle_cursor: bool = True,
|
||||
|
|
@ -150,17 +150,10 @@ class Menu(TerminalMenu):
|
|||
self._skip = skip
|
||||
self._default_option = default_option
|
||||
self._multi = multi
|
||||
self._raise_error_on_interrupt = raise_error_on_interrupt
|
||||
self._raise_error_warning_msg = raise_error_warning_msg
|
||||
self._raise_error_on_interrupt = allow_reset
|
||||
self._raise_error_warning_msg = allow_reset_warning_msg
|
||||
self._preview_command = preview_command
|
||||
|
||||
menu_title = f'\n{title}\n\n'
|
||||
|
||||
if header:
|
||||
if not isinstance(header,(list,tuple)):
|
||||
header = [header]
|
||||
menu_title += '\n'.join(header)
|
||||
|
||||
action_info = ''
|
||||
if skip:
|
||||
action_info += str(_('ESC to skip'))
|
||||
|
|
@ -173,7 +166,15 @@ class Menu(TerminalMenu):
|
|||
action_info += ', ' if len(action_info) > 0 else ''
|
||||
action_info += str(_('TAB to select'))
|
||||
|
||||
menu_title += action_info + '\n'
|
||||
if action_info:
|
||||
action_info += '\n\n'
|
||||
|
||||
menu_title = f'\n{action_info}{title}\n'
|
||||
|
||||
if header:
|
||||
if not isinstance(header,(list,tuple)):
|
||||
header = [header]
|
||||
menu_title += '\n' + '\n'.join(header)
|
||||
|
||||
if default_option:
|
||||
# if a default value was specified we move that one
|
||||
|
|
@ -215,7 +216,7 @@ class Menu(TerminalMenu):
|
|||
try:
|
||||
idx = self.show()
|
||||
except KeyboardInterrupt:
|
||||
return MenuSelection(type_=MenuSelectionType.Ctrl_c)
|
||||
return MenuSelection(type_=MenuSelectionType.Reset)
|
||||
|
||||
def check_default(elem):
|
||||
if self._default_option is not None and f'{self._default_option} {self._default_str}' in elem:
|
||||
|
|
@ -234,7 +235,7 @@ class Menu(TerminalMenu):
|
|||
result = check_default(self._menu_options[idx])
|
||||
return MenuSelection(type_=MenuSelectionType.Selection, value=result)
|
||||
else:
|
||||
return MenuSelection(type_=MenuSelectionType.Esc)
|
||||
return MenuSelection(type_=MenuSelectionType.Skip)
|
||||
|
||||
def _preview_wrapper(self, preview_command: Optional[Callable], current_selection: str) -> Optional[str]:
|
||||
if preview_command:
|
||||
|
|
@ -246,15 +247,15 @@ class Menu(TerminalMenu):
|
|||
def run(self) -> MenuSelection:
|
||||
ret = self._show()
|
||||
|
||||
if ret.type_ == MenuSelectionType.Ctrl_c:
|
||||
if ret.type_ == MenuSelectionType.Reset:
|
||||
if self._raise_error_on_interrupt and len(self._raise_error_warning_msg) > 0:
|
||||
response = Menu(self._raise_error_warning_msg, Menu.yes_no(), skip=False).run()
|
||||
if response.value == Menu.no():
|
||||
return self.run()
|
||||
|
||||
if ret.type_ is not MenuSelectionType.Selection and not self._skip:
|
||||
system('clear')
|
||||
return self.run()
|
||||
elif ret.type_ is MenuSelectionType.Skip:
|
||||
if not self._skip:
|
||||
system('clear')
|
||||
return self.run()
|
||||
|
||||
return ret
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,107 @@
|
|||
from typing import Any, Tuple, List, Dict, Optional
|
||||
|
||||
from .menu import MenuSelectionType, MenuSelection
|
||||
from ..output import FormattedOutput
|
||||
from ..menu import Menu
|
||||
|
||||
|
||||
class TableMenu(Menu):
|
||||
def __init__(
|
||||
self,
|
||||
title: str,
|
||||
data: List[Any] = [],
|
||||
table_data: Optional[Tuple[List[Any], str]] = None,
|
||||
custom_menu_options: List[str] = [],
|
||||
default: Any = None,
|
||||
multi: bool = False
|
||||
):
|
||||
"""
|
||||
param title: Text that will be displayed above the menu
|
||||
:type title: str
|
||||
|
||||
param data: List of objects that will be displayed as rows
|
||||
:type data: List
|
||||
|
||||
param table_data: Tuple containing a list of objects and the corresponding
|
||||
Table representation of the data as string; this can be used in case the table
|
||||
has to be crafted in a more sophisticated manner
|
||||
:type table_data: Optional[Tuple[List[Any], str]]
|
||||
|
||||
param custom_options: List of custom options that will be displayed under the table
|
||||
:type custom_menu_options: List
|
||||
"""
|
||||
if not data and not table_data:
|
||||
raise ValueError('Either "data" or "table_data" must be provided')
|
||||
|
||||
self._custom_options = custom_menu_options
|
||||
self._multi = multi
|
||||
|
||||
if multi:
|
||||
header_padding = 7
|
||||
else:
|
||||
header_padding = 2
|
||||
|
||||
if len(data):
|
||||
table_text = FormattedOutput.as_table(data)
|
||||
rows = table_text.split('\n')
|
||||
table = self._create_table(data, rows, header_padding=header_padding)
|
||||
elif table_data is not None:
|
||||
# we assume the table to be
|
||||
# h1 | h2
|
||||
# -----------
|
||||
# r1 | r2
|
||||
data = table_data[0]
|
||||
rows = table_data[1].split('\n')
|
||||
table = self._create_table(data, rows, header_padding=header_padding)
|
||||
|
||||
self._options, header = self._prepare_selection(table)
|
||||
|
||||
super().__init__(
|
||||
title,
|
||||
self._options,
|
||||
header=header,
|
||||
skip_empty_entries=True,
|
||||
show_search_hint=False,
|
||||
allow_reset=True,
|
||||
multi=multi,
|
||||
default_option=default
|
||||
)
|
||||
|
||||
def run(self) -> MenuSelection:
|
||||
choice = super().run()
|
||||
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Selection:
|
||||
if self._multi:
|
||||
choice.value = [self._options[val] for val in choice.value] # type: ignore
|
||||
else:
|
||||
choice.value = self._options[choice.value] # type: ignore
|
||||
|
||||
return choice
|
||||
|
||||
def _create_table(self, data: List[Any], rows: List[str], header_padding: int = 2) -> Dict[str, Any]:
|
||||
# these are the header rows of the table and do not map to any data 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
|
||||
padding = ' ' * header_padding
|
||||
display_data = {f'{padding}{rows[0]}': None, f'{padding}{rows[1]}': None}
|
||||
|
||||
for row, entry in zip(rows[2:], data):
|
||||
row = row.replace('|', '\\|')
|
||||
display_data[row] = entry
|
||||
|
||||
return display_data
|
||||
|
||||
def _prepare_selection(self, table: Dict[str, Any]) -> Tuple[Dict[str, Any], str]:
|
||||
# header rows are mapped to None so make sure to exclude those from the selectable data
|
||||
options = {key: val for key, val in table.items() if val is not None}
|
||||
header = ''
|
||||
|
||||
if len(options) > 0:
|
||||
table_header = [key for key, val in table.items() if val is None]
|
||||
header = '\n'.join(table_header)
|
||||
|
||||
custom = {key: None for key in self._custom_options}
|
||||
options.update(custom)
|
||||
|
||||
return options, header
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
from dataclasses import dataclass, field
|
||||
from enum import Enum, auto
|
||||
from typing import Optional, List, Dict, TYPE_CHECKING, Any
|
||||
|
||||
from archinstall.lib.hsm.fido import Fido2Device
|
||||
|
||||
if TYPE_CHECKING:
|
||||
_: Any
|
||||
|
||||
|
||||
class EncryptionType(Enum):
|
||||
Partition = auto()
|
||||
# FullDiskEncryption = auto()
|
||||
|
||||
@classmethod
|
||||
def _encryption_type_mapper(cls) -> Dict[str, 'EncryptionType']:
|
||||
return {
|
||||
# str(_('Full disk encryption')): EncryptionType.FullDiskEncryption,
|
||||
str(_('Partition encryption')): EncryptionType.Partition
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def text_to_type(cls, text: str) -> 'EncryptionType':
|
||||
mapping = cls._encryption_type_mapper()
|
||||
return mapping[text]
|
||||
|
||||
@classmethod
|
||||
def type_to_text(cls, type_: 'EncryptionType') -> str:
|
||||
mapping = cls._encryption_type_mapper()
|
||||
type_to_text = {type_: text for text, type_ in mapping.items()}
|
||||
return type_to_text[type_]
|
||||
|
||||
|
||||
@dataclass
|
||||
class DiskEncryption:
|
||||
encryption_type: EncryptionType = EncryptionType.Partition
|
||||
encryption_password: str = ''
|
||||
partitions: List[str] = field(default_factory=list)
|
||||
hsm_device: Optional[Fido2Device] = None
|
||||
|
||||
def generate_encryption_file(self, partition) -> bool:
|
||||
return partition in self.partitions and partition['mountpoint'] != '/'
|
||||
|
||||
|
|
@ -4,7 +4,7 @@ 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
|
||||
from .network_conf import ask_to_configure_network
|
||||
from .partitioning_conf import select_partition, select_encrypted_partitions
|
||||
from .partitioning_conf import select_partition
|
||||
from .general_conf import (ask_ntp, ask_for_a_timezone, ask_for_audio_selection, select_language, select_mirror_regions,
|
||||
select_profile, select_archinstall_language, ask_additional_packages_to_install,
|
||||
select_additional_repositories, ask_hostname, add_number_of_parrallel_downloads)
|
||||
|
|
|
|||
|
|
@ -45,13 +45,13 @@ def select_disk_layout(preset: Optional[Dict[str, Any]], block_devices: list, ad
|
|||
choice = Menu(
|
||||
_('Select what you wish to do with the selected block devices'),
|
||||
modes,
|
||||
raise_error_on_interrupt=True,
|
||||
raise_error_warning_msg=warning
|
||||
allow_reset=True,
|
||||
allow_reset_warning_msg=warning
|
||||
).run()
|
||||
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Esc: return preset
|
||||
case MenuSelectionType.Ctrl_c: return None
|
||||
case MenuSelectionType.Skip: return preset
|
||||
case MenuSelectionType.Reset: return None
|
||||
case MenuSelectionType.Selection:
|
||||
if choice.value == wipe_mode:
|
||||
return get_default_partition_layout(block_devices, advanced_options)
|
||||
|
|
@ -77,7 +77,7 @@ def select_disk(dict_o_disks: Dict[str, BlockDevice]) -> Optional[BlockDevice]:
|
|||
|
||||
choice = Menu(title, drives).run()
|
||||
|
||||
if choice.type_ == MenuSelectionType.Esc:
|
||||
if choice.type_ == MenuSelectionType.Skip:
|
||||
return None
|
||||
|
||||
drive = dict_o_disks[choice.value]
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ def ask_for_a_timezone(preset: str = None) -> str:
|
|||
).run()
|
||||
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Esc: return preset
|
||||
case MenuSelectionType.Skip: return preset
|
||||
case MenuSelectionType.Selection: return choice.value
|
||||
|
||||
|
||||
|
|
@ -60,7 +60,7 @@ def ask_for_audio_selection(desktop: bool = True, preset: str = None) -> str:
|
|||
choice = Menu(_('Choose an audio server'), choices, preset_values=preset, default_option=default).run()
|
||||
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Esc: return preset
|
||||
case MenuSelectionType.Skip: return preset
|
||||
case MenuSelectionType.Selection: return choice.value
|
||||
|
||||
|
||||
|
|
@ -107,12 +107,12 @@ def select_mirror_regions(preset_values: Dict[str, Any] = {}) -> Dict[str, Any]:
|
|||
list(mirrors.keys()),
|
||||
preset_values=preselected,
|
||||
multi=True,
|
||||
raise_error_on_interrupt=True
|
||||
allow_reset=True
|
||||
).run()
|
||||
|
||||
match selected_mirror.type_:
|
||||
case MenuSelectionType.Ctrl_c: return {}
|
||||
case MenuSelectionType.Esc: return preset_values
|
||||
case MenuSelectionType.Reset: return {}
|
||||
case MenuSelectionType.Skip: return preset_values
|
||||
case _: return {selected: mirrors[selected] for selected in selected_mirror.value}
|
||||
|
||||
|
||||
|
|
@ -134,7 +134,7 @@ def select_archinstall_language(languages: List[Language], preset_value: Languag
|
|||
).run()
|
||||
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Esc:
|
||||
case MenuSelectionType.Skip:
|
||||
return preset_value
|
||||
case MenuSelectionType.Selection:
|
||||
return options[choice.value]
|
||||
|
|
@ -163,21 +163,21 @@ def select_profile(preset) -> Optional[Profile]:
|
|||
selection = Menu(
|
||||
title=title,
|
||||
p_options=list(options.keys()),
|
||||
raise_error_on_interrupt=True,
|
||||
raise_error_warning_msg=warning
|
||||
allow_reset=True,
|
||||
allow_reset_warning_msg=warning
|
||||
).run()
|
||||
|
||||
match selection.type_:
|
||||
case MenuSelectionType.Selection:
|
||||
return options[selection.value] if selection.value is not None else None
|
||||
case MenuSelectionType.Ctrl_c:
|
||||
case MenuSelectionType.Reset:
|
||||
storage['profile_minimal'] = False
|
||||
storage['_selected_servers'] = []
|
||||
storage['_desktop_profile'] = None
|
||||
storage['arguments']['desktop-environment'] = None
|
||||
storage['arguments']['gfx_driver_packages'] = None
|
||||
return None
|
||||
case MenuSelectionType.Esc:
|
||||
case MenuSelectionType.Skip:
|
||||
return None
|
||||
|
||||
|
||||
|
|
@ -259,10 +259,10 @@ def select_additional_repositories(preset: List[str]) -> List[str]:
|
|||
sort=False,
|
||||
multi=True,
|
||||
preset_values=preset,
|
||||
raise_error_on_interrupt=True
|
||||
allow_reset=True
|
||||
).run()
|
||||
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Esc: return preset
|
||||
case MenuSelectionType.Ctrl_c: return []
|
||||
case MenuSelectionType.Skip: return preset
|
||||
case MenuSelectionType.Reset: return []
|
||||
case MenuSelectionType.Selection: return choice.value
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ def select_locale_lang(preset: str = None) -> str:
|
|||
|
||||
match selected_locale.type_:
|
||||
case MenuSelectionType.Selection: return selected_locale.value
|
||||
case MenuSelectionType.Esc: return preset
|
||||
case MenuSelectionType.Skip: return preset
|
||||
|
||||
|
||||
def select_locale_enc(preset: str = None) -> str:
|
||||
|
|
@ -39,4 +39,4 @@ def select_locale_enc(preset: str = None) -> str:
|
|||
|
||||
match selected_locale.type_:
|
||||
case MenuSelectionType.Selection: return selected_locale.value
|
||||
case MenuSelectionType.Esc: return preset
|
||||
case MenuSelectionType.Skip: return preset
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class ManualNetworkConfig(ListManager):
|
|||
available = set(all_ifaces) - set(existing_ifaces)
|
||||
choice = Menu(str(_('Select interface to add')), list(available), skip=True).run()
|
||||
|
||||
if choice.type_ == MenuSelectionType.Esc:
|
||||
if choice.type_ == MenuSelectionType.Skip:
|
||||
return None
|
||||
|
||||
return choice.value
|
||||
|
|
@ -154,13 +154,13 @@ def ask_to_configure_network(
|
|||
list(network_options.values()),
|
||||
cursor_index=cursor_idx,
|
||||
sort=False,
|
||||
raise_error_on_interrupt=True,
|
||||
raise_error_warning_msg=warning
|
||||
allow_reset=True,
|
||||
allow_reset_warning_msg=warning
|
||||
).run()
|
||||
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Esc: return preset
|
||||
case MenuSelectionType.Ctrl_c: return None
|
||||
case MenuSelectionType.Skip: return preset
|
||||
case MenuSelectionType.Reset: return None
|
||||
|
||||
if choice.value == network_options['none']:
|
||||
return None
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ def select_partition(
|
|||
|
||||
choice = Menu(title, partition_indexes, multi=multiple).run()
|
||||
|
||||
if choice.type_ == MenuSelectionType.Esc:
|
||||
if choice.type_ == MenuSelectionType.Skip:
|
||||
return None
|
||||
|
||||
if isinstance(choice.value, list):
|
||||
|
|
@ -150,7 +150,6 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str,
|
|||
delete_all_partitions = str(_('Clear/Delete all partitions'))
|
||||
assign_mount_point = str(_('Assign mount-point for a partition'))
|
||||
mark_formatted = str(_('Mark/Unmark a partition to be formatted (wipes data)'))
|
||||
mark_encrypted = str(_('Mark/Unmark a partition as encrypted'))
|
||||
mark_compressed = str(_('Mark/Unmark a partition as compressed (btrfs only)'))
|
||||
mark_bootable = str(_('Mark/Unmark a partition as bootable (automatic for /boot)'))
|
||||
set_filesystem_partition = str(_('Set desired filesystem for a partition'))
|
||||
|
|
@ -167,7 +166,6 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str,
|
|||
delete_all_partitions,
|
||||
assign_mount_point,
|
||||
mark_formatted,
|
||||
mark_encrypted,
|
||||
mark_bootable,
|
||||
mark_compressed,
|
||||
set_filesystem_partition,
|
||||
|
|
@ -207,7 +205,7 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str,
|
|||
|
||||
fs_choice = Menu(_('Enter a desired filesystem type for the partition'), fs_types()).run()
|
||||
|
||||
if fs_choice.type_ == MenuSelectionType.Esc:
|
||||
if fs_choice.type_ == MenuSelectionType.Skip:
|
||||
continue
|
||||
|
||||
prompt = str(_('Enter the start sector (percentage or block number, default: {}): ')).format(
|
||||
|
|
@ -322,15 +320,6 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str,
|
|||
# Negate the current wipe marking
|
||||
block_device_struct["partitions"][partition]['wipe'] = not block_device_struct["partitions"][partition].get('wipe', False)
|
||||
|
||||
elif task == mark_encrypted:
|
||||
title = _('{}\n\nSelect which partition to mark as encrypted').format(current_layout)
|
||||
partition = select_partition(title, block_device_struct["partitions"])
|
||||
|
||||
if partition is not None:
|
||||
# Negate the current encryption marking
|
||||
block_device_struct["partitions"][partition]['encrypted'] = \
|
||||
not block_device_struct["partitions"][partition].get('encrypted', False)
|
||||
|
||||
elif task == mark_bootable:
|
||||
title = _('{}\n\nSelect which partition to mark as bootable').format(current_layout)
|
||||
partition = select_partition(title, block_device_struct["partitions"])
|
||||
|
|
@ -371,30 +360,3 @@ def manage_new_and_existing_partitions(block_device: 'BlockDevice') -> Dict[str,
|
|||
block_device_struct["partitions"][partition]['btrfs']['subvolumes'] = result
|
||||
|
||||
return block_device_struct
|
||||
|
||||
|
||||
def select_encrypted_partitions(
|
||||
title :str,
|
||||
partitions :List[Partition],
|
||||
multiple :bool = True,
|
||||
filter_ :Callable = None
|
||||
) -> Optional[int, List[int]]:
|
||||
partition_indexes = _get_partitions(partitions, filter_)
|
||||
|
||||
if len(partition_indexes) == 0:
|
||||
return None
|
||||
|
||||
# show current partition layout:
|
||||
if len(partitions):
|
||||
title += current_partition_layout(partitions, with_idx=True) + '\n'
|
||||
|
||||
choice = Menu(title, partition_indexes, multi=multiple).run()
|
||||
|
||||
if choice.type_ == MenuSelectionType.Esc:
|
||||
return None
|
||||
|
||||
if isinstance(choice.value, list):
|
||||
for partition_index in choice.value:
|
||||
yield int(partition_index)
|
||||
else:
|
||||
yield (partition_index)
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ def save_config(config: Dict):
|
|||
preview_command=preview
|
||||
).run()
|
||||
|
||||
if choice.type_ == MenuSelectionType.Esc:
|
||||
if choice.type_ == MenuSelectionType.Skip:
|
||||
return
|
||||
|
||||
while True:
|
||||
|
|
|
|||
|
|
@ -32,13 +32,13 @@ def select_kernel(preset: List[str] = None) -> List[str]:
|
|||
sort=True,
|
||||
multi=True,
|
||||
preset_values=preset,
|
||||
raise_error_on_interrupt=True,
|
||||
raise_error_warning_msg=warning
|
||||
allow_reset=True,
|
||||
allow_reset_warning_msg=warning
|
||||
).run()
|
||||
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Esc: return preset
|
||||
case MenuSelectionType.Ctrl_c: return []
|
||||
case MenuSelectionType.Skip: return preset
|
||||
case MenuSelectionType.Reset: return []
|
||||
case MenuSelectionType.Selection: return choice.value
|
||||
|
||||
|
||||
|
|
@ -62,13 +62,13 @@ def select_harddrives(preset: List[str] = []) -> List[str]:
|
|||
list(options.keys()),
|
||||
preset_values=preset,
|
||||
multi=True,
|
||||
raise_error_on_interrupt=True,
|
||||
raise_error_warning_msg=warning
|
||||
allow_reset=True,
|
||||
allow_reset_warning_msg=warning
|
||||
).run()
|
||||
|
||||
match selected_harddrive.type_:
|
||||
case MenuSelectionType.Ctrl_c: return []
|
||||
case MenuSelectionType.Esc: return preset
|
||||
case MenuSelectionType.Reset: return []
|
||||
case MenuSelectionType.Skip: return preset
|
||||
case MenuSelectionType.Selection: return [options[i] for i in selected_harddrive.value]
|
||||
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ def ask_for_bootloader(advanced_options: bool = False, preset: str = None) -> st
|
|||
).run()
|
||||
|
||||
match selection.type_:
|
||||
case MenuSelectionType.Esc: return preset
|
||||
case MenuSelectionType.Skip: return preset
|
||||
case MenuSelectionType.Selection: bootloader = 'grub-install' if selection.value == Menu.yes() else bootloader
|
||||
else:
|
||||
# We use the common names for the bootloader as the selection, and map it back to the expected values.
|
||||
|
|
@ -141,7 +141,7 @@ def ask_for_bootloader(advanced_options: bool = False, preset: str = None) -> st
|
|||
|
||||
value = ''
|
||||
match selection.type_:
|
||||
case MenuSelectionType.Esc: value = preset_val
|
||||
case MenuSelectionType.Skip: value = preset_val
|
||||
case MenuSelectionType.Selection: value = selection.value
|
||||
|
||||
if value != "":
|
||||
|
|
@ -165,5 +165,5 @@ def ask_for_swap(preset: bool = True) -> bool:
|
|||
choice = Menu(prompt, Menu.yes_no(), default_option=Menu.yes(), preset_values=preset_val).run()
|
||||
|
||||
match choice.type_:
|
||||
case MenuSelectionType.Esc: return preset
|
||||
case MenuSelectionType.Skip: return preset
|
||||
case MenuSelectionType.Selection: return False if choice.value == Menu.no() else True
|
||||
|
|
|
|||
|
|
@ -721,7 +721,7 @@ msgstr ""
|
|||
msgid "Should \"{}\" be a superuser (sudo)?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr ""
|
||||
|
||||
msgid "very weak"
|
||||
|
|
|
|||
|
|
@ -761,7 +761,7 @@ msgstr ""
|
|||
msgid "Should \"{}\" be a superuser (sudo)?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr ""
|
||||
|
||||
msgid "very weak"
|
||||
|
|
|
|||
|
|
@ -756,8 +756,8 @@ msgstr "Zadané uživatelské jméno není platné. Zkuste to znovu"
|
|||
msgid "Should \"{}\" be a superuser (sudo)?"
|
||||
msgstr "Má být \"{}\" superuživatelem (sudoer)?"
|
||||
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr "Zvolte oddíl, který bude označen jako šifrovaný:"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Zvolte oddíl, který bude označen jako šifrovaný"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr "velmi slabé"
|
||||
|
|
|
|||
|
|
@ -776,11 +776,8 @@ msgid "Should \"{}\" be a superuser (sudo)?"
|
|||
msgstr "Soll {} ein superuser sein (sudoer)?"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr ""
|
||||
"{}\n"
|
||||
"\n"
|
||||
"Bitte wählen sie welche Partition verschlüsselt werden soll"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Bitte wählen sie welche Partition verschlüsselt werden soll"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -763,7 +763,7 @@ msgstr "Το όνομα χρήστη που εισάγατε δεν είναι
|
|||
msgid "Should \"{}\" be a superuser (sudo)?"
|
||||
msgstr "Θα έπρεπε ο \"{}\" να είναι υπερχρήστης (sudo);"
|
||||
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Επιλέξτε ποιες διαμερίσεις να κρυπτογραφηθούν."
|
||||
|
||||
msgid "very weak"
|
||||
|
|
|
|||
|
|
@ -717,7 +717,7 @@ msgstr ""
|
|||
msgid "Should \"{}\" be a superuser (sudo)?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr ""
|
||||
|
||||
msgid "very weak"
|
||||
|
|
|
|||
|
|
@ -762,8 +762,8 @@ msgstr "El nombre de usuario que ingresó no es válido. Intente nuevamente"
|
|||
msgid "Should \"{}\" be a superuser (sudo)?"
|
||||
msgstr "¿Debe \"{}\" ser un superusuario (sudo)?"
|
||||
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr "Seleccione qué particiones cifrar:"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Seleccione qué particiones cifrar"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -765,11 +765,8 @@ msgid "Should \"{}\" be a superuser (sudo)?"
|
|||
msgstr "\"{}\" devrait-il être un superutilisateur (sudo) ?"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr ""
|
||||
"{}\n"
|
||||
"\n"
|
||||
"Sélectionner la partition à marquer comme chiffrée"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Sélectionner la partition à marquer comme chiffrée"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -763,8 +763,8 @@ msgstr "Nama pengguna yang Anda masukkan tidak valid. Coba lagi"
|
|||
msgid "Should \"{}\" be a superuser (sudo)?"
|
||||
msgstr "Haruskah \"{}\" menjadi superuser (sudo)?"
|
||||
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr "Pilih partisi mana yang akan dienkripsi:"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Pilih partisi mana yang akan dienkripsi"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr "sangat lemah"
|
||||
|
|
|
|||
|
|
@ -763,8 +763,8 @@ msgstr "Il nome utente inserito non è valido. Riprova"
|
|||
msgid "Should \"{}\" be a superuser (sudo)?"
|
||||
msgstr "\"{}\" dovrebbe essere un superutente? (sudo)"
|
||||
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr "Seleziona le partizioni da crittografare:"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Seleziona le partizioni da crittografare"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr "molto debole"
|
||||
|
|
|
|||
|
|
@ -791,11 +791,8 @@ msgid "Should \"{}\" be a superuser (sudo)?"
|
|||
msgstr "Moet {} gebruiker een beheerder (sudoer) worden?"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr ""
|
||||
"{}\n"
|
||||
"\n"
|
||||
"Kies welke partitie moet worden versleuteld"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Kies welke partitie moet worden versleuteld"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -763,11 +763,9 @@ msgstr "Wprowadzona nazwa użytkownika jest nieprawidłowa. Spróbuj ponownie"
|
|||
msgid "Should \"{}\" be a superuser (sudo)?"
|
||||
msgstr "Czy \"{}\" powinien być superuserem (sudo)?"
|
||||
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr ""
|
||||
"{}\n"
|
||||
"\n"
|
||||
"Wybierz partycje, które mają zostać zaszyfrowane:"
|
||||
#, fuzzy
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Wybierz partycja która ma zostać zaszyfrowana"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr "bardzo słabe"
|
||||
|
|
|
|||
|
|
@ -811,11 +811,8 @@ msgid "Should \"{}\" be a superuser (sudo)?"
|
|||
msgstr "Deve {} ser um superutilizador (sudoer)?"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr ""
|
||||
"{}\n"
|
||||
"\n"
|
||||
"Seleciona a partição a marcar como encriptada"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Seleciona a partição a marcar como encriptada"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -761,8 +761,8 @@ msgstr "O nome de usuário que você digitou é inválido. Tente novamente"
|
|||
msgid "Should \"{}\" be a superuser (sudo)?"
|
||||
msgstr "\"{}\" deve ser um superusuário (sudo)?"
|
||||
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr "Selecione quais partições encriptar:"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Selecione quais partições encriptar"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr "muito fraca"
|
||||
|
|
|
|||
|
|
@ -764,8 +764,8 @@ msgstr "Введенное вами имя пользователя недейс
|
|||
msgid "Should \"{}\" be a superuser (sudo)?"
|
||||
msgstr "Должен ли \"{}\" быть суперпользователем (sudo)?"
|
||||
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr "Выберите разделы для шифрования:"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Выберите разделы для шифрования"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr "очень слабый"
|
||||
|
|
|
|||
|
|
@ -772,11 +772,8 @@ msgid "Should \"{}\" be a superuser (sudo)?"
|
|||
msgstr "Är detta en superanvändare (sudo-rättigheter)?"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr ""
|
||||
"{}\n"
|
||||
"\n"
|
||||
"Välj vilken partition som skall markeras för kryptering"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Välj vilken partition som skall markeras för kryptering"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -763,8 +763,8 @@ msgstr "நீங்கள் உள்ளிட்ட பயனர்பெய
|
|||
msgid "Should \"{}\" be a superuser (sudo)?"
|
||||
msgstr "\"{}\" ஒரு சூப்பர் யூசராக (sudo) இருக்க வேண்டுமா?"
|
||||
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr "குறியாக்கம் செய்ய வேண்டிய பகிர்வுகளைத் தேர்ந்தெடுக்கவும்:"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "குறியாக்கம் செய்ய வேண்டிய பகிர்வுகளைத் தேர்ந்தெடுக்கவும்"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr "மிகவும் பலவீனமானது"
|
||||
|
|
|
|||
|
|
@ -772,11 +772,8 @@ msgid "Should \"{}\" be a superuser (sudo)?"
|
|||
msgstr "{} bir süper kullanıcı (sudoer) olmalı mı?"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr ""
|
||||
"{}\n"
|
||||
"\n"
|
||||
"Hangi disk bölümünün şifrelenmiş olarak işaretleneceğini seçin"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "Hangi disk bölümünün şifrelenmiş olarak işaretleneceğini seçin"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -794,11 +794,8 @@ msgid "Should \"{}\" be a superuser (sudo)?"
|
|||
msgstr "کیا {} کو سپر یوزر (sudoer) ہونا چاہیے؟"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select which partitions to encrypt:"
|
||||
msgstr ""
|
||||
"{}\n"
|
||||
"\n"
|
||||
"منتخب کریں کہ کس پارٹیشن کو انکرپٹڈ یا خفیہ رکھنا ہے"
|
||||
msgid "Select which partitions to encrypt"
|
||||
msgstr "منتخب کریں کہ کس پارٹیشن کو انکرپٹڈ یا خفیہ رکھنا ہے"
|
||||
|
||||
msgid "very weak"
|
||||
msgstr ""
|
||||
|
|
|
|||
|
|
@ -54,8 +54,8 @@ def ask_user_questions():
|
|||
|
||||
global_menu.enable('disk_layouts')
|
||||
|
||||
# Get disk encryption password (or skip if blank)
|
||||
global_menu.enable('!encryption-password')
|
||||
# Specify disk encryption options
|
||||
global_menu.enable('disk_encryption')
|
||||
|
||||
if archinstall.arguments.get('advanced', False) or archinstall.arguments.get('HSM', None):
|
||||
# Enables the use of HSM
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class OnlyHDMenu(archinstall.GlobalMenu):
|
|||
super()._setup_selection_menu_options()
|
||||
options_list = []
|
||||
mandatory_list = []
|
||||
options_list = ['harddrives', 'disk_layouts', '!encryption-password','swap']
|
||||
options_list = ['harddrives', 'disk_layouts', 'disk_encryption','swap']
|
||||
mandatory_list = ['harddrives']
|
||||
options_list.extend(['save_config','install','abort'])
|
||||
|
||||
|
|
|
|||
|
|
@ -153,7 +153,7 @@ def select_installed_locale(mode):
|
|||
_menus
|
||||
"""
|
||||
|
||||
class SetupMenu(archinstall.GeneralMenu):
|
||||
class SetupMenu(archinstall.AbstractMenu):
|
||||
def __init__(self,storage_area):
|
||||
super().__init__(data_store=storage_area)
|
||||
|
||||
|
|
@ -230,14 +230,14 @@ class MyMenu(archinstall.GlobalMenu):
|
|||
mandatory_list = []
|
||||
if self._execution_mode in ('full','lineal'):
|
||||
options_list = ['keyboard-layout', 'mirror-region', 'harddrives', 'disk_layouts',
|
||||
'!encryption-password','swap', 'bootloader', 'hostname', '!root-password',
|
||||
'disk_encryption','swap', 'bootloader', 'hostname', '!root-password',
|
||||
'!users', 'profile', 'audio', 'kernels', 'packages','additional-repositories','nic',
|
||||
'timezone', 'ntp']
|
||||
if archinstall.arguments.get('advanced',False):
|
||||
options_list.extend(['sys-language','sys-encoding'])
|
||||
mandatory_list = ['harddrives','bootloader','hostname']
|
||||
elif self._execution_mode == 'only_hd':
|
||||
options_list = ['harddrives', 'disk_layouts', '!encryption-password','swap']
|
||||
options_list = ['harddrives', 'disk_layouts', 'disk_encryption','swap']
|
||||
mandatory_list = ['harddrives']
|
||||
elif self._execution_mode == 'only_os':
|
||||
options_list = ['keyboard-layout', 'mirror-region','bootloader', 'hostname',
|
||||
|
|
|
|||
Loading…
Reference in New Issue