🏡 index : quine-global/quine-anaconda-webui.git

# Copyright (C) 2022 Red Hat, Inc.
# SPDX-License-Identifier: LGPL-2.1-or-later

import os
import sys

from testlib import Error  # pylint: disable=import-error

HELPERS_DIR = os.path.dirname(__file__)
sys.path.append(HELPERS_DIR)

from password import Password
from step_logger import log_step
from steps import ACCOUNTS

USERS_SERVICE = "org.fedoraproject.Anaconda.Modules.Users"
USERS_INTERFACE = USERS_SERVICE
USERS_OBJECT_PATH = "/org/fedoraproject/Anaconda/Modules/Users"

ACCOUNTS_SCREEN = ACCOUNTS
CREATE_ACCOUNT_ID_PREFIX = f"{ACCOUNTS_SCREEN}-create-account"
ROOT_ACCOUNT_ID_PREFIX = f"{ACCOUNTS_SCREEN}-root-account"

# Read-only mode (kickstart-defined users): data-testid selectors
ACCOUNTS_READONLY_USERS = "[data-testid='accounts-users-readonly']"
ACCOUNTS_READONLY_USER_ROW = "accounts-users-readonly-user"


def override_user_interface_conf_keys(test, **kwargs):
    """Patch `[User Interface]` boolean keys in `/run/anaconda/anaconda.conf` (single restore)."""
    conf = '/run/anaconda/anaconda.conf'
    test.restore_file(conf)
    for key, value in kwargs.items():
        text = value if isinstance(value, str) else str(value)
        if "\n" in text:
            lines = [line.strip() for line in text.strip().splitlines() if line.strip()]
            sed_text = "\\\n".join([f"{key} =", *[f"    {line}" for line in lines]])
            test.machine.execute(f"sed -i '/^{key} =/,/^$/c\\\n{sed_text}' {conf}")
        else:
            test.machine.execute(f"sed -i '/^{key} =/c\\{key} = {text}' {conf}")


class UsersDBus():
    def __init__(self, machine):
        self.machine = machine
        self._bus_address = self.machine.execute("cat /run/anaconda/bus.address")

    def dbus_get_users(self):
        return self.machine.execute(
            f'busctl --address="{self._bus_address}" \
            get-property  \
            {USERS_SERVICE} \
            {USERS_OBJECT_PATH} \
            {USERS_INTERFACE} Users'
        )

    def dbus_clear_users(self):
        self.machine.execute(f'busctl --address="{self._bus_address}" \
            set-property  \
            {USERS_SERVICE} \
            {USERS_OBJECT_PATH} \
            {USERS_INTERFACE} Users aa{{sv}} 0')

    def dbus_get_root_locked(self):
        ret = self.machine.execute(f'busctl --address="{self._bus_address}" \
            get-property  \
            {USERS_SERVICE} \
            {USERS_OBJECT_PATH} \
            {USERS_INTERFACE} IsRootAccountLocked')

        return ret.split()[1].strip() == "true"

    def dbus_set_root_locked(self, locked):
        self.machine.execute(f'busctl --address="{self._bus_address}" \
            set-property  \
            {USERS_SERVICE} \
            {USERS_OBJECT_PATH} \
            {USERS_INTERFACE} IsRootAccountLocked b {"true" if locked else "false"}')

    def dbus_get_is_root_password_set(self):
        ret = self.machine.execute(f'busctl --address="{self._bus_address}" \
            get-property  \
            {USERS_SERVICE} \
            {USERS_OBJECT_PATH} \
            {USERS_INTERFACE} IsRootPasswordSet')

        return ret.split()[1].strip() == "true"


class Users(UsersDBus):
    def __init__(self, browser, machine):
        self.browser = browser

        UsersDBus.__init__(self, machine)

    @log_step(snapshot_before=True)
    def set_user_name(self, user_name, append=False, value_check=True):
        sel = f"#{ACCOUNTS_SCREEN}-create-account-user-name"
        self.browser.set_input_text(sel, user_name, append=append, value_check=value_check)

    @log_step(snapshot_before=True)
    def check_user_name(self, user_name):
        sel = f"#{ACCOUNTS_SCREEN}-create-account-user-name"
        self.browser.wait_val(sel, user_name)

    @log_step(snapshot_before=True)
    def set_full_name(self, full_name, append=False, value_check=True):
        sel = f"#{ACCOUNTS_SCREEN}-create-account-full-name"
        self.browser.set_input_text(sel, full_name, append=append, value_check=value_check)

    @log_step(snapshot_before=True)
    def check_full_name(self, full_name):
        sel = f"#{ACCOUNTS_SCREEN}-create-account-full-name"
        self.browser.wait_val(sel, full_name)

    @log_step(snapshot_before=True)
    def enable_root_account(self, enable):
        sel = f"#{ACCOUNTS_SCREEN}-root-account-enable-root-account"
        # FIXME:
        # Tests are sometimes failing to enable the root account for unknown reasons.
        # Let's be resilient and try to set the checkbox twice.
        try:
            self.browser.set_checked(sel, enable)
            self.browser.wait_visible(f"{sel}:checked" if enable else f"{sel}:not(:checked)")
        except Error:
            self.browser.set_checked(sel, enable)
            self.browser.wait_visible(f"{sel}:checked" if enable else f"{sel}:not(:checked)")

        if enable:
            # Wait that the root password field is visible and focused
            p = Password(self.browser, ROOT_ACCOUNT_ID_PREFIX)
            p.check_pw_focused()

    @log_step(snapshot_before=True)
    def enable_user_account(self, enable):
        sel = f"#{ACCOUNTS_SCREEN}-create-account-set-user-account"
        self.browser.set_checked(sel, enable)

    def set_valid_root_password(self, valid=True):
        p = Password(self.browser, ROOT_ACCOUNT_ID_PREFIX)
        password = "password"
        p.set_password(password)
        p.set_password_confirm(password if valid else "X")

    # Read-only mode (kickstart-defined users)

    def check_readonly_users_section_visible(self):
        """Assert the Accounts step shows the read-only user summary (kickstart-defined users)."""
        self.browser.wait_visible(ACCOUNTS_READONLY_USERS)

    def check_readonly_user_displayed(self, username, full_name=None):
        """
        Assert a user is listed in the read-only summary.
        Optionally check the displayed text: 'Full Name (username)' or 'username'.
        """
        sel = f"[data-testid='{ACCOUNTS_READONLY_USER_ROW}-{username}']"
        self.browser.wait_visible(sel)
        if full_name is not None:
            expected = f"{full_name} ({username})"
            self.browser.wait_text(sel, expected)


def create_user(browser, machine):
    p = Password(browser, CREATE_ACCOUNT_ID_PREFIX)
    u = Users(browser, machine)

    password = "password"
    p.set_password(password)
    p.set_password_confirm(password)
    u.set_user_name("tester")