v0.1.4-pre.013
This commit is contained in:
105
crates/ksp-app-config-desk/src/secrets.rs
Normal file
105
crates/ksp-app-config-desk/src/secrets.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
// file: crates/ksp-app-config-desk/src/secrets.rs
|
||||
// version: 1
|
||||
|
||||
//! Privileged, explicitly requested Config Secret reveal boundary for Config Desk.
|
||||
|
||||
use ts_rs::TS; // rust-rules: derive-import
|
||||
|
||||
/// Privileged reveal request kept separate from ordinary environment-report DTOs.
|
||||
#[derive(serde::Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_config_desk/secrets/SecretRevealRequestDto.ts")]
|
||||
pub(crate) struct SecretRevealRequestDto {
|
||||
/// Secret KSP/KSPB variable to reveal.
|
||||
pub(crate) variable_name: String,
|
||||
/// Requested source: `effective` or `dotenv`.
|
||||
pub(crate) source: String,
|
||||
}
|
||||
|
||||
/// Privileged reveal response. This type deliberately does not derive `Clone` or `Debug`.
|
||||
#[derive(serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_config_desk/secrets/SecretRevealResponseDto.ts")]
|
||||
pub(crate) struct SecretRevealResponseDto {
|
||||
/// Secret variable that was explicitly requested.
|
||||
pub(crate) variable_name: String,
|
||||
/// Source that was explicitly requested.
|
||||
pub(crate) source: String,
|
||||
/// Real value when the requested source contains one; never include this field in logs or ordinary application state.
|
||||
pub(crate) value: std::option::Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||
enum SecretRevealSource {
|
||||
Effective,
|
||||
DotEnv,
|
||||
}
|
||||
|
||||
impl SecretRevealSource {
|
||||
fn parse(value: &str) -> ksp_core_lib::Result<Self> {
|
||||
return match value {
|
||||
"effective" => std::result::Result::Ok(Self::Effective),
|
||||
"dotenv" => std::result::Result::Ok(Self::DotEnv),
|
||||
_ => std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_SECRET_REVEAL_SOURCE_INVALID,
|
||||
"Secret reveal source must be effective or dotenv",
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
const fn label(self) -> &'static str {
|
||||
return match self {
|
||||
Self::Effective => "effective",
|
||||
Self::DotEnv => "dotenv",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Reveals one real Secret value only after the dedicated Tauri command has been explicitly invoked.
|
||||
pub(crate) fn reveal(state: &crate::AppState, request: SecretRevealRequestDto) -> ksp_core_lib::Result<SecretRevealResponseDto> {
|
||||
return reveal_from_management(state.config_management(), request);
|
||||
}
|
||||
|
||||
fn reveal_from_management(management: &ksp_config_lib::ConfigManagement, request: SecretRevealRequestDto) -> ksp_core_lib::Result<SecretRevealResponseDto> {
|
||||
let source = validate_request(request.variable_name.as_str(), request.source.as_str());
|
||||
let source = match source {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let value = match source {
|
||||
SecretRevealSource::Effective => management.reveal_effective_environment_value(request.variable_name.as_str()),
|
||||
SecretRevealSource::DotEnv => management.reveal_dotenv_value(request.variable_name.as_str()),
|
||||
};
|
||||
let value = match value {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
ksp_logging_lib::debug!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_SECRETS,
|
||||
variable_name = request.variable_name.as_str(),
|
||||
source = source.label(),
|
||||
value_present = value.is_some(),
|
||||
"privileged Config Secret reveal completed"
|
||||
);
|
||||
return std::result::Result::Ok(SecretRevealResponseDto { variable_name: request.variable_name, source: source.label().to_owned(), value });
|
||||
}
|
||||
|
||||
fn validate_request(variable_name: &str, source: &str) -> ksp_core_lib::Result<SecretRevealSource> {
|
||||
let sensitivity = ksp_config_lib::ConfigSensitivity::from_variable_name(variable_name);
|
||||
let sensitivity = match sensitivity {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if sensitivity != ksp_config_lib::ConfigSensitivity::Secret {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_SECRET_REVEAL_REQUIRES_SECRET,
|
||||
"Privileged reveal is restricted to KSP/KSPB Secret namespaces",
|
||||
));
|
||||
}
|
||||
return SecretRevealSource::parse(source);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/secrets.rs"]
|
||||
mod tests;
|
||||
Reference in New Issue
Block a user