systemprompt_security/authz/
keep.rs1use std::collections::HashSet;
14
15use systemprompt_identifiers::UserId;
16
17use super::error::AuthzResult;
18use super::repository::AccessControlRepository;
19use super::resolver::{ResolveInput, ResolveParent, resolve};
20use super::subject::{SubjectAttributes, SubjectDimension};
21use super::types::{Decision, EntityKind, EntityRef};
22
23#[derive(Debug, Clone, Copy)]
25pub struct BulkKeepQuery<'a> {
26 pub user_id: &'a UserId,
27 pub roles: &'a [String],
28 pub kind: EntityKind,
29 pub ids: &'a [String],
30 pub parents: &'a [ResolveParent<'a>],
31 pub attributes: &'a SubjectAttributes,
32 pub dimensions: &'a [SubjectDimension],
33}
34
35pub async fn allowed_ids(
36 repo: &AccessControlRepository,
37 query: BulkKeepQuery<'_>,
38) -> AuthzResult<HashSet<String>> {
39 if query.ids.is_empty() {
40 return Ok(HashSet::new());
41 }
42 let rules = repo.list_rules_bulk(query.kind, query.ids).await?;
43 let entities = repo.list_entities_bulk(query.kind, query.ids).await?;
44 let mut keep = HashSet::with_capacity(query.ids.len());
45 for id in query.ids {
46 let entity_rules = rules.get(id).map_or(&[][..], Vec::as_slice);
47 let default_included = entities.get(id).map(|e| e.default_included);
48 let entity = EntityRef::from_kind_and_id(query.kind, id);
49 let decision = resolve(ResolveInput {
50 entity: &entity,
51 rules: entity_rules,
52 user_id: query.user_id,
53 user_roles: query.roles,
54 default_included,
55 parents: query.parents,
56 attributes: query.attributes,
57 dimensions: query.dimensions,
58 });
59 if matches!(decision, Decision::Allow { .. }) {
60 keep.insert(id.clone());
61 }
62 }
63 Ok(keep)
64}