package delivery import ( "encoding/json" "fmt" "reflect" "strings" ) // **The grants issued** (novox/hq ADR 0528 clause 2, ADR 0429 §6 rule 6, ADR 0547 §4, §5, ADR 0590): when the grants // part has issued a module's bus credentials for a fingerprint, it writes `.` in its state // `grants` (PartGrantsBucket). The assigner reads it: a node's want moves to a fingerprint only once its grant exists. // Only the grants part writes the bucket. const ( // PartGrantsState is the grants part's state of issued credentials by fingerprint, as its manifest names it. PartGrantsState = "grants" // PartGrantsBucket is that state's bucket, `_`. PartGrantsBucket = "mesh-grants_" + PartGrantsState // PartGrantsRead is that state as a reader names it in `reads`. PartGrantsRead = "mesh-grants." + PartGrantsState // PartGrantsHistory is the revisions the bucket keeps per key. PartGrantsHistory = 5 ) // PartGrantKey is the key, in PartGrantsBucket, of a module's credential issued for a fingerprint: `.`, // the hex being the fingerprint's key part (FingerprintKeyPart). func PartGrantKey(module, fingerprint string) (string, error) { return BuilderBuildKey(module, fingerprint) } // ParsePartGrantKey is the inverse of PartGrantKey; it refuses a want or held key, naming it. func ParsePartGrantKey(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, .: %w", key, err) } return module, fingerprint, nil } // Grant 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 Grant struct { V int `json:"v"` Accounts []string `json:"accounts,omitempty"` } func (g Grant) 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 } // EncodeGrant is g as JSON at Version; it refuses an empty or spaced account. func EncodeGrant(g Grant) ([]byte, error) { if err := g.check(); err != nil { return nil, err } g.V = Version return json.Marshal(g) } // DecodeGrant reads a grant; it refuses an unknown version (ErrUnknownVersion) and an empty or spaced account. func DecodeGrant(data []byte) (Grant, error) { var g Grant if err := decode(data, &g); err != nil { return Grant{}, err } if err := g.check(); err != nil { return Grant{}, err } return g, nil } // SameGrant says whether two grants are the same value. func SameGrant(a, b Grant) bool { return reflect.DeepEqual(a, b) } // NamesNode says whether the grant names module's accounts on node: its own `.` or a process's // `..`. A grant that names no account (a module that does not speak on the bus) names every // node. The assigner wants a node only once the grant names its account (issue 1945), and a put waits for it (ADR // 0546 clause 4). func (g Grant) NamesNode(node, module string) bool { if len(g.Accounts) == 0 { return true } own := node + "." + module for _, a := range g.Accounts { if a == own || strings.HasPrefix(a, own+".") { return true } } return false }