diff options
Diffstat (limited to 'src/remake/api.rs')
| -rw-r--r-- | src/remake/api.rs | 68 |
1 files changed, 68 insertions, 0 deletions
diff --git a/src/remake/api.rs b/src/remake/api.rs index 7888c37..79b551f 100644 --- a/src/remake/api.rs +++ b/src/remake/api.rs @@ -8,6 +8,11 @@ //! * `GET /admin/remakes` lists all remakes. //! * `GET /admin/remakes/{id}` reports one; `?wait=true` long-polls until decided //! (bounded by the proposal's own expiry). +//! * `GET /admin/strict-manifest`: a manifest turning the swarm strict (`access::strict`), +//! keeping every value at the places values are signed as they are now, for the +//! caller to look over and sign; with those values and the heads they are at, the +//! base to propose it with (`{"kind": "strict", "spec": {"manifest": …}, "baseHeads": +//! …}`). //! * `GET /admin/epochs` shows the local epoch, the archived previous ones and whether //! the node is catching up with a newer epoch from a peer. //! * `DELETE /admin/remakes/{id}` asks the coordinator to abort. Only possible while @@ -374,6 +379,29 @@ async fn start_kind<H: Host>( } }; + // A strict remake: proposed only with a manifest this node would vote for. + if kind.name() == "strict" { + let swarm = host + .read_doc(|doc| { + crate::document::store::read_path( + doc, + &crate::document::meta::path(&[crate::document::meta::SWARM]), + ) + .ok() + .flatten() + .and_then(|swarm| swarm.as_str().map(str::to_owned)) + .unwrap_or_default() + }) + .await; + if let Err(why) = super::vote::strict_taken(&host, prepared.content.as_deref(), &swarm) { + return error_response( + StatusCode::FORBIDDEN, + format!("this node takes no such manifest: {why}"), + None, + ); + } + } + if kind.base_at_commit() && request.base_heads.is_some() { return error_response( StatusCode::BAD_REQUEST, @@ -782,6 +810,46 @@ async fn abort_expired<H: Host>(host: &H, current: Report, force: &Force) -> Res } } +/// `GET /admin/strict-manifest`. +pub(crate) async fn strict_manifest<H: Host>( + State(host): State<H>, + axum::Extension(user): axum::Extension<crate::api::auth::AuthenticatedUser>, +) -> Response { + let at = host.now_millis().as_u64(); + let caller = user.0.clone(); + let (manifest, values, heads) = host + .read_doc(move |doc| { + let read = |path: &[&str]| { + crate::document::store::read_plain(doc, &crate::document::meta::path(path)) + .ok() + .flatten() + }; + let swarm = read(&[crate::document::meta::SWARM]) + .and_then(|swarm| swarm.as_str().map(str::to_owned)) + .unwrap_or_default(); + let mut policy = match read(&[crate::document::meta::ACCESS, "policy"]) { + Some(JsonValue::Object(policy)) => JsonValue::Object(policy), + _ => json!({"version": 1}), + }; + policy["values"] = json!("signed"); + let manifest = crate::access::strict::manifest_of(doc, &swarm, policy, &caller, at); + let values: Vec<JsonValue> = crate::access::strict::held(doc) + .into_iter() + .map(|(path, value)| json!({ "path": path, "value": value })) + .collect(); + let heads = records::encode_heads(&doc.get_heads()); + (manifest, values, heads) + }) + .await; + Json(json!({ + "manifest": manifest, + "values": values, + "heads": heads, + "namespace": crate::access::strict::NAMESPACE, + })) + .into_response() +} + /// `GET /admin/epochs`: the local epoch, the previous epochs this node still keeps, and /// an ongoing catch-up with a peer's newer epoch (`null` if none). pub(crate) async fn epochs<H: Host>(State(host): State<H>) -> Response { |
