Write grants.<module>.<fp> once the bus's user list carrying a module's manifest at that fingerprint is sent, so the assigner's sixth rule reads that its credential exists (issue 1394, subtask C9b; ADR 0528 clause 2, ADR 0429 §6)
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
"github.com/nats-io/nats.go/jetstream"
|
||||
|
||||
"github.com/novox/mesh-controller/internal/broker"
|
||||
)
|
||||
|
||||
func grantFP(c byte) string { return sdkdelivery.SourcePrefix2 + strings.Repeat(string(c), 64) }
|
||||
|
||||
// 1394/AC (unit half): the list sent issues the credential of every module whose accounts all have a password, at the
|
||||
// fingerprint its build names; a module with an account the list leaves out, and one registered at a fingerprint not of
|
||||
// the new form, get no grant; a module with no account of its own is granted with none.
|
||||
func TestTheGrantsAListIssues(t *testing.T) {
|
||||
x := broker.Principal{Kind: broker.KindModule, Node: "novox", Module: "mesh-x"}
|
||||
xp := broker.Principal{Kind: broker.KindProcess, Node: "novox", Module: "mesh-x", Process: "run"}
|
||||
y := broker.Principal{Kind: broker.KindModule, Node: "ace", Module: "mesh-y"}
|
||||
node := broker.Principal{Kind: broker.KindNode, Node: "novox"}
|
||||
hashes := map[string]string{x.Username(): "h1", xp.Username(): "h2", node.Username(): "h3"}
|
||||
got := grantsFrom([]broker.Principal{x, xp, y, node}, hashes,
|
||||
map[string]string{"mesh-x": grantFP('a'), "mesh-y": grantFP('b'), "web": grantFP('c'), "old": "src1:abc"})
|
||||
xKey, _ := sdkdelivery.ControllerGrantKey("mesh-x", grantFP('a'))
|
||||
webKey, _ := sdkdelivery.ControllerGrantKey("web", grantFP('c'))
|
||||
if len(got) != 2 {
|
||||
t.Fatalf("grants = %v, want mesh-x's and web's", got)
|
||||
}
|
||||
if g := got[xKey]; len(g.Accounts) != 2 || g.Accounts[0] != x.Username() && g.Accounts[1] != x.Username() {
|
||||
t.Errorf("mesh-x's grant = %+v, want both its accounts", g)
|
||||
}
|
||||
if g, ok := got[webKey]; !ok || len(g.Accounts) != 0 {
|
||||
t.Errorf("web, with no account of its own, is granted %+v, %v", g, ok)
|
||||
}
|
||||
}
|
||||
|
||||
// fakeGrants is the grants bucket in memory.
|
||||
type fakeGrants struct {
|
||||
values map[string][]byte
|
||||
creates int
|
||||
updates int
|
||||
}
|
||||
|
||||
type fakeEntry struct {
|
||||
key string
|
||||
value []byte
|
||||
rev uint64
|
||||
}
|
||||
|
||||
func (e fakeEntry) Bucket() string { return broker.GrantsBucket }
|
||||
func (e fakeEntry) Key() string { return e.key }
|
||||
func (e fakeEntry) Value() []byte { return e.value }
|
||||
func (e fakeEntry) Revision() uint64 { return e.rev }
|
||||
func (e fakeEntry) Created() time.Time { return time.Time{} }
|
||||
func (e fakeEntry) Delta() uint64 { return 0 }
|
||||
func (e fakeEntry) Operation() jetstream.KeyValueOp { return jetstream.KeyValuePut }
|
||||
|
||||
func (f *fakeGrants) Get(_ context.Context, key string) (jetstream.KeyValueEntry, error) {
|
||||
v, ok := f.values[key]
|
||||
if !ok {
|
||||
return nil, jetstream.ErrKeyNotFound
|
||||
}
|
||||
return fakeEntry{key: key, value: v, rev: 1}, nil
|
||||
}
|
||||
|
||||
func (f *fakeGrants) Create(_ context.Context, key string, value []byte, _ ...jetstream.KVCreateOpt) (uint64, error) {
|
||||
if _, ok := f.values[key]; ok {
|
||||
return 0, jetstream.ErrKeyExists
|
||||
}
|
||||
f.values[key] = value
|
||||
f.creates++
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (f *fakeGrants) Update(_ context.Context, key string, value []byte, _ uint64) (uint64, error) {
|
||||
f.values[key] = value
|
||||
f.updates++
|
||||
return 2, nil
|
||||
}
|
||||
|
||||
// 1394/AC (unit half): a grant is created once; a second send of the same list writes nothing; a reissue for other
|
||||
// accounts is a new revision of the same key.
|
||||
func TestAGrantIsWrittenOnceAndMovedOnAReissue(t *testing.T) {
|
||||
kv := &fakeGrants{values: map[string][]byte{}}
|
||||
key, _ := sdkdelivery.ControllerGrantKey("mesh-x", grantFP('a'))
|
||||
grants := map[string]sdkdelivery.ControllerGrant{key: {Accounts: []string{"novox.mesh-x"}}}
|
||||
for range 2 {
|
||||
if _, err := keepGrants(t.Context(), kv, grants); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if kv.creates != 1 || kv.updates != 0 {
|
||||
t.Errorf("creates %d, updates %d, want one create", kv.creates, kv.updates)
|
||||
}
|
||||
grants[key] = sdkdelivery.ControllerGrant{Accounts: []string{"ace.mesh-x"}}
|
||||
if wrote, err := keepGrants(t.Context(), kv, grants); err != nil || wrote != 1 || kv.updates != 1 {
|
||||
t.Errorf("a reissue wrote %d (%v), updates %d", wrote, err, kv.updates)
|
||||
}
|
||||
if g, err := sdkdelivery.DecodeControllerGrant(kv.values[key]); err != nil || g.Accounts[0] != "ace.mesh-x" {
|
||||
t.Errorf("the grant reads %+v, %v", g, err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user