add one shared templated agent policy and name each agent's bao entity after it
This commit is contained in:
parent
d893097ffe
commit
4cdf807afc
1 changed files with 777 additions and 13 deletions
|
|
@ -200,10 +200,83 @@ pub fn render_agent(agent: &str) -> Result<String, Error> {
|
|||
Ok(format!("{read}{list}"))
|
||||
}
|
||||
|
||||
/// The one policy every agent's cert-auth role attaches next to its own.
|
||||
///
|
||||
/// Inside [`HIVE_PREFIX`] so that the controller may write it, and without an
|
||||
/// agent's suffix so that it is never an agent's object name:
|
||||
/// [`agents_from_role_names`] needs `hive-agent-` with a segment after it. The
|
||||
/// hive name that would spell it is `agent`, which `nix/reserved-hive-fragments.nix`
|
||||
/// forbids.
|
||||
pub const SHARED_AGENT_POLICY: &str = "hive-agent";
|
||||
|
||||
/// The template that stands for the agent's name in [`render_shared_agent`]:
|
||||
/// the name of the login's identity entity, which swarm-controller sets to the
|
||||
/// agent's name.
|
||||
const ENTITY_NAME_TEMPLATE: &str = "{{identity.entity.name}}";
|
||||
|
||||
/// Render the policy [`SHARED_AGENT_POLICY`] holds: the stanzas of
|
||||
/// [`render_agent`], with the agent's name taken from the identity entity of
|
||||
/// whoever presents the token instead of written into the text.
|
||||
///
|
||||
/// The text is the same for every agent, so a change to the rule is one write.
|
||||
/// An entity whose name is not an agent's grants nothing: the controller names
|
||||
/// each agent's entity after the agent, and a login that no entity of that
|
||||
/// name owns reads `swarm/agents/<generated name>/`, where nothing is stored.
|
||||
#[must_use]
|
||||
pub fn render_shared_agent() -> String {
|
||||
let kind = <&str>::from(Kind::Agent);
|
||||
let read = read_stanza(&format!(
|
||||
"{MOUNT}/data/{ROOT}/{kind}/{ENTITY_NAME_TEMPLATE}/*"
|
||||
));
|
||||
let list = list_stanza(&format!(
|
||||
"{MOUNT}/metadata/{ROOT}/{kind}/{ENTITY_NAME_TEMPLATE}/*"
|
||||
));
|
||||
format!("{read}{list}")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn the_shared_document_is_the_per_agent_one_with_the_entity_name_for_the_agent() {
|
||||
let shared = render_shared_agent();
|
||||
assert_eq!(
|
||||
shared,
|
||||
"path \"secret/data/swarm/agents/{{identity.entity.name}}/*\" {\n capabilities = [\"read\"]\n}\n\
|
||||
path \"secret/metadata/swarm/agents/{{identity.entity.name}}/*\" {\n capabilities = [\"list\"]\n}\n"
|
||||
);
|
||||
// Same shape as the per-agent document, so the rule is one rule.
|
||||
assert_eq!(
|
||||
shared.replace("{{identity.entity.name}}", "atlas"),
|
||||
render_agent("atlas").expect("legal")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_shared_document_names_no_agent_and_grants_no_wider_than_one() {
|
||||
let shared = render_shared_agent();
|
||||
assert!(!shared.contains("atlas"));
|
||||
assert!(!shared.contains("agents/*"), "{shared}");
|
||||
assert!(!shared.contains("create"));
|
||||
assert!(!shared.contains("update"));
|
||||
assert!(!shared.contains("delete"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_shared_policy_name_is_writable_and_no_agents_or_hives() {
|
||||
assert!(SHARED_AGENT_POLICY.starts_with(HIVE_PREFIX));
|
||||
assert!(
|
||||
agents_from_role_names(&[SHARED_AGENT_POLICY.to_owned()]).is_empty(),
|
||||
"it must not be read back as an agent"
|
||||
);
|
||||
// The only hive name that would render it carries `agent`.
|
||||
assert_eq!(
|
||||
hive_object_name("agent").expect("legal as a segment"),
|
||||
SHARED_AGENT_POLICY
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_document_grants_every_agent_and_this_hive_alone() {
|
||||
assert_eq!(
|
||||
|
|
|
|||
Loading…
Reference in a new issue