summaryrefslogtreecommitdiffstats
path: root/src/api/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/api/mod.rs')
-rw-r--r--src/api/mod.rs53
1 files changed, 49 insertions, 4 deletions
diff --git a/src/api/mod.rs b/src/api/mod.rs
index 7328059..1fba48b 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -2,18 +2,21 @@
pub(crate) mod auth;
mod debug;
+mod document;
mod log;
mod peers;
+mod stats;
mod store;
use axum::{
+ Json, Router,
+ extract::State,
http::{
- header::{CONTENT_TYPE, ETAG},
HeaderValue, StatusCode,
+ header::{CONTENT_TYPE, ETAG},
},
response::{IntoResponse, Response},
routing::{delete, get, put},
- Router,
};
use serde_json::Value as JsonValue;
use sha2::{Digest, Sha256};
@@ -21,7 +24,7 @@ use tracing::{error, warn};
use utoipa::OpenApi;
use utoipa_swagger_ui::SwaggerUi;
-use crate::api::auth::{basic_auth, AuthState};
+use crate::api::auth::{AuthState, AuthenticatedUser, basic_auth};
use crate::document::store::StoreError;
use crate::state::AppState;
@@ -38,7 +41,13 @@ pub(crate) fn router(state: AppState, auth_state: AuthState, debug_api: bool) ->
.route("/stream/{*key}", get(store::stream_store_entry))
.route("/peers", get(peers::list_peers))
.route("/peers/{peer}", delete(peers::delete_peer))
- .route("/admin/log", get(log::get_log_filter).put(log::put_log_filter))
+ .route(
+ "/admin/log",
+ get(log::get_log_filter).put(log::put_log_filter),
+ )
+ .route("/admin/stats", get(stats::get_stats))
+ .route("/admin/document", get(document::get_document))
+ .route("/admin/config", get(get_config))
.merge(crate::remake::router::<AppState>());
if debug_api {
@@ -56,6 +65,12 @@ pub(crate) fn router(state: AppState, auth_state: AuthState, debug_api: bool) ->
.with_state(state)
}
+/// `GET /admin/config`: the effective configuration with each value's source;
+/// imported values are left out.
+async fn get_config(State(state): State<AppState>) -> Json<JsonValue> {
+ Json((*state.config).clone())
+}
+
// --- shared helpers ----------------------------------------------------------
pub(crate) fn store_status(error: StoreError, context: &str) -> StatusCode {
@@ -89,6 +104,31 @@ fn parse_key_path(key: &str) -> Result<Vec<String>, StatusCode> {
Ok(segments)
}
+/// A path for a write. Protocol data (`_continuum`: remake records, members, forget
+/// records, epoch metadata) is what every node acts on, so a write there acts on the
+/// whole swarm: only users listed in `--protocol-writers` may, and each such write is
+/// logged. Epoch metadata (and `_continuum` as a whole, which contains it) is never
+/// writable: the protocol reads it at the snapshot change, so an edit only misleads
+/// whoever reads it later. `403` otherwise.
+fn parse_write_path(
+ state: &AppState,
+ user: &AuthenticatedUser,
+ key: &str,
+ method: &str,
+) -> Result<Vec<String>, StatusCode> {
+ let path = parse_key_path(key)?;
+ if path.first().map(String::as_str) != Some(crate::remake::snapshot::META_ROOT) {
+ return Ok(path);
+ }
+ let epochs = path.len() < 2 || path[1] == crate::remake::snapshot::EPOCHS;
+ if epochs || !state.protocol_writers.contains(&user.0) {
+ warn!(user = %user.0, method, path = %key, "Refused a write to protocol data");
+ return Err(StatusCode::FORBIDDEN);
+ }
+ warn!(user = %user.0, method, path = %key, "Protocol data written through the store API; this acts on the whole swarm");
+ Ok(path)
+}
+
fn response_for_value(
value: &JsonValue,
accept: Option<&HeaderValue>,
@@ -150,6 +190,11 @@ fn response_for_value(
peers::PeerListResponse,
peers::PeerLivenessStatus,
peers::EpochInfo,
+ crate::transport::traffic::PeerTraffic,
+ crate::transport::traffic::ByKind,
+ crate::transport::traffic::Counter,
+ crate::state::Incidents,
+ peers::ThroughputInfo,
)
),
tags(