119 lines
4.6 KiB
Go
119 lines
4.6 KiB
Go
package grantsstate
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/keys"
|
|
"git.novox.be/novox/mesh-sdk/go/seal"
|
|
)
|
|
|
|
// 1394/B1b: a grant key round-trips; a want or held key is refused, naming it; a grant round-trips and one naming an
|
|
// empty account is refused.
|
|
func TestTheGrantsIssued(t *testing.T) {
|
|
fp := "src2:" + strings.Repeat("c", 64)
|
|
key, err := PartGrantKey("mesh-delivery-assigner", fp)
|
|
if err != nil || key != "mesh-delivery-assigner."+strings.Repeat("c", 64) {
|
|
t.Fatalf("PartGrantKey = %q, %v", key, err)
|
|
}
|
|
module, back, err := ParsePartGrantKey(key)
|
|
if err != nil || module != "mesh-delivery-assigner" || back != fp {
|
|
t.Errorf("ParsePartGrantKey = %q, %q, %v", module, back, err)
|
|
}
|
|
held := "mesh-mail." + strings.Repeat("c", 64) + ".held"
|
|
for _, bad := range []string{"want.ace.mesh-mail", held, "ace.mesh-mail", ""} {
|
|
if _, _, err := ParsePartGrantKey(bad); err == nil || !strings.Contains(err.Error(), "\""+bad+"\"") {
|
|
t.Errorf("ParsePartGrantKey(%q) = %v, want a refusal naming it", bad, err)
|
|
}
|
|
}
|
|
for _, g := range []Grant{{}, {Accounts: []string{"UABC", "UDEF"}}} {
|
|
data, err := EncodeGrant(g)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := DecodeGrant(data)
|
|
g.V = keys.Version
|
|
if err != nil || !SameGrant(got, g) {
|
|
t.Errorf("grant %+v came back %+v, %v", g, got, err)
|
|
}
|
|
}
|
|
if _, err := EncodeGrant(Grant{Accounts: []string{""}}); err == nil {
|
|
t.Errorf("a grant naming an empty account was accepted")
|
|
}
|
|
g := Grant{Accounts: []string{"ace.mesh-build-go", "shanks.mesh-mail.reader"}}
|
|
for _, c := range []struct {
|
|
g Grant
|
|
node, module string
|
|
names bool
|
|
}{
|
|
{g, "ace", "mesh-build-go", true},
|
|
{g, "shanks", "mesh-mail", true},
|
|
{g, "g14", "mesh-build-go", false},
|
|
{g, "ace", "mesh-build", false},
|
|
{Grant{}, "novox", "nats", true},
|
|
} {
|
|
if got := c.g.NamesNode(c.node, c.module); got != c.names {
|
|
t.Errorf("%v names %s.%s: %v, want %v", c.g.Accounts, c.node, c.module, got, c.names)
|
|
}
|
|
}
|
|
}
|
|
|
|
// The grants part's list sealed to the bus node (novox/hq ADR 0547 clause 7): the key's value round-trips and refuses what
|
|
// the bus node could not follow, and the object opens with the node's key alone, to the digest the key names.
|
|
func TestTheGrantsPartsSealedUserList(t *testing.T) {
|
|
list := []byte("accounts { MESH { users = [ { user: \"controller\" } ] } }\n")
|
|
digest := UserListDigest(list)
|
|
node, err := seal.GenerateKeyPair()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
other, _ := seal.GenerateKeyPair()
|
|
g := GrantsUserList{Follow: FollowGrants, Digest: digest, Object: UserListObjectName(digest),
|
|
SealedTo: SealingKeyID(node.PublicKey), Node: "novox", Revision: 1, Users: 1}
|
|
raw, err := EncodeGrantsUserList(g)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
back, err := DecodeGrantsUserList(raw)
|
|
if err != nil || back.Digest != digest || back.LeftOut == nil || back.Follow != FollowGrants {
|
|
t.Fatalf("decoded %+v, %v", back, err)
|
|
}
|
|
// Issue 1126 B4a (ADR 0590): a key written with the lease's flag still reads; the controller's list is no source.
|
|
if old, err := DecodeGrantsUserList([]byte(`{"v":1,"follow":"grants","digest":"","object":"","sealed_to":"",` +
|
|
`"node":"novox","revision":3,"users":40,"left_out":[],"at":"2026-10-30T10:00:00Z","lease-handed":true}`)); err != nil || old.Revision != 3 {
|
|
t.Errorf("a key with the lease's flag decodes %+v, %v", old, err)
|
|
}
|
|
for _, bad := range []GrantsUserList{{Follow: "both", Digest: digest, Object: g.Object}, {Follow: "controller"},
|
|
{Follow: FollowGrants, Digest: digest, Object: "other"}, {Follow: FollowGrants, Digest: "md5:x", Object: "md5:x"}} {
|
|
if _, err := EncodeGrantsUserList(bad); err == nil {
|
|
t.Errorf("%+v was written", bad)
|
|
}
|
|
}
|
|
if _, err := EncodeGrantsUserList(GrantsUserList{Follow: FollowGrants}); err != nil {
|
|
t.Errorf("a switch with no list yet refused: %v", err)
|
|
}
|
|
object, err := SealUserList(list, node.PublicKey)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if bytes.Contains(object, []byte("controller")) {
|
|
t.Fatal("the object carries the list in the clear")
|
|
}
|
|
opener := func(priv string) func(seal.Box) ([]byte, error) {
|
|
return func(b seal.Box) ([]byte, error) { s, err := seal.Open(b, priv); return []byte(s), err }
|
|
}
|
|
if got, err := OpenUserList(object, digest, opener(node.PrivateKey)); err != nil || !bytes.Equal(got, list) {
|
|
t.Fatalf("opened %q, %v", got, err)
|
|
}
|
|
if _, err := OpenUserList(object, digest, opener(other.PrivateKey)); err == nil {
|
|
t.Error("another key opened the list")
|
|
}
|
|
if _, err := OpenUserList(object, "sha256:00", opener(node.PrivateKey)); err == nil {
|
|
t.Error("a list of another digest was taken")
|
|
}
|
|
if _, err := SealUserList(list, "not a key"); err == nil {
|
|
t.Error("a list was sealed to no key")
|
|
}
|
|
}
|