Skip to main content

platform_core/runtime_config/
generated.rs

1use crate::db::DbPool;
2use crate::error::AppResult;
3use crate::runtime_config::descriptor::{
4    RuntimeConfigGeneratedValue, RuntimeConfigRegistry, RuntimeConfigVisibilityCondition,
5};
6use crate::runtime_config::snapshot::RuntimeConfigSnapshot;
7use crate::runtime_config::store::upsert_value_if_missing;
8use serde_json::Value;
9use std::collections::BTreeMap;
10use std::fmt::Write as _;
11
12/// Initialize generated values whose conditions are true and whose stored row is
13/// missing or JSON null.
14pub async fn initialize_generated_values(
15    pool: &DbPool,
16    registry: &RuntimeConfigRegistry,
17    service_key: &str,
18    stored: &BTreeMap<(String, String), Value>,
19    actor: Option<&str>,
20) -> AppResult<Vec<(String, String)>> {
21    let snapshot = RuntimeConfigSnapshot::resolve(registry, service_key, stored);
22    let mut generated = Vec::new();
23    for descriptor in registry.iter() {
24        let descriptor_service = descriptor.scope.as_service_key();
25        if descriptor_service != "*" && descriptor_service != service_key {
26            continue;
27        }
28        let Some(RuntimeConfigGeneratedValue::Secret { bytes, when }) = &descriptor.generated
29        else {
30            continue;
31        };
32        if !condition_matches(when, service_key, &snapshot) {
33            continue;
34        }
35        let value = serde_json::json!(generate_secret(*bytes));
36        if upsert_value_if_missing(pool, descriptor_service, &descriptor.key, &value, actor)
37            .await?
38            .is_some()
39        {
40            generated.push((descriptor_service.to_owned(), descriptor.key.to_owned()));
41        }
42    }
43    Ok(generated)
44}
45
46fn condition_matches(
47    condition: &RuntimeConfigVisibilityCondition,
48    service_key: &str,
49    snapshot: &RuntimeConfigSnapshot,
50) -> bool {
51    match condition {
52        RuntimeConfigVisibilityCondition::Equals {
53            service,
54            key,
55            value,
56        } if *service == "*" || *service == service_key => {
57            snapshot.raw(key).is_some_and(|current| current == value)
58        }
59        RuntimeConfigVisibilityCondition::Equals { .. } => false,
60    }
61}
62
63fn generate_secret(bytes: usize) -> String {
64    let mut raw = vec![0_u8; bytes];
65    getrandom::fill(&mut raw).expect("OS randomness should be available");
66    let mut out = String::with_capacity(bytes * 2);
67    for byte in raw {
68        let _ = write!(&mut out, "{byte:02x}");
69    }
70    out
71}