internal/cli/ca_test.go
Ref: Size: 3.6 KiB History
package cli
import (
"context"
"encoding/json"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"golang.org/x/crypto/ssh"
)
// A real (public) ed25519 key line — uploads are parse-validated locally.
const testUserCALine = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPZ8BFXvSU9tCz3sm5uuXG8UXsRWCkEBHYBJk8OjJgeA me@laptop\n"
func TestUploadUserCA(t *testing.T) {
var gotPath, gotAuth, gotKey string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotPath = r.URL.Path
gotAuth = r.Header.Get("Authorization")
body, _ := io.ReadAll(r.Body)
var m map[string]string
json.Unmarshal(body, &m)
gotKey = m["public_key"]
w.Write([]byte(`{"fingerprint":"SHA256:server-echo"}`))
}))
defer srv.Close()
pub := filepath.Join(t.TempDir(), "ca.pub")
os.WriteFile(pub, []byte(testUserCALine), 0o644)
out, err := UploadUserCA(context.Background(), srv.URL, "tok123", "default", pub)
if err != nil {
t.Fatal(err)
}
if gotPath != "/api/v1/tenants/default/user-cas" || gotAuth != "Bearer tok123" {
t.Errorf("request: %s %s", gotPath, gotAuth)
}
if gotKey != testUserCALine {
t.Errorf("public_key = %q", gotKey)
}
// The summary line carries the LOCALLY computed fingerprint of the
// uploaded key (same value the server echoes) and the tenant.
pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(testUserCALine))
if err != nil {
t.Fatal(err)
}
if !strings.Contains(out, ssh.FingerprintSHA256(pk)) || !strings.Contains(out, "default") {
t.Errorf("out = %q", out)
}
}
// An empty tenant targets the tenant-less endpoint (the token names the tenant)
// and the summary line makes no tenant claim.
func TestUploadUserCATokenNamedTenant(t *testing.T) {
var gotPath string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotPath = r.URL.Path
w.Write([]byte(`{"fingerprint":"SHA256:server-echo"}`))
}))
defer srv.Close()
pub := filepath.Join(t.TempDir(), "ca.pub")
os.WriteFile(pub, []byte(testUserCALine), 0o644)
out, err := UploadUserCA(context.Background(), srv.URL, "tok123", "", pub)
if err != nil {
t.Fatal(err)
}
if gotPath != "/api/v1/user-cas" {
t.Errorf("path = %q, want /api/v1/user-cas", gotPath)
}
if strings.Contains(out, "for tenant") {
t.Errorf("out must not claim a tenant we did not pin: %q", out)
}
pk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(testUserCALine))
if err != nil {
t.Fatal(err)
}
if !strings.Contains(out, ssh.FingerprintSHA256(pk)) {
t.Errorf("out = %q", out)
}
}
func TestUploadUserCAErrorSurfacesBody(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "tenant not found", http.StatusNotFound)
}))
defer srv.Close()
pub := filepath.Join(t.TempDir(), "ca.pub")
os.WriteFile(pub, []byte(testUserCALine), 0o644)
_, err := UploadUserCA(context.Background(), srv.URL, "t", "nope", pub)
if err == nil || !strings.Contains(err.Error(), "tenant not found") {
t.Fatalf("want body in error, got %v", err)
}
}
func TestUploadUserCARejectsGarbageLocally(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Error("garbage key must be rejected before any request leaves the machine")
}))
defer srv.Close()
pub := filepath.Join(t.TempDir(), "ca.pub")
os.WriteFile(pub, []byte("not a key at all\n"), 0o644)
_, err := UploadUserCA(context.Background(), srv.URL, "t", "default", pub)
if err == nil || !strings.Contains(err.Error(), "not an SSH public key") {
t.Fatalf("want local parse error, got %v", err)
}
}