From the lease write the vault makes the token (ADR 0587 §3), mesh-cli opens and prints it, and the node-engine parses it: three repositories, so the type leaves mesh-host's internal/identity (which keeps an alias) for go/delivery, field names unchanged from the controller's token issue, the tunnel kept (the lead's settlement 7). Encode is added, refusing a token with a part missing. The test reads a token the controller's own code made and encodes it back to the same line. Paid by merging TestGateBuckets into TestKeysOfPlainNamesAreUnchanged. (issue 774, 774/S36) Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
54 lines
2.7 KiB
Go
54 lines
2.7 KiB
Go
package delivery
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// controllerToken is a token made by the controller's own `token issue` code: mesh-control origin/main 5c281ad9,
|
|
// internal/token Token.Encode, run on 2026-11-02 for node anchor, adopted, with a first tunnel, its signer the ed25519
|
|
// key of the seed 00 01 .. 1f. Copied, never written by hand.
|
|
const controllerToken = "eyJ2IjoxLCJub2RlIjoiYW5jaG9yIiwiYnJva2VyIjoiMTkyLjAuMi4xMDo0MjIyIiwiZmluZ2VycHJpbnQiOiJzaGEyNTY6YWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYiIsInNpZ25lciI6IkE2RUh2L1BPRUw0ZGNOMFk1MHZBbVdmazFqQ2JwUTFmSGR5R1pCSlZNYmc9Iiwic2VjcmV0IjoiYS1vbmUtdGltZS1zZWNyZXQiLCJhZG9wdGVkIjp0cnVlLCJ0dW5uZWwiOnsia2V5IjoiZEdobExXMWhZMmhwYm1WekxXdGxlUT09IiwiYWRkcmVzcyI6IjEwLjAuMC43LzI0IiwicmFuZ2UiOiIxMC4wLjAuMC8yNCIsImh1Yl9rZXkiOiJkR2hsTFdoMVluTXRhMlY1IiwiaHViX2VuZHBvaW50IjoiMTk4LjUxLjEwMC4xOjUxODIwIn19"
|
|
|
|
// 774/S36/AC1: a token the controller made reads, with the moved code, field for field as it was written; encoding it
|
|
// again gives the same line; a part missing is refused naming it; garbage and another version are not tokens.
|
|
func TestATokenTheControllerMadeReads(t *testing.T) {
|
|
signer, _ := hex.DecodeString("03a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8")
|
|
want := Token{Version: 1, Node: "anchor", Broker: "192.0.2.10:4222",
|
|
Fingerprint: "sha256:" + strings.Repeat("ab", 32), Signer: signer, Secret: "a-one-time-secret", Adopted: true,
|
|
Tunnel: &TokenTunnel{Key: "dGhlLW1hY2hpbmVzLWtleQ==", Address: "10.0.0.7/24", Range: "10.0.0.0/24",
|
|
HubKey: "dGhlLWh1YnMta2V5", HubEndpoint: "198.51.100.1:51820"}}
|
|
got, err := ParseToken(" " + controllerToken + "\n")
|
|
if err != nil || !reflect.DeepEqual(got, want) || len(got.SignerKey()) != 32 {
|
|
t.Fatalf("parsed %+v, %v", got, err)
|
|
}
|
|
if again, err := got.Encode(); err != nil || again != controllerToken {
|
|
t.Errorf("encoded again: %q, %v", again, err)
|
|
}
|
|
for _, c := range []struct {
|
|
cut func(*Token)
|
|
expect string
|
|
}{
|
|
{func(t *Token) { t.Broker = "" }, "broker's address"},
|
|
{func(t *Token) { t.Fingerprint = "" }, "fingerprint"},
|
|
{func(t *Token) { t.Signer = t.Signer[:5] }, "signing key"},
|
|
{func(t *Token) { t.Secret = "" }, "one-time secret"},
|
|
{func(t *Token) { t.Tunnel = &TokenTunnel{Key: "k"} }, "where the hub's tunnel is dialled"},
|
|
} {
|
|
cut := want
|
|
cut.Tunnel = &TokenTunnel{}
|
|
*cut.Tunnel = *want.Tunnel
|
|
c.cut(&cut)
|
|
if _, err := cut.Encode(); err == nil || !strings.Contains(err.Error(), c.expect) {
|
|
t.Errorf("a token without %s: %v", c.expect, err)
|
|
}
|
|
}
|
|
for _, bad := range []string{"", "!!!not base64!!!", "aGVsbG8", "eyJ2Ijo5OX0"} {
|
|
if _, err := ParseToken(bad); err == nil {
|
|
t.Errorf("%q parsed as a token", bad)
|
|
}
|
|
}
|
|
}
|