Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions archinstall/lib/disk/device_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,12 +271,15 @@ def format(
options = []

match fs_type:
case FilesystemType.Btrfs | FilesystemType.F2fs | FilesystemType.Xfs:
case FilesystemType.Btrfs | FilesystemType.Xfs:
# Force overwrite
options.append('-f')
case FilesystemType.F2fs:
# Force overwrite and enable encrypt
options.extend(('-f', '-O', 'encrypt'))
case FilesystemType.Ext2 | FilesystemType.Ext3 | FilesystemType.Ext4:
# Force create
options.append('-F')
# Force create and enable encrypt
options.extend(('-F', '-O', 'encrypt'))
case FilesystemType.Fat12 | FilesystemType.Fat16 | FilesystemType.Fat32:
mkfs_type = 'fat'
# Set FAT size
Expand Down
45 changes: 33 additions & 12 deletions archinstall/lib/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

from .args import arch_config_handler
from .exceptions import DiskError, HardwareIncompatibilityError, RequirementError, ServiceException, SysCallError
from .general import SysCommand, run
from .general import SysCommand, SysCommandWorker, run
from .hardware import SysInfo
from .locale.utils import verify_keyboard_layout, verify_x11_keyboard_layout
from .luks import Luks2
Expand Down Expand Up @@ -1701,27 +1701,48 @@ def _create_user(self, user: User) -> None:
if not handled_by_plugin:
info(f'Creating user {user.username}')

cmd = f'arch-chroot {self.target} useradd -m'
if user.homed is not None:
self.enable_service('systemd-homed')

if user.sudo:
cmd += ' -G wheel'
homed_groups = []
if user.sudo:
homed_groups.append('wheel')
homed_groups.extend(user.groups)

cmd += f' {user.username}'
cmd = f'homectl create --enforce-password-policy=no'
cmd += f' --member-of {",".join(homed_groups)}' if homed_groups else ''
cmd += f' --storage {user.homed.storage_mechanism}'
cmd += f' {user.username}'

try:
SysCommand(cmd)
except SysCallError as err:
raise SystemError(f'Could not create user inside installation: {err}')
from .boot import Boot

with Boot(self) as session:
worker = session.SysCommandWorker(cmd.split(" "))
while worker.is_alive():
worker.write(bytes(f'{user.password.plaintext}\n', 'UTF-8'), line_ending=False)
else:
cmd = f'arch-chroot {self.target} useradd -m'

if user.sudo:
cmd += ' -G wheel'

cmd += f' {user.username}'

try:
SysCommand(cmd)
except SysCallError as err:
raise SystemError(f'Could not create user inside installation: {err}')

for plugin in plugins.values():
if hasattr(plugin, 'on_user_created'):
if result := plugin.on_user_created(self, user):
handled_by_plugin = result

self.set_user_password(user)
if user.homed is None:
self.set_user_password(user)

for group in user.groups:
SysCommand(f'arch-chroot {self.target} gpasswd -a {user.username} {group}')
for group in user.groups:
SysCommand(f'arch-chroot {self.target} gpasswd -a {user.username} {group}')

if user.sudo:
self.enable_sudo(user)
Expand Down
53 changes: 50 additions & 3 deletions archinstall/lib/interactions/manage_users_conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
from archinstall.tui.curses_menu import EditMenu, SelectMenu
from archinstall.tui.menu_item import MenuItem, MenuItemGroup
from archinstall.tui.result import ResultType
from archinstall.tui.types import Alignment, Orientation
from archinstall.tui.types import Alignment, FrameProperties, Orientation

from ..menu.list_manager import ListManager
from ..models.users import User
from ..models.users import User, StorageMechanism, HomedConfiguration
from ..utils.util import get_password


Expand Down Expand Up @@ -111,8 +111,55 @@ def _add_user(self) -> User | None:
case _:
raise ValueError('Unhandled result type')

return User(username, password, sudo)
homed_configuration = self._configure_homed()

return User(username, password, sudo, homed_configuration)


def _configure_homed(self) -> HomedConfiguration | None:
header = str(tr('Should the user use systemd-homed?\n'))

group = MenuItemGroup.yes_no()
group.focus_item = MenuItem.no()

result = SelectMenu[bool](
group,
header=header,
alignment=Alignment.CENTER,
columns=2,
orientation=Orientation.HORIZONTAL,
search_enabled=False,
allow_skip=False,
).run()

match result.type_:
case ResultType.Selection:
if result.item() != MenuItem.yes():
return None
case _:
raise ValueError('Unhandled result type')

items = [
MenuItem('Directory', value=StorageMechanism.DIRECTORY),
MenuItem('LUKS', value=StorageMechanism.LUKS),
MenuItem('fscrypt', value=StorageMechanism.FSCRYPT),
]

group = MenuItemGroup(items, sort_items=False)
result = SelectMenu[StorageMechanism](
group,
alignment=Alignment.CENTER,
frame=FrameProperties.min('Filesystem'),
allow_skip=False,
).run()

match result.type_:
case ResultType.Selection:
return HomedConfiguration(result.get_value())
case _:
raise ValueError('Unhandled result type')

return None

def ask_for_additional_users(prompt: str = '', defined_users: list[User] = []) -> list[User]:
users = UserList(prompt, defined_users).run()
Expand Down
24 changes: 22 additions & 2 deletions archinstall/lib/models/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,28 @@ def _check_password_strength(
return PasswordStrength.VERY_WEAK



class StorageMechanism:
DIRECTORY = 'directory'
LUKS = 'luks'
FSCRYPT = 'fscrypt'


class HomedConfiguration:
def __init__(
self,
storage_mechanism: StorageMechanism,
):
self.storage_mechanism = storage_mechanism


UserSerialization = TypedDict(
'UserSerialization',
{
'username': str,
'!password': NotRequired[str],
'sudo': bool,
'homed': HomedConfiguration | None,
'groups': list[str],
'enc_password': str | None,
},
Expand Down Expand Up @@ -158,18 +174,20 @@ class User:
username: str
password: Password
sudo: bool
homed: HomedConfiguration | None = None
groups: list[str] = field(default_factory=list)

@override
def __str__(self) -> str:
# safety overwrite to make sure password is not leaked
return f'User({self.username=}, {self.sudo=}, {self.groups=})'
return f'User({self.username=}, {self.sudo=}, {self.homed=}, {self.groups=})'

def table_data(self) -> dict[str, str | bool | list[str]]:
def table_data(self) -> dict[str, str | bool | StorageMechanism | list[str]]:
return {
'username': self.username,
'password': self.password.hidden(),
'sudo': self.sudo,
'homed': self.homed.storage_mechanism if self.homed else False,
'groups': self.groups,
}

Expand All @@ -178,6 +196,7 @@ def json(self) -> UserSerialization:
'username': self.username,
'enc_password': self.password.enc_password,
'sudo': self.sudo,
'homed': self.homed,
'groups': self.groups,
}

Expand Down Expand Up @@ -208,6 +227,7 @@ def parse_arguments(
username=username,
password=password,
sudo=entry.get('sudo', False) is True,
homed=entry.get('homed', None),
groups=groups,
)

Expand Down