Skip to main content

micromegas_auth/
env.rs

1//! Shared prefixed-env-var resolution, used by every `{prefix}_*`-with-fallback knob in this
2//! crate (`{prefix}_OIDC_CONFIG`, `{prefix}_DEFAULT_AUDIENCE`), and the three surviving
3//! `{prefix}_API_KEY_*CACHE*` knobs (`{prefix}_API_KEY_CACHE_SIZE`,
4//! `{prefix}_API_KEY_UNKNOWN_CACHE_TTL_SECONDS`, `{prefix}_API_KEY_UNKNOWN_CACHE_SIZE`).
5
6/// Resolves `{prefix}_{suffix}`, falling back to unprefixed `MICROMEGAS_{suffix}` when the
7/// prefixed name is unset, or always when `prefix` is empty.
8///
9/// `suffix` is passed **without** the `MICROMEGAS_` prefix (e.g. `"OIDC_CONFIG"`,
10/// `"DEFAULT_AUDIENCE"`).
11pub fn resolve_prefixed_var(prefix: &str, suffix: &str) -> String {
12    if prefix.is_empty() {
13        format!("MICROMEGAS_{suffix}")
14    } else {
15        let prefixed = format!("{prefix}_{suffix}");
16        if std::env::var(&prefixed).is_ok() {
17            prefixed
18        } else {
19            format!("MICROMEGAS_{suffix}")
20        }
21    }
22}
23
24/// Returns the subset of `removed` that is set to any value, empty string included. Pure
25/// detection, split out from the `warn_removed_*` wrappers so it is unit-testable without
26/// capturing a log sink. Not part of this crate's public API; `pub` only so the tests in
27/// `tests/env_tests.rs` can reach it.
28#[doc(hidden)]
29pub fn removed_vars_that_are_set(removed: &[&'static str]) -> Vec<&'static str> {
30    removed
31        .iter()
32        .copied()
33        .filter(|var| std::env::var(var).is_ok())
34        .collect()
35}
36
37/// Warns when any of the three removed env-var API-keyrings --
38/// `MICROMEGAS_API_KEYS`, `MICROMEGAS_INGESTION_API_KEYS`, `MICROMEGAS_ANALYTICS_API_KEYS` -- is
39/// still set. `ingestion_api_keys` / `analytics_api_keys` are the only sources from here on, and
40/// the keys they hold are minted there -- a still-set keyring's own key strings cannot be carried
41/// over, so every client presenting one needs a freshly minted key. Unlike the earlier
42/// removed-var families, this removal warns rather than refusing startup.
43pub(crate) fn warn_removed_api_key_vars() {
44    const REMOVED: [&str; 3] = [
45        "MICROMEGAS_API_KEYS",
46        "MICROMEGAS_INGESTION_API_KEYS",
47        "MICROMEGAS_ANALYTICS_API_KEYS",
48    ];
49    let set = removed_vars_that_are_set(&REMOVED);
50    if !set.is_empty() {
51        let api_keys_caveat = if set.contains(&"MICROMEGAS_API_KEYS") {
52            " on ingestion/flight-sql (`object-cache-srv` still requires it)"
53        } else {
54            ""
55        };
56        micromegas_tracing::warn!(
57            "{} {} set but no longer read -- mint replacement keys into ingestion_api_keys / \
58             analytics_api_keys from the web app's admin pages, redistribute them to the \
59             clients still presenting the old ones, then unset {}{}",
60            set.join(", "),
61            if set.len() == 1 { "is" } else { "are" },
62            if set.len() == 1 { "it" } else { "them" },
63            api_keys_caveat
64        );
65    }
66}
67
68/// Warns when either of the two removed env-var audience-grant maps --
69/// `MICROMEGAS_AUDIENCE_GRANTS`, `MICROMEGAS_ANALYTICS_AUDIENCE_GRANTS` -- is still set. The
70/// `audience_grants` table is the only source from here on; `micromegas-grants create <audience>
71/// <axis> <selector>` creates a grant row. `MICROMEGAS_INGESTION_AUDIENCE_GRANTS` is excluded --
72/// it was never read by any `AudienceReadPolicy::from_env` call, so warning about it would tell
73/// an operator they lost a setting that never did anything.
74pub(crate) fn warn_removed_audience_grant_vars() {
75    const REMOVED: [&str; 2] = [
76        "MICROMEGAS_AUDIENCE_GRANTS",
77        "MICROMEGAS_ANALYTICS_AUDIENCE_GRANTS",
78    ];
79    let set = removed_vars_that_are_set(&REMOVED);
80    if !set.is_empty() {
81        micromegas_tracing::warn!(
82            "{} {} set but no longer read -- create the grant with \
83             `micromegas-grants create <audience> <axis> <selector>`, then unset {}",
84            set.join(", "),
85            if set.len() == 1 { "is" } else { "are" },
86            if set.len() == 1 { "it" } else { "them" }
87        );
88    }
89}