Skip to main content

acme_proxy_admin/webadmin/pages/
audit.rs

1//! `/ui/audit` — the CA's audit trail, and one row in full.
2//!
3//! Read-only, like its API twin: there is no route here that writes or deletes
4//! an audit row, which is why this file contributes nothing to
5//! `tests/admin_pages.rs::mutating_page_endpoints()`. See
6//! [`crate::webadmin::handlers::audit`] for why.
7
8use axum::extract::{Path, Query, State};
9use axum::response::Html;
10use serde_json::Value;
11
12use crate::admin;
13use crate::webadmin::AdminState;
14use crate::webadmin::error::AdminError;
15use crate::webadmin::handlers::audit::AuditListParams;
16use crate::webadmin::handlers::paging::PageParams;
17use crate::webadmin::pages::auth::PageSession;
18use crate::webadmin::pages::error::PageError;
19use crate::webadmin::pages::{ListFilters, chrome, page_value, pager, respond};
20use acme_proxy_core::audit::ALL_AUDIT_EVENTS;
21use acme_proxy_store::audit::AuditEntry;
22use acme_proxy_store::audit::AuditQuery;
23
24/// `GET /ui/audit?profile=&accountId=&orderId=&certSerial=&event=&outcome=&limit=&offset=`
25pub async fn list_audit(
26    State(state): State<AdminState>,
27    Query(params): Query<AuditListParams>,
28    session: PageSession,
29) -> Result<Html<String>, PageError> {
30    let page = PageParams::from(params.limit, params.offset).resolve(&state.config);
31    // Every filter the query below applies, and no other: a filter missing here
32    // is dropped by the next page step and by the next change to the form.
33    let filters = ListFilters::new()
34        .with("profile", params.profile.as_deref())
35        .with("event", params.event.as_deref())
36        .with("outcome", params.outcome.as_deref())
37        .with("accountId", params.account_id.as_deref())
38        .with("orderId", params.order_id.as_deref())
39        .with("certSerial", params.cert_serial.as_deref());
40
41    let (entries, total) = admin::list_audit(
42        &AuditQuery {
43            profile: params.profile.clone(),
44            account_id: params.account_id.clone(),
45            order_id: params.order_id.clone(),
46            cert_serial: params.cert_serial.clone(),
47            event: params.event.clone(),
48            outcome: params.outcome.clone(),
49            since: None,
50            limit: page.limit,
51            offset: page.offset,
52        },
53        state.database.clone(),
54    )
55    .await?;
56
57    let items: Vec<Value> = entries.iter().map(AuditEntry::to_json).collect();
58    let mut context = chrome(&session, "audit", "Audit");
59    context.insert("page".to_string(), page_value(items, total));
60    context.insert(
61        "pager".to_string(),
62        pager(page, total, "/ui/audit", &filters.pairs(), "#audit-table"),
63    );
64    context.insert("filters".to_string(), filters.to_value());
65    context.insert(
66        "events".to_string(),
67        Value::Array(
68            ALL_AUDIT_EVENTS
69                .iter()
70                .map(|event| Value::from(event.as_str()))
71                .collect(),
72        ),
73    );
74    context.insert(
75        "profiles".to_string(),
76        Value::Array(crate::webadmin::handlers::misc::profile_rows(&state)),
77    );
78
79    respond(
80        &state,
81        session.hx,
82        "audit/list.html",
83        "audit/_table.html",
84        context,
85    )
86}
87
88/// `GET /ui/audit/{id}`
89pub async fn get_audit(
90    State(state): State<AdminState>,
91    Path(id): Path<i64>,
92    session: PageSession,
93) -> Result<Html<String>, PageError> {
94    let entry = admin::find_audit(id, state.database.clone())
95        .await?
96        .ok_or_else(|| not_found(id))?;
97
98    let mut context = chrome(&session, "audit", &format!("Audit {id}"));
99    context.insert("entry".to_string(), entry.to_json());
100    respond(
101        &state,
102        session.hx,
103        "audit/detail.html",
104        "audit/_card.html",
105        context,
106    )
107}
108
109fn not_found(id: i64) -> PageError {
110    AdminError::not_found(format!("audit row {id} not found")).into()
111}