Skip to main content

aviso_ecpds/
lib.rs

1//! ECPDS destination authorization plugin for `aviso-server`.
2//!
3//! This crate decides whether a given user is allowed to read a given
4//! ECPDS destination, by consulting one or more ECPDS monitor servers
5//! and caching the result. It is consumed by `aviso-server` behind the
6//! `ecpds` Cargo feature; deployments that don't need ECPDS auth
7//! compile without this crate at all.
8//!
9//! See `aviso-ecpds/README.md` for an architectural overview, the
10//! "ECPDS Destination Authorization" section in the operator
11//! documentation for setup, and the "ECPDS Plugin Runbook" for on-call
12//! triage.
13//!
14//! Public surface, at a glance:
15//!
16//! - [`config::EcpdsConfig`] — serde-deserialised configuration.
17//! - [`config::PartialOutagePolicy`] — strict (default) vs any-success
18//!   merge across multiple ECPDS servers.
19//! - [`checker::EcpdsChecker`] — the single facade. `new` is fallible;
20//!   `check_access` returns [`checker::AccessCheckResult`] (cache
21//!   outcome plus authorisation result) so the route layer can label
22//!   hit/miss and fetch metrics on every code path.
23//! - [`client::EcpdsError`] / [`client::FetchOutcome`] /
24//!   [`client::DenyReason`] — domain error type and typed sub-reasons
25//!   with stable Prometheus label strings.
26
27#![warn(missing_docs)]
28
29/// In-process single-flight bounded cache of authorised ECPDS
30/// destination lists, keyed by username.
31pub mod cache;
32
33/// The single public facade combining the HTTP client, the cache, and
34/// the destination match logic.
35pub mod checker;
36
37/// HTTP client to one or more ECPDS servers, plus the typed error /
38/// fetch-outcome / deny-reason types consumed by the route layer.
39pub mod client;
40
41/// Static configuration for the ECPDS plugin (deserialised from YAML
42/// at startup).
43pub mod config;
44
45pub use checker::EcpdsChecker;
46pub use client::EcpdsError;
47
48use std::sync::OnceLock;
49
50static SERVICE_NAME_OVERRIDE: OnceLock<&'static str> = OnceLock::new();
51static SERVICE_VERSION_OVERRIDE: OnceLock<&'static str> = OnceLock::new();
52
53/// Register the parent-process service identity used in this crate's
54/// structured tracing events.
55///
56/// The aviso-ecpds crate emits its own `auth.ecpds.fetch.*` and
57/// `auth.ecpds.cache.*` tracing events. To keep log routing and
58/// dashboards consistent with events emitted from `aviso-server`
59/// itself, the parent crate calls this once at startup with its own
60/// `SERVICE_NAME` and `SERVICE_VERSION` so subcrate events identify
61/// under the same service identity. Subsequent calls are no-ops
62/// (`OnceLock`).
63///
64/// When unset (e.g. running the subcrate's own tests in isolation),
65/// events fall back to the subcrate's own `CARGO_PKG_NAME` /
66/// `CARGO_PKG_VERSION`.
67pub fn set_service_identity(name: &'static str, version: &'static str) {
68    let _ = SERVICE_NAME_OVERRIDE.set(name);
69    let _ = SERVICE_VERSION_OVERRIDE.set(version);
70}
71
72pub(crate) fn service_name() -> &'static str {
73    SERVICE_NAME_OVERRIDE
74        .get()
75        .copied()
76        .unwrap_or(env!("CARGO_PKG_NAME"))
77}
78
79pub(crate) fn service_version() -> &'static str {
80    SERVICE_VERSION_OVERRIDE
81        .get()
82        .copied()
83        .unwrap_or(env!("CARGO_PKG_VERSION"))
84}