1{ pkgs, lib, ... }:
2
3let
4 user = "alice"; # from ./common/user-account.nix
5 password = "foobar"; # from ./common/user-account.nix
6in
7{
8 name = "cockpit";
9 meta = {
10 maintainers = with lib.maintainers; [ lucasew ];
11 };
12 nodes = {
13 server =
14 { config, ... }:
15 {
16 imports = [ ./common/user-account.nix ];
17 security.polkit.enable = true;
18 users.users.${user} = {
19 extraGroups = [ "wheel" ];
20 };
21 services.cockpit = {
22 enable = true;
23 port = 7890;
24 openFirewall = true;
25 allowed-origins = [
26 "https://server:${toString config.services.cockpit.port}"
27 ];
28 };
29 };
30 client =
31 { config, ... }:
32 {
33 imports = [ ./common/user-account.nix ];
34 environment.systemPackages =
35 let
36 seleniumScript =
37 pkgs.writers.writePython3Bin "selenium-script"
38 {
39 libraries = with pkgs.python3Packages; [ selenium ];
40 }
41 ''
42 from selenium import webdriver
43 from selenium.webdriver.common.by import By
44 from selenium.webdriver.firefox.options import Options
45 from selenium.webdriver.support.ui import WebDriverWait
46 from selenium.webdriver.support import expected_conditions as EC
47 from time import sleep
48
49
50 def log(msg):
51 from sys import stderr
52 print(f"[*] {msg}", file=stderr)
53
54
55 log("Initializing")
56
57 options = Options()
58 options.add_argument("--headless")
59
60 service = webdriver.FirefoxService(executable_path="${lib.getExe pkgs.geckodriver}") # noqa: E501
61 driver = webdriver.Firefox(options=options, service=service)
62
63 driver.implicitly_wait(10)
64
65 log("Opening homepage")
66 driver.get("https://server:7890")
67
68
69 def wait_elem(by, query, timeout=10):
70 wait = WebDriverWait(driver, timeout)
71 wait.until(EC.presence_of_element_located((by, query)))
72
73
74 def wait_title_contains(title, timeout=10):
75 wait = WebDriverWait(driver, timeout)
76 wait.until(EC.title_contains(title))
77
78
79 def find_element(by, query):
80 return driver.find_element(by, query)
81
82
83 def set_value(elem, value):
84 script = 'arguments[0].value = arguments[1]'
85 return driver.execute_script(script, elem, value)
86
87
88 log("Waiting for the homepage to load")
89
90 # cockpit sets initial title as hostname
91 wait_title_contains("server")
92 wait_elem(By.CSS_SELECTOR, 'input#login-user-input')
93
94 log("Homepage loaded!")
95
96 log("Filling out username")
97 login_input = find_element(By.CSS_SELECTOR, 'input#login-user-input')
98 set_value(login_input, "${user}")
99
100 log("Filling out password")
101 password_input = find_element(By.CSS_SELECTOR, 'input#login-password-input')
102 set_value(password_input, "${password}")
103
104 log("Submitting credentials for login")
105 driver.find_element(By.CSS_SELECTOR, 'button#login-button').click()
106
107 # driver.implicitly_wait(1)
108 # driver.get("https://server:7890/system")
109
110 log("Waiting dashboard to load")
111 wait_title_contains("${user}@server")
112
113 log("Waiting for the frontend to initialize")
114 sleep(1)
115
116 log("Looking for that banner that tells about limited access")
117 container_iframe = find_element(By.CSS_SELECTOR, 'iframe.container-frame')
118 driver.switch_to.frame(container_iframe)
119
120 assert "Web console is running in limited access mode" in driver.page_source
121
122 log("Clicking the sudo button")
123 for button in driver.find_elements(By.TAG_NAME, "button"):
124 if 'admin' in button.text:
125 button.click()
126 driver.switch_to.default_content()
127
128 log("Checking that /nonexistent is not a thing")
129 assert '/nonexistent' not in driver.page_source
130 assert len(driver.find_elements(By.CSS_SELECTOR, '#machine-reconnect')) == 0
131
132 driver.close()
133 '';
134 in
135 with pkgs;
136 [
137 firefox-unwrapped
138 geckodriver
139 seleniumScript
140 ];
141 };
142 };
143
144 testScript = ''
145 start_all()
146
147 server.wait_for_unit("sockets.target")
148 server.wait_for_open_port(7890)
149
150 client.succeed("curl -k https://server:7890 -o /dev/stderr")
151 print(client.succeed("whoami"))
152 client.succeed('PYTHONUNBUFFERED=1 selenium-script')
153 '';
154}