Node principals read every node's own facts, where its record rides; the composer copy is checked to grant all the controller's grants and only that and the issue 1790 rows more (issue 1126, ADR 0555 §3)
This commit is contained in:
@@ -14,10 +14,26 @@ import (
|
||||
// byte what the controller's composes. testdata/controller-user-lists.json was written by the controller's own
|
||||
// internal/broker (mesh-controller branch fix/1945-mesh-grants-publishes-memberships, and fix/1790-grants-create-buckets for the grants part creating declared streams), lists and memberships, from fixtures_test.go compiled there unchanged; composed.conf is the
|
||||
// controller's golden of the whole server configuration. A row that differs is the copy drifting from the controller,
|
||||
// which is a fault while both compose (until the controller loses its lease, ADR 0547 §13).
|
||||
// which is a fault while both compose (until the controller loses its lease, ADR 0547 §13), but for the rows the copy
|
||||
// grants beyond it on purpose: the copy must grant everything the controller's grants, and only those rows more.
|
||||
// createdAdvisory is one subscription to a bucket's creation in a composed list, as stateCreatedHeard composes it.
|
||||
var createdAdvisory = regexp.MustCompile(`"\$JS\.EVENT\.ADVISORY\.STREAM\.CREATED\.KV_[^"]*", `)
|
||||
|
||||
// nodeRecordReads are the two rows the copy grants a node principal beyond the controller's on purpose (novox/hq ADR 0555
|
||||
// §3, issue 1126): a read of every node's own facts, `<node>.node` of the have bucket, where the node's record rides, so a
|
||||
// node agent composing its own modules reads the other nodes' public records. After the take-over (ADR 0547 §8) the grants
|
||||
// part's list is the one the bus loads; a hand-back loads the controller's, which lacks these reads, and keeps working
|
||||
// without the second milestone's composing.
|
||||
var nodeRecordReads = regexp.MustCompile(`"\$JS\.API\.(DIRECT\.GET|CONSUMER\.CREATE)\.KV_node-engine_have\.(\*\.)?\$KV\.node-engine_have\.\*\.node",? ?`)
|
||||
|
||||
// beyondTheControllers is a list the copy composed with the rows it grants beyond the controller's on purpose taken
|
||||
// out, and how many node-record rows it held: what is left must be the controller's list exactly, so the copy grants
|
||||
// everything the controller's grants and nothing more but those rows.
|
||||
func beyondTheControllers(list string) (string, int) {
|
||||
n := len(nodeRecordReads.FindAllString(list, -1))
|
||||
return nodeRecordReads.ReplaceAllString(createdAdvisory.ReplaceAllString(list, ""), ""), n
|
||||
}
|
||||
|
||||
func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) {
|
||||
raw, err := os.ReadFile(filepath.Join("testdata", "controller-user-lists.json"))
|
||||
if err != nil {
|
||||
@@ -56,9 +72,13 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) {
|
||||
// the take-over of ADR 0547 milestone 2): each principal hears its own and read buckets created, so a state
|
||||
// missing at its start is bound when it appears. The controller's composer is frozen and its list no longer
|
||||
// loaded; while it is not, this row is left out of the comparison and nothing else.
|
||||
list = createdAdvisory.ReplaceAllString(list, "")
|
||||
list, records := beyondTheControllers(list)
|
||||
if list != want[i].List {
|
||||
t.Errorf("the list differs from the controller's:\n%s", list)
|
||||
t.Errorf("the list differs from the controller's beyond the intended rows:\n%s", list)
|
||||
}
|
||||
// And the intended rows are there: two for each node principal of the fixture.
|
||||
if nodes := strings.Count(want[i].List, `"$KV.node-engine_have.`); nodes > 0 && records == 0 {
|
||||
t.Errorf("no node principal reads the other nodes' records")
|
||||
}
|
||||
// And every membership the records issue, as the controller's MembershipFor makes it (ADR 0547 §5): the
|
||||
// grants part publishes them.
|
||||
@@ -135,8 +155,12 @@ func TestTheCopiedComposerComposesAsTheControllers(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, records := beyondTheControllers(got)
|
||||
if records != 2 {
|
||||
t.Errorf("node one is granted %d of the two reads of the other nodes' records", records)
|
||||
}
|
||||
if got != string(want) {
|
||||
t.Errorf("the configuration differs from the controller's golden:\n%s", got)
|
||||
t.Errorf("the configuration differs from the controller's golden beyond the intended rows:\n%s", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -882,6 +882,10 @@ func PermissionsFor(p Principal) (Permissions, error) {
|
||||
return Permissions{}, err
|
||||
}
|
||||
pub = append(pub, haveKeys...)
|
||||
// And every node's own facts, `<node>.node`, its record among them (issue 1126, ADR 0555 §3): a node agent composing
|
||||
// its own modules reads the other nodes' public records, which its firewall and resolver need (ADR 0547 §2). Read
|
||||
// only: each node still writes its own keys alone.
|
||||
pub = append(pub, readGrants(delivery.HaveBucket, "*."+delivery.HaveNodeWord)...)
|
||||
// And an object of the declaration bucket by its name, and nothing of the bucket's list or writing
|
||||
// (novox/hq ADR 0486 §2, issue 1697).
|
||||
fileReads, err := DeclarationFilesReads(p.Node)
|
||||
|
||||
Reference in New Issue
Block a user