Files
khadhroony-solana-project/crates/ksp-config-lib/src/sensitivity.rs
2026-08-16 04:35:23 +02:00

210 lines
7.7 KiB
Rust

// file: crates/ksp-config-lib/src/sensitivity.rs
// version: 1
/// Replacement used for secret environment fragments in safe diagnostic representations.
pub const REDACTED_CONFIG_VALUE: &str = "********";
/// Sensitivity assigned to one Config value after environment resolution.
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub enum ConfigSensitivity {
/// Value may be exposed by a public projection when the consumer contract allows it.
Public,
/// Value is available to the runtime but is not generically public.
Internal,
/// Value must remain available to legitimate runtime/management consumers while being redacted from ordinary diagnostics.
Secret,
}
impl ConfigSensitivity {
/// Classifies one supported KSP/KSPB environment variable by its namespace.
pub fn from_variable_name(variable_name: &str) -> ksp_core_lib::Result<Self> {
let validation = crate::environment::validate_supported_variable_name(variable_name);
if let std::result::Result::Err(error) = validation {
return std::result::Result::Err(error);
}
if variable_name.starts_with("KSP_SECRET_") || variable_name.starts_with("KSPB_SECRET_") {
return std::result::Result::Ok(Self::Secret);
}
if variable_name.starts_with("KSP_PUBLIC_") || variable_name.starts_with("KSPB_PUBLIC_") {
return std::result::Result::Ok(Self::Public);
}
return std::result::Result::Ok(Self::Internal);
}
/// Returns the strongest of two sensitivities using `Secret > Internal > Public`.
#[must_use]
pub const fn strongest(self, other: Self) -> Self {
if self as u8 >= other as u8 {
return self;
}
return other;
}
/// Returns whether this sensitivity requires ordinary diagnostic redaction.
#[must_use]
pub const fn is_secret(self) -> bool {
return matches!(self, Self::Secret);
}
}
/// Provenance segment participating in one resolved Config value.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ConfigValueProvenance {
/// Literal text/value came directly from the Config document.
DocumentLiteral,
/// Environment substitution came from the inherited process environment.
EnvironmentProcess {
/// Referenced variable name; never its value.
variable_name: String,
},
/// Environment substitution came from the local `.env` file.
EnvironmentDotEnv {
/// Referenced variable name; never its value.
variable_name: String,
},
/// Environment substitution used the placeholder/API fallback.
EnvironmentFallback {
/// Referenced variable name; never its value.
variable_name: String,
},
}
impl ConfigValueProvenance {
/// Returns the referenced variable name for environment provenance.
#[must_use]
pub fn variable_name(&self) -> std::option::Option<&str> {
return match self {
Self::DocumentLiteral => std::option::Option::None,
Self::EnvironmentProcess { variable_name } | Self::EnvironmentDotEnv { variable_name } | Self::EnvironmentFallback { variable_name } => {
std::option::Option::Some(variable_name.as_str())
},
};
}
/// Returns the environment source represented by this provenance segment when applicable.
#[must_use]
pub const fn environment_source(&self) -> std::option::Option<crate::ConfigEnvironmentSource> {
return match self {
Self::DocumentLiteral => std::option::Option::None,
Self::EnvironmentProcess { .. } => std::option::Option::Some(crate::ConfigEnvironmentSource::Process),
Self::EnvironmentDotEnv { .. } => std::option::Option::Some(crate::ConfigEnvironmentSource::DotEnv),
Self::EnvironmentFallback { .. } => std::option::Option::Some(crate::ConfigEnvironmentSource::Fallback),
};
}
}
/// One resolved Config string with real/safe representations, sensitivity and ordered provenance.
#[derive(Clone, Eq, PartialEq)]
pub struct ResolvedConfigText {
value: String,
safe_value: String,
sensitivity: ConfigSensitivity,
provenance: std::vec::Vec<ConfigValueProvenance>,
}
impl ResolvedConfigText {
pub(crate) fn new(value: String, safe_value: String, sensitivity: ConfigSensitivity, provenance: std::vec::Vec<ConfigValueProvenance>) -> Self {
return Self { value, safe_value, sensitivity, provenance };
}
/// Returns the real runtime value.
#[must_use]
pub fn value(&self) -> &str {
return self.value.as_str();
}
/// Returns the representation safe for ordinary diagnostics.
#[must_use]
pub fn safe_value(&self) -> &str {
return self.safe_value.as_str();
}
/// Returns the strongest sensitivity contributed by referenced environment placeholders.
#[must_use]
pub const fn sensitivity(&self) -> ConfigSensitivity {
return self.sensitivity;
}
/// Returns ordered provenance segments participating in the resolved string.
#[must_use]
pub fn provenance(&self) -> &[ConfigValueProvenance] {
return self.provenance.as_slice();
}
}
impl std::fmt::Debug for ResolvedConfigText {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("ResolvedConfigText")
.field("safe_value", &self.safe_value)
.field("sensitivity", &self.sensitivity)
.field("provenance", &self.provenance)
.finish();
}
}
/// Recursively resolved JSON value preserving a real tree, a safe tree and provenance indexed by JSON Pointer.
#[derive(Clone, Eq, PartialEq)]
pub struct ResolvedConfigJson {
value: serde_json::Value,
safe_value: serde_json::Value,
sensitivity: ConfigSensitivity,
provenance: std::collections::BTreeMap<String, std::vec::Vec<ConfigValueProvenance>>,
}
impl ResolvedConfigJson {
pub(crate) fn new(
value: serde_json::Value,
safe_value: serde_json::Value,
sensitivity: ConfigSensitivity,
provenance: std::collections::BTreeMap<String, std::vec::Vec<ConfigValueProvenance>>,
) -> Self {
return Self { value, safe_value, sensitivity, provenance };
}
/// Returns the real JSON tree intended for legitimate runtime consumers.
#[must_use]
pub const fn value(&self) -> &serde_json::Value {
return &self.value;
}
/// Returns the JSON tree safe for ordinary diagnostics.
#[must_use]
pub const fn safe_value(&self) -> &serde_json::Value {
return &self.safe_value;
}
/// Returns the strongest sensitivity found anywhere in the resolved JSON tree.
#[must_use]
pub const fn sensitivity(&self) -> ConfigSensitivity {
return self.sensitivity;
}
/// Returns provenance indexed by RFC 6901 JSON Pointer strings.
#[must_use]
pub const fn provenance(&self) -> &std::collections::BTreeMap<String, std::vec::Vec<ConfigValueProvenance>> {
return &self.provenance;
}
/// Returns provenance for one JSON Pointer when the resolved value recorded that location.
#[must_use]
pub fn provenance_at(&self, json_pointer: &str) -> std::option::Option<&[ConfigValueProvenance]> {
return self.provenance.get(json_pointer).map(std::vec::Vec::as_slice);
}
}
impl std::fmt::Debug for ResolvedConfigJson {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("ResolvedConfigJson")
.field("safe_value", &self.safe_value)
.field("sensitivity", &self.sensitivity)
.field("provenance", &self.provenance)
.finish();
}
}
#[cfg(test)]
#[path = "../unit_tests/sensitivity.rs"]
mod tests;