v0.35.0
This commit is contained in:
1 parent
2c03fcf248
commit
d3b492874b
95 files changed
+1628
-743
No files matched your search
@@ -194,7 +194,7 @@ async fn certificate_deploy(
|
||||
)
|
||||
.await
|
||||
.map_err(auth::internal)?;
|
||||
return Ok(Json(json!({
|
||||
Ok(Json(json!({
|
||||
"deployed": true,
|
||||
"operation": "metadata-updated",
|
||||
"created": false,
|
||||
@@ -207,7 +207,7 @@ async fn certificate_deploy(
|
||||
"public_tls": true,
|
||||
"http3_new_handshakes": state.config.load().quic_tls.is_some(),
|
||||
"administration_restart_required": false
|
||||
})));
|
||||
})))
|
||||
}
|
||||
crate::tls::ExternalPairDeployment::Staged {
|
||||
cert_file,
|
||||
|
||||
@@ -62,10 +62,12 @@ async fn dashboard_presets(
|
||||
tracing::warn!("skipped invalid saved Overview preset");
|
||||
continue;
|
||||
};
|
||||
let mut prefs = AdminPreferences::default();
|
||||
prefs.dashboard_widgets = layout.dashboard_widgets;
|
||||
prefs.dashboard_hidden_widgets = layout.dashboard_hidden_widgets;
|
||||
prefs.dashboard_widget_sizes = layout.dashboard_widget_sizes;
|
||||
let mut prefs = AdminPreferences {
|
||||
dashboard_widgets: layout.dashboard_widgets,
|
||||
dashboard_hidden_widgets: layout.dashboard_hidden_widgets,
|
||||
dashboard_widget_sizes: layout.dashboard_widget_sizes,
|
||||
..Default::default()
|
||||
};
|
||||
reconcile_dashboard_widgets(&mut prefs);
|
||||
layout.dashboard_widgets = prefs.dashboard_widgets;
|
||||
layout.dashboard_hidden_widgets = prefs.dashboard_hidden_widgets;
|
||||
|
||||
@@ -182,7 +182,7 @@ fn validate_file_log_query(query: &FileLogQuery) -> Result<(), ApiError> {
|
||||
.is_some_and(|value| value == 0 || value > FILE_LOG_MAX_AGGREGATE_RESULTS)
|
||||
|| query
|
||||
.aggregate_min_count
|
||||
.is_some_and(|value| value < 2 || value > FILE_LOG_SCAN_LINES)
|
||||
.is_some_and(|value| !(2..=FILE_LOG_SCAN_LINES).contains(&value))
|
||||
|| query.offset.unwrap_or(0) > FILE_LOG_MAX_OFFSET
|
||||
|| query
|
||||
.limit
|
||||
@@ -233,7 +233,7 @@ async fn file_logs_query(
|
||||
));
|
||||
}
|
||||
} else {
|
||||
sources.sort_by(|a, b| b.modified_ms.cmp(&a.modified_ms));
|
||||
sources.sort_by_key(|source| std::cmp::Reverse(source.modified_ms));
|
||||
if sources.len() > FILE_LOG_MAX_SOURCES {
|
||||
truncated = true;
|
||||
sources.truncate(FILE_LOG_MAX_SOURCES);
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"openapi": "3.0.3",
|
||||
"info": {
|
||||
"title": "RustProxy Administration API",
|
||||
"version": "0.34.16",
|
||||
"version": "0.35.0",
|
||||
"description": "Administration API for the active panel origin. Sign in through the panel; the same-origin HttpOnly cookie authenticates all private operations. Mutations also require the exact Origin, X-RustProxy-Request: 1 and a session-bound X-CSRF-Token. Loopback HTTP is the default; Settings can enable HTTPS before using a non-loopback administration bind. This document contains no live configuration or credentials. Object schemas marked extensible are summaries: full configuration validation is authoritative in Rust. The API also permits explicitly scoped GeneralApiBearer tokens on a narrow route allowlist, with RO/RW and source CIDR checks; API-token management and Prometheus-token management are browser-session-only."
|
||||
},
|
||||
"servers": [
|
||||
|
||||
@@ -86,18 +86,20 @@ fn corner_style_defaults_and_rejects_unknown_presets() {
|
||||
|
||||
#[test]
|
||||
fn old_widget_preferences_preserve_order_opt_in_and_survive_unknown_ids() {
|
||||
let mut prefs = AdminPreferences::default();
|
||||
prefs.dashboard_widgets = vec![
|
||||
"errors".into(),
|
||||
"from_future_release".into(),
|
||||
"requests".into(),
|
||||
"errors".into(),
|
||||
];
|
||||
prefs.dashboard_hidden_widgets = vec![
|
||||
"errors".into(),
|
||||
"from_future_release".into(),
|
||||
"errors".into(),
|
||||
];
|
||||
let mut prefs = AdminPreferences {
|
||||
dashboard_widgets: vec![
|
||||
"errors".into(),
|
||||
"from_future_release".into(),
|
||||
"requests".into(),
|
||||
"errors".into(),
|
||||
],
|
||||
dashboard_hidden_widgets: vec![
|
||||
"errors".into(),
|
||||
"from_future_release".into(),
|
||||
"errors".into(),
|
||||
],
|
||||
..Default::default()
|
||||
};
|
||||
reconcile_dashboard_widgets(&mut prefs);
|
||||
assert_eq!(prefs.dashboard_widgets[0], "errors");
|
||||
assert_eq!(prefs.dashboard_widgets[1], "requests");
|
||||
@@ -210,9 +212,11 @@ fn saved_dashboard_order_and_visibility_survive_new_factory_preset() {
|
||||
|
||||
#[test]
|
||||
fn explicit_visible_stream_opt_in_is_not_reset_when_normalizing_saved_order() {
|
||||
let mut prefs = AdminPreferences::default();
|
||||
prefs.dashboard_widgets = default_admin_dashboard_widgets();
|
||||
prefs.dashboard_hidden_widgets.clear();
|
||||
let mut prefs = AdminPreferences {
|
||||
dashboard_widgets: default_admin_dashboard_widgets(),
|
||||
dashboard_hidden_widgets: Vec::new(),
|
||||
..Default::default()
|
||||
};
|
||||
reconcile_dashboard_widgets(&mut prefs);
|
||||
assert!(prefs.dashboard_hidden_widgets.is_empty());
|
||||
assert!(prefs.dashboard_widgets.contains(&"cache_outcomes".into()));
|
||||
@@ -224,8 +228,10 @@ fn explicit_visible_stream_opt_in_is_not_reset_when_normalizing_saved_order() {
|
||||
fn dashboard_width_preferences_preserve_legacy_and_allow_custom_grids() {
|
||||
let old: AdminPreferences = serde_json::from_str(r#"{"theme":"dark"}"#).unwrap();
|
||||
assert!(old.dashboard_widget_sizes.is_none());
|
||||
let mut prefs = AdminPreferences::default();
|
||||
prefs.dashboard_widget_sizes = Some(std::collections::BTreeMap::new());
|
||||
let mut prefs = AdminPreferences {
|
||||
dashboard_widget_sizes: Some(std::collections::BTreeMap::new()),
|
||||
..Default::default()
|
||||
};
|
||||
assert!(validate_admin_preferences(&prefs).is_ok());
|
||||
for (id, span) in [
|
||||
("http_protocol", 4),
|
||||
|
||||
+18
-4
@@ -461,6 +461,7 @@ mod tests {
|
||||
let (router, store, _dir, secret, _) = fixture().await;
|
||||
let old = request("/api/status")
|
||||
.header("authorization", format!("Bearer {}", "a".repeat(64)))
|
||||
.extension(PeerAddr("127.0.0.1:45000".parse().unwrap()))
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
@@ -1645,6 +1646,7 @@ mod tests {
|
||||
.method("POST")
|
||||
.header("authorization", format!("Bearer {token}"))
|
||||
.header("content-type", "application/json")
|
||||
.extension(PeerAddr("127.0.0.1:45000".parse().unwrap()))
|
||||
.body(Body::from("{}"))
|
||||
.unwrap(),
|
||||
)
|
||||
@@ -1663,7 +1665,11 @@ mod tests {
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(forbidden_scope.status(), StatusCode::UNAUTHORIZED);
|
||||
assert_eq!(forbidden_scope.status(), StatusCode::FORBIDDEN);
|
||||
assert_eq!(
|
||||
json_body(forbidden_scope).await,
|
||||
json!({"error":"certificate_api.invalid_route"})
|
||||
);
|
||||
|
||||
let revoked = router
|
||||
.clone()
|
||||
@@ -1804,7 +1810,11 @@ mod tests {
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(denied.status(), StatusCode::UNAUTHORIZED);
|
||||
assert_eq!(denied.status(), StatusCode::FORBIDDEN);
|
||||
assert_eq!(
|
||||
json_body(denied).await,
|
||||
json!({"error":"certificate_api.invalid_route"})
|
||||
);
|
||||
let no_csrf = router
|
||||
.clone()
|
||||
.oneshot(
|
||||
@@ -2774,6 +2784,7 @@ mod tests {
|
||||
request(&path)
|
||||
.header("cookie", cookie)
|
||||
.header("authorization", format!("Bearer {secret}"))
|
||||
.extension(PeerAddr("127.0.0.1:45000".parse().unwrap()))
|
||||
.body(Body::empty())
|
||||
.unwrap(),
|
||||
)
|
||||
@@ -3953,7 +3964,7 @@ mod tests {
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(old_route.status(), StatusCode::METHOD_NOT_ALLOWED);
|
||||
assert_eq!(old_route.status(), StatusCode::NOT_FOUND);
|
||||
store.close().await;
|
||||
}
|
||||
|
||||
@@ -4103,7 +4114,10 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(summary.status(), StatusCode::OK);
|
||||
assert_eq!(summary.headers()["cache-control"], "no-store");
|
||||
assert_eq!(
|
||||
summary.headers()["cache-control"],
|
||||
"private, no-store, no-cache, no-transform"
|
||||
);
|
||||
let summary = json_body(summary).await;
|
||||
assert!(summary.get("prometheus").is_none());
|
||||
assert_eq!(
|
||||
|
||||
Reference in new issue
Block a user