rullst_orm/privacy/
secret_serde.rs1use super::{
10 ENVELOPE_PREFIX, PrivacyError, SecretString, configured_key, current_key, current_key_id,
11 decrypt_envelope, encrypt_with_context, parse_envelope,
12};
13use serde::{Deserialize, Deserializer, Serialize, Serializer};
14use std::cell::Cell;
15
16const SERDE_CONTEXT: &[u8] = b"rullst-orm:secret-string:serde";
20
21thread_local! {
22 static REDACTION_DEPTH: Cell<usize> = const { Cell::new(0) };
23}
24
25struct RedactionScope;
26
27impl RedactionScope {
28 fn enter() -> Self {
29 REDACTION_DEPTH.with(|depth| depth.set(depth.get().saturating_add(1)));
30 Self
31 }
32}
33
34impl Drop for RedactionScope {
35 fn drop(&mut self) {
36 REDACTION_DEPTH.with(|depth| depth.set(depth.get().saturating_sub(1)));
37 }
38}
39
40#[doc(hidden)]
45pub fn with_redacted_secrets<R>(serialize: impl FnOnce() -> R) -> R {
46 let _scope = RedactionScope::enter();
47 serialize()
48}
49
50fn redacting() -> bool {
51 REDACTION_DEPTH.with(|depth| depth.get() > 0)
52}
53
54fn encrypt_serialized(plaintext: &str) -> Result<String, PrivacyError> {
55 let key_id = current_key_id()?;
56 let key = current_key()?;
57 encrypt_with_context(plaintext, &key, &key_id, SERDE_CONTEXT)
58}
59
60fn decrypt_serialized(encrypted: &str) -> Result<String, PrivacyError> {
61 let envelope = parse_envelope(encrypted)?;
62 let key = configured_key(envelope.key_id)?;
63 decrypt_envelope(&envelope, &key, SERDE_CONTEXT)
64}
65
66fn is_envelope(value: &str) -> bool {
67 value
68 .strip_prefix(ENVELOPE_PREFIX)
69 .is_some_and(|rest| rest.starts_with(':'))
70}
71
72impl Serialize for SecretString {
73 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
76 if redacting() {
77 return serializer.serialize_str(crate::audit::REDACTED_VALUE);
78 }
79 let envelope = encrypt_serialized(&self.0).map_err(serde::ser::Error::custom)?;
80 serializer.serialize_str(&envelope)
81 }
82}
83
84impl<'de> Deserialize<'de> for SecretString {
85 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
89 let value = String::deserialize(deserializer)?;
90 if !is_envelope(&value) {
91 return Ok(SecretString(value));
92 }
93 decrypt_serialized(&value)
94 .map(SecretString)
95 .map_err(serde::de::Error::custom)
96 }
97}