swarm-controller: stop minting the per-hive matrix sender account and token nothing reads
This commit is contained in:
parent
cb5fb3c730
commit
c66832febf
3 changed files with 40 additions and 556 deletions
|
|
@ -47,8 +47,7 @@ pub fn agent_token_path(agent: &str) -> Result<String, Error> {
|
|||
/// basic-auth header that token travels in.
|
||||
///
|
||||
/// The `hive-` prefix keeps the account reading as a hive's rather than an
|
||||
/// agent's, as [`crate::matrix::hive_localpart`] does on the homeserver.
|
||||
/// `swarm-controller` refuses a new agent named with this prefix, and tells an
|
||||
/// agent's. `swarm-controller` refuses a new agent named with this prefix, and tells an
|
||||
/// older one apart from the hive's account by the account's email.
|
||||
#[must_use]
|
||||
pub fn hive_login(hive: &str) -> String {
|
||||
|
|
|
|||
|
|
@ -70,44 +70,6 @@ impl std::fmt::Debug for Recovery {
|
|||
}
|
||||
}
|
||||
|
||||
/// The localpart `hive` acts as on the homeserver: the account
|
||||
/// `swarm-controller` creates for it with the swarm's appservice token, and
|
||||
/// the one hive-c0re's sender token belongs to.
|
||||
///
|
||||
/// **Derived from the hive's name, which is what makes it one account per
|
||||
/// hive.** It used to be the bare constant `hive`: one swarm runs one
|
||||
/// homeserver, so every hive on it logged in as the same `@hive:` and the
|
||||
/// homeserver could not tell them apart — no attribution, and no way to revoke
|
||||
/// one hive without revoking all of them.
|
||||
///
|
||||
/// The prefix is kept so the account still reads as a hive's rather than an
|
||||
/// agent's. Both `swarm-controller` and hive-c0re derive it here, so the two
|
||||
/// cannot disagree on which account a hive is.
|
||||
///
|
||||
/// Infallible on purpose: a hive name is `[a-z0-9-]` (`hive_types::Ident`) and
|
||||
/// the appservice namespace regex admits that charset, so there is no
|
||||
/// rejection arm to write. [`sender_token_path`] below does the checking, and
|
||||
/// it is the one that interpolates into a store path.
|
||||
#[must_use]
|
||||
pub fn hive_localpart(hive: &str) -> String {
|
||||
format!("hive-{hive}")
|
||||
}
|
||||
|
||||
/// The path holding `hive`'s matrix sender account's homeserver access token.
|
||||
///
|
||||
/// Keyed per **hive**, like [`crate::queue::hive_client_path`]. Under
|
||||
/// [`Kind::Hive`] the grant that reaches it is the hive's **own** stanza,
|
||||
/// `secret/data/swarm/hives/<this hive>/*`, which interpolates the name: hive
|
||||
/// `alpha` reads its own token and gets a 403 on `beta`'s.
|
||||
///
|
||||
/// # Errors
|
||||
/// [`Error::PathSegment`] when `hive` contains anything but `[A-Za-z0-9_-]`,
|
||||
/// which is what keeps one hive's name from addressing another hive's secret.
|
||||
pub fn sender_token_path(hive: &str) -> Result<String, Error> {
|
||||
let prefix = principal_prefix(Kind::Hive, hive)?;
|
||||
Ok(format!("{prefix}/matrix/sender-token"))
|
||||
}
|
||||
|
||||
/// The name segment of the controller's own subtree, and the cert-auth role it
|
||||
/// logs in under (`swarm-controller`'s `store::CERT_ROLE`).
|
||||
pub(crate) const CONTROLLER: &str = "swarm-controller";
|
||||
|
|
@ -254,48 +216,6 @@ mod tests {
|
|||
assert!(recovery_path("a-b_C9", "d-e_F0").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_sender_token_lands_under_the_hive_prefix_the_grant_covers() {
|
||||
// Spelled out rather than rebuilt from the same pieces the code uses,
|
||||
// for the reason above — and with a second job here: the hive's own
|
||||
// policy grants read on `secret/data/swarm/hives/<this hive>/*`, so
|
||||
// this exact string is what makes the path reachable at all.
|
||||
assert_eq!(
|
||||
sender_token_path("pr1ma").expect("a plain name is legal"),
|
||||
"swarm/hives/pr1ma/matrix/sender-token"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_hives_sender_token_is_not_another_hives() {
|
||||
// The property the per-hive path exists for: two hives never name the
|
||||
// same object, so the `hives/<name>/*` grant that reaches one cannot
|
||||
// reach the other.
|
||||
let a = sender_token_path("alpha").expect("legal");
|
||||
let b = sender_token_path("beta").expect("legal");
|
||||
assert_ne!(a, b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_traversal_in_the_hive_name_is_refused_by_the_sender_path_too() {
|
||||
let e = sender_token_path("../beta").expect_err("a traversal is not legal");
|
||||
assert!(matches!(e, Error::PathSegment { kind: "hive", .. }), "{e}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_localpart_is_derived_from_the_hive_name() {
|
||||
// The literal is the point: an existing hive's account on the
|
||||
// homeserver carries this localpart, so a change here strands it.
|
||||
assert_eq!(hive_localpart("pr1ma"), "hive-pr1ma");
|
||||
assert_ne!(
|
||||
hive_localpart("pr1ma"),
|
||||
hive_localpart("secunda"),
|
||||
"two hives must not land on one account"
|
||||
);
|
||||
// And it is never the bare `hive` that used to be shared swarm-wide.
|
||||
assert_ne!(hive_localpart("pr1ma"), "hive");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_swarm_appservice_token_lands_under_the_controller() {
|
||||
assert_eq!(
|
||||
|
|
|
|||
|
|
@ -377,15 +377,15 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
/// 🩸 The per-hive matrix sender-token scoping, asserted on the rendered
|
||||
/// 🩸 The per-hive forge-token scoping, asserted on the rendered
|
||||
/// document rather than on the path function alone: it is the policy text
|
||||
/// that decides what a hive may fetch, so "the path is per-hive" only
|
||||
/// means something if the stanza that reaches it is too.
|
||||
#[test]
|
||||
fn a_hive_reaches_its_own_matrix_sender_token_and_no_other_hives() {
|
||||
fn a_hive_reaches_its_own_forge_token_and_no_other_hives() {
|
||||
let alpha = effective_hive("alpha");
|
||||
let own = crate::matrix::sender_token_path("alpha").expect("legal");
|
||||
let other = crate::matrix::sender_token_path("beta").expect("legal");
|
||||
let own = crate::forge::hive_token_path("alpha").expect("legal");
|
||||
let other = crate::forge::hive_token_path("beta").expect("legal");
|
||||
|
||||
// Reachable: the hive's own stanza is the prefix of its own path.
|
||||
assert!(
|
||||
|
|
|
|||
Loading…
Reference in a new issue