107 lines
4.1 KiB
Go
107 lines
4.1 KiB
Go
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)
|
|
}
|
|
}
|