Loading nixos/modules/programs/ydotool.nix +17 −8 Original line number Diff line number Diff line Loading @@ -14,23 +14,32 @@ in options.programs.ydotool = { enable = lib.mkEnableOption '' ydotoold system service and install ydotool. Add yourself to the 'ydotool' group to be able to use it. ydotoold system service and {command}`ydotool` for members of {option}`programs.ydotool.group`. ''; group = lib.mkOption { type = lib.types.str; default = "ydotool"; description = '' Group which users must be in to use {command}`ydotool`. ''; }; }; config = lib.mkIf cfg.enable { users.groups.ydotool = { }; config = let runtimeDirectory = "ydotoold"; in lib.mkIf cfg.enable { users.groups."${config.programs.ydotool.group}" = { }; systemd.services.ydotoold = { description = "ydotoold - backend for ydotool"; wantedBy = [ "multi-user.target" ]; partOf = [ "multi-user.target" ]; serviceConfig = { Group = "ydotool"; RuntimeDirectory = "ydotoold"; Group = config.programs.ydotool.group; RuntimeDirectory = runtimeDirectory; RuntimeDirectoryMode = "0750"; ExecStart = "${lib.getExe' pkgs.ydotool "ydotoold"} --socket-path=/run/ydotoold/socket --socket-perm=0660"; ExecStart = "${lib.getExe' pkgs.ydotool "ydotoold"} --socket-path=${config.environment.variables.YDOTOOL_SOCKET} --socket-perm=0660"; # hardening Loading Loading @@ -76,7 +85,7 @@ in }; environment.variables = { YDOTOOL_SOCKET = "/run/ydotoold/socket"; YDOTOOL_SOCKET = "/run/${runtimeDirectory}/socket"; }; environment.systemPackages = with pkgs; [ ydotool ]; }; Loading nixos/tests/ydotool.nix +163 −94 Original line number Diff line number Diff line import ./make-test-python.nix ( { pkgs, lib, ... }: { system ? builtins.currentSystem, config ? { }, pkgs ? import ../.. { inherit system config; }, lib ? pkgs.lib, }: let makeTest = import ./make-test-python.nix; textInput = "This works."; inputBoxText = "Enter input"; inputBox = pkgs.writeShellScript "zenity-input" '' ${lib.getExe pkgs.gnome.zenity} --entry --text '${inputBoxText}:' > /tmp/output & ''; asUser = '' def as_user(cmd: str): """ Return a shell command for running a shell command as a specific user. """ return f"sudo -u alice -i {cmd}" ''; in { name = "ydotool"; headless = makeTest { name = "headless"; meta = { maintainers = with lib.maintainers; [ OPNA2608 quantenzitrone ]; }; enableOCR = true; nodes = { headless = { config, ... }: { nodes.machine = { imports = [ ./common/user-account.nix ]; users.users.alice.extraGroups = [ "ydotool" ]; Loading @@ -30,9 +35,32 @@ import ./make-test-python.nix ( services.getty.autologinUser = "alice"; }; x11 = { config, ... }: { testScript = asUser + '' start_all() machine.wait_for_unit("multi-user.target") machine.wait_for_text("alice") machine.succeed(as_user("ydotool type 'echo ${textInput} > /tmp/output'")) # text input machine.succeed(as_user("ydotool key 28:1 28:0")) # text input machine.screenshot("headless_input") machine.wait_for_file("/tmp/output") machine.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input ''; meta.maintainers = with lib.maintainers; [ OPNA2608 quantenzitrone ]; }; x11 = makeTest { name = "x11"; enableOCR = true; nodes.machine = { imports = [ ./common/user-account.nix ./common/auto.nix Loading @@ -52,9 +80,34 @@ import ./make-test-python.nix ( services.displayManager.defaultSession = lib.mkForce "none+dwm"; }; wayland = { config, ... }: { testScript = asUser + '' start_all() machine.wait_for_x() machine.execute(as_user("${inputBox}")) machine.wait_for_text("${inputBoxText}") machine.succeed(as_user("ydotool type '${textInput}'")) # text input machine.screenshot("x11_input") machine.succeed(as_user("ydotool mousemove -a 400 110")) # mouse input machine.succeed(as_user("ydotool click 0xC0")) # mouse input machine.wait_for_file("/tmp/output") machine.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input ''; meta.maintainers = with lib.maintainers; [ OPNA2608 quantenzitrone ]; }; wayland = makeTest { name = "wayland"; enableOCR = true; nodes.machine = { imports = [ ./common/user-account.nix ]; services.cage = { Loading @@ -66,50 +119,66 @@ import ./make-test-python.nix ( services.cage.program = inputBox; }; testScript = '' start_all() machine.wait_for_unit("graphical.target") machine.wait_for_text("${inputBoxText}") machine.succeed("ydotool type '${textInput}'") # text input machine.screenshot("wayland_input") machine.succeed("ydotool mousemove -a 100 100") # mouse input machine.succeed("ydotool click 0xC0") # mouse input machine.wait_for_file("/tmp/output") machine.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input ''; meta.maintainers = with lib.maintainers; [ OPNA2608 quantenzitrone ]; }; enableOCR = true; customGroup = let name = "customGroup"; nodeName = "${name}Node"; insideGroupUsername = "ydotool-user"; outsideGroupUsername = "other-user"; groupName = "custom-group"; in makeTest { inherit name; testScript = { nodes, ... }: '' def as_user(cmd: str): """ Return a shell command for running a shell command as a specific user. """ return f"sudo -u alice -i {cmd}" nodes."${nodeName}" = { programs.ydotool = { enable = true; group = groupName; }; users.users = { "${insideGroupUsername}" = { isNormalUser = true; extraGroups = [ groupName ]; }; "${outsideGroupUsername}".isNormalUser = true; }; }; testScript = '' start_all() # Headless headless.wait_for_unit("multi-user.target") headless.wait_for_text("alice") headless.succeed(as_user("ydotool type 'echo ${textInput} > /tmp/output'")) # text input headless.succeed(as_user("ydotool key 28:1 28:0")) # text input headless.screenshot("headless_input") headless.wait_for_file("/tmp/output") headless.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input # X11 x11.wait_for_x() x11.execute(as_user("${inputBox}")) x11.wait_for_text("${inputBoxText}") x11.succeed(as_user("ydotool type '${textInput}'")) # text input x11.screenshot("x11_input") x11.succeed(as_user("ydotool mousemove -a 400 110")) # mouse input x11.succeed(as_user("ydotool click 0xC0")) # mouse input x11.wait_for_file("/tmp/output") x11.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input # Wayland wayland.wait_for_unit("graphical.target") wayland.wait_for_text("${inputBoxText}") wayland.succeed("ydotool type '${textInput}'") # text input wayland.screenshot("wayland_input") wayland.succeed("ydotool mousemove -a 100 100") # mouse input wayland.succeed("ydotool click 0xC0") # mouse input wayland.wait_for_file("/tmp/output") wayland.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input # Wait for service to start ${nodeName}.wait_for_unit("multi-user.target") ${nodeName}.wait_for_unit("ydotoold.service") # Verify that user with the configured group can use the service ${nodeName}.succeed("sudo --login --user=${insideGroupUsername} ydotool type 'Hello, World!'") # Verify that user without the configured group can't use the service ${nodeName}.fail("sudo --login --user=${outsideGroupUsername} ydotool type 'Hello, World!'") ''; meta.maintainers = with lib.maintainers; [ l0b0 ]; }; } ) Loading
nixos/modules/programs/ydotool.nix +17 −8 Original line number Diff line number Diff line Loading @@ -14,23 +14,32 @@ in options.programs.ydotool = { enable = lib.mkEnableOption '' ydotoold system service and install ydotool. Add yourself to the 'ydotool' group to be able to use it. ydotoold system service and {command}`ydotool` for members of {option}`programs.ydotool.group`. ''; group = lib.mkOption { type = lib.types.str; default = "ydotool"; description = '' Group which users must be in to use {command}`ydotool`. ''; }; }; config = lib.mkIf cfg.enable { users.groups.ydotool = { }; config = let runtimeDirectory = "ydotoold"; in lib.mkIf cfg.enable { users.groups."${config.programs.ydotool.group}" = { }; systemd.services.ydotoold = { description = "ydotoold - backend for ydotool"; wantedBy = [ "multi-user.target" ]; partOf = [ "multi-user.target" ]; serviceConfig = { Group = "ydotool"; RuntimeDirectory = "ydotoold"; Group = config.programs.ydotool.group; RuntimeDirectory = runtimeDirectory; RuntimeDirectoryMode = "0750"; ExecStart = "${lib.getExe' pkgs.ydotool "ydotoold"} --socket-path=/run/ydotoold/socket --socket-perm=0660"; ExecStart = "${lib.getExe' pkgs.ydotool "ydotoold"} --socket-path=${config.environment.variables.YDOTOOL_SOCKET} --socket-perm=0660"; # hardening Loading Loading @@ -76,7 +85,7 @@ in }; environment.variables = { YDOTOOL_SOCKET = "/run/ydotoold/socket"; YDOTOOL_SOCKET = "/run/${runtimeDirectory}/socket"; }; environment.systemPackages = with pkgs; [ ydotool ]; }; Loading
nixos/tests/ydotool.nix +163 −94 Original line number Diff line number Diff line import ./make-test-python.nix ( { pkgs, lib, ... }: { system ? builtins.currentSystem, config ? { }, pkgs ? import ../.. { inherit system config; }, lib ? pkgs.lib, }: let makeTest = import ./make-test-python.nix; textInput = "This works."; inputBoxText = "Enter input"; inputBox = pkgs.writeShellScript "zenity-input" '' ${lib.getExe pkgs.gnome.zenity} --entry --text '${inputBoxText}:' > /tmp/output & ''; asUser = '' def as_user(cmd: str): """ Return a shell command for running a shell command as a specific user. """ return f"sudo -u alice -i {cmd}" ''; in { name = "ydotool"; headless = makeTest { name = "headless"; meta = { maintainers = with lib.maintainers; [ OPNA2608 quantenzitrone ]; }; enableOCR = true; nodes = { headless = { config, ... }: { nodes.machine = { imports = [ ./common/user-account.nix ]; users.users.alice.extraGroups = [ "ydotool" ]; Loading @@ -30,9 +35,32 @@ import ./make-test-python.nix ( services.getty.autologinUser = "alice"; }; x11 = { config, ... }: { testScript = asUser + '' start_all() machine.wait_for_unit("multi-user.target") machine.wait_for_text("alice") machine.succeed(as_user("ydotool type 'echo ${textInput} > /tmp/output'")) # text input machine.succeed(as_user("ydotool key 28:1 28:0")) # text input machine.screenshot("headless_input") machine.wait_for_file("/tmp/output") machine.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input ''; meta.maintainers = with lib.maintainers; [ OPNA2608 quantenzitrone ]; }; x11 = makeTest { name = "x11"; enableOCR = true; nodes.machine = { imports = [ ./common/user-account.nix ./common/auto.nix Loading @@ -52,9 +80,34 @@ import ./make-test-python.nix ( services.displayManager.defaultSession = lib.mkForce "none+dwm"; }; wayland = { config, ... }: { testScript = asUser + '' start_all() machine.wait_for_x() machine.execute(as_user("${inputBox}")) machine.wait_for_text("${inputBoxText}") machine.succeed(as_user("ydotool type '${textInput}'")) # text input machine.screenshot("x11_input") machine.succeed(as_user("ydotool mousemove -a 400 110")) # mouse input machine.succeed(as_user("ydotool click 0xC0")) # mouse input machine.wait_for_file("/tmp/output") machine.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input ''; meta.maintainers = with lib.maintainers; [ OPNA2608 quantenzitrone ]; }; wayland = makeTest { name = "wayland"; enableOCR = true; nodes.machine = { imports = [ ./common/user-account.nix ]; services.cage = { Loading @@ -66,50 +119,66 @@ import ./make-test-python.nix ( services.cage.program = inputBox; }; testScript = '' start_all() machine.wait_for_unit("graphical.target") machine.wait_for_text("${inputBoxText}") machine.succeed("ydotool type '${textInput}'") # text input machine.screenshot("wayland_input") machine.succeed("ydotool mousemove -a 100 100") # mouse input machine.succeed("ydotool click 0xC0") # mouse input machine.wait_for_file("/tmp/output") machine.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input ''; meta.maintainers = with lib.maintainers; [ OPNA2608 quantenzitrone ]; }; enableOCR = true; customGroup = let name = "customGroup"; nodeName = "${name}Node"; insideGroupUsername = "ydotool-user"; outsideGroupUsername = "other-user"; groupName = "custom-group"; in makeTest { inherit name; testScript = { nodes, ... }: '' def as_user(cmd: str): """ Return a shell command for running a shell command as a specific user. """ return f"sudo -u alice -i {cmd}" nodes."${nodeName}" = { programs.ydotool = { enable = true; group = groupName; }; users.users = { "${insideGroupUsername}" = { isNormalUser = true; extraGroups = [ groupName ]; }; "${outsideGroupUsername}".isNormalUser = true; }; }; testScript = '' start_all() # Headless headless.wait_for_unit("multi-user.target") headless.wait_for_text("alice") headless.succeed(as_user("ydotool type 'echo ${textInput} > /tmp/output'")) # text input headless.succeed(as_user("ydotool key 28:1 28:0")) # text input headless.screenshot("headless_input") headless.wait_for_file("/tmp/output") headless.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input # X11 x11.wait_for_x() x11.execute(as_user("${inputBox}")) x11.wait_for_text("${inputBoxText}") x11.succeed(as_user("ydotool type '${textInput}'")) # text input x11.screenshot("x11_input") x11.succeed(as_user("ydotool mousemove -a 400 110")) # mouse input x11.succeed(as_user("ydotool click 0xC0")) # mouse input x11.wait_for_file("/tmp/output") x11.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input # Wayland wayland.wait_for_unit("graphical.target") wayland.wait_for_text("${inputBoxText}") wayland.succeed("ydotool type '${textInput}'") # text input wayland.screenshot("wayland_input") wayland.succeed("ydotool mousemove -a 100 100") # mouse input wayland.succeed("ydotool click 0xC0") # mouse input wayland.wait_for_file("/tmp/output") wayland.wait_until_succeeds("grep '${textInput}' /tmp/output") # text input # Wait for service to start ${nodeName}.wait_for_unit("multi-user.target") ${nodeName}.wait_for_unit("ydotoold.service") # Verify that user with the configured group can use the service ${nodeName}.succeed("sudo --login --user=${insideGroupUsername} ydotool type 'Hello, World!'") # Verify that user without the configured group can't use the service ${nodeName}.fail("sudo --login --user=${outsideGroupUsername} ydotool type 'Hello, World!'") ''; meta.maintainers = with lib.maintainers; [ l0b0 ]; }; } )