Files
mesh-controller/internal/link/setuid_search_test.go
T
jochen 9cef820117
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
Ask a node's engine for a fresh setuid search at the terminal (hq issue 361)
After the operator removes by hand what the last search found, no apply says so and nothing searched again
until the next day. node setuid-search <node> signs the ask as a hand-over is, under its own context, on a
subject only the node's engine hears and only the controller may publish.
2026-10-10 01:56:56 +02:00

60 lines
2.3 KiB
Go

package link
import (
"context"
"crypto/ed25519"
"encoding/json"
"strings"
"testing"
"time"
"github.com/nats-io/nats.go"
"github.com/novox/mesh-controller/internal/broker"
"github.com/novox/mesh-controller/internal/testbus"
)
// A fresh setuid search is asked of the machine on its own subject, signed under its own context — never a
// hand-over's — naming no path, and the engine's answer comes back (novox/hq issue 361). The engine holds the
// same subject and context (mesh-host internal/link, TestTheSetuidSearchAskKeepsItsSubjectAndContext).
func TestASetuidSearchIsAskedOfTheMachineSignedUnderItsOwnContext(t *testing.T) {
if broker.AskSetuidSearchSubject("laptop") != "mesh.node.laptop.ask.setuid-search" ||
SetuidSearchContext != "novox-mesh setuid-search v1\n" {
t.Fatalf("the subject %q, the context %q", broker.AskSetuidSearchSubject("laptop"), SetuidSearchContext)
}
conn, err := nats.Connect(testbus.URL(t))
if err != nil {
t.Fatal(err)
}
t.Cleanup(conn.Close)
signer, public := testSigner(t)
var heard HandOverAsk
engine, err := conn.Subscribe(broker.AskSetuidSearchSubject("laptop"), func(msg *nats.Msg) {
var signed SignedHandOver
_ = json.Unmarshal(msg.Data, &signed)
if ed25519.Verify(public, append([]byte(HandOverContext), signed.Ask...), signed.Signature) ||
!ed25519.Verify(public, append([]byte(SetuidSearchContext), signed.Ask...), signed.Signature) {
_ = msg.Respond([]byte(`{"refused":"not signed as a setuid search"}`))
return
}
_ = json.Unmarshal(signed.Ask, &heard)
body, _ := json.Marshal(HandOverAnswer{Said: "a new search starts, asked by " + heard.By})
_ = msg.Respond(body)
})
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = engine.Unsubscribe() })
a, err := AskSetuidSearch(context.Background(), conn, signer, "laptop", "jo", 5*time.Second)
if err != nil || a.Said != "a new search starts, asked by jo" {
t.Fatalf("answered %+v, %v", a, err)
}
if heard.Node != "laptop" || heard.Path != "" || heard.Nonce == "" || heard.Expires.IsZero() {
t.Fatalf("the engine heard %+v", heard)
}
if _, err := AskSetuidSearch(context.Background(), conn, signer, "anchor", "jo", 5*time.Second); err == nil ||
!strings.Contains(err.Error(), "no search was started") {
t.Fatalf("a machine with no engine listening: %v", err)
}
}