Skip to content

Commit 6ead203

Browse files
committed
Refactor testPolkit to use XML output
1 parent bbe421d commit 6ead203

File tree

6 files changed

+219
-344
lines changed

6 files changed

+219
-344
lines changed
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package main
2+
3+
import (
4+
"log"
5+
"os"
6+
"os/user"
7+
"strconv"
8+
"syscall"
9+
10+
"testing"
11+
)
12+
13+
const (
14+
polkitRulesDir = "/etc/polkit-1/rules.d/"
15+
testRuleFile = "/etc/polkit-1/rules.d/42-integration-test.rules"
16+
newHostname = "polkit-test-hostname"
17+
testUser = "bernhard"
18+
)
19+
20+
type testCase struct {
21+
Name string
22+
Function func(t *testing.T)
23+
}
24+
25+
const polkitRule = `
26+
// This is an example. Do not use this in production.
27+
// This rule lets any user change the hostname without authentication
28+
polkit.addRule(function(action, subject) {
29+
if (action.id == "org.freedesktop.hostname1.set-static-hostname") {
30+
return polkit.Result.YES;
31+
}
32+
});
33+
`
34+
35+
func TestPolkit(t *testing.T) {
36+
log.Println("Starting Polkit integration test...")
37+
38+
// Save original hostname
39+
originalHostname, ok := saveOriginalHostname(t)
40+
if !ok {
41+
t.Error()
42+
return
43+
}
44+
testCases := []testCase{
45+
{"Check polkit rules directory permissions (root:polkitd)", checkPermissions},
46+
{"Add polkit rule and restart service", addRuleAndRestart},
47+
{"Change hostname without authentication", changeHostnameWithAuth},
48+
{"Verify hostname was changed", verifyHostnameChanged},
49+
{"Remove polkit rule and restart service", removeRuleAndRestart},
50+
{"Hostname change should fail without authentication", changeHostnameShouldFail},
51+
{"Verify hostname was not changed", verifyHostnameUnchanged},
52+
}
53+
54+
for _, tc := range testCases {
55+
t.Run(tc.Name, func(t *testing.T) {
56+
tc.Function(t)
57+
})
58+
}
59+
restoreHostname(originalHostname, t)
60+
}
61+
62+
// checkPermissions checks the permissions of the polkit rules directory
63+
func checkPermissions(t *testing.T) {
64+
log.Println("- Checking permissions for", polkitRulesDir)
65+
66+
info, err := os.Stat(polkitRulesDir)
67+
if err != nil {
68+
t.Fatalf("Could not stat %s: %v", polkitRulesDir, err)
69+
}
70+
71+
stat, ok := info.Sys().(*syscall.Stat_t)
72+
if !ok {
73+
t.Errorf("Could not get file stat for %s", polkitRulesDir)
74+
}
75+
76+
// Check owner is root (uid 0)
77+
if stat.Uid != 0 {
78+
t.Errorf("Owner of %s is not root (UID 0). Found UID: %d", polkitRulesDir, stat.Uid)
79+
}
80+
81+
// Check group is polkitd
82+
polkitdGroup, err := user.LookupGroup("polkitd")
83+
if err != nil {
84+
t.Errorf("Could not look up group 'polkitd': %v. This test requires the 'polkitd' group to exist.", err)
85+
}
86+
polkitdGid, _ := strconv.Atoi(polkitdGroup.Gid)
87+
if stat.Gid != uint32(polkitdGid) {
88+
t.Errorf("Group of %s is not 'polkitd'. Expected GID: %d, Found GID: %d", polkitRulesDir, polkitdGid, stat.Gid)
89+
}
90+
}
91+
92+
// saveOriginalHostname saves the original hostname
93+
func saveOriginalHostname(t *testing.T) (string, bool) {
94+
log.Println("- Saving original hostname.")
95+
96+
originalHostname, err := os.Hostname()
97+
if err != nil {
98+
t.Errorf("Could not get original hostname: %v", err)
99+
}
100+
log.Println("Original hostname is:", originalHostname)
101+
return originalHostname, true
102+
}
103+
104+
// addRuleAndRestart adds the polkit rule and restarts the service
105+
func addRuleAndRestart(t *testing.T) {
106+
log.Println("- Adding test polkit rule to", testRuleFile)
107+
108+
if err := os.WriteFile(testRuleFile, []byte(polkitRule), 0644); err != nil {
109+
t.Errorf("Failed to write polkit rule file: %v. Make sure you are running this test as root.", err)
110+
}
111+
112+
log.Println("Restarting polkit service...")
113+
result := RunCommandTimeout(MediumTimeout, "systemctl", "restart", "polkit")
114+
if result.Error != nil {
115+
t.Errorf("Exit code: %d\nError: %v\nStdout: %s\nStderr: %s",
116+
result.ExitCode, result.Error, result.Stdout, result.Stderr)
117+
}
118+
}
119+
120+
// changeHostnameWithAuth attempts to change hostname (should succeed with polkit rule)
121+
func changeHostnameWithAuth(t *testing.T) {
122+
log.Printf("- Attempting to change hostname to %s as user %s (should succeed without password).", newHostname, testUser)
123+
124+
result := RunCommandTimeout(MediumTimeout, "sudo", "-u", testUser, "hostnamectl", "set-hostname", newHostname)
125+
if result.Error != nil {
126+
t.Errorf("Exit code: %d\nError: %v\nStdout: %s\nStderr: %s",
127+
result.ExitCode, result.Error, result.Stdout, result.Stderr)
128+
}
129+
}
130+
131+
// verifyHostnameChanged verifies that the hostname was actually changed
132+
func verifyHostnameChanged(t *testing.T) {
133+
log.Println("- Verifying hostname change.")
134+
135+
currentHostname, _ := os.Hostname()
136+
if currentHostname != newHostname {
137+
t.Errorf("Hostname was not changed. Expected '%s', but found '%s'", newHostname, currentHostname)
138+
}
139+
}
140+
141+
// removeRuleAndRestart removes the polkit rule and restarts the service
142+
func removeRuleAndRestart(t *testing.T) {
143+
log.Println("- Removing test polkit rule and restarting service.")
144+
145+
// Clean up now to test the next step
146+
log.Println("Cleaning up test rule file:", testRuleFile)
147+
if err := os.Remove(testRuleFile); err != nil {
148+
// Log as a warning because the file might have been removed already.
149+
log.Printf("WARN: Could not remove test rule file %s: %v", testRuleFile, err)
150+
} else {
151+
log.Println("Test rule file removed.")
152+
}
153+
154+
log.Println("Restarting polkit service...")
155+
result := RunCommandTimeout(MediumTimeout, "systemctl", "restart", "polkit")
156+
if result.Error != nil {
157+
t.Fatalf("Exit code: %d\nError: %v\nStdout: %s\nStderr: %s",
158+
result.ExitCode, result.Error, result.Stdout, result.Stderr)
159+
}
160+
}
161+
162+
// changeHostnameShouldFail attempts to change hostname (should fail without polkit rule)
163+
func changeHostnameShouldFail(t *testing.T) {
164+
log.Printf("- Attempting to change hostname to 'should-fail-hostname' as user %s (should fail or ask for password).", testUser)
165+
166+
// We expect this to fail because it will require authentication, which we can't provide.
167+
// The command will either timeout or return an error.
168+
result := RunCommandTimeout(ShortTimeout, "sudo", "-u", testUser, "hostnamectl", "set-hostname", "should-fail-hostname")
169+
if result.Error == nil {
170+
t.Error("Changing hostname succeeded when it should have failed")
171+
}
172+
173+
if result.ExitCode != -1 {
174+
t.Errorf("Unexpected exit code: %d error : %s", result.ExitCode, result.Error)
175+
}
176+
}
177+
178+
// verifyHostnameUnchanged verifies that the hostname was NOT changed in the previous step
179+
func verifyHostnameUnchanged(t *testing.T) {
180+
log.Println("- Verifying hostname has not been changed.")
181+
182+
currentHostname, _ := os.Hostname()
183+
if currentHostname != newHostname {
184+
t.Errorf("Hostname was changed when it should not have been. Expected '%s', but found '%s'", newHostname, currentHostname)
185+
}
186+
}
187+
188+
// restoreHostname restores the machine's original hostname
189+
func restoreHostname(hostname string, t *testing.T) {
190+
log.Println("- Restoring original hostname to", hostname)
191+
192+
// This command needs to be run because the test might have failed,
193+
// leaving the system in a state where permissions are messed up.
194+
result := RunCommandTimeout(MediumTimeout, "hostnamectl", "set-hostname", hostname)
195+
if result.Error != nil {
196+
t.Errorf("Failed to restore original hostname '%s'\nExit code: %d\nError: %v\nStdout: %s\nStderr: %s",
197+
hostname, result.ExitCode, result.Error, result.Stdout, result.Stderr)
198+
log.Printf("ERROR: Please restore hostname manually to: %s", hostname)
199+
} else {
200+
t.Log("Hostname restored successfully")
201+
}
202+
}

data/security/testPolkit/runtest

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ if [ "$(id -u)" -ne 0 ]; then
4141
exit 1
4242
fi
4343

44-
# build the go program
45-
go build -o testPolkit
46-
exec ./testPolkit
44+
# ensure gotestsum is installed
45+
if ! command -v gotestsum &> /dev/null; then
46+
echo "gotestsum could not be found, please install it first."
47+
exit 1
48+
fi
49+
50+
# empty the go test cache to avoid stale results
51+
go clean -testcache
52+
# run tests with gotestsum
53+
exec gotestsum --format=standard-verbose --junitfile results.xml
54+

data/security/testPolkit/tap/tap.go

Lines changed: 0 additions & 105 deletions
This file was deleted.

0 commit comments

Comments
 (0)