use serde::{Deserialize, Serialize};
use crate::transformer::Transformer;
use crate::types::Column;
pub struct RedactedTransformer {
database_name: String,
table_name: String,
column_name: String,
options: RedactedTransformerOptions,
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Clone, Copy)]
pub struct RedactedTransformerOptions {
pub character: char,
pub width: u8,
}
impl Default for RedactedTransformerOptions {
fn default() -> Self {
RedactedTransformerOptions {
character: '*',
width: 10,
}
}
}
impl RedactedTransformer {
pub fn new<S>(
database_name: S,
table_name: S,
column_name: S,
options: RedactedTransformerOptions,
) -> Self
where
S: Into<String>,
{
RedactedTransformer {
database_name: database_name.into(),
table_name: table_name.into(),
column_name: column_name.into(),
options,
}
}
}
impl Default for RedactedTransformer {
fn default() -> Self {
RedactedTransformer {
database_name: String::default(),
table_name: String::default(),
column_name: String::default(),
options: RedactedTransformerOptions::default(),
}
}
}
impl Transformer for RedactedTransformer {
fn id(&self) -> &str {
"redacted"
}
fn description(&self) -> &str {
"Obfuscate your sensitive data (string only). [4242 4242 4242 4242]->[424****************]"
}
fn database_name(&self) -> &str {
self.database_name.as_str()
}
fn table_name(&self) -> &str {
self.table_name.as_str()
}
fn column_name(&self) -> &str {
self.column_name.as_str()
}
fn transform(&self, column: Column) -> Column {
match column {
Column::StringValue(column_name, value) => {
let new_value = match value.len() {
len if len > 3 => {
format!(
"{}{}",
value.chars().take(3).collect::<String>(),
self.options
.character
.to_string()
.repeat(self.options.width.into())
)
}
_ => value,
};
Column::StringValue(column_name, new_value)
}
column => column,
}
}
}
#[cfg(test)]
mod tests {
use crate::{transformer::Transformer, types::Column};
use super::{RedactedTransformer, RedactedTransformerOptions};
#[test]
fn redact() {
let transformer = get_transformer();
let column = Column::StringValue(
"credit_card_number".to_string(),
"4242 4242 4242 4242".to_string(),
);
let transformed_column = transformer.transform(column);
let transformed_value = transformed_column.string_value().unwrap();
assert_eq!(transformed_value.to_owned(), "424**********")
}
#[test]
fn redact_with_multi_byte_char() {
let transformer = get_transformer();
let column = Column::StringValue(
"multi_byte_column".to_string(),
"🦀ë池cd".to_string(),
);
let transformed_column = transformer.transform(column);
let transformed_value = transformed_column.string_value().unwrap();
assert_eq!(transformed_value.to_owned(), "🦀ë池**********")
}
#[test]
fn strings_lower_than_3_chars_remains_visible() {
let transformer = get_transformer();
let column = Column::StringValue("credit_card_number".to_string(), "424".to_string());
let transformed_column = transformer.transform(column);
let transformed_value = transformed_column.string_value().unwrap();
assert_eq!(transformed_value.to_owned(), "424")
}
#[test]
fn redact_with_custom_char() {
let transformer = RedactedTransformer::new(
"github",
"users",
"credit_card_number",
RedactedTransformerOptions {
character: '#',
width: 20,
},
);
let column = Column::StringValue(
"credit_card_number".to_string(),
"4242 4242 4242 4242".to_string(),
);
let transformed_column = transformer.transform(column);
let transformed_value = transformed_column.string_value().unwrap();
assert_eq!(transformed_value.to_owned(), "424####################")
}
fn get_transformer() -> RedactedTransformer {
RedactedTransformer::new(
"github",
"users",
"credit_card_number",
RedactedTransformerOptions::default(),
)
}
}