From 645bae8fb8065a353237d24aff274009f37d694e Mon Sep 17 00:00:00 2001 From: tv Date: Sat, 7 Nov 2026 22:35:21 +0100 Subject: access: a node registers before it writes Under a policy, a change by a node that isn't a member is refused with all built on it: a node's registration is written before any write of its own or through the API, not after (the registration itself exempt). Auth plan M6 (fix). --- src/swarm/members.rs | 49 +++++++++++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 18 deletions(-) (limited to 'src/swarm') diff --git a/src/swarm/members.rs b/src/swarm/members.rs index cbd970d..0cadb00 100644 --- a/src/swarm/members.rs +++ b/src/swarm/members.rs @@ -293,6 +293,34 @@ const RENEWAL_MOST_OFTEN: Duration = Duration::from_secs(3600); pub(crate) const MIN_ELECTORATE_WINDOW: Duration = Duration::from_secs(7 * RENEWAL_MOST_OFTEN.as_secs()); +/// Whether `node` is registered in `doc`'s member list. +pub(crate) fn registered(doc: &impl ReadDoc, node: &str) -> bool { + store::read_path(doc, &member_path(node)) + .ok() + .flatten() + .is_some() +} + +/// This node's entry in the member list, as it registers (`ensure_registered`). +pub(crate) fn entry(state: &AppState) -> JsonValue { + let now = state.clock.now_millis().as_u64(); + let mut addresses = state.transport.local_addresses(); + addresses.truncate(MAX_ADDRESSES); + let mut entry = json!({ + "since": now, + "alive": now, + "addresses": addresses, + "window": state.electorate_window.as_millis() as u64, + }); + if let Some(alias) = &state.alias { + entry["alias"] = json!(alias); + } + if let Some(url) = &state.dashboard_url { + entry["dashboard"] = json!(url); + } + entry +} + /// Registers this node if it is ready and its entry is missing, keeps its alias and its /// dashboard's URL as configured (set, changed or removed after a restart with another /// `--alias` or `--dashboard-url`) and its @@ -315,12 +343,8 @@ pub(crate) async fn ensure_registered(state: &AppState) { shortest, ) = { let replica = state.doc.read().await; - let registered = store::read_path(&replica.doc, &own) - .ok() - .flatten() - .is_some(); ( - registered, + registered(&replica.doc, &local), alias(&replica.doc, &local), dashboard(&replica.doc, &local), addresses(&replica.doc, &local), @@ -335,20 +359,9 @@ pub(crate) async fn ensure_registered(state: &AppState) { let now = state.clock.now_millis().as_u64(); if !registered { - let mut entry = json!({ - "since": now, - "alive": now, - "addresses": own_addresses, - "window": window, - }); - if let Some(alias) = &wanted { - entry["alias"] = json!(alias); - } - if let Some(url) = &state.dashboard_url { - entry["dashboard"] = json!(url); - } + let entry = entry(state); match state - .mutate_doc(move |doc| store::write_path(doc, &own, &entry)) + .mutate_doc_registering(move |doc| store::write_path(doc, &own, &entry)) .await { Ok(()) => info!( -- cgit v1.3.1