mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-11-03 00:53:58 +00:00
dab7582c95
The copied Unix install and setup commands settled sudo with `sudo -v` before the silent token prompt. sudo applies its verifypw rule to -v, so it asks for a password whenever any matching sudoers entry needs one, even if the rule that would run a command is NOPASSWD. Ubuntu cloud images match both `%sudo ALL=(ALL:ALL) ALL` and the cloud-init NOPASSWD rule, and the default user there has no password, so the command failed with "a password is required" for exactly the hosts most providers spin up. Found while installing an agent on a fresh MSP client VM. The preflight now tries a silent `sudo -n true` first. It runs a command, so sudo applies the rule it would actually use, and it passes for NOPASSWD users and cached credentials. Otherwise the old `sudo -v` runs unchanged, so hosts that need a password are prompted as before and a policy that allows only bash (where `true` is denied) still validates. Both producers (the Go install command builder and the frontend one) change together, and their tests pin the new preflight; the bootstrap fixtures' fake sudo answers `-n true` the way it answers `-v`, so the refusal cases still refuse. Verified on an Ubuntu 24.04 cloud-init host: the old preflight prints "sudo: a password is required", the new one passes without a prompt. The Settings installer renders the new preflight in every Unix command at desktop and phone widths. Contract-Neutral: sudo preflight only; command shape, privilege dispatch and token handling unchanged
464 lines
18 KiB
Go
464 lines
18 KiB
Go
package api
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/api/agenttokens"
|
|
"github.com/rcourtman/pulse-go-rewrite/internal/config"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func requirePrivateBootstrapPrivilegeDispatch(t *testing.T, command string) {
|
|
t.Helper()
|
|
// The shared producer selects its root/sudo argv inside an isolated
|
|
// subshell. Duplicating the credential reader into literal root and sudo
|
|
// branches would exceed the common pasted-line budget.
|
|
// `sudo -v` alone refuses NOPASSWD cloud-image users whose %sudo rule
|
|
// needs a password; a silent `sudo -n true` must be tried first.
|
|
require.Contains(t, command, `elif command -v sudo >/dev/null 2>&1; then if ! sudo -n true 2>/dev/null; then sudo -v; fi;`)
|
|
require.Contains(t, command, `if [ "$(id -u)" -eq 0 ]; then set --; else set -- sudo; fi;`)
|
|
require.Contains(t, command, `"$@" bash -c `)
|
|
require.Equal(t, 1, strings.Count(command, "bash -c "), "the privileged body must be emitted once")
|
|
}
|
|
|
|
func TestNormalizeProxmoxInstallType(t *testing.T) {
|
|
installType, err := normalizeProxmoxInstallType(" PBS ")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "pbs", installType)
|
|
|
|
_, err = normalizeProxmoxInstallType("vmware")
|
|
require.Error(t, err)
|
|
require.Equal(t, "Type must be 'pve' or 'pbs'", err.Error())
|
|
}
|
|
|
|
func TestAgentInstallCommandCarriesCommandPolicyIntent(t *testing.T) {
|
|
cfg := &config.Config{DataPath: t.TempDir(), AuthUser: "admin", AuthPass: "hashed-password"}
|
|
handler := newTestConfigHandlers(t, cfg)
|
|
req := httptest.NewRequest(
|
|
http.MethodPost,
|
|
"/api/agent-install-command",
|
|
bytes.NewBufferString(`{"type":"pve","enableCommands":true}`),
|
|
)
|
|
req.Host = "pulse.example:7655"
|
|
rec := httptest.NewRecorder()
|
|
handler.HandleAgentInstallCommand(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("install command status = %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
var response AgentInstallCommandResponse
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &response); err != nil {
|
|
t.Fatalf("decode install command response: %v", err)
|
|
}
|
|
if len(cfg.APITokens) != 1 || response.Token == "" {
|
|
t.Fatalf("install command did not mint one token: response=%+v tokens=%d", response, len(cfg.APITokens))
|
|
}
|
|
if got := cfg.APITokens[0].Metadata[agenttokens.CommandPolicyIntentMetadataKey]; got != agenttokens.CommandPolicyIntentEnabled {
|
|
t.Fatalf("command policy intent = %q, want enabled", got)
|
|
}
|
|
if !cfg.APITokens[0].HasScope(config.ScopeAgentExec) {
|
|
t.Fatalf("explicit command install token is missing %s: %v", config.ScopeAgentExec, cfg.APITokens[0].Scopes)
|
|
}
|
|
if !strings.Contains(response.Command, "--enable-commands") {
|
|
t.Fatalf("commands-enabled install omitted flag: %s", response.Command)
|
|
}
|
|
}
|
|
|
|
func TestAgentInstallCommandDefaultsToMonitoringOnlyAuthority(t *testing.T) {
|
|
cfg := &config.Config{DataPath: t.TempDir(), AuthUser: "admin", AuthPass: "hashed-password"}
|
|
handler := newTestConfigHandlers(t, cfg)
|
|
req := httptest.NewRequest(
|
|
http.MethodPost,
|
|
"/api/agent-install-command",
|
|
bytes.NewBufferString(`{"type":"pve"}`),
|
|
)
|
|
req.Host = "pulse.example:7655"
|
|
rec := httptest.NewRecorder()
|
|
handler.HandleAgentInstallCommand(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("install command status = %d: %s", rec.Code, rec.Body.String())
|
|
}
|
|
if len(cfg.APITokens) != 1 {
|
|
t.Fatalf("install command minted %d tokens, want 1", len(cfg.APITokens))
|
|
}
|
|
if cfg.APITokens[0].HasScope(config.ScopeAgentExec) {
|
|
t.Fatalf("monitoring-only install token includes %s: %v", config.ScopeAgentExec, cfg.APITokens[0].Scopes)
|
|
}
|
|
if got := cfg.APITokens[0].Metadata[agenttokens.CommandPolicyIntentMetadataKey]; got != agenttokens.CommandPolicyIntentDisabled {
|
|
t.Fatalf("command policy intent = %q, want disabled", got)
|
|
}
|
|
}
|
|
|
|
func TestBuildProxmoxAgentInstallCommand(t *testing.T) {
|
|
command := buildProxmoxAgentInstallCommand(agentInstallCommandOptions{
|
|
BaseURL: "https://pulse.example.com/",
|
|
Token: "token-123",
|
|
InstallType: "pbs",
|
|
IncludeInstallType: true,
|
|
})
|
|
require.Contains(t, command, posixShellQuote("https://pulse.example.com/install.sh"))
|
|
require.NotContains(t, command, "token-123")
|
|
require.Contains(t, command, `--token-file "$token_file"`)
|
|
require.Contains(t, command, `token_dir=$(mktemp -d /tmp/pulse-agent-bootstrap.XXXXXX)`)
|
|
requirePrivateBootstrapPrivilegeDispatch(t, command)
|
|
require.Contains(t, command, `rmdir -- "$token_dir"`)
|
|
require.Contains(t, command, "pbs")
|
|
require.NotContains(t, command, "--enable-commands")
|
|
}
|
|
|
|
func TestBuildProxmoxAgentInstallCommand_IncludesInsecureForPlainHTTP(t *testing.T) {
|
|
command := buildProxmoxAgentInstallCommand(agentInstallCommandOptions{
|
|
BaseURL: "http://pulse.example.com:7655/",
|
|
Token: "token-123",
|
|
InstallType: "pve",
|
|
IncludeInstallType: true,
|
|
})
|
|
|
|
require.Contains(t, command, posixShellQuote("http://pulse.example.com:7655/install.sh"))
|
|
require.Contains(t, command, "http://pulse.example.com:7655")
|
|
require.Contains(t, command, "--insecure")
|
|
}
|
|
|
|
func TestBuildProxmoxAgentInstallCommand_IncludesExplicitInsecureForSelfSignedHTTPS(t *testing.T) {
|
|
command := buildProxmoxAgentInstallCommand(agentInstallCommandOptions{
|
|
BaseURL: "https://pulse.example.com:7655/",
|
|
Token: "token-123",
|
|
InstallType: "pve",
|
|
IncludeInstallType: true,
|
|
Insecure: true,
|
|
})
|
|
|
|
require.Contains(t, command, "curl -kfsSL "+posixShellQuote("https://pulse.example.com:7655/install.sh"))
|
|
require.Contains(t, command, "https://pulse.example.com:7655")
|
|
require.Contains(t, command, "--insecure")
|
|
}
|
|
|
|
func TestBuildProxmoxAgentInstallCommand_UsesPrivilegeEscalationWrapper(t *testing.T) {
|
|
command := buildProxmoxAgentInstallCommand(agentInstallCommandOptions{
|
|
BaseURL: "https://pulse.example.com/",
|
|
Token: "token-123",
|
|
InstallType: "pve",
|
|
IncludeInstallType: true,
|
|
})
|
|
|
|
require.Contains(t, command, `if [ "$(id -u)" -eq 0 ]; then`)
|
|
require.Contains(t, command, `elif command -v sudo >/dev/null 2>&1; then`)
|
|
require.NotContains(t, command, "token-123")
|
|
require.Contains(t, command, `curl -fsSL 'https://pulse.example.com/install.sh' -o "$install_script"`)
|
|
require.Contains(t, command, `echo "Root privileges required. Run as root (su -) and retry." >&2`)
|
|
}
|
|
|
|
func TestBuildProxmoxAgentInstallCommand_OmitsTokenWhenNotProvided(t *testing.T) {
|
|
command := buildProxmoxAgentInstallCommand(agentInstallCommandOptions{
|
|
BaseURL: "https://pulse.example.com/",
|
|
Token: "",
|
|
InstallType: "pbs",
|
|
IncludeInstallType: true,
|
|
})
|
|
|
|
require.Contains(t, command, "https://pulse.example.com")
|
|
require.Contains(t, command, "pbs")
|
|
require.NotContains(t, command, "--token")
|
|
}
|
|
|
|
func TestBuildProxmoxAgentInstallCommand_ShellEscapesArguments(t *testing.T) {
|
|
baseURL := "https://pulse.example.com' && touch /tmp/pwned #"
|
|
token := "tok'en"
|
|
command := buildProxmoxAgentInstallCommand(agentInstallCommandOptions{
|
|
BaseURL: baseURL,
|
|
Token: token,
|
|
InstallType: "pve",
|
|
IncludeInstallType: true,
|
|
})
|
|
|
|
require.Contains(t, command, posixShellQuote(baseURL+"/install.sh"))
|
|
require.Contains(t, command, "--url")
|
|
require.NotContains(t, command, token)
|
|
require.Contains(t, command, `--token-file "$token_file"`)
|
|
require.Contains(t, command, "pve")
|
|
}
|
|
|
|
func TestBuildProxmoxAgentInstallCommand_NormalizesTrailingSlashes(t *testing.T) {
|
|
command := buildProxmoxAgentInstallCommand(agentInstallCommandOptions{
|
|
BaseURL: "https://pulse.example.com/base///",
|
|
Token: "token-123",
|
|
InstallType: "pve",
|
|
IncludeInstallType: true,
|
|
})
|
|
|
|
require.Contains(t, command, posixShellQuote("https://pulse.example.com/base/install.sh"))
|
|
require.Contains(t, command, "https://pulse.example.com/base")
|
|
require.Contains(t, command, `--token-file "$token_file"`)
|
|
require.NotContains(t, command, "//install.sh")
|
|
require.Contains(t, command, "--url "+posixShellQuote("https://pulse.example.com/base"))
|
|
require.NotContains(t, command, posixShellQuote("https://pulse.example.com/base/"))
|
|
}
|
|
|
|
func TestBuildProxmoxAgentInstallCommand_IncludesCommandsWhenRequested(t *testing.T) {
|
|
command := buildProxmoxAgentInstallCommand(agentInstallCommandOptions{
|
|
BaseURL: "https://pulse.example.com/",
|
|
Token: "token-123",
|
|
InstallType: "pve",
|
|
IncludeInstallType: true,
|
|
EnableCommands: true,
|
|
})
|
|
|
|
require.Contains(t, command, "--enable-proxmox")
|
|
require.Contains(t, command, "pve")
|
|
require.Contains(t, command, "--enable-commands")
|
|
require.Contains(t, command, `token_dir=$(mktemp -d /tmp/pulse-agent-bootstrap.XXXXXX)`)
|
|
require.Contains(t, command, `rmdir -- "$token_dir"`)
|
|
}
|
|
|
|
func TestBuildContainerRuntimeAgentInstallCommand_UsesLifecycleTransport(t *testing.T) {
|
|
command := buildContainerRuntimeAgentInstallCommand("https://pulse.example.com/base", "token-123", true)
|
|
|
|
require.Contains(t, command, posixShellQuote("https://pulse.example.com/base/install.sh"))
|
|
require.Contains(t, command, "https://pulse.example.com/base")
|
|
require.NotContains(t, command, "token-123")
|
|
require.Contains(t, command, `--token-file "$token_file"`)
|
|
require.Contains(t, command, "--enable-docker")
|
|
require.Contains(t, command, "--enable-host")
|
|
require.NotContains(t, command, "--enable-host=false")
|
|
require.Contains(t, command, "--interval 30s")
|
|
requirePrivateBootstrapPrivilegeDispatch(t, command)
|
|
require.Contains(t, command, "--preflight-only --output json --non-interactive")
|
|
require.Contains(t, command, `read -r -s -p`)
|
|
require.NotContains(t, command, "| bash")
|
|
require.NotContains(t, command, "| sudo")
|
|
require.NotContains(t, command, "\n")
|
|
}
|
|
|
|
func TestBuildContainerRuntimeAgentInstallCommand_OmitsTokenAndAddsInsecureForHTTP(t *testing.T) {
|
|
command := buildContainerRuntimeAgentInstallCommand("http://pulse.example.com:7655/", "", true)
|
|
|
|
require.Contains(t, command, posixShellQuote("http://pulse.example.com:7655/install.sh"))
|
|
require.Contains(t, command, "http://pulse.example.com:7655")
|
|
require.NotContains(t, command, "--token")
|
|
require.Contains(t, command, "--insecure")
|
|
}
|
|
|
|
func TestBuildContainerRuntimeAgentInstallCommand_PreservesWorkloadOnlyMode(t *testing.T) {
|
|
command := buildContainerRuntimeAgentInstallCommand("https://pulse.example.com", "token-123", false)
|
|
|
|
require.Contains(t, command, "--enable-docker")
|
|
require.Contains(t, command, "--enable-host=false")
|
|
}
|
|
|
|
func TestContainerRuntimeAgentScopesFollowHostMode(t *testing.T) {
|
|
dualMode := containerRuntimeAgentScopes(true)
|
|
require.ElementsMatch(t, []string{
|
|
config.ScopeDockerReport,
|
|
config.ScopeAgentReport,
|
|
config.ScopeAgentConfigRead,
|
|
}, dualMode)
|
|
|
|
require.Equal(t, []string{config.ScopeDockerReport}, containerRuntimeAgentScopes(false))
|
|
}
|
|
|
|
func TestBuildSetupScriptCommand_UsesFailFastQuotedTransport(t *testing.T) {
|
|
command := buildSetupScriptCommand("https://pulse.example.com/api/setup-script?type=pve&host=pve1.local", "token-123")
|
|
|
|
require.Contains(t, command, "curl -fsSL "+posixShellQuote("https://pulse.example.com/api/setup-script?type=pve&host=pve1.local")+` -o "$install_script"`)
|
|
require.NotContains(t, command, "token-123")
|
|
require.Contains(t, command, `PULSE_SETUP_TOKEN_FILE="$token_file" bash "$1"`)
|
|
require.Contains(t, command, `else echo "Root privileges required. Run as root (su -) and retry." >&2; exit 1; fi;`)
|
|
}
|
|
|
|
func TestBuildSetupScriptCommand_OmitsTokenWhenNotProvided(t *testing.T) {
|
|
command := buildSetupScriptCommand("https://pulse.example.com/api/setup-script?type=pbs", "")
|
|
|
|
require.Contains(t, command, "curl -fsSL "+posixShellQuote("https://pulse.example.com/api/setup-script?type=pbs")+` -o "$install_script"`)
|
|
require.Contains(t, command, `if [ "$(id -u)" -eq 0 ]; then`)
|
|
requirePrivateBootstrapPrivilegeDispatch(t, command)
|
|
require.NotContains(t, command, "PULSE_SETUP_TOKEN=")
|
|
}
|
|
|
|
func TestBuildSetupScriptURL_PreservesEncodedHostAndBackupPerms(t *testing.T) {
|
|
scriptURL := buildSetupScriptURL(
|
|
"https://pulse.example.com:7656/",
|
|
"pve",
|
|
"https://[2001:db8::1]:8006",
|
|
"https://pulse.example.com:7656",
|
|
true,
|
|
)
|
|
|
|
require.Equal(
|
|
t,
|
|
"https://pulse.example.com:7656/api/setup-script?backup_perms=true&host=https%3A%2F%2F%5B2001%3Adb8%3A%3A1%5D%3A8006&pulse_url=https%3A%2F%2Fpulse.example.com%3A7656&type=pve",
|
|
scriptURL,
|
|
)
|
|
}
|
|
|
|
func TestBuildSetupScriptInstallArtifact_UsesSharedBackendShape(t *testing.T) {
|
|
expiresAt := time.Unix(1900000000, 0).UTC().Unix()
|
|
|
|
artifact := buildSetupScriptInstallArtifact(
|
|
"https://pulse.example",
|
|
"pve",
|
|
"https://pve1.local:8006",
|
|
"https://pulse.example",
|
|
true,
|
|
"setup-token-123",
|
|
expiresAt,
|
|
)
|
|
|
|
require.Equal(t, "pve", artifact.Type)
|
|
require.Equal(t, "https://pve1.local:8006", artifact.Host)
|
|
require.Contains(t, artifact.URL, "/api/setup-script?")
|
|
require.Equal(t, artifact.URL, artifact.DownloadURL)
|
|
require.Equal(t, "pulse-setup-pve.sh", artifact.ScriptFileName)
|
|
require.Equal(t, artifact.Command, artifact.CommandWithEnv)
|
|
require.NotContains(t, artifact.CommandWithEnv, "setup-token-123")
|
|
require.Contains(t, artifact.CommandWithEnv, "PULSE_SETUP_TOKEN_FILE=")
|
|
require.NotContains(t, artifact.CommandWithoutEnv, "PULSE_SETUP_TOKEN=")
|
|
require.Equal(t, expiresAt, artifact.Expires)
|
|
require.Equal(t, "setup-token-123", artifact.SetupToken)
|
|
require.Equal(t, "set…123", artifact.TokenHint)
|
|
}
|
|
|
|
func TestRenderSetupScript_UsesSharedInstallArtifactShape(t *testing.T) {
|
|
artifact := buildSetupScriptInstallArtifact(
|
|
"https://pulse.example",
|
|
"pve",
|
|
"https://pve1.local:8006",
|
|
"https://pulse.example",
|
|
true,
|
|
"setup-token-123",
|
|
0,
|
|
)
|
|
|
|
script := renderSetupScript("pve", setupScriptRenderContext{
|
|
ServerName: deriveSetupScriptServerName("https://pve1.local:8006"),
|
|
PulseURL: "https://pulse.example",
|
|
ServerHost: "https://pve1.local:8006",
|
|
SetupToken: "setup-token-123",
|
|
TokenName: buildPulseMonitorTokenName("https://pulse.example"),
|
|
TokenMatchPrefix: buildPulseMonitorTokenName("https://pulse.example"),
|
|
StoragePerms: `
|
|
pveum aclmod /storage -user pulse-monitor@pve -role PVEDatastoreAdmin
|
|
if [ "$TOKEN_CREATED" = true ]; then
|
|
pveum aclmod /storage -token "$PULSE_TOKEN_ID" -role PVEDatastoreAdmin
|
|
fi`,
|
|
SensorsPublicKey: "ssh-ed25519 AAAATEST pulse@test",
|
|
Artifact: artifact,
|
|
})
|
|
|
|
require.Contains(t, script, `SETUP_SCRIPT_URL="https://pulse.example/api/setup-script?backup_perms=true&host=https%3A%2F%2Fpve1.local%3A8006&pulse_url=https%3A%2F%2Fpulse.example&type=pve"`)
|
|
require.Contains(t, script, "PULSE_BOOTSTRAP_COMMAND_WITH_ENV="+posixShellQuote(artifact.CommandWithEnv))
|
|
require.Contains(t, script, `PULSE_SETUP_TOKEN="${PULSE_SETUP_TOKEN:-setup-token-123}"`)
|
|
require.Contains(t, script, `pveum aclmod /storage -user pulse-monitor@pve -role PVEDatastoreAdmin`)
|
|
require.Contains(t, script, `pveum aclmod /storage -token "$PULSE_TOKEN_ID" -role PVEDatastoreAdmin`)
|
|
}
|
|
|
|
func TestInstallBaseURLRequiresInsecure(t *testing.T) {
|
|
require.True(t, installBaseURLRequiresInsecure("http://pulse.example.com:7655"))
|
|
require.True(t, installBaseURLRequiresInsecure(" HTTP://pulse.example.com:7655/ "))
|
|
require.False(t, installBaseURLRequiresInsecure("https://pulse.example.com"))
|
|
require.False(t, installBaseURLRequiresInsecure(""))
|
|
}
|
|
|
|
func TestResolveConfigAgentInstallBaseURL(t *testing.T) {
|
|
cfg := &config.Config{
|
|
AgentConnectURL: "https://agents.example.com/",
|
|
PublicURL: "https://public.example.com",
|
|
FrontendPort: 7655,
|
|
}
|
|
req := httptest.NewRequest("POST", "/api/agent-install-command", nil)
|
|
req.Host = "127.0.0.1:7655"
|
|
req.Header.Set("X-Forwarded-Proto", "https")
|
|
|
|
require.Equal(t, "https://agents.example.com", resolveConfigAgentInstallBaseURL(req, cfg, false))
|
|
|
|
cfg.AgentConnectURL = ""
|
|
require.Equal(t, "https://public.example.com", resolveConfigAgentInstallBaseURL(req, cfg, false))
|
|
}
|
|
|
|
func TestResolveConfigAgentInstallBaseURL_PreservesConfiguredPublicSchemeOnLoopback(t *testing.T) {
|
|
cfg := &config.Config{
|
|
PublicURL: "https://public.example.com/base/",
|
|
FrontendPort: 7655,
|
|
}
|
|
req := httptest.NewRequest("POST", "/api/agent-install-command", nil)
|
|
req.Host = "127.0.0.1:7655"
|
|
|
|
require.Equal(t, "https://public.example.com/base", resolveConfigAgentInstallBaseURL(req, cfg, false))
|
|
}
|
|
|
|
func TestResolveConfigAgentInstallBaseURL_UsesConfiguredPublicURLForIPv6Loopback(t *testing.T) {
|
|
cfg := &config.Config{
|
|
PublicURL: "https://public.example.com",
|
|
FrontendPort: 7655,
|
|
}
|
|
req := httptest.NewRequest("POST", "/api/agent-install-command", nil)
|
|
req.Host = "[::1]:7655"
|
|
|
|
require.Equal(t, "https://public.example.com", resolveConfigAgentInstallBaseURL(req, cfg, false))
|
|
}
|
|
|
|
func TestIssueAndPersistAgentInstallToken(t *testing.T) {
|
|
dataDir := t.TempDir()
|
|
cfg := &config.Config{DataPath: dataDir}
|
|
persistence := config.NewConfigPersistence(dataDir)
|
|
|
|
rawToken, record, err := issueAndPersistAgentInstallToken(cfg, persistence, issueAgentInstallTokenOptions{
|
|
TokenName: "test-install-token",
|
|
OrgID: "acme",
|
|
OwnerUserID: "alice",
|
|
Metadata: map[string]string{
|
|
"install_type": "pbs",
|
|
"issued_via": "test",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
require.NotEmpty(t, rawToken)
|
|
require.NotNil(t, record)
|
|
require.Equal(t, "acme", record.OrgID)
|
|
require.Equal(t, "alice", record.Metadata[apiTokenMetadataOwnerUserID])
|
|
require.Equal(t, "pbs", record.Metadata["install_type"])
|
|
require.Equal(t, "test", record.Metadata["issued_via"])
|
|
|
|
require.Len(t, cfg.APITokens, 1)
|
|
savedRecord, ok := cfg.ValidateAPIToken(rawToken)
|
|
require.True(t, ok)
|
|
require.Equal(t, "acme", savedRecord.OrgID)
|
|
require.Equal(t, "alice", savedRecord.Metadata[apiTokenMetadataOwnerUserID])
|
|
|
|
tokens, err := persistence.LoadAPITokens()
|
|
require.NoError(t, err)
|
|
require.Len(t, tokens, 1)
|
|
require.True(t, strings.HasPrefix(tokens[0].Name, "test-install-token"))
|
|
require.Equal(t, "alice", tokens[0].Metadata[apiTokenMetadataOwnerUserID])
|
|
}
|
|
|
|
func TestIssueAndPersistAgentInstallTokenRejectsOwnerMetadataOverride(t *testing.T) {
|
|
dataDir := t.TempDir()
|
|
cfg := &config.Config{DataPath: dataDir}
|
|
persistence := config.NewConfigPersistence(dataDir)
|
|
|
|
rawToken, record, err := issueAndPersistAgentInstallToken(cfg, persistence, issueAgentInstallTokenOptions{
|
|
TokenName: "bad-install-token",
|
|
OrgID: "acme",
|
|
OwnerUserID: "alice",
|
|
Metadata: map[string]string{
|
|
apiTokenMetadataOwnerUserID: "mallory",
|
|
"install_type": "pbs",
|
|
},
|
|
})
|
|
require.Error(t, err)
|
|
require.ErrorIs(t, err, errAgentInstallTokenRecord)
|
|
require.Contains(t, err.Error(), "reserved token metadata key")
|
|
require.Empty(t, rawToken)
|
|
require.Nil(t, record)
|
|
require.Empty(t, cfg.APITokens)
|
|
|
|
tokens, loadErr := persistence.LoadAPITokens()
|
|
require.NoError(t, loadErr)
|
|
require.Empty(t, tokens)
|
|
}
|