Skip to content

Commit

Permalink
Merge pull request NixOS#317138 from l0b0/ydotool-group
Browse files Browse the repository at this point in the history
nixos/ydotool: Make group configurable
  • Loading branch information
drupol authored Jun 16, 2024
2 parents 31ec299 + 408406c commit 22279ca
Show file tree
Hide file tree
Showing 2 changed files with 180 additions and 102 deletions.
25 changes: 17 additions & 8 deletions nixos/modules/programs/ydotool.nix
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -76,7 +85,7 @@ in
};

environment.variables = {
YDOTOOL_SOCKET = "/run/ydotoold/socket";
YDOTOOL_SOCKET = "/run/${runtimeDirectory}/socket";
};
environment.systemPackages = with pkgs; [ ydotool ];
};
Expand Down
257 changes: 163 additions & 94 deletions nixos/tests/ydotool.nix
Original file line number Diff line number Diff line change
@@ -1,115 +1,184 @@
import ./make-test-python.nix (
{ pkgs, lib, ... }:
let
textInput = "This works.";
inputBoxText = "Enter input";
inputBox = pkgs.writeShellScript "zenity-input" ''
${lib.getExe pkgs.gnome.zenity} --entry --text '${inputBoxText}:' > /tmp/output &
'';
in
{
name = "ydotool";

meta = {
maintainers = with lib.maintainers; [
OPNA2608
quantenzitrone
];
};
{
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
{
headless = makeTest {
name = "headless";

nodes = {
headless =
{ config, ... }:
{
imports = [ ./common/user-account.nix ];
enableOCR = true;

users.users.alice.extraGroups = [ "ydotool" ];
nodes.machine = {
imports = [ ./common/user-account.nix ];

programs.ydotool.enable = true;
users.users.alice.extraGroups = [ "ydotool" ];

services.getty.autologinUser = "alice";
};
programs.ydotool.enable = true;

x11 =
{ config, ... }:
{
imports = [
./common/user-account.nix
./common/auto.nix
./common/x11.nix
];
services.getty.autologinUser = "alice";
};

users.users.alice.extraGroups = [ "ydotool" ];
testScript =
asUser
+ ''
start_all()
programs.ydotool.enable = true;
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
'';

test-support.displayManager.auto = {
enable = true;
user = "alice";
};
meta.maintainers = with lib.maintainers; [
OPNA2608
quantenzitrone
];
};

services.xserver.windowManager.dwm.enable = true;
services.displayManager.defaultSession = lib.mkForce "none+dwm";
};
x11 = makeTest {
name = "x11";

wayland =
{ config, ... }:
{
imports = [ ./common/user-account.nix ];
enableOCR = true;

services.cage = {
enable = true;
user = "alice";
};
nodes.machine = {
imports = [
./common/user-account.nix
./common/auto.nix
./common/x11.nix
];

programs.ydotool.enable = true;
users.users.alice.extraGroups = [ "ydotool" ];

services.cage.program = inputBox;
};
programs.ydotool.enable = true;

test-support.displayManager.auto = {
enable = true;
user = "alice";
};

services.xserver.windowManager.dwm.enable = true;
services.displayManager.defaultSession = lib.mkForce "none+dwm";
};

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;

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.machine = {
imports = [ ./common/user-account.nix ];

services.cage = {
enable = true;
user = "alice";
};

programs.ydotool.enable = true;

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
];
};

customGroup =
let
name = "customGroup";
nodeName = "${name}Node";
insideGroupUsername = "ydotool-user";
outsideGroupUsername = "other-user";
groupName = "custom-group";
in
makeTest {
inherit name;

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 ];
};
}

0 comments on commit 22279ca

Please sign in to comment.