1use cedar_policy::{
2 Authorizer, Entities, Entity, Policy, PolicyId, PolicySet, Request as CedarRequest, Schema,
3};
4use std::collections::{HashMap, HashSet};
5use std::sync::{Arc, OnceLock};
6use std::time::{Duration, Instant, SystemTime};
7use std::vec;
8
9use crate::labels::LabelRegistry;
10use crate::policy_match::{
11 action_match_reason, matches_effect, principal_match_reason, resource_match_reason,
12};
13use crate::query::{ActionQuery, PrincipalQuery, ResourceQuery};
14use crate::timers::PhaseTimer;
15use crate::traits::CedarAtom;
16use crate::types::{
17 Decision, DecisionDiagnostics, FromDecisionWithPolicy, PermitPolicies, PermitPolicy,
18 PolicyEffectFilter, PolicyMatchReason, PolicyVersion, Request, RequestContext, Resource,
19 UserPolicies,
20};
21use crate::{Groups, Principal};
22use crate::{error::PolicyError, loader};
23use arc_swap::ArcSwap;
24
25use sha2::{Digest, Sha256};
26use tracing::debug;
27#[cfg(feature = "observability")]
28use tracing::info_span;
29
30#[cfg(feature = "observability")]
31use crate::metrics::{
32 EvaluationObservation, EvaluationPhases, MatchedPolicySource, get_sink, metrics_enabled,
33 record_evaluation_observation, record_reload,
34};
35
36fn get_authorizer() -> &'static Authorizer {
38 static AUTHORIZER: OnceLock<Authorizer> = OnceLock::new();
39 AUTHORIZER.get_or_init(Authorizer::new)
40}
41
42#[derive(Debug)]
44struct EvalTimers {
45 total_start: Option<Instant>,
47 measure_enabled: bool,
49 debug_enabled: bool,
51 labels: Duration,
53 construct_req: Duration,
55 entities: Duration,
57 groups: Duration,
59 authz: Duration,
61}
62
63impl EvalTimers {
64 fn start(measure_enabled: bool, debug_enabled: bool) -> Self {
65 Self {
66 total_start: measure_enabled.then(Instant::now),
67 measure_enabled,
68 debug_enabled,
69 labels: Duration::ZERO,
70 construct_req: Duration::ZERO,
71 entities: Duration::ZERO,
72 groups: Duration::ZERO,
73 authz: Duration::ZERO,
74 }
75 }
76
77 fn total_elapsed(&self) -> Duration {
78 self.total_start
79 .map_or(Duration::ZERO, |start| start.elapsed())
80 }
81}
82
83struct PreparedRequest {
85 cedar_req: CedarRequest,
86 entities: Entities,
87 snapshot: Snapshot,
88 timers: EvalTimers,
89 #[cfg(feature = "observability")]
90 sink: crate::metrics::SinkGuard,
91 #[cfg(feature = "observability")]
92 metrics_enabled: bool,
93}
94
95#[derive(Debug)]
97struct PolicySnapshot {
98 set: PolicySet,
99 version: PolicyVersion,
100 permit_policies: HashMap<PolicyId, PermitPolicy>,
101 forbid_policy_ids: HashMap<PolicyId, String>,
102 schema: Option<Arc<Schema>>,
103}
104
105type Snapshot = Arc<PolicySnapshot>;
107
108impl PolicySnapshot {
109 fn from_policy_text(policy_text: &str) -> Result<Self, PolicyError> {
110 Self::from_policy_text_with_schema(policy_text, None)
111 }
112
113 fn from_policy_text_with_schema(
114 policy_text: &str,
115 schema: Option<Arc<Schema>>,
116 ) -> Result<Self, PolicyError> {
117 let set = match schema.as_deref() {
118 Some(schema) => loader::compile_policy_with_schema(policy_text, schema)?,
119 None => loader::compile_policy(policy_text)?,
120 };
121 let permit_policies = loader::precompute_permit_policies(&set)?;
122 let forbid_policy_ids = loader::precompute_forbid_policy_ids(&set);
123
124 let mut hasher = Sha256::new();
125 hasher.update(policy_text.as_bytes());
126 let digest = hasher.finalize();
127 let mut hash = String::with_capacity(digest.len() * 2);
128 const HEX: &[u8; 16] = b"0123456789abcdef";
129 for byte in digest {
130 hash.push(char::from(HEX[usize::from(byte >> 4)]));
131 hash.push(char::from(HEX[usize::from(byte & 0x0f)]));
132 }
133
134 Ok(PolicySnapshot {
135 set,
136 version: PolicyVersion {
137 hash: hash.into(),
138 loaded_at: humantime::format_rfc3339(SystemTime::now())
139 .to_string()
140 .into(),
141 },
142 permit_policies,
143 forbid_policy_ids,
144 schema,
145 })
146 }
147
148 fn policy_set(&self) -> &PolicySet {
149 &self.set
150 }
151
152 fn version(&self) -> PolicyVersion {
153 self.version.clone()
154 }
155
156 fn schema(&self) -> Option<&Schema> {
157 self.schema.as_deref()
158 }
159}
160
161#[inline]
163fn extract_permit_policies(
164 snapshot: &PolicySnapshot,
165 result: &cedar_policy::Response,
166) -> PermitPolicies {
167 if result.decision() != cedar_policy::Decision::Allow {
168 return PermitPolicies::empty();
169 }
170
171 result
172 .diagnostics()
173 .reason()
174 .filter_map(|reason| snapshot.permit_policies.get(reason))
175 .cloned()
176 .collect()
177}
178
179#[inline]
181fn extract_forbid_policy_ids(
182 snapshot: &PolicySnapshot,
183 result: &cedar_policy::Response,
184) -> Vec<String> {
185 if result.decision() != cedar_policy::Decision::Deny {
186 return Vec::new();
187 }
188
189 let mut ids: Vec<String> = result
190 .diagnostics()
191 .reason()
192 .filter_map(|reason| snapshot.forbid_policy_ids.get(reason).cloned())
193 .collect();
194 ids.sort();
195 ids.dedup();
196 ids
197}
198
199#[inline]
201fn request_groups(request: &Request) -> Option<&Groups> {
202 match &request.principal {
203 Principal::User(user) => Some(user.groups()),
204 Principal::Group(_) => None,
205 }
206}
207
208#[inline]
210fn apply_labels(
211 registry: &LabelRegistry,
212 resource: &crate::types::Resource,
213 timers: &mut EvalTimers,
214) -> Option<crate::types::Resource> {
215 let measure_enabled = timers.measure_enabled;
216 let _timer = PhaseTimer::new_if(&mut timers.labels, measure_enabled);
217 #[cfg(feature = "observability")]
218 let _label_span = info_span!("apply_labels").entered();
219 registry.apply_to_clone_if_applicable(resource)
220}
221
222#[inline]
224fn build_effective_context(
225 request_context: Option<&RequestContext>,
226) -> Result<cedar_policy::Context, PolicyError> {
227 match request_context {
228 Some(context) if !context.is_empty() => context.to_cedar_context(),
229 _ => Ok(cedar_policy::Context::empty()),
230 }
231}
232
233#[inline]
236fn build_cedar_req(
237 principal_uid: cedar_policy::EntityUid,
238 action_uid: cedar_policy::EntityUid,
239 resource_uid: cedar_policy::EntityUid,
240 context: cedar_policy::Context,
241 schema: Option<&Schema>,
242 timers: &mut EvalTimers,
243) -> Result<CedarRequest, PolicyError> {
244 let measure_enabled = timers.measure_enabled;
245 let _timer = PhaseTimer::new_if(&mut timers.construct_req, measure_enabled);
246 #[cfg(feature = "observability")]
247 let _req_span = info_span!("construct_cedar_req").entered();
248
249 Ok(CedarRequest::new(
250 principal_uid,
251 action_uid,
252 resource_uid,
253 context,
254 schema,
255 )?)
256}
257
258#[inline]
261fn build_entities(
262 principal_uid: cedar_policy::EntityUid,
263 resource_uid: cedar_policy::EntityUid,
264 resource: &crate::types::Resource,
265 groups: Option<&Groups>,
266 schema: Option<&Schema>,
267 timers: &mut EvalTimers,
268) -> Result<Entities, PolicyError> {
269 let group_uids = {
270 let measure_enabled = timers.measure_enabled;
271 let _timer = PhaseTimer::new_if(&mut timers.groups, measure_enabled);
272 #[cfg(feature = "observability")]
273 let _groups_span = info_span!("resolve_groups").entered();
274
275 let mut group_uids = HashSet::with_capacity(groups.map_or(0, Groups::len));
276 if let Some(groups) = groups {
277 for group in groups {
278 group_uids.insert(group.cedar_entity_uid()?);
279 }
280 }
281 group_uids
282 };
283
284 let entities = {
287 let measure_enabled = timers.measure_enabled;
288 let _timer = PhaseTimer::new_if(&mut timers.entities, measure_enabled);
289 #[cfg(feature = "observability")]
290 let _entity_span = info_span!("construct_entities").entered();
291
292 let resource_attrs = resource.cedar_attr()?;
294 let resource_entity =
295 cedar_policy::Entity::new(resource_uid, resource_attrs, Default::default())?;
296
297 let mut all_entities = Vec::with_capacity(group_uids.len() + 2);
300 all_entities.extend(group_uids.iter().cloned().map(Entity::with_uid));
301
302 let principal_entity = Entity::new(principal_uid, HashMap::new(), group_uids)?;
305 all_entities.push(principal_entity);
306 all_entities.push(resource_entity);
307
308 Entities::empty().add_entities(all_entities, schema)?
310 };
311
312 if timers.debug_enabled {
313 debug!(
314 event = "Request",
315 phase = "Entities",
316 time = timers.entities.as_micros(),
317 entity_count = entities.iter().count()
318 );
319 }
320
321 Ok(entities)
322}
323
324#[derive(Clone)]
333pub struct PolicyEngine {
334 inner: Arc<ArcSwap<PolicySnapshot>>,
336 label_registry: Option<Arc<LabelRegistry>>,
338}
339
340impl From<PolicyEngine> for PolicyVersion {
341 fn from(engine: PolicyEngine) -> Self {
342 engine.current_version()
343 }
344}
345
346impl From<&PolicyEngine> for PolicyVersion {
347 fn from(engine: &PolicyEngine) -> Self {
348 engine.current_version()
349 }
350}
351
352impl PolicyEngine {
353 pub fn new_from_str(policy_text: &str) -> Result<Self, PolicyError> {
354 let snapshot: Snapshot = Arc::new(PolicySnapshot::from_policy_text(policy_text)?);
355 Ok(PolicyEngine {
356 inner: Arc::new(ArcSwap::from(snapshot)),
357 label_registry: None,
358 })
359 }
360
361 pub fn new_from_str_with_schema(
363 policy_text: &str,
364 schema: Schema,
365 ) -> Result<Self, PolicyError> {
366 let snapshot: Snapshot = Arc::new(PolicySnapshot::from_policy_text_with_schema(
367 policy_text,
368 Some(Arc::new(schema)),
369 )?);
370 Ok(PolicyEngine {
371 inner: Arc::new(ArcSwap::from(snapshot)),
372 label_registry: None,
373 })
374 }
375
376 pub fn new_from_str_with_cedarschema(
378 policy_text: &str,
379 schema_text: &str,
380 ) -> Result<Self, PolicyError> {
381 let schema: Schema = schema_text
382 .parse()
383 .map_err(|e| PolicyError::ParseError(format!("failed to parse Cedar schema: {e}")))?;
384 Self::new_from_str_with_schema(policy_text, schema)
385 }
386
387 pub fn with_label_registry(mut self, registry: LabelRegistry) -> Self {
391 self.label_registry = Some(Arc::new(registry));
392 self
393 }
394
395 pub fn set_label_registry(&mut self, registry: LabelRegistry) {
399 self.label_registry = Some(Arc::new(registry));
400 }
401
402 pub fn label_registry(&self) -> Option<&LabelRegistry> {
404 self.label_registry.as_deref()
405 }
406
407 pub fn reload_from_str(&self, policy_text: &str) -> Result<(), PolicyError> {
408 let current_snapshot = self.current_snapshot();
409 let had_schema = current_snapshot.schema.is_some();
410 let schema = current_snapshot.schema.clone();
411 let new_snapshot: Snapshot = Arc::new(PolicySnapshot::from_policy_text_with_schema(
412 policy_text,
413 schema,
414 )?);
415 self.inner.store(new_snapshot);
416 debug!(
417 event = "PolicyReload",
418 schema_enabled = had_schema,
419 schema_reloaded = false
420 );
421 #[cfg(feature = "observability")]
423 record_reload();
424 Ok(())
425 }
426
427 pub fn reload_from_str_with_schema(
429 &self,
430 policy_text: &str,
431 schema: Schema,
432 ) -> Result<(), PolicyError> {
433 let had_schema = self.current_snapshot().schema.is_some();
434 let new_snapshot: Snapshot = Arc::new(PolicySnapshot::from_policy_text_with_schema(
435 policy_text,
436 Some(Arc::new(schema)),
437 )?);
438 self.inner.store(new_snapshot);
439 debug!(
440 event = "PolicyReload",
441 schema_enabled = true,
442 schema_reloaded = true,
443 schema_previously_enabled = had_schema
444 );
445 #[cfg(feature = "observability")]
447 record_reload();
448 Ok(())
449 }
450
451 pub fn reload_from_str_with_cedarschema(
453 &self,
454 policy_text: &str,
455 schema_text: &str,
456 ) -> Result<(), PolicyError> {
457 let schema: Schema = schema_text
458 .parse()
459 .map_err(|e| PolicyError::ParseError(format!("failed to parse Cedar schema: {e}")))?;
460 self.reload_from_str_with_schema(policy_text, schema)
461 }
462
463 fn current_snapshot(&self) -> Snapshot {
465 self.inner.load_full()
466 }
467
468 pub fn current_version(&self) -> PolicyVersion {
473 self.current_snapshot().version()
474 }
475
476 fn prepare(
481 &self,
482 request: &Request,
483 request_context: Option<&RequestContext>,
484 ) -> Result<PreparedRequest, PolicyError> {
485 let snapshot = self.current_snapshot();
486 let schema = snapshot.schema();
487 #[cfg(feature = "observability")]
488 let sink = get_sink();
489 #[cfg(feature = "observability")]
490 let metrics_enabled = metrics_enabled(&sink);
491 #[cfg(not(feature = "observability"))]
492 let metrics_enabled = false;
493 let debug_enabled = tracing::enabled!(tracing::Level::DEBUG);
494 let mut timers = EvalTimers::start(debug_enabled || metrics_enabled, debug_enabled);
495
496 let groups = request_groups(request);
497
498 if timers.debug_enabled {
499 debug!(
500 event = "Request",
501 phase = "Evaluation",
502 group_count = groups.map_or(0, Groups::len)
503 );
504 }
505
506 let principal_uid = request.principal.cedar_entity_uid()?;
508 let action_uid = request.action.cedar_entity_uid()?;
509
510 let labelled_resource = if let Some(registry) = &self.label_registry {
511 let labelled_resource = apply_labels(registry, &request.resource, &mut timers);
512 if timers.debug_enabled {
513 let resource_for_metrics = labelled_resource.as_ref().unwrap_or(&request.resource);
514 debug!(
515 event = "Request",
516 phase = "LabelsApplied",
517 time = timers.labels.as_micros(),
518 attribute_count = resource_for_metrics.attributes().len()
519 );
520 }
521 labelled_resource
522 } else {
523 if timers.debug_enabled {
524 debug!(
525 event = "Request",
526 phase = "LabelsApplied",
527 time = timers.labels.as_micros()
528 );
529 }
530 None
531 };
532 let resource_for_entities = labelled_resource.as_ref().unwrap_or(&request.resource);
533 let resource_uid = resource_for_entities.cedar_entity_uid()?;
534 let context = build_effective_context(request_context)?;
535
536 if timers.debug_enabled {
537 debug!(
538 event = "Request",
539 phase = "Parsed",
540 group_count = groups.map_or(0, Groups::len),
541 attribute_count = resource_for_entities.attributes().len(),
542 request_context_attribute_count = request_context.map_or(0, RequestContext::len)
543 );
544 }
545
546 let principal_uid_for_entities = principal_uid.clone();
550 let resource_uid_for_entities = resource_uid.clone();
551
552 let cedar_req = build_cedar_req(
554 principal_uid,
555 action_uid,
556 resource_uid,
557 context,
558 schema,
559 &mut timers,
560 )?;
561
562 let entities = build_entities(
564 principal_uid_for_entities,
565 resource_uid_for_entities,
566 resource_for_entities,
567 groups,
568 schema,
569 &mut timers,
570 )?;
571
572 if timers.debug_enabled {
573 debug!(
574 event = "Request",
575 phase = "GroupsResolved",
576 time = timers.groups.as_micros(),
577 );
578 }
579
580 Ok(PreparedRequest {
581 cedar_req,
582 entities,
583 snapshot,
584 timers,
585 #[cfg(feature = "observability")]
586 sink,
587 #[cfg(feature = "observability")]
588 metrics_enabled,
589 })
590 }
591
592 #[cfg_attr(
645 feature = "observability",
646 tracing::instrument(name = "policy_evaluation", skip_all)
647 )]
648 pub fn evaluate(&self, request: &Request) -> Result<Decision, PolicyError> {
649 Ok(self.evaluate_internal(request, None, false)?.decision)
650 }
651
652 pub fn evaluate_with_context(
654 &self,
655 request: &Request,
656 request_context: &RequestContext,
657 ) -> Result<Decision, PolicyError> {
658 Ok(self
659 .evaluate_internal(request, Some(request_context), false)?
660 .decision)
661 }
662
663 pub fn evaluate_with_diagnostics(
665 &self,
666 request: &Request,
667 ) -> Result<DecisionDiagnostics, PolicyError> {
668 self.evaluate_internal(request, None, true)
669 }
670
671 pub fn evaluate_with_context_and_diagnostics(
673 &self,
674 request: &Request,
675 request_context: &RequestContext,
676 ) -> Result<DecisionDiagnostics, PolicyError> {
677 self.evaluate_internal(request, Some(request_context), true)
678 }
679
680 fn evaluate_internal(
681 &self,
682 request: &Request,
683 request_context: Option<&RequestContext>,
684 include_forbid_diagnostics: bool,
685 ) -> Result<DecisionDiagnostics, PolicyError> {
686 let mut prepared = self.prepare(request, request_context)?;
688
689 let result = {
691 let measure_enabled = prepared.timers.measure_enabled;
692 let _timer = PhaseTimer::new_if(&mut prepared.timers.authz, measure_enabled);
693 #[cfg(feature = "observability")]
694 let _authz_span = info_span!("authorize").entered();
695 get_authorizer().is_authorized(
696 &prepared.cedar_req,
697 &prepared.snapshot.set,
698 &prepared.entities,
699 )
700 };
701
702 if prepared.timers.debug_enabled {
703 debug!(
704 event = "Request",
705 phase = "Authorized",
706 time = prepared.timers.authz.as_micros(),
707 decision = ?result.decision(),
708 );
709 }
710
711 let version = prepared.snapshot.version();
712 if prepared.timers.debug_enabled {
713 debug!(
714 event = "Request",
715 phase = "Result",
716 time = prepared.timers.total_elapsed().as_micros(),
717 result = ?result.decision(),
718 policy_hash = %version.hash,
719 policy_loaded_at = %version.loaded_at,
720 );
721 }
722
723 let permit_policies = extract_permit_policies(&prepared.snapshot, &result);
727 let collect_forbid_ids = include_forbid_diagnostics;
728 let forbid_policy_ids = if collect_forbid_ids {
729 extract_forbid_policy_ids(&prepared.snapshot, &result)
730 } else {
731 Vec::new()
732 };
733 let decision =
734 Decision::from_decision_with_policy(result.decision(), permit_policies, version)?;
735
736 #[cfg(feature = "observability")]
738 {
739 if prepared.metrics_enabled {
740 let dur = prepared.timers.total_elapsed();
741 let allowed = result.decision() == cedar_policy::Decision::Allow;
742 let phases = EvaluationPhases {
743 apply_labels_ms: prepared.timers.labels.as_secs_f64() * 1000.0,
744 construct_entities_ms: prepared.timers.entities.as_secs_f64() * 1000.0,
745 resolve_groups_ms: prepared.timers.groups.as_secs_f64() * 1000.0,
746 authorize_ms: prepared.timers.authz.as_secs_f64() * 1000.0,
747 total_ms: dur.as_secs_f64() * 1000.0,
748 };
749 let matched_policies = match &decision {
750 Decision::Allow { policies, .. } => MatchedPolicySource::Allow(policies),
751 Decision::Deny { .. } => MatchedPolicySource::Deny {
752 diagnostics: result.diagnostics(),
753 policy_ids: &prepared.snapshot.forbid_policy_ids,
754 },
755 };
756 let observation = EvaluationObservation::new(
757 dur,
758 allowed,
759 &request.action,
760 phases,
761 matched_policies,
762 );
763
764 record_evaluation_observation(&prepared.sink, &observation);
765 }
766 }
767
768 Ok(DecisionDiagnostics {
769 decision,
770 matched_forbid_policy_ids: forbid_policy_ids,
771 })
772 }
773
774 pub fn list_policies_for_user(
823 &self,
824 user: &str,
825 groups: &[&str],
826 namespace: &[&str],
827 ) -> Result<UserPolicies, PolicyError> {
828 self.list_policies_for_user_with_resource_and_effect(
829 user,
830 groups,
831 namespace,
832 None,
833 PolicyEffectFilter::Permit,
834 )
835 }
836
837 pub fn list_policies(&self, request: &Request) -> Result<UserPolicies, PolicyError> {
848 self.list_policies_with_effect(request, PolicyEffectFilter::Permit)
849 }
850
851 pub fn list_policies_with_effect(
855 &self,
856 request: &Request,
857 effect_filter: PolicyEffectFilter,
858 ) -> Result<UserPolicies, PolicyError> {
859 let principal = PrincipalQuery::from_principal(&request.principal)?;
860 let action = ActionQuery::from_action(&request.action)?;
861 self.list_policies_dispatch(
862 &request.principal.to_string(),
863 &principal,
864 Some(&action),
865 Some(&request.resource),
866 effect_filter,
867 )
868 }
869
870 pub fn list_policies_for_user_with_resource(
882 &self,
883 user: &str,
884 groups: &[&str],
885 namespace: &[&str],
886 resource: Option<&Resource>,
887 ) -> Result<UserPolicies, PolicyError> {
888 self.list_policies_for_user_with_resource_and_effect(
889 user,
890 groups,
891 namespace,
892 resource,
893 PolicyEffectFilter::Permit,
894 )
895 }
896
897 pub fn list_policies_for_user_with_resource_and_effect(
899 &self,
900 user: &str,
901 groups: &[&str],
902 namespace: &[&str],
903 resource: Option<&Resource>,
904 effect_filter: PolicyEffectFilter,
905 ) -> Result<UserPolicies, PolicyError> {
906 let principal = PrincipalQuery::for_user(user, groups, namespace)?;
907 self.list_policies_dispatch(user, &principal, None, resource, effect_filter)
908 }
909
910 pub fn list_policies_for_group(
919 &self,
920 group: &str,
921 namespace: &[&str],
922 ) -> Result<UserPolicies, PolicyError> {
923 self.list_policies_for_group_with_resource_and_effect(
924 group,
925 namespace,
926 None,
927 PolicyEffectFilter::Permit,
928 )
929 }
930
931 pub fn list_policies_for_group_with_resource(
936 &self,
937 group: &str,
938 namespace: &[&str],
939 resource: Option<&Resource>,
940 ) -> Result<UserPolicies, PolicyError> {
941 self.list_policies_for_group_with_resource_and_effect(
942 group,
943 namespace,
944 resource,
945 PolicyEffectFilter::Permit,
946 )
947 }
948
949 pub fn list_policies_for_group_with_resource_and_effect(
951 &self,
952 group: &str,
953 namespace: &[&str],
954 resource: Option<&Resource>,
955 effect_filter: PolicyEffectFilter,
956 ) -> Result<UserPolicies, PolicyError> {
957 let principal = PrincipalQuery::for_group(group, namespace)?;
958 self.list_policies_dispatch(group, &principal, None, resource, effect_filter)
959 }
960
961 fn list_policies_dispatch(
962 &self,
963 principal_id: &str,
964 principal: &PrincipalQuery,
965 action: Option<&ActionQuery>,
966 resource: Option<&Resource>,
967 effect_filter: PolicyEffectFilter,
968 ) -> Result<UserPolicies, PolicyError> {
969 let snapshot = self.current_snapshot();
970 let policies = snapshot.set.policies();
971 let resource_query = match resource {
972 Some(resource) => Some(ResourceQuery::from_resource(resource)?),
973 None => None,
974 };
975 let mut matching_policies: Vec<(Policy, Vec<PolicyMatchReason>)> = Vec::new();
976
977 for policy in policies {
978 if !matches_effect(policy.effect(), effect_filter) {
979 continue;
980 }
981
982 let Some(principal_reason) =
983 principal_match_reason(policy.principal_constraint(), principal)
984 else {
985 continue;
986 };
987
988 let Some(action_reason) = action_match_reason(policy.action_constraint(), action)
989 else {
990 continue;
991 };
992
993 let Some(resource_reason) =
994 resource_match_reason(policy.resource_constraint(), resource_query.as_ref())
995 else {
996 continue;
997 };
998
999 let mut reasons = vec![principal_reason];
1000 if let Some(action_reason) = action_reason {
1001 reasons.push(action_reason);
1002 }
1003 if let Some(resource_reason) = resource_reason {
1004 reasons.push(resource_reason);
1005 }
1006
1007 matching_policies.push((policy.clone(), reasons));
1008 }
1009
1010 Ok(UserPolicies::new_with_matches(
1011 principal_id,
1012 matching_policies,
1013 ))
1014 }
1015
1016 pub fn policies(&self) -> Result<Vec<Policy>, PolicyError> {
1017 let snapshot = self.current_snapshot();
1018 Ok(snapshot.policy_set().policies().cloned().collect())
1019 }
1020}
1021
1022#[cfg(test)]
1023mod tests;