aboutsummaryrefslogtreecommitdiffstats
path: root/src/api/changes.rs
diff options
context:
space:
mode:
authortv <tv@krebsco.de>2026-11-07 22:35:21 +0100
committertv <tv@krebsco.de>2026-11-07 22:35:21 +0100
commitf44fbf5b93269bb3573e8a2701e70ab0d7613153 (patch)
tree32915998f6e797afcc13f485ccde66bac0e94150 /src/api/changes.rs
parent389b11b597b6bf60f120b190002bbb76fff28dbf (diff)
access: the policy enforced
_continuum/access/policy ({"version": 1}) has every node screen every change it receives, on every path changes come in. A policy a node doesn't know stops its writes through the API. GET /admin/refused. Auth plan M6.
Diffstat (limited to 'src/api/changes.rs')
-rw-r--r--src/api/changes.rs40
1 files changed, 40 insertions, 0 deletions
diff --git a/src/api/changes.rs b/src/api/changes.rs
index c3a847d..13c8dd3 100644
--- a/src/api/changes.rs
+++ b/src/api/changes.rs
@@ -139,3 +139,43 @@ fn changes(
}
ChangesReport { changes, more }
}
+/// A change the policy refused (`access::screen`): its hash, when its writer made it
+/// (by its clock), who made it and for whom, as it says, what it touched, and why.
+#[derive(Serialize)]
+#[serde(rename_all = "camelCase")]
+pub(crate) struct RefusedReport {
+ hash: String,
+ at: i64,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ node: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ user: Option<String>,
+ paths: Vec<String>,
+ why: String,
+}
+
+/// `GET /admin/refused`: the changes this node refused under the swarm's policy
+/// (`_continuum/access/policy`), newest first, the last thousand at most, since it
+/// started or switched epochs.
+pub(crate) async fn get_refused(State(state): State<AppState>) -> Json<Vec<RefusedReport>> {
+ use crate::locks::MutexExt;
+ let screening = state.screening.lock_or_recover();
+ Json(
+ screening
+ .refused()
+ .rev()
+ .map(|refused| RefusedReport {
+ hash: refused.change.hash().to_string(),
+ at: refused.change.timestamp(),
+ node: refused.committer.clone(),
+ user: refused.user.clone(),
+ paths: refused
+ .paths
+ .iter()
+ .map(|path| format!("/{}", path.join("/")))
+ .collect(),
+ why: refused.why.clone(),
+ })
+ .collect(),
+ )
+}