diff --git a/SCAutolib/cli_commands.py b/SCAutolib/cli_commands.py index 8ce3a34..a736fa7 100644 --- a/SCAutolib/cli_commands.py +++ b/SCAutolib/cli_commands.py @@ -17,6 +17,9 @@ def check_conf_path(conf): return click.Path(exists=True, resolve_path=True)(conf) +# In Help output, force the subcommand list to match the order +# listed in this file. Solution was found here: +# https://github.com/pallets/click/issues/513#issuecomment-301046782 class NaturalOrderGroup(click.Group): """ Command group trying to list subcommands in the order they were added. @@ -34,8 +37,8 @@ def __init__(self, name=None, commands=None, **attrs): elif not isinstance(commands, OrderedDict): commands = OrderedDict(commands) click.Group.__init__(self, name=name, - commands=commands, - **attrs) + commands=commands, + **attrs) def list_commands(self, ctx): """ @@ -64,8 +67,10 @@ def cli(ctx, force, verbose, conf): logger.setLevel(verbose) ctx.ensure_object(dict) # Create a dict to store the context ctx.obj["FORCE"] = force # Store the force option in the context - if ctx.invoked_subcommand and not gui: - ctx.obj["CONTROLLER"] = Controller(check_conf_path(conf)) + parsed_conf = None + if ctx.invoked_subcommand != "gui": + parsed_conf = check_conf_path(conf) + ctx.obj["CONTROLLER"] = Controller(parsed_conf) @cli.command() @@ -202,8 +207,13 @@ def cleanup(ctx): @cli.group(cls=NaturalOrderGroup, chain=True) +@click.option("--install-missing", "-i", + required=False, + default=False, + is_flag=True, + help="Install missing packages") @click.pass_context -def gui(ctx): +def gui(ctx, install_missing): """ Run GUI Test commands """ pass @@ -215,19 +225,19 @@ def init(): @gui.command() +@click.option("--no", + required=False, + default=False, + is_flag=True, + help="Reverse the action") @click.argument("name") -def assert_text(name): +def assert_text(name, no): """ Check if a word is found on the screen """ + if no: + return f"assert_no_text:{name}" return f"assert_text:{name}" -@gui.command() -@click.argument("name") -def assert_no_text(name): - """ Check that a word is not found on the screen """ - return f"assert_no_text:{name}" - - @gui.command() @click.argument("name") def click_on(name): @@ -236,9 +246,16 @@ def click_on(name): @gui.command() -def check_home_screen(): +@click.option("--no", + required=False, + default=False, + is_flag=True, + help="Reverse the action") +def check_home_screen(no): """ Check if screen appears to be the home screen """ - return f"check_home_screen" + if no: + return "check_no_home_screen" + return "check_home_screen" @gui.command() @@ -263,8 +280,9 @@ def done(): @gui.result_callback() @click.pass_context -def run_all(ctx, actions): - click.echo(actions) +def run_all(ctx, actions, install_missing): + """ Run all cli actions in order """ + ctx.obj["CONTROLLER"].setup_graphical(install_missing, True) from SCAutolib.models.gui import GUI gui = GUI() @@ -272,30 +290,28 @@ def run_all(ctx, actions): if "init" in action: gui.__enter__() if "assert_text" in action: - assert_text = action.split(":")[1] - click.echo(f"CLICK: assert_text:{assert_text}") + assert_text = action.split(":", 1)[1] gui.assert_text(assert_text) if "assert_no_text" in action: - assert_text = action.split(":")[1] - click.echo(f"CLICK: assert_text:{assert_text}") - gui.assert_text(assert_text) + assert_text = action.split(":", 1)[1] + gui.assert_no_text(assert_text) if "click_on" in action: - click_on = action.split(":")[1] - click.echo(f"CLICK: click_on:{click_on}") + click_on = action.split(":", 1)[1] gui.click_on(click_on) if "check_home_screen" in action: - click.echo("CLICK: check_home_screen") gui.check_home_screen() + if "check_no_home_screen" in action: + gui.check_home_screen(False) if "kb_send" in action: - params = action.split(":")[1].split() - click.echo(f"CLICK: kb_send:{params}") - gui.kb_send(*params) + params = action.split(":", 1)[1].split()[0] + gui.kb_send(params) if "kb_write" in action: - params = action.split(":")[1].split() - click.echo(f"CLICK: kb_write:{params}") - gui.kb_write(*params) + params = action.split(":", 1)[1].split()[0] + gui.kb_write(params) + gui.kb_send('enter') if "done" in action: gui.__exit__(None, None, None) + ctx.obj["CONTROLLER"].cleanup() if __name__ == "__main__": diff --git a/SCAutolib/controller.py b/SCAutolib/controller.py index 52266e6..9b8dece 100644 --- a/SCAutolib/controller.py +++ b/SCAutolib/controller.py @@ -34,7 +34,7 @@ class Controller: def conf_path(self): return self._lib_conf_path - def __init__(self, config: Union[Path, str], params: {} = None): + def __init__(self, config: Union[Path, str] = None, params: {} = None): """ Constructor will parse and check input configuration file. If some required fields in the configuration are missing, CLI parameters @@ -55,15 +55,18 @@ def __init__(self, config: Union[Path, str], params: {} = None): # Check params # Parse config file - self._lib_conf_path = config.absolute() if isinstance(config, Path) \ - else Path(config).absolute() - - with self._lib_conf_path.open("r") as f: - tmp_conf = json.load(f) - if tmp_conf is None: - raise exceptions.SCAutolibException( - "Data are not loaded correctly.") - self.lib_conf = self._validate_configuration(tmp_conf, params) + self.lib_conf = None + if config: + self._lib_conf_path = config.absolute() if isinstance(config, Path) \ + else Path(config).absolute() + + with self._lib_conf_path.open("r") as f: + tmp_conf = json.load(f) + if tmp_conf is None: + raise exceptions.SCAutolibException( + "Data are not loaded correctly.") + self.lib_conf = self._validate_configuration(tmp_conf, params) + self.users = [] for d in (LIB_DIR, LIB_BACKUP, LIB_DUMP, LIB_DUMP_USERS, LIB_DUMP_CAS, LIB_DUMP_CARDS, LIB_DUMP_CONFS): @@ -147,12 +150,6 @@ def setup_system(self, install_missing: bool, gdm: bool, graphical: bool): packages = ["opensc", "httpd", "sssd", "sssd-tools", "gnutls-utils", "openssl", "nss-tools"] - if gdm: - packages.append("gdm") - - if graphical: - # ffmpeg-free is in EPEL repo - packages += ["tesseract", "ffmpeg-free"] # Prepare for virtual cards if any(c["card_type"] == CardType.virtual @@ -181,21 +178,7 @@ def setup_system(self, install_missing: bool, gdm: bool, graphical: bool): raise exceptions.SCAutolibException(msg) if graphical: - if not isDistro('fedora'): - run(['dnf', 'groupinstall', 'Server with GUI', '-y', - '--allowerasing']) - run(['pip', 'install', 'python-uinput']) - else: - # Fedora doesn't have server with GUI group so installed gdm - # manually and also python3-uinput should be installed from RPM - run(['dnf', 'install', 'gdm', 'python3-uinput', '-y']) - # disable subscription message - run(['systemctl', '--global', 'mask', - 'org.gnome.SettingsDaemon.Subscription.target']) - # disable welcome message - self.dconf_file.create() - self.dconf_file.save() - run('dconf update') + self.setup_graphical(install_missing, gdm) if not isDistro('fedora'): run(['dnf', 'groupinstall', "Smart Card Support", '-y', @@ -216,6 +199,38 @@ def setup_system(self, install_missing: bool, gdm: bool, graphical: bool): dump_to_json(user.User(username="root", password=self.lib_conf["root_passwd"])) + def setup_graphical(self, install_missing: bool, gdm: bool): + packages = ["gcc", "tesseract", "ffmpeg-free"] + + if gdm: + packages.append("gdm") + + missing = _check_packages(packages) + if install_missing and missing: + _install_packages(missing) + elif missing: + msg = "Can't continue with graphical. Some packages are missing: " \ + f"{', '.join(missing)}" + logger.critical(msg) + raise exceptions.SCAutolibException(msg) + + if not isDistro('fedora'): + run(['dnf', 'groupinstall', 'Server with GUI', '-y', + '--allowerasing']) + run(['pip', 'install', 'python-uinput']) + else: + # Fedora doesn't have server with GUI group so installed gdm + # manually and also python3-uinput should be installed from RPM + run(['dnf', 'install', 'gdm', 'python3-uinput', '-y']) + # disable subscription message + run(['systemctl', '--global', 'mask', + 'org.gnome.SettingsDaemon.Subscription.target']) + # disable welcome message + if not self.dconf_file.exists(): + self.dconf_file.create() + self.dconf_file.save() + run('dconf update') + def setup_local_ca(self, force: bool = False): """ Setup local CA based on configuration from the configuration file. All diff --git a/SCAutolib/models/file.py b/SCAutolib/models/file.py index 91784a1..f3c170e 100644 --- a/SCAutolib/models/file.py +++ b/SCAutolib/models/file.py @@ -92,6 +92,12 @@ def remove(self): f"Removed file {self._conf_file}." ) + def exists(self): + """ + Checks if a file exists. Returns boolean. + """ + return self._conf_file.exists() + def set(self, key: str, value: Union[int, str, bool], section: str = None, separator: str = "="): """