use crate::Audience;
use super::policy::{DocumentScope, ProductGroupAccessPolicy};
#[derive(Debug, Clone)]
pub struct PolicyDecision {
pub audience: Audience,
pub redacted_fields: Vec<String>,
pub filtered_data: serde_json::Value,
}
pub fn filter_by_audience(
data: &serde_json::Value,
policy: &ProductGroupAccessPolicy,
audience: Audience,
) -> PolicyDecision {
filter_by_audience_in_scope(data, policy, audience, DocumentScope::Envelope)
}
pub fn filter_by_audience_in_scope(
data: &serde_json::Value,
policy: &ProductGroupAccessPolicy,
audience: Audience,
root_scope: DocumentScope,
) -> PolicyDecision {
let mut redacted_fields = Vec::new();
let mut segments: Vec<String> = Vec::new();
let filtered_data = filter_value(
data,
policy,
audience,
"",
root_scope,
&mut segments,
&mut redacted_fields,
);
PolicyDecision {
audience,
redacted_fields,
filtered_data,
}
}
const PRODUCT_GROUP_DATA: &str = "productGroupData";
fn filter_value(
data: &serde_json::Value,
policy: &ProductGroupAccessPolicy,
audience: Audience,
prefix: &str,
scope: DocumentScope,
segments: &mut Vec<String>,
redacted: &mut Vec<String>,
) -> serde_json::Value {
match data {
serde_json::Value::Object(map) => {
let mut filtered = serde_json::Map::new();
for (key, value) in map {
let path = if prefix.is_empty() {
key.clone()
} else {
format!("{prefix}.{key}")
};
segments.push(key.clone());
let borrowed: Vec<&str> = segments.iter().map(String::as_str).collect();
let class = policy.disclosure_for_path(&borrowed, scope);
drop(borrowed);
let child_scope = if scope == DocumentScope::ProductGroupData
|| key.as_str() == PRODUCT_GROUP_DATA
{
DocumentScope::ProductGroupData
} else {
DocumentScope::Envelope
};
if audience.may_see(class) {
filtered.insert(
key.clone(),
filter_value(
value,
policy,
audience,
&path,
child_scope,
segments,
redacted,
),
);
} else {
redacted.push(path);
}
segments.pop();
}
serde_json::Value::Object(filtered)
}
serde_json::Value::Array(items) => {
let mut out = Vec::with_capacity(items.len());
for (i, item) in items.iter().enumerate() {
out.push(filter_value(
item,
policy,
audience,
&format!("{prefix}[{i}]"),
scope,
segments,
redacted,
));
}
serde_json::Value::Array(out)
}
other => other.clone(),
}
}