Watch
0
0
Fork
You've already forked hyperhive
0

grant manage_root_agent by stored permission only, not by the name ruth

Permission::default_for and hive-sh4re's MANAGER_AGENT are gone: every
agent with no permissions key holds Permission::DEFAULT, ruth included.
The agent-name parameters that only fed default_for go with it
(permissions::effective, HiveCopy::before_read/after_read and their
callers).

swarm-controller seeds the grant ruth held by name: at start, a declared
ruth whose permissions are unset gets DEFAULT plus manage_root_agent
written into her key, once, logged. A set key is never touched. It is a
one-shot to delete once every swarm has passed it.

The swarm UI setup assistant opens the create form with
manage_root_agent checked, so a new ruth is created with the grant.
permissions_import no longer adds ruth to the import or splits her
default. Docs and help text no longer say ruth holds the grant by
default; swarmctl-cli.md regenerated.
This commit is contained in:
atlas 2026-10-16 23:10:05 +02:00 • committed by mara
commit 7b5400ecb8
26 changed files with 380 additions and 216 deletions

View file

@ -3,7 +3,7 @@
//!
//! **swarm-controller is the only writer**, and creates the bucket when it
//! starts. A missing key or an unset `permissions` field leaves the agent on
//! the compiled-in default (`Permission::default_for`); an empty list grants
//! the compiled-in default (`Permission::DEFAULT`); an empty list grants
//! nothing.
//!
//! **Two readers, each enforcing its own part.** The hive running the agent
@ -171,29 +171,25 @@ impl HiveCopy {
})
}
/// What `agent` holds before any read of its key has succeeded: the
/// What the agent holds before any read of its key has succeeded: the
/// stored list, or the default when the hive stored none.
#[must_use]
pub fn before_read(&self, agent: &str) -> Vec<hive_sh4re::permissions::Permission> {
pub fn before_read(&self) -> Vec<hive_sh4re::permissions::Permission> {
self.stored
.permissions
.clone()
.unwrap_or_else(|| hive_sh4re::permissions::Permission::default_for(agent))
.unwrap_or_else(|| hive_sh4re::permissions::Permission::DEFAULT.to_vec())
}
/// What `agent` holds after reading `read` from its key. An unset key
/// What the agent holds after reading `read` from its key. An unset key
/// while the import is pending may only mean the grant is not imported
/// yet, so the stored list stands; otherwise unset is the default.
#[must_use]
pub fn after_read(
&self,
agent: &str,
read: &AgentPermissions,
) -> Vec<hive_sh4re::permissions::Permission> {
pub fn after_read(&self, read: &AgentPermissions) -> Vec<hive_sh4re::permissions::Permission> {
match &read.permissions {
Some(list) => list.clone(),
None if self.import_pending => self.before_read(agent),
None => hive_sh4re::permissions::Permission::default_for(agent),
None if self.import_pending => self.before_read(),
None => hive_sh4re::permissions::Permission::DEFAULT.to_vec(),
}
}
}
@ -307,20 +303,17 @@ mod tests {
let web = vec![Permission::WebTools];
let default = Permission::DEFAULT.to_vec();
assert_eq!(
copy(r#"{"permissions":["web_tools"]}"#).before_read("atlas"),
web
);
assert_eq!(copy("{}").before_read("atlas"), default);
assert_eq!(copy(r#"{"permissions":["web_tools"]}"#).before_read(), web);
assert_eq!(copy("{}").before_read(), default);
let pending = copy(r#"{"permissions":["web_tools"],"import_pending":true}"#);
assert_eq!(pending.after_read("atlas", &record("{}")), web);
assert_eq!(pending.after_read(&record("{}")), web);
assert_eq!(
pending.after_read("atlas", &record(r#"{"permissions":[]}"#)),
pending.after_read(&record(r#"{"permissions":[]}"#)),
Vec::new()
);
let settled = copy(r#"{"permissions":["web_tools"]}"#);
assert_eq!(settled.after_read("atlas", &record("{}")), default);
assert_eq!(settled.after_read(&record("{}")), default);
}
/// Fields a newer controller adds must not cost an older hive the ones it