mesh-grants: a list in force with no users kept is checked against the users the bus loaded, so the first list after take-over keeps every user (review of the switch, issue 1945)
This commit is contained in:
@@ -975,6 +975,17 @@ func TestGrants(t *testing.T) {
|
||||
if now, _, _ := p.Sealed(); now.Digest != g.Digest {
|
||||
t.Errorf("the list in force moved without a.mod-x: %s", now.Digest)
|
||||
}
|
||||
// A list in force written before the part kept its users (no sealed-users): checked against the users the
|
||||
// bus loaded, so the first list after take-over has the same protection.
|
||||
b.mu.Lock()
|
||||
delete(b.own[grants.StateUserList], grants.KeyWrittenUsers)
|
||||
b.mu.Unlock()
|
||||
if res := p.Compose(); res.Sealed == nil || !strings.Contains(res.Sealed.Why, "a.mod-x would be left out") {
|
||||
t.Errorf("a pass with no kept users that drops a.mod-x's hash = %+v", res.Sealed)
|
||||
}
|
||||
if now, _, _ := p.Sealed(); now.Digest != g.Digest {
|
||||
t.Errorf("the list in force moved without a.mod-x and no kept users: %s", now.Digest)
|
||||
}
|
||||
b.put(grants.ReadAccounts, "a.mod-x", json.RawMessage(hash))
|
||||
if p.Compose(); func() bool { now, _, _ := p.Sealed(); return now.Digest != g.Digest }() {
|
||||
t.Errorf("the list in force moved with every read and hash back")
|
||||
|
||||
@@ -229,7 +229,12 @@ func (p *Part) Compose() Result {
|
||||
if list, n, leftOut, err := ComposeList(recs, in.Hashes); err != nil {
|
||||
res.Errors = append(res.Errors, "the user list could not be composed whole: "+err.Error())
|
||||
} else {
|
||||
p.writeSealed(&res, list, n, leftOut, in)
|
||||
// The users the bus loaded: what a first list after take-over must keep where the part has not yet written one.
|
||||
var bus []string
|
||||
if st.Compare.Unread == "" {
|
||||
bus = names(loaded)
|
||||
}
|
||||
p.writeSealed(&res, list, n, leftOut, in, bus)
|
||||
}
|
||||
ms := Memberships(recs)
|
||||
res.Memberships = len(ms)
|
||||
@@ -342,3 +347,11 @@ func sortedKeys[V any](m map[string]V) []string {
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
func names(us []User) []string {
|
||||
out := make([]string, 0, len(us))
|
||||
for _, u := range us {
|
||||
out = append(out, u.User)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ func followOf(state string) string {
|
||||
|
||||
// writeSealed puts the list sealed to the bus node and writes the key, each only when what it says changed. It says in
|
||||
// res what it wrote, or why it wrote nothing.
|
||||
func (p *Part) writeSealed(res *Result, list []byte, users int, leftOut []string, in Inputs) {
|
||||
func (p *Part) writeSealed(res *Result, list []byte, users int, leftOut []string, in Inputs, bus []string) {
|
||||
cur, _, had := p.Sealed()
|
||||
next := cur
|
||||
next.Follow = followOf(p.SwitchState())
|
||||
@@ -90,10 +90,10 @@ func (p *Part) writeSealed(res *Result, list []byte, users int, leftOut []string
|
||||
why = "no node runs the bus's module, so the list is sealed to nobody"
|
||||
case key == "":
|
||||
why = "the bus node's have of the bus module names no key yet (its node-engine says it from this release)"
|
||||
case next.Follow == delivery.FollowGrants && cur.Digest != digest && p.holdsBack(in, leftOut) != "":
|
||||
case next.Follow == delivery.FollowGrants && cur.Digest != digest && p.holdsBack(in, leftOut, bus) != "":
|
||||
// After take-over the list in force is never replaced by one composed past a failed read, or one that drops a user
|
||||
// of it for want of a hash: that user's module would be locked out of the bus (review of the switch).
|
||||
why = p.holdsBack(in, leftOut)
|
||||
why = p.holdsBack(in, leftOut, bus)
|
||||
case cur.Digest != digest || cur.SealedTo != delivery.SealingKeyID(key) || cur.Node != in.BusNode:
|
||||
object, err := delivery.SealUserList(list, key)
|
||||
if err == nil {
|
||||
@@ -265,8 +265,10 @@ func listUsers(list []byte) []string {
|
||||
}
|
||||
|
||||
// holdsBack says why a list composed now may not replace the one in force after take-over: a read failed this pass, or
|
||||
// a user of the list in force is left out for want of a hash. Empty when it may.
|
||||
func (p *Part) holdsBack(in Inputs, leftOut []string) string {
|
||||
// a user of the list in force is left out for want of a hash. The list in force is the one the part wrote, by the users
|
||||
// it kept of it; where it kept none (a list written before this release), the users the bus loaded (bus, nil when
|
||||
// they could not be read). Empty when it may.
|
||||
func (p *Part) holdsBack(in Inputs, leftOut, bus []string) string {
|
||||
if f := in.ReadFailures(); len(f) > 0 {
|
||||
return "held: the list in force is kept while a read of this pass failed (" + strings.Join(f, "; ") + ")"
|
||||
}
|
||||
@@ -275,8 +277,13 @@ func (p *Part) holdsBack(in Inputs, leftOut []string) string {
|
||||
return "held: the users of the list in force cannot be read: " + err.Error()
|
||||
}
|
||||
var had []string
|
||||
if found {
|
||||
switch {
|
||||
case found:
|
||||
_ = json.Unmarshal(raw, &had)
|
||||
case bus == nil:
|
||||
return "held: the users of the list in force are not known: the part kept none, and the loaded list could not be read"
|
||||
default:
|
||||
had = bus
|
||||
}
|
||||
var lost []string
|
||||
for _, u := range leftOut {
|
||||
|
||||
Reference in New Issue
Block a user