Files
mesh-sdk/go/delivery/grants.go
T

76 lines
2.9 KiB
Go

package delivery
import (
"encoding/json"
"fmt"
"reflect"
"strings"
)
// The controller's grants (novox/hq ADR 0528 clause 2, ADR 0429 §6 rule 6): when the controller has issued a module's
// bus credential for a fingerprint, it writes `<module>.<fingerprint hex>` in its bucket grants. The assigner reads
// it: a node's want moves to a fingerprint only once its grant exists. Only the controller writes the bucket.
//
// The names say Controller, since the catalogue's GrantKey (catalogue/streams.go) is a stream's grant pattern, unrelated.
const (
// ControllerGrantsBucket is the controller's bucket of issued credentials by fingerprint.
ControllerGrantsBucket = "mesh-controller_grants"
// ControllerGrantsHistory is the revisions the bucket keeps per key.
ControllerGrantsHistory = 5
)
// ControllerGrantKey is the key, in ControllerGrantsBucket, of a module's credential issued for a fingerprint:
// `<module>.<hex>`, the hex being the fingerprint's key part (FingerprintKeyPart).
func ControllerGrantKey(module, fingerprint string) (string, error) {
return BuilderBuildKey(module, fingerprint)
}
// ParseControllerGrantKey is the inverse of ControllerGrantKey; it refuses a want or held key, naming it.
func ParseControllerGrantKey(key string) (module, fingerprint string, err error) {
if module, fingerprint, err = ParseBuilderBuildKey(key); err != nil {
return "", "", fmt.Errorf("delivery: %q is not a grant key, <module>.<fingerprint hex>: %w", key, err)
}
return module, fingerprint, nil
}
// ControllerGrant is the value of a grant key: the bus accounts (their public keys) the module's processes were issued
// for that fingerprint, none for a module whose grants the tool runner's account carries, so a reissue for another
// account is a new revision. The entry's own time says when it was issued.
type ControllerGrant struct {
V int `json:"v"`
Accounts []string `json:"accounts,omitempty"`
}
func (g ControllerGrant) check() error {
for _, a := range g.Accounts {
if strings.TrimSpace(a) == "" || strings.ContainsAny(a, " \t\n") {
return fmt.Errorf("delivery: a grant names an empty or spaced account %q", a)
}
}
return nil
}
// EncodeControllerGrant is g as JSON at Version; it refuses an empty or spaced account.
func EncodeControllerGrant(g ControllerGrant) ([]byte, error) {
if err := g.check(); err != nil {
return nil, err
}
g.V = Version
return json.Marshal(g)
}
// DecodeControllerGrant reads a grant; it refuses an unknown version (ErrUnknownVersion) and an empty or spaced account.
func DecodeControllerGrant(data []byte) (ControllerGrant, error) {
var g ControllerGrant
if err := decode(data, &g); err != nil {
return ControllerGrant{}, err
}
if err := g.check(); err != nil {
return ControllerGrant{}, err
}
return g, nil
}
// SameControllerGrant says whether two grants are the same value.
func SameControllerGrant(a, b ControllerGrant) bool { return reflect.DeepEqual(a, b) }