mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-25 09:01:29 +02:00
111 lines
3.6 KiB
Go
111 lines
3.6 KiB
Go
//go:build linux
|
|
|
|
package elevate
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// fakePkexec puts a pkexec on PATH that exits with the given code, so the
|
|
// mapping from polkit's exit codes onto our errors can be exercised without a
|
|
// polkit agent.
|
|
func fakePkexec(t *testing.T, exitCode int, stderr string) {
|
|
t.Helper()
|
|
|
|
dir := t.TempDir()
|
|
script := fmt.Sprintf("#!/bin/sh\necho %s >&2\nexit %d\n", shellQuote(stderr), exitCode)
|
|
require.NoError(t, os.WriteFile(filepath.Join(dir, "pkexec"), []byte(script), 0o700), "write the fake pkexec")
|
|
t.Setenv("PATH", dir)
|
|
}
|
|
|
|
func shellQuote(s string) string {
|
|
return "'" + strings.ReplaceAll(s, "'", `'\''`) + "'"
|
|
}
|
|
|
|
func TestRunMapsPkexecExitCodes(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
exitCode int
|
|
stderr string
|
|
wantErr error
|
|
}{
|
|
{name: "applied", exitCode: 0},
|
|
{
|
|
name: "dialog dismissed",
|
|
exitCode: exitDismissed,
|
|
stderr: "Error executing command as another user: Request dismissed",
|
|
wantErr: ErrDeclined,
|
|
},
|
|
{
|
|
// What a graphical agent reports for a cancelled prompt. Not a
|
|
// failure: the user was asked and answered.
|
|
name: "prompt cancelled",
|
|
exitCode: exitNotAuthorized,
|
|
stderr: "Error executing command as another user: Not authorized",
|
|
wantErr: ErrDeclined,
|
|
},
|
|
{
|
|
// The same status, but pkexec never got to ask anybody.
|
|
name: "no agent and no terminal to fall back on",
|
|
exitCode: exitNotAuthorized,
|
|
stderr: "Error creating textual authentication agent: Error opening current controlling terminal for the process (`/dev/tty'): No such device or address",
|
|
wantErr: ErrUnavailable,
|
|
},
|
|
{
|
|
// And the same status again once the authorization succeeded and
|
|
// pkexec could not run what it had been authorized to run. Reading
|
|
// that as a refusal would revert the control in silence on a host
|
|
// where elevation is broken.
|
|
name: "authorized but not runnable",
|
|
exitCode: exitNotAuthorized,
|
|
stderr: "Error executing command as another user: No such file or directory",
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
fakePkexec(t, tt.exitCode, tt.stderr)
|
|
|
|
err := run(context.Background(), "/nonexistent/netbird-ui", []string{"--flag"})
|
|
switch {
|
|
case tt.wantErr != nil:
|
|
require.ErrorIs(t, err, tt.wantErr, "exit %d said %q", tt.exitCode, tt.stderr)
|
|
case tt.exitCode == 0:
|
|
require.NoError(t, err, "a pkexec that exited cleanly applied the change")
|
|
default:
|
|
require.Error(t, err, "exit %d said %q", tt.exitCode, tt.stderr)
|
|
assert.NotErrorIs(t, err, ErrDeclined, "not the user's answer")
|
|
assert.NotErrorIs(t, err, ErrUnavailable, "not a missing mechanism")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// An exit code that is not polkit's is the one-shot's own failure, and has to
|
|
// stay distinguishable from a declined prompt: the caller reports it.
|
|
func TestRunReportsOneShotFailure(t *testing.T) {
|
|
fakePkexec(t, 3, "the one-shot said no")
|
|
|
|
err := run(context.Background(), "/nonexistent/netbird-ui", nil)
|
|
|
|
require.Error(t, err, "a one-shot that failed is not a prompt that was answered")
|
|
assert.NotErrorIs(t, err, ErrDeclined, "not the user's answer")
|
|
assert.NotErrorIs(t, err, ErrUnavailable, "not a missing mechanism")
|
|
}
|
|
|
|
func TestRunWithoutPkexecIsUnavailable(t *testing.T) {
|
|
t.Setenv("PATH", t.TempDir())
|
|
|
|
err := run(context.Background(), "/nonexistent/netbird-ui", nil)
|
|
require.ErrorIs(t, err, ErrUnavailable, "no pkexec means no mechanism")
|
|
assert.False(t, mechanismAvailable(), "mechanismAvailable without pkexec on PATH")
|
|
}
|