1use std::sync::{
11 Arc,
12 LazyLock,
13 OnceLock,
14};
15
16use super::redaction_limits::RedactionLimits;
17use super::{
18 AllowRule,
19 FieldClassification,
20 FieldNameMatching,
21 MaskingPolicy,
22 RedactionFloor,
23 RedactionPolicyBuilder,
24 RedactionRules,
25 SensitiveFieldRule,
26 Sensitivity,
27 UnknownFieldPolicy,
28 internal::RedactionPolicyInner,
29};
30#[cfg(feature = "json")]
31use super::{
32 JsonDepthBudget,
33 UnkeyedJsonValuePolicy,
34};
35
36pub(super) const STANDARD_EXTRA_FIELDS: &[(&str, Sensitivity)] = &[
38 ("auth_app_token", Sensitivity::High),
39 ("auth_user_token", Sensitivity::High),
40 ("connection_string", Sensitivity::Secret),
41 ("database_uri", Sensitivity::Secret),
42 ("database_url", Sensitivity::Secret),
43 ("license_key", Sensitivity::Medium),
44 ("mysql_pwd", Sensitivity::Secret),
45 ("rediscli_auth", Sensitivity::Secret),
46 ("sig", Sensitivity::Secret),
47 ("signature", Sensitivity::Secret),
48];
49
50static STANDARD_POLICY: LazyLock<RedactionPolicy> = LazyLock::new(|| {
51 RedactionPolicy::from_rules(
52 RedactionRules::new(
53 RedactionPolicyInner {
54 sensitive: Default::default(),
55 allow_exact: Default::default(),
56 allow_suffix: Default::default(),
57 matching: FieldNameMatching::ExactOrTokenSuffix,
58 unknown_field_policy: UnknownFieldPolicy::PassThrough,
59 },
60 Some(RedactionFloor::standard()),
61 ),
62 MaskingPolicy::default(),
63 RedactionLimits::default(),
64 #[cfg(feature = "http")]
65 crate::http::HttpPolicyBuilder::new()
66 .build()
67 .expect("the built-in HTTP policy must be valid"),
68 #[cfg(feature = "uri")]
69 crate::uri::UriPolicyBuilder::new()
70 .build()
71 .expect("the built-in URI policy must be valid"),
72 #[cfg(feature = "json")]
73 UnkeyedJsonValuePolicy::PassThrough,
74 )
75});
76static STRICT_POLICY: LazyLock<RedactionPolicy> = LazyLock::new(|| {
77 RedactionPolicy::from_rules(
78 RedactionRules::new(
79 RedactionPolicyInner {
80 sensitive: Default::default(),
81 allow_exact: Default::default(),
82 allow_suffix: Default::default(),
83 matching: FieldNameMatching::ExactOrTokenSuffix,
84 unknown_field_policy: UnknownFieldPolicy::Redact(
85 Sensitivity::Secret,
86 ),
87 },
88 Some(RedactionFloor::standard()),
89 ),
90 MaskingPolicy::default(),
91 RedactionLimits::default(),
92 #[cfg(feature = "http")]
93 {
94 let mut http = crate::http::HttpPolicyBuilder::new();
95 http.url_path_mut(crate::http::UrlPathPolicy::Redact);
96 http.text_body_mut(crate::http::TextBodyPolicy::Redact);
97 http.build()
98 .expect("the built-in HTTP policy must be valid")
99 },
100 #[cfg(feature = "uri")]
101 crate::uri::UriPolicyBuilder::new()
102 .build()
103 .expect("the built-in URI policy must be valid"),
104 #[cfg(feature = "json")]
105 UnkeyedJsonValuePolicy::Redact,
106 )
107});
108static GLOBAL_POLICY: OnceLock<RedactionPolicy> = OnceLock::new();
109#[must_use]
111#[derive(Debug, Clone, PartialEq, Eq)]
112pub struct RedactionPolicy {
113 rules: RedactionRules,
114 masking: Arc<MaskingPolicy>,
115 limits: RedactionLimits,
116 #[cfg(feature = "http")]
117 http: Arc<crate::http::HttpPolicy>,
118 #[cfg(feature = "uri")]
119 uri: Arc<crate::uri::UriPolicy>,
120 #[cfg(feature = "json")]
121 unkeyed_json_value_policy: UnkeyedJsonValuePolicy,
122}
123
124impl RedactionPolicy {
125 pub fn install_global(
148 policy: Self,
149 ) -> Result<(), crate::InstallGlobalPolicyError> {
150 GLOBAL_POLICY
151 .set(policy)
152 .map_err(crate::InstallGlobalPolicyError)
153 }
154
155 #[inline]
171 pub fn global() -> &'static Self {
172 GLOBAL_POLICY.get().unwrap_or(&STANDARD_POLICY)
173 }
174
175 #[inline]
181 pub fn standard() -> Self {
182 STANDARD_POLICY.clone()
183 }
184
185 #[inline]
191 pub fn strict() -> Self {
192 STRICT_POLICY.clone()
193 }
194
195 #[inline]
197 pub fn builder_from_default() -> RedactionPolicyBuilder {
198 Self::builder_from(&Self::default())
199 }
200
201 #[inline]
204 pub fn builder() -> RedactionPolicyBuilder {
205 RedactionPolicyBuilder::new()
206 }
207
208 #[inline]
212 pub fn to_builder(&self) -> RedactionPolicyBuilder {
213 RedactionPolicyBuilder::from_policy(self)
214 }
215
216 #[inline]
218 pub fn builder_from(base: &Self) -> RedactionPolicyBuilder {
219 base.to_builder()
220 }
221
222 pub(crate) fn from_rules(
224 rules: RedactionRules,
225 masking: MaskingPolicy,
226 limits: RedactionLimits,
227 #[cfg(feature = "http")] http: crate::http::HttpPolicy,
228 #[cfg(feature = "uri")] uri: crate::uri::UriPolicy,
229 #[cfg(feature = "json")]
230 unkeyed_json_value_policy: UnkeyedJsonValuePolicy,
231 ) -> Self {
232 Self {
233 rules,
234 masking: Arc::new(masking),
235 limits,
236 #[cfg(feature = "http")]
237 http: Arc::new(http),
238 #[cfg(feature = "uri")]
239 uri: Arc::new(uri),
240 #[cfg(feature = "json")]
241 unkeyed_json_value_policy,
242 }
243 }
244
245 #[inline]
247 pub const fn limits(&self) -> &RedactionLimits {
248 &self.limits
249 }
250
251 #[cfg(feature = "http")]
253 #[inline]
254 pub fn http(&self) -> &crate::http::HttpPolicy {
255 self.http.as_ref()
256 }
257
258 #[cfg(feature = "uri")]
260 #[inline]
261 pub fn uri(&self) -> &crate::uri::UriPolicy {
262 self.uri.as_ref()
263 }
264
265 #[cfg(feature = "http")]
267 #[inline]
268 pub fn header_rules(&self) -> &RedactionRules {
269 self.http.header_rules()
270 }
271
272 #[cfg(feature = "http")]
274 #[inline]
275 pub fn query_rules(&self) -> &RedactionRules {
276 self.http.query_rules()
277 }
278
279 #[cfg(feature = "http")]
281 #[inline]
282 pub fn body_rules(&self) -> &RedactionRules {
283 self.http.body_rules()
284 }
285
286 #[cfg(feature = "http")]
288 #[inline]
289 pub fn url_path_policy(&self) -> crate::http::UrlPathPolicy {
290 self.http.url_path_policy()
291 }
292
293 #[cfg(feature = "http")]
295 #[inline]
296 pub fn text_body_policy(&self) -> crate::http::TextBodyPolicy {
297 self.http.text_body_policy()
298 }
299
300 #[cfg(feature = "http")]
302 #[inline]
303 pub fn body_budget(&self) -> crate::http::BodyBudget {
304 self.limits.http_body()
305 }
306
307 #[cfg(feature = "uri")]
309 #[inline]
310 pub fn path_policy(&self) -> crate::uri::UriPathPolicy {
311 self.uri.path_policy()
312 }
313
314 #[cfg(feature = "uri")]
316 #[inline]
317 pub fn fragment_policy(&self) -> crate::uri::UriFragmentPolicy {
318 self.uri.fragment_policy()
319 }
320
321 #[cfg(feature = "json")]
323 #[inline]
324 pub const fn json_depth_budget(&self) -> JsonDepthBudget {
325 self.limits.json_depth_budget()
326 }
327
328 #[cfg(feature = "json")]
330 #[inline]
331 pub const fn unkeyed_json_value_policy(&self) -> UnkeyedJsonValuePolicy {
332 self.unkeyed_json_value_policy
333 }
334 #[inline]
336 pub const fn rules(&self) -> &RedactionRules {
337 &self.rules
338 }
339
340 #[inline]
342 pub const fn fields(&self) -> &RedactionRules {
343 &self.rules
344 }
345
346 #[inline]
349 pub fn floor(&self) -> Option<&RedactionFloor> {
350 self.rules.floor()
351 }
352
353 pub fn with_floor(mut self, floor: RedactionFloor) -> Self {
355 self.rules = self.rules.with_floor(floor);
356 self
357 }
358 pub fn disable_floor(mut self) -> Self {
364 self.rules = self.rules.disable_floor();
365 self
366 }
367
368 #[inline]
374 pub fn classify_field<'a>(
375 &'a self,
376 field: &str,
377 ) -> FieldClassification<'a> {
378 self.rules.classify_field(field)
379 }
380
381 #[inline]
387 pub fn sensitivity_for(&self, field: &str) -> Option<Sensitivity> {
388 self.rules.sensitivity_for(field)
389 }
390
391 #[inline]
393 pub(crate) fn sensitivity_for_exact(
394 &self,
395 field: &str,
396 ) -> Option<Sensitivity> {
397 self.rules.sensitivity_for_exact(field)
398 }
399
400 #[inline]
402 pub(crate) fn resolve_field_exact(
403 &self,
404 field: &str,
405 ) -> super::ResolvedField {
406 self.rules.resolve_field_exact(field)
407 }
408
409 #[inline]
414 pub fn matching(&self) -> FieldNameMatching {
415 self.rules.matching()
416 }
417
418 #[inline]
422 pub fn unknown_field_policy(&self) -> UnknownFieldPolicy {
423 self.rules.unknown_field_policy()
424 }
425 #[inline]
431 pub fn masking(&self) -> &MaskingPolicy {
432 self.masking.as_ref()
433 }
434
435 #[inline]
440 pub fn application_sensitive_rules(
441 &self,
442 ) -> impl Iterator<Item = SensitiveFieldRule<'_>> {
443 self.rules.application_sensitive_rules()
444 }
445
446 #[inline]
450 pub fn application_allow_rules(
451 &self,
452 ) -> impl Iterator<Item = AllowRule<'_>> {
453 self.rules.application_allow_rules()
454 }
455
456 #[inline]
458 pub(crate) fn resolve_field(&self, field: &str) -> super::ResolvedField {
459 self.rules.resolve_field(field)
460 }
461}
462
463impl Default for RedactionPolicy {
464 fn default() -> Self {
474 Self::global().clone()
475 }
476}