Skip to content
quarry Artifact [7b4a9c1450]
Login

Artifact [7b4a9c1450]

Artifact 7b4a9c145069cd2fd650f9898569407c4196ad0388e960ee67dfccd0647f9cdc:


package xfer

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"testing"
	"time"
)

// fossilPeer stands up a real fossil server with known users. The
// /user row serialisation is outside the protocol profile, so the only
// honest way to pin it is against fossil itself.
func fossilPeer(t *testing.T) (url, projectCode string) {
	t.Helper()
	if _, err := exec.LookPath("fossil"); err != nil {
		t.Skip("fossil not on PATH")
	}
	// Not t.TempDir(): fossil server forks per-connection children, and
	// a killed parent's stragglers can still be touching the repository
	// when the framework's RemoveAll sweeps — "directory not empty",
	// the flake quarry's own CI caught on its first week. Own dir, own
	// patient cleanup.
	dir, err := os.MkdirTemp("", "fossilpeer-")
	if err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() {
		for i := 0; i < 40; i++ {
			if os.RemoveAll(dir) == nil {
				return
			}
			time.Sleep(50 * time.Millisecond)
		}
		t.Logf("fossilPeer: %s not fully removed", dir)
	})
	repo := filepath.Join(dir, "peer.fossil")
	run := func(args ...string) string {
		t.Helper()
		out, err := exec.Command("fossil", args...).CombinedOutput()
		if err != nil {
			t.Fatalf("fossil %v: %v\n%s", args, err, out)
		}
		return string(out)
	}
	run("init", repo, "--admin-user", "root")
	run("user", "password", "root", "rootpw", "-R", repo)
	run("user", "new", "alice", "Alice Dev", "secretpw", "-R", repo)
	run("user", "capabilities", "alice", "ei", "-R", repo)

	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	port := ln.Addr().(*net.TCPAddr).Port
	ln.Close()
	cmd := exec.Command("fossil", "server", repo, "--port", fmt.Sprint(port))
	// Its own process group, so the kill reaches the per-connection
	// children fossil forks, not only the listener.
	cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
	if err := cmd.Start(); err != nil {
		t.Fatal(err)
	}
	t.Cleanup(func() {
		syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
		cmd.Wait()
	})
	url = fmt.Sprintf("http://127.0.0.1:%d", port)
	deadline := time.Now().Add(10 * time.Second)
	for time.Now().Before(deadline) {
		if c, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", port), time.Second); err == nil {
			c.Close()
			break
		}
		time.Sleep(50 * time.Millisecond)
	}
	for _, line := range strings.Split(run("info", repo), "\n") {
		if f := strings.Fields(line); len(f) == 2 && f[0] == "project-code:" {
			projectCode = f[1]
		}
	}
	if projectCode == "" {
		t.Fatal("no project code")
	}
	return url, projectCode
}

// An admin credential fetches the peer's users, caps verbatim.
func TestFetchUsersFromFossil(t *testing.T) {
	url, code := fossilPeer(t)
	users, err := FetchUsers(nil, url, code, "root", "rootpw")
	if err != nil {
		t.Fatalf("fetch: %v", err)
	}
	got := map[string]RemoteUser{}
	for _, u := range users {
		got[u.Login] = u
	}
	if u, ok := got["alice"]; !ok || u.Caps != "ei" {
		t.Fatalf("alice's caps did not arrive verbatim: %+v", got)
	}
	if u, ok := got["root"]; !ok || u.Caps != "s" {
		t.Fatalf("root's caps did not arrive: %+v", got)
	}
	// The credential is fossil's own §6.3 shared secret, so quarry can
	// verify a login against it directly (RFC 0004 §6).
	if u := got["alice"]; u.PWHash != SharedSecret(code, "alice", "secretpw") {
		t.Fatalf("alice's hash is not the §6.3 shared secret: %q", u.PWHash)
	}
	if got["alice"].Info != "Alice Dev" {
		t.Fatalf("info lost: %q", got["alice"].Info)
	}
}

// The REMOTE enforces admin — that is what makes this legitimate
// rather than a bypass. A developer gets no user rows at all (fossil
// strips the group from the mask), and a bad password fails the login.
func TestFetchUsersRequiresRemoteAdmin(t *testing.T) {
	url, code := fossilPeer(t)
	if _, err := FetchUsers(nil, url, code, "alice", "secretpw"); err == nil {
		t.Fatal("a non-admin fetched the user table")
	}
	if _, err := FetchUsers(nil, url, code, "root", "wrong"); err == nil {
		t.Fatal("a bad password fetched the user table")
	}
}

// The row parser is pinned independently of the network so a format
// drift is a readable failure rather than a mysterious empty import.
func TestParseUserRow(t *testing.T) {
	u, err := parseUserRow(`1787417078 'root' pw 'a5df9879' cap 's' info 'it''s mine' jx '{}'`)
	if err != nil {
		t.Fatal(err)
	}
	if u.Login != "root" || u.Caps != "s" || u.PWHash != "a5df9879" {
		t.Fatalf("row parsed wrong: %+v", u)
	}
	if u.Info != "it's mine" {
		t.Fatalf("doubled quote not undoubled: %q", u.Info)
	}
	if _, err := parseUserRow("garbage"); err == nil {
		t.Fatal("a malformed row parsed clean")
	}
}