nats: say whether a user's grant is in force on the running server, not only written (hq issue 353)
mesh/merge-gate pass: builds nats, sent nowhere; merging this needs a person's bus upgrade; 1 wait(s) for a person; every machine composes with the change …
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
mesh/merge-gate pass: builds nats, sent nowhere; merging this needs a person's bus upgrade; 1 wait(s) for a person; every machine composes with the change …
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery delivered
nats_user_can read the user list on the machine. A list written is not a list the bus enforces until the server reloads it: on 2026-10-09 the controller asked between the write and the reload's effect. The answer now carries in_force — allowed in the file, and the server's config_load_time after the file's write — with why when it is not, and anything not known is not in force.
This commit is contained in:
@@ -536,3 +536,39 @@ func UserCan(conf []byte, user, action, subject string) (map[string]any, error)
|
||||
}
|
||||
return nil, fmt.Errorf("the bus has no user %q; nats_users lists them", user)
|
||||
}
|
||||
|
||||
// InForce adds to a UserCan answer whether the running server holds what the file says (novox/hq issue 353):
|
||||
// `in_force` only when the grant is allowed in the file and the server's last configuration load
|
||||
// (`config_load_time` of /varz) came after the file was last written. Anything not known is not in force, said
|
||||
// in `why`: an asker waiting for its grant must not take a list sent for a list the bus enforces.
|
||||
func InForce(answer map[string]any, written time.Time, writtenErr error, varz []byte, varzErr error) map[string]any {
|
||||
answer["in_force"] = false
|
||||
switch {
|
||||
case writtenErr != nil:
|
||||
answer["why"] = writtenErr.Error()
|
||||
return answer
|
||||
case varzErr != nil:
|
||||
answer["why"] = "the server's last configuration load could not be read: " + varzErr.Error()
|
||||
return answer
|
||||
}
|
||||
var v struct {
|
||||
ConfigLoadTime time.Time `json:"config_load_time"`
|
||||
}
|
||||
if err := json.Unmarshal(varz, &v); err != nil || v.ConfigLoadTime.IsZero() {
|
||||
answer["why"] = "the server does not say when it last loaded its configuration"
|
||||
return answer
|
||||
}
|
||||
answer["file_written"] = written.UTC().Format(time.RFC3339Nano)
|
||||
answer["server_loaded"] = v.ConfigLoadTime.UTC().Format(time.RFC3339Nano)
|
||||
loaded := !v.ConfigLoadTime.Before(written)
|
||||
allowed, _ := answer["allowed"].(bool)
|
||||
switch {
|
||||
case !allowed:
|
||||
answer["why"] = "the user list on the machine does not allow it"
|
||||
case !loaded:
|
||||
answer["why"] = "the user list on the machine allows it, and the server has not reloaded since it was written"
|
||||
default:
|
||||
answer["in_force"] = true
|
||||
}
|
||||
return answer
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user