use std::collections::HashMap;
use sqlx::PgConnection;
use super::super::config::{RuleEntry, RuleTarget};
use super::super::error::AuthzResult;
use super::super::repository::ingestion::IngestionRepository;
use super::super::types::{Access, EntityKind};
use super::glob::glob_matches;
use super::{IngestOptions, RegisteredEntities};
pub(super) struct ResolvedRule<'a> {
pub(super) entity_kind: EntityKind,
pub(super) ids: Vec<String>,
pub(super) access: &'static str,
pub(super) default_included: bool,
pub(super) roles: &'a [String],
pub(super) justification: Option<&'a str>,
}
pub(super) struct ValidatedRules<'a>(Vec<ResolvedRule<'a>>);
impl<'a> ValidatedRules<'a> {
pub(super) fn rules(&self) -> &[ResolvedRule<'a>] {
&self.0
}
}
pub(super) async fn prune_role_rules(
tx: &mut PgConnection,
resolved: &[ResolvedRule<'_>],
options: &IngestOptions,
) -> AuthzResult<usize> {
let mut entity_types: Vec<String> = Vec::new();
let mut entity_ids: Vec<String> = Vec::new();
for rule in resolved {
for id in &rule.ids {
if !options.scope.owns(rule.entity_kind, id) {
continue;
}
entity_types.push(rule.entity_kind.as_str().to_owned());
entity_ids.push(id.clone());
}
}
let deleted =
IngestionRepository::delete_role_rules_for(tx, &entity_types, &entity_ids, &options.source)
.await?;
Ok(deleted as usize)
}
pub(super) async fn resolve_rules<'a>(
tx: &mut PgConnection,
rules: &'a [RuleEntry],
registered: &RegisteredEntities,
) -> AuthzResult<ValidatedRules<'a>> {
let mut catalog_cache: HashMap<EntityKind, Vec<String>> = HashMap::new();
let mut out = Vec::with_capacity(rules.len());
for rule in rules {
let access = match rule.access {
Access::Allow => "allow",
Access::Deny => "deny",
};
let ids = match &rule.target {
RuleTarget::Id(id) => {
registered.require(rule.entity_type, id)?;
vec![id.clone()]
},
RuleTarget::Match(pattern) => {
if let std::collections::hash_map::Entry::Vacant(entry) =
catalog_cache.entry(rule.entity_type)
{
entry.insert(IngestionRepository::list_entity_ids(tx, rule.entity_type).await?);
}
catalog_cache[&rule.entity_type]
.iter()
.filter(|id| glob_matches(pattern, id))
.cloned()
.collect()
},
};
out.push(ResolvedRule {
entity_kind: rule.entity_type,
ids,
access,
default_included: rule.default_included,
roles: &rule.roles,
justification: rule.justification.as_deref(),
});
}
Ok(ValidatedRules(out))
}