use super::*;
pub(crate) fn parse_bucket_policy_json(json: &str) -> Result<Policy, RuntimeError> {
let value: serde_json::Value = serde_json::from_str(json)
.map_err(|error| RuntimeError::InvalidBucketPolicy(error.to_string()))?;
let statements = value
.get("Statement")
.ok_or_else(|| RuntimeError::InvalidBucketPolicy("missing Statement".to_string()))?;
let statement_values = match statements {
serde_json::Value::Array(values) => values.clone(),
serde_json::Value::Object(_) => vec![statements.clone()],
_ => {
return Err(RuntimeError::InvalidBucketPolicy(
"Statement must be an object or array".to_string(),
))
}
};
let mut policy = Policy::new();
for statement_value in statement_values {
let statement_object = statement_value.as_object().ok_or_else(|| {
RuntimeError::InvalidBucketPolicy("Statement entries must be objects".to_string())
})?;
let effect = match statement_object
.get("Effect")
.and_then(serde_json::Value::as_str)
{
Some("Allow") => Effect::Allow,
Some("Deny") => Effect::Deny,
Some(value) => {
return Err(RuntimeError::InvalidBucketPolicy(format!(
"unsupported Effect: {value}"
)))
}
None => {
return Err(RuntimeError::InvalidBucketPolicy(
"missing Effect".to_string(),
))
}
};
let principals = statement_object
.get("Principal")
.map(policy_principal_values)
.transpose()?
.unwrap_or_else(|| vec!["*".to_string()]);
let actions = policy_string_values(
statement_object
.get("Action")
.ok_or_else(|| RuntimeError::InvalidBucketPolicy("missing Action".to_string()))?,
"Action",
)?;
let resources = policy_string_values(
statement_object
.get("Resource")
.ok_or_else(|| RuntimeError::InvalidBucketPolicy("missing Resource".to_string()))?,
"Resource",
)?;
let mut statement = match effect {
Effect::Allow => Statement::allow_many(principals, actions, resources),
Effect::Deny => Statement::deny_many(principals, actions, resources),
};
if let Some(string_equals) = statement_object
.get("Condition")
.and_then(|condition| condition.get("StringEquals"))
.and_then(serde_json::Value::as_object)
{
for (key, value) in string_equals {
let values = policy_string_values(value, &format!("StringEquals.{key}"))?;
statement = statement.with_string_equals_any(key.clone(), values);
}
}
policy.add(statement);
}
Ok(policy)
}
pub(crate) fn policy_principal_values(
value: &serde_json::Value,
) -> Result<Vec<String>, RuntimeError> {
match value {
serde_json::Value::Object(object) => object
.get("AWS")
.map(|value| policy_string_values(value, "Principal.AWS"))
.unwrap_or_else(|| Ok(vec!["*".to_string()])),
_ => policy_string_values(value, "Principal"),
}
}
pub(crate) fn policy_string_values(
value: &serde_json::Value,
field: &str,
) -> Result<Vec<String>, RuntimeError> {
match value {
serde_json::Value::String(value) => Ok(vec![value.clone()]),
serde_json::Value::Array(values) => values
.iter()
.map(|value| {
value.as_str().map(str::to_string).ok_or_else(|| {
RuntimeError::InvalidBucketPolicy(format!(
"{field} array entries must be strings"
))
})
})
.collect(),
_ => Err(RuntimeError::InvalidBucketPolicy(format!(
"{field} must be a string or string array"
))),
}
}