swarm-secret-client: one module per kind of secret, not one struct
mara, reviewing the previous commit: "the field is specific to matrix, why add it to the general struct". She is right, and the answer is that there was no general struct — `Credential` had one consumer, the crate's only path builder was `matrix_account`, and `value` is pinned by `glue-matrix-bao-token.nix`, a matrix unit. It was matrix's throughout, wearing a general name; adding `homeserver` is what made that visible. `client` now moves whatever type a caller names and decodes nothing itself. That is forwarding rather than machinery: `vaultrs::kv2::read`/`set` are already generic over the payload. The matrix agreement moves to its own module holding both halves — where a credential lives (`account_path`, was `path::matrix_account`) and what the object at that path holds. `path` keeps only what every path obeys, so a second kind of swarm secret becomes a module beside `matrix` rather than another optional field on a struct it shares. argus raised the same collision from the other direction on #4092: two mutually-exclusive `Option`s modelling one concept is the failure mode this forecloses. `checked_segment` stays public in `path`: hive-priv builds an on-disk path from the same names and must accept the same charset. Behaviour is unchanged. The compatibility properties move with the struct — `Option` is what lets a pre-`homeserver` stored object decode, and `skip_serializing_if` is what keeps a token-only object free of `"homeserver":null` for that nix reader. Refs #3726
This commit is contained in:
parent
d1c0963fbd
commit
e263681f1d
6 changed files with 205 additions and 123 deletions
|
|
@ -21,7 +21,7 @@
|
|||
use anyhow::{Context, Result};
|
||||
use hive_types::Ident;
|
||||
use swarm_queue_client::CredentialNotice;
|
||||
use swarm_secret_client::{SecretStore, path};
|
||||
use swarm_secret_client::{SecretStore, matrix};
|
||||
|
||||
/// Read the credential `notice` names and write it into the agent's state dir.
|
||||
///
|
||||
|
|
@ -36,13 +36,13 @@ pub async fn deliver(notice: &CredentialNotice, cert_role: &str) -> Result<()> {
|
|||
// not a round trip to the store.
|
||||
let agent = Ident::parse(¬ice.agent)
|
||||
.map_err(|e| anyhow::anyhow!("agent name {:?} off the queue: {e}", notice.agent))?;
|
||||
let secret_path = path::matrix_account(¬ice.agent, ¬ice.account)
|
||||
let secret_path = matrix::account_path(¬ice.agent, ¬ice.account)
|
||||
.context("building the credential's path in the store")?;
|
||||
|
||||
let store = SecretStore::from_env(cert_role)
|
||||
.await
|
||||
.context("connecting to the swarm secret store")?;
|
||||
let credential = store
|
||||
let credential: matrix::Credential = store
|
||||
.read(&secret_path)
|
||||
.await
|
||||
.with_context(|| format!("reading {secret_path} from the store"))?;
|
||||
|
|
@ -74,16 +74,16 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn a_name_that_could_address_another_agent_is_refused() {
|
||||
// `path::matrix_account` owns this rule; asserted here because this is
|
||||
// `matrix::account_path` owns this rule; asserted here because this is
|
||||
// the module that feeds it names off the wire.
|
||||
assert!(path::matrix_account("../argus", "ccc").is_err());
|
||||
assert!(path::matrix_account("dmatrix", "../../etc/x").is_err());
|
||||
assert!(path::matrix_account("dmatrix", "ccc").is_ok());
|
||||
assert!(matrix::account_path("../argus", "ccc").is_err());
|
||||
assert!(matrix::account_path("dmatrix", "../../etc/x").is_err());
|
||||
assert!(matrix::account_path("dmatrix", "ccc").is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_agent_name_off_the_queue_must_pass_the_ident_parser_too() {
|
||||
// Two independent refusals, not one restated: `path::matrix_account`
|
||||
// Two independent refusals, not one restated: `matrix::account_path`
|
||||
// guards the address in the *store*, and `Ident` guards the name this
|
||||
// module hands to hive-priv, which builds the on-disk path from it.
|
||||
for bad in ["../argus", "dmatrix/../argus", "Dmatrix", "d matrix", ""] {
|
||||
|
|
@ -102,7 +102,7 @@ mod tests {
|
|||
fn an_account_name_off_the_queue_is_refused_for_the_store_path() {
|
||||
for bad in ["../argus", "a/b", "a b", ""] {
|
||||
assert!(
|
||||
path::matrix_account("dmatrix", bad).is_err(),
|
||||
matrix::account_path("dmatrix", bad).is_err(),
|
||||
"account {bad:?} must be refused"
|
||||
);
|
||||
}
|
||||
|
|
@ -110,7 +110,7 @@ mod tests {
|
|||
// passing because everything is refused. Uppercase and underscore are
|
||||
// deliberate — `matrixAccounts` is an attrset, so both are names an
|
||||
// operator can already write, and hive-priv must accept them too.
|
||||
assert!(path::matrix_account("dmatrix", "ops-relay").is_ok());
|
||||
assert!(path::matrix_account("dmatrix", "Ops_Relay9").is_ok());
|
||||
assert!(matrix::account_path("dmatrix", "ops-relay").is_ok());
|
||||
assert!(matrix::account_path("dmatrix", "Ops_Relay9").is_ok());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue