use std::borrow::Cow;
use super::MaskPolicy;
use crate::policy::PolicyError;
use crate::policy::PolicyLocation;
use crate::policy::Sensitivity;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MaskingPolicyBuilder {
low: MaskPolicy,
medium: MaskPolicy,
high: MaskPolicy,
secret: MaskPolicy,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MaskingPolicy {
low: MaskPolicy,
medium: MaskPolicy,
high: MaskPolicy,
secret: MaskPolicy,
}
impl MaskingPolicy {
#[must_use]
#[inline]
pub fn builder() -> MaskingPolicyBuilder {
MaskingPolicyBuilder::default()
}
#[must_use]
#[inline]
pub(crate) fn builder_from(base: &Self) -> MaskingPolicyBuilder {
MaskingPolicyBuilder {
low: base.low.clone(),
medium: base.medium.clone(),
high: base.high.clone(),
secret: base.secret.clone(),
}
}
#[must_use]
#[inline(always)]
pub fn mask<'a>(&self, level: Sensitivity, value: &'a str) -> Cow<'a, str> {
self.for_level(level).mask(value)
}
#[must_use]
#[inline(always)]
pub fn mask_opaque(&self, level: Sensitivity) -> &str {
self.for_level(level).opaque_mask()
}
#[must_use]
#[inline(always)]
#[cfg(feature = "http")]
pub(crate) fn mask_bounded<'a>(&self, level: Sensitivity, value: &'a str, max_bytes: usize) -> Cow<'a, str> {
self.for_level(level).mask_bounded(value, max_bytes)
}
#[inline(always)]
pub(crate) fn mask_bounded_with_truncation<'a>(
&self,
level: Sensitivity,
value: &'a str,
max_bytes: usize,
) -> (Cow<'a, str>, bool) {
self.for_level(level).mask_bounded_with_truncation(value, max_bytes)
}
#[must_use]
#[inline(always)]
pub(crate) fn mask_opaque_bounded(&self, level: Sensitivity, max_bytes: usize) -> String {
self.for_level(level).opaque_mask_bounded(max_bytes)
}
#[must_use]
#[inline(always)]
pub const fn for_level(&self, level: Sensitivity) -> &MaskPolicy {
match level {
Sensitivity::Low => &self.low,
Sensitivity::Medium => &self.medium,
Sensitivity::High => &self.high,
Sensitivity::Secret => &self.secret,
}
}
pub(crate) fn validate(&self, location: PolicyLocation) -> Result<(), PolicyError> {
for level in [
Sensitivity::Low,
Sensitivity::Medium,
Sensitivity::High,
Sensitivity::Secret,
] {
if matches!(
self.for_level(level),
MaskPolicy::Fixed { replacement } if replacement.is_empty()
) {
return Err(PolicyError::EmptyFixedReplacement { location, level });
}
}
Ok(())
}
}
impl MaskingPolicyBuilder {
#[inline]
pub fn low(&mut self, policy: MaskPolicy) -> &mut Self {
self.low = policy;
self
}
#[inline]
pub fn medium(&mut self, policy: MaskPolicy) -> &mut Self {
self.medium = policy;
self
}
#[inline]
pub fn high(&mut self, policy: MaskPolicy) -> &mut Self {
self.high = policy;
self
}
#[inline]
pub fn secret(&mut self, policy: MaskPolicy) -> &mut Self {
self.secret = policy;
self
}
#[inline]
pub(crate) fn policy(&mut self, level: Sensitivity, policy: MaskPolicy) {
match level {
Sensitivity::Low => self.low(policy),
Sensitivity::Medium => self.medium(policy),
Sensitivity::High => self.high(policy),
Sensitivity::Secret => self.secret(policy),
};
}
#[must_use]
#[inline]
pub fn build(self) -> MaskingPolicy {
MaskingPolicy {
low: self.low,
medium: self.medium,
high: self.high,
secret: self.secret,
}
}
}
impl Default for MaskingPolicyBuilder {
fn default() -> Self {
Self {
low: MaskPolicy::preserve_edges(2, 2, "****", 4),
medium: MaskPolicy::preserve_suffix(1, "*******", 1),
high: MaskPolicy::fixed("****"),
secret: MaskPolicy::fixed("<redacted>"),
}
}
}
impl Default for MaskingPolicy {
fn default() -> Self {
Self::builder().build()
}
}
#[cfg(test)]
mod tests {
use super::MaskingPolicy;
use crate::MaskPolicy;
use crate::Sensitivity;
#[test]
fn builder_low_and_medium_replace_their_respective_policies() {
let mut builder = MaskingPolicy::builder();
builder.low(MaskPolicy::fixed("low"));
builder.medium(MaskPolicy::fixed("medium"));
let policy = builder.build();
assert_eq!(policy.for_level(Sensitivity::Low).mask("value"), "low");
assert_eq!(policy.for_level(Sensitivity::Medium).mask("value"), "medium");
}
}