The vault's get judges a licence's secret by a rule of its own, before the asker rule: a grant goes to any proven caller, and an access token to a machine on which a consumer's binding in licence-bindings names that licence's key; a refusal names the rule and is recorded. Builds: design 39 bus-and-switch §16, §17, A1, B3, B8, B25 Claude-Session: https://claude.ai/code/session_01NcbgfHb9d4T9nq5Q6WqdiJ
101 lines
4.2 KiB
Go
101 lines
4.2 KiB
Go
package main
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// putLicence hands one part of a licence to the vault in licence-put's two steps.
|
|
func putLicence(t *testing.T, v testVault, caller, key, part string, generation int, value string) (Changed, error) {
|
|
t.Helper()
|
|
o, err := v.LicencePutOpen(caller, key, part, generation)
|
|
if err != nil {
|
|
return Changed{}, err
|
|
}
|
|
return v.LicencePutTake(caller, o.Call, sealToCall(t, o.SealTo, value))
|
|
}
|
|
|
|
// design 39 bus-and-switch §15, B3, B5: the caller's generation is the record's; an older one is refused; the
|
|
// event names the secret and its generation, never the value.
|
|
func TestLicencePutKeepsTheNewestGenerationAndRefusesAnOlder(t *testing.T) {
|
|
v := newVault(t, nil)
|
|
const manager = "claude-licence-manager"
|
|
if _, err := putLicence(t, v, manager, "jane_at_example.org", LicenceGrant, 3, `{"refreshToken":"rt-3"}`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rec, held, err := v.Store.Read("licence.jane_at_example.org.grant")
|
|
if err != nil || !held || rec.Generation != 3 {
|
|
t.Fatalf("held %v at generation %d (%v), want generation 3", held, rec.Generation, err)
|
|
}
|
|
if _, err := putLicence(t, v, manager, "jane_at_example.org", LicenceGrant, 2, `{"refreshToken":"rt-2"}`); err == nil ||
|
|
!strings.Contains(err.Error(), "not newer") {
|
|
t.Fatalf("an older generation was taken: %v", err)
|
|
}
|
|
if c, err := putLicence(t, v, manager, "jane_at_example.org", LicenceGrant, 4, `{"refreshToken":"rt-4"}`); err != nil ||
|
|
c.Generation != 4 {
|
|
t.Fatalf("generation 4: %+v %v", c, err)
|
|
}
|
|
said := 0
|
|
for _, raw := range v.bus.raw {
|
|
if strings.Contains(raw, "rt-") {
|
|
t.Fatalf("an event carries the value: %s", raw)
|
|
}
|
|
if strings.Contains(raw, `"secret-changed"`) && strings.Contains(raw, "licence.jane_at_example.org.grant") {
|
|
said++
|
|
}
|
|
}
|
|
if said != 2 {
|
|
t.Fatalf("secret-changed said %d times, want 2: %v", said, v.bus.raw)
|
|
}
|
|
for _, bad := range []struct{ key, part string }{{"Jane@x", LicenceGrant}, {"jane", "refresh"}} {
|
|
if _, err := v.LicencePutOpen(manager, bad.key, bad.part, 1); err == nil {
|
|
t.Errorf("licence-put took %s/%s", bad.key, bad.part)
|
|
}
|
|
}
|
|
if _, err := v.LicencePutOpen("", "jane", LicenceAccess, 1); err == nil {
|
|
t.Error("licence-put took an unproven caller")
|
|
}
|
|
}
|
|
|
|
// design 39 bus-and-switch §17/AC1, §16, B3, B8, B25: a licence's access token is refused to a machine no binding names
|
|
// and for a licence its binding does not name, and given after a binding.changed naming it, to any consumer of that
|
|
// machine; the grant goes to the manager's tool runner, with no machine guard.
|
|
func TestATokenIsGivenOnlyToAMachineABindingNames(t *testing.T) {
|
|
v := newVault(t, nil)
|
|
lb, _ := memLicenceBindings()
|
|
v.LicenceBindings = &lb
|
|
const manager = "novox.node-tools"
|
|
for _, key := range []string{"jane_at_example.org", "api"} {
|
|
for _, part := range []string{LicenceGrant, LicenceAccess} {
|
|
if _, err := putLicence(t, v, manager, key, part, 1, key+"-"+part); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
access := "licence.jane_at_example.org.access"
|
|
if _, _, err := asks(v, "g14.node-tools", access); err == nil || !strings.Contains(err.Error(), "no consumer on g14") {
|
|
t.Fatalf("a machine no binding names was handed the token: %v", err)
|
|
}
|
|
if err := lb.Heard([]byte(`{"consumer":"g14","licence":"api","key":"api","generation":4}`)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, _, err := asks(v, "g14.node-tools", access); err == nil {
|
|
t.Fatal("a licence its binding does not name was handed to g14")
|
|
}
|
|
if got, _, err := asks(v, "g14.node-tools", "licence.api.access"); err != nil || got != "api-access" {
|
|
t.Fatalf("g14's own licence: %q %v", got, err)
|
|
}
|
|
if err := lb.Heard([]byte(`{"consumer":"g14.worker-1","licence":"jane@example.org","key":"jane_at_example.org","generation":2}`)); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got, _, err := asks(v, "g14.node-tools", access); err != nil || got != "jane_at_example.org-access" {
|
|
t.Fatalf("after a binding.changed naming it: %q %v", got, err)
|
|
}
|
|
if _, _, err := asks(v, "ace.node-tools", access); err == nil {
|
|
t.Fatal("another machine was handed g14's token")
|
|
}
|
|
if got, _, err := asks(v, manager, "licence.jane_at_example.org.grant"); err != nil || got != "jane_at_example.org-grant" {
|
|
t.Fatalf("the manager's grant: %q %v", got, err)
|
|
}
|
|
}
|