1use crate::ast::*;
18use itertools::Itertools;
19use serde::{Deserialize, Serialize};
20use smol_str::SmolStr;
21use std::collections::BTreeMap;
22use std::{collections::HashMap, sync::Arc};
23use thiserror::Error;
24
25#[derive(Clone, Hash, Eq, PartialEq, Debug, Serialize, Deserialize)]
28#[serde(from = "TemplateBody")]
29#[serde(into = "TemplateBody")]
30pub struct Template {
31 body: TemplateBody,
32 slots: Vec<SlotId>,
35}
36
37impl From<Template> for TemplateBody {
38 fn from(val: Template) -> Self {
39 val.body
40 }
41}
42
43impl Template {
44 #[cfg(test)]
46 pub fn check_invariant(&self) {
47 let cond = self.body.condition();
48 let slots = cond.slots().collect::<Vec<_>>();
49 for slot in slots.iter() {
50 assert!(self.slots.contains(slot));
51 }
52 for slot in self.slots() {
53 assert!(slots.contains(&slot));
54 }
55 }
56 pub fn new(
64 id: PolicyID,
65 annotations: BTreeMap<Id, SmolStr>,
66 effect: Effect,
67 principal_constraint: PrincipalConstraint,
68 action_constraint: ActionConstraint,
69 resource_constraint: ResourceConstraint,
70 non_head_constraint: Expr,
71 ) -> Self {
72 let body = TemplateBody::new(
73 id,
74 annotations,
75 effect,
76 principal_constraint,
77 action_constraint,
78 resource_constraint,
79 non_head_constraint,
80 );
81 Template::from(body)
84 }
85
86 pub fn principal_constraint(&self) -> &PrincipalConstraint {
88 self.body.principal_constraint()
89 }
90
91 pub fn action_constraint(&self) -> &ActionConstraint {
93 self.body.action_constraint()
94 }
95
96 pub fn resource_constraint(&self) -> &ResourceConstraint {
98 self.body.resource_constraint()
99 }
100
101 pub fn non_head_constraints(&self) -> &Expr {
103 self.body.non_head_constraints()
104 }
105
106 pub fn id(&self) -> &PolicyID {
108 self.body.id()
109 }
110
111 pub fn new_id(&self, id: PolicyID) -> Self {
113 Template {
114 body: self.body.new_id(id),
115 slots: self.slots.clone(),
116 }
117 }
118
119 pub fn effect(&self) -> Effect {
121 self.body.effect()
122 }
123
124 pub fn annotation(&self, key: &Id) -> Option<&SmolStr> {
126 self.body.annotation(key)
127 }
128
129 pub fn annotations(&self) -> impl Iterator<Item = (&Id, &SmolStr)> {
131 self.body.annotations()
132 }
133
134 pub fn condition(&self) -> Expr {
140 self.body.condition()
141 }
142
143 pub fn slots(&self) -> impl Iterator<Item = &SlotId> {
145 self.slots.iter()
146 }
147
148 pub fn is_static(&self) -> bool {
153 self.slots.is_empty()
154 }
155
156 pub fn check_binding(
160 template: &Template,
161 values: &HashMap<SlotId, EntityUID>,
162 ) -> Result<(), LinkingError> {
163 let unbound = template
165 .slots
166 .iter()
167 .filter(|slot| !values.contains_key(slot))
168 .collect::<Vec<_>>();
169
170 let extra = values
171 .iter()
172 .filter_map(|(slot, _)| {
173 if !template.slots.contains(slot) {
174 Some(slot)
175 } else {
176 None
177 }
178 })
179 .collect::<Vec<_>>();
180
181 if unbound.is_empty() && extra.is_empty() {
182 Ok(())
183 } else {
184 Err(LinkingError::from_unbound_and_extras(
185 unbound.into_iter().map(SlotId::clone),
186 extra.into_iter().map(SlotId::clone),
187 ))
188 }
189 }
190
191 pub fn link(
195 template: Arc<Template>,
196 new_id: PolicyID,
197 values: HashMap<SlotId, EntityUID>,
198 ) -> Result<Policy, LinkingError> {
199 Template::check_binding(&template, &values)
201 .map(|_| Policy::new(template, Some(new_id), values))
202 }
203
204 pub fn link_static_policy(p: StaticPolicy) -> (Arc<Template>, Policy) {
207 let body: TemplateBody = p.into();
208 let t = Arc::new(Self {
212 body,
213 slots: vec![],
214 });
215 #[cfg(test)]
216 {
217 t.check_invariant();
218 }
219 let p = Policy::new(Arc::clone(&t), None, HashMap::new());
225 (t, p)
226 }
227}
228
229impl From<TemplateBody> for Template {
230 fn from(body: TemplateBody) -> Self {
231 let slots = body.condition().slots().copied().collect::<Vec<_>>();
234 Self { body, slots }
235 }
236}
237
238impl std::fmt::Display for Template {
239 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240 write!(f, "{}", self.body)
241 }
242}
243
244#[derive(Debug, Clone, PartialEq, Eq, Error)]
246pub enum LinkingError {
247 #[error("{}", describe_arity_error(.unbound_values, .extra_values))]
250 ArityError {
251 unbound_values: Vec<SlotId>,
253 extra_values: Vec<SlotId>,
255 },
256
257 #[error("failed to find a template with id `{id}`")]
259 NoSuchTemplate {
260 id: PolicyID,
262 },
263
264 #[error("template-linked policy id `{id}` conflicts with an existing policy id")]
266 PolicyIdConflict {
267 id: PolicyID,
269 },
270}
271
272impl LinkingError {
273 fn from_unbound_and_extras<T>(unbound: T, extra: T) -> Self
274 where
275 T: Iterator<Item = SlotId>,
276 {
277 Self::ArityError {
278 unbound_values: unbound.collect(),
279 extra_values: extra.collect(),
280 }
281 }
282}
283
284fn describe_arity_error(unbound_values: &[SlotId], extra_values: &[SlotId]) -> String {
285 match (unbound_values.len(), extra_values.len()) {
286 #[allow(clippy::unreachable)]
288 (0,0) => unreachable!(),
289 (_unbound, 0) => format!("the following slots were not provided as arguments: {}", unbound_values.iter().join(",")),
290 (0, _extra) => format!("the following slots were provided as arguments, but did not exist in the template: {}", extra_values.iter().join(",")),
291 (_unbound, _extra) => format!("the following slots were not provided as arguments: {}. The following slots were provided as arguments, but did not exist in the template: {}", unbound_values.iter().join(","), extra_values.iter().join(","))
292 }
293}
294
295#[derive(Debug, Clone, Eq, PartialEq)]
303pub struct Policy {
304 template: Arc<Template>,
306 link: Option<PolicyID>,
309 values: HashMap<SlotId, EntityUID>,
315}
316
317impl Policy {
318 fn new(template: Arc<Template>, link_id: Option<PolicyID>, values: SlotEnv) -> Self {
322 #[cfg(test)]
323 {
324 Template::check_binding(&template, &values).expect("(values total map) does not hold!");
325 }
326 Self {
332 template,
333 link: link_id,
334 values,
335 }
336 }
337
338 pub fn from_when_clause(effect: Effect, when: Expr, id: PolicyID) -> Self {
340 let t = Template::new(
341 id,
342 BTreeMap::new(),
343 effect,
344 PrincipalConstraint::any(),
345 ActionConstraint::any(),
346 ResourceConstraint::any(),
347 when,
348 );
349 Self::new(Arc::new(t), None, SlotEnv::new())
350 }
351
352 pub fn template(&self) -> &Template {
354 &self.template
355 }
356
357 pub(crate) fn template_arc(&self) -> Arc<Template> {
359 Arc::clone(&self.template)
360 }
361
362 pub fn effect(&self) -> Effect {
364 self.template.effect()
365 }
366
367 pub fn annotation(&self, key: &Id) -> Option<&SmolStr> {
369 self.template.annotation(key)
370 }
371
372 pub fn annotations(&self) -> impl Iterator<Item = (&Id, &SmolStr)> {
374 self.template.annotations()
375 }
376
377 pub fn principal_constraint(&self) -> PrincipalConstraint {
383 let constraint = self.template.principal_constraint().clone();
384 match self.values.get(&SlotId::principal()) {
385 None => constraint,
386 Some(principal) => constraint.with_filled_slot(Arc::new(principal.clone())),
387 }
388 }
389
390 pub fn action_constraint(&self) -> &ActionConstraint {
392 self.template.action_constraint()
393 }
394
395 pub fn resource_constraint(&self) -> ResourceConstraint {
401 let constraint = self.template.resource_constraint().clone();
402 match self.values.get(&SlotId::resource()) {
403 None => constraint,
404 Some(resource) => constraint.with_filled_slot(Arc::new(resource.clone())),
405 }
406 }
407
408 pub fn non_head_constraints(&self) -> &Expr {
410 self.template.non_head_constraints()
411 }
412
413 pub fn condition(&self) -> Expr {
415 self.template.condition()
416 }
417
418 pub fn env(&self) -> &SlotEnv {
421 &self.values
422 }
423
424 pub fn id(&self) -> &PolicyID {
426 self.link.as_ref().unwrap_or_else(|| self.template.id())
427 }
428
429 pub fn new_id(&self, id: PolicyID) -> Self {
431 match self.link {
432 None => Policy {
433 template: Arc::new(self.template.new_id(id)),
434 link: None,
435 values: self.values.clone(),
436 },
437 Some(_) => Policy {
438 template: self.template.clone(),
439 link: Some(id),
440 values: self.values.clone(),
441 },
442 }
443 }
444
445 pub fn is_static(&self) -> bool {
447 self.link.is_none()
448 }
449}
450
451impl std::fmt::Display for Policy {
452 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
453 if self.is_static() {
454 write!(f, "{}", self.template())
455 } else {
456 write!(
457 f,
458 "Template Instance of {}, slots: [{}]",
459 self.template().id(),
460 display_slot_env(self.env())
461 )
462 }
463 }
464}
465
466pub type SlotEnv = HashMap<SlotId, EntityUID>;
468
469#[derive(Debug, Clone, Eq, Serialize, Deserialize)]
472pub struct LiteralPolicy {
473 template_id: PolicyID,
475 link_id: Option<PolicyID>,
479 values: SlotEnv,
481}
482
483#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
485pub struct BorrowedLiteralPolicy<'a> {
486 template_id: &'a PolicyID,
488 link_id: Option<&'a PolicyID>,
492 values: &'a SlotEnv,
494}
495
496impl<'a> From<&'a Policy> for BorrowedLiteralPolicy<'a> {
497 fn from(p: &'a Policy) -> Self {
498 Self {
499 template_id: p.template.id(),
500 link_id: p.link.as_ref(),
501 values: &p.values,
502 }
503 }
504}
505
506impl std::hash::Hash for LiteralPolicy {
509 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
510 self.template_id.hash(state);
511 let mut buf = self.values.iter().collect::<Vec<_>>();
513 buf.sort();
514 for (id, euid) in buf {
515 id.hash(state);
516 euid.hash(state);
517 }
518 }
519}
520
521impl std::cmp::PartialEq for LiteralPolicy {
522 fn eq(&self, other: &Self) -> bool {
523 self.template_id() == other.template_id()
524 && self.link_id == other.link_id
525 && self.values == other.values
526 }
527}
528
529#[cfg(test)]
531mod hashing_tests {
532 use std::{
533 collections::hash_map::DefaultHasher,
534 hash::{Hash, Hasher},
535 };
536
537 use super::*;
538
539 fn compute_hash(ir: LiteralPolicy) -> u64 {
540 let mut s = DefaultHasher::new();
541 ir.hash(&mut s);
542 s.finish()
543 }
544
545 fn build_template_linked_policy() -> LiteralPolicy {
546 let mut map = HashMap::new();
547 map.insert(SlotId::principal(), EntityUID::with_eid("eid"));
548 LiteralPolicy {
549 template_id: PolicyID::from_string("template"),
550 link_id: Some(PolicyID::from_string("id")),
551 values: map,
552 }
553 }
554
555 #[test]
556 fn hash_property_instances() {
557 let a = build_template_linked_policy();
558 let b = build_template_linked_policy();
559 assert_eq!(a, b);
560 assert_eq!(compute_hash(a), compute_hash(b));
561 }
562}
563#[derive(Debug, Error)]
571pub enum ReificationError {
572 #[error("the id linked to does not exist")]
574 NoSuchTemplate(PolicyID),
575 #[error(transparent)]
577 Instantiation(#[from] LinkingError),
578}
579
580impl LiteralPolicy {
581 pub fn reify(
586 self,
587 templates: &HashMap<PolicyID, Arc<Template>>,
588 ) -> Result<Policy, ReificationError> {
589 let template = templates
590 .get(&self.template_id)
591 .ok_or_else(|| ReificationError::NoSuchTemplate(self.template_id().clone()))?;
592 Template::check_binding(template, &self.values).map_err(ReificationError::Instantiation)?;
594 Ok(Policy::new(template.clone(), self.link_id, self.values))
595 }
596
597 pub fn get(&self, id: &SlotId) -> Option<&EntityUID> {
599 self.values.get(id)
600 }
601
602 pub fn id(&self) -> &PolicyID {
605 self.link_id.as_ref().unwrap_or(&self.template_id)
606 }
607
608 pub fn template_id(&self) -> &PolicyID {
610 &self.template_id
611 }
612
613 pub fn is_static(&self) -> bool {
615 self.link_id.is_none()
616 }
617}
618
619fn display_slot_env(env: &SlotEnv) -> String {
620 env.iter()
621 .map(|(slot, value)| format!("{slot} -> {value}"))
622 .join(",")
623}
624
625impl std::fmt::Display for LiteralPolicy {
626 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
627 if self.is_static() {
628 write!(f, "Static policy w/ ID {}", self.template_id())
629 } else {
630 write!(
631 f,
632 "Template linked policy of {}, slots: [{}]",
633 self.template_id(),
634 display_slot_env(&self.values),
635 )
636 }
637 }
638}
639
640impl From<Policy> for LiteralPolicy {
641 fn from(p: Policy) -> Self {
642 Self {
643 template_id: p.template.id().clone(),
644 link_id: p.link,
645 values: p.values,
646 }
647 }
648}
649
650#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
654pub struct StaticPolicy(TemplateBody);
655
656impl StaticPolicy {
657 pub fn id(&self) -> &PolicyID {
659 self.0.id()
660 }
661
662 pub fn new_id(&mut self, id: PolicyID) -> Self {
664 StaticPolicy(self.0.new_id(id))
665 }
666
667 pub fn effect(&self) -> Effect {
669 self.0.effect()
670 }
671
672 pub fn annotation(&self, key: &Id) -> Option<&SmolStr> {
674 self.0.annotation(key)
675 }
676
677 pub fn annotations(&self) -> impl Iterator<Item = (&Id, &SmolStr)> {
679 self.0.annotations()
680 }
681
682 pub fn principal_constraint(&self) -> &PrincipalConstraint {
684 self.0.principal_constraint()
685 }
686
687 pub fn principal_constraint_expr(&self) -> Expr {
691 self.0.principal_constraint_expr()
692 }
693
694 pub fn action_constraint(&self) -> &ActionConstraint {
696 self.0.action_constraint()
697 }
698
699 pub fn action_constraint_expr(&self) -> Expr {
703 self.0.action_constraint_expr()
704 }
705
706 pub fn resource_constraint(&self) -> &ResourceConstraint {
708 self.0.resource_constraint()
709 }
710
711 pub fn resource_constraint_expr(&self) -> Expr {
715 self.0.resource_constraint_expr()
716 }
717
718 pub fn non_head_constraints(&self) -> &Expr {
723 self.0.non_head_constraints()
724 }
725
726 pub fn condition(&self) -> Expr {
732 self.0.condition()
733 }
734
735 pub fn new(
737 id: PolicyID,
738 annotations: BTreeMap<Id, SmolStr>,
739 effect: Effect,
740 principal_constraint: PrincipalConstraint,
741 action_constraint: ActionConstraint,
742 resource_constraint: ResourceConstraint,
743 non_head_constraints: Expr,
744 ) -> Result<Self, UnexpectedSlotError> {
745 let body = TemplateBody::new(
746 id,
747 annotations,
748 effect,
749 principal_constraint,
750 action_constraint,
751 resource_constraint,
752 non_head_constraints,
753 );
754 let num_slots = body.condition().slots().next().map(SlotId::clone);
755 match num_slots {
757 Some(slot_id) => Err(UnexpectedSlotError::FoundSlot(slot_id))?,
758 None => Ok(Self(body)),
759 }
760 }
761}
762
763impl TryFrom<Template> for StaticPolicy {
764 type Error = UnexpectedSlotError;
765
766 fn try_from(value: Template) -> Result<Self, Self::Error> {
767 let o = value.slots().next().map(SlotId::clone);
769 match o {
770 Some(slot_id) => Err(Self::Error::FoundSlot(slot_id)),
771 None => Ok(Self(value.body)),
772 }
773 }
774}
775
776impl From<StaticPolicy> for Policy {
777 fn from(inline: StaticPolicy) -> Policy {
778 let (_, policy) = Template::link_static_policy(inline);
779 policy
780 }
781}
782
783impl From<StaticPolicy> for Arc<Template> {
784 fn from(p: StaticPolicy) -> Self {
785 let (t, _) = Template::link_static_policy(p);
786 t
787 }
788}
789
790#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
795pub struct TemplateBody {
796 id: PolicyID,
798 annotations: BTreeMap<Id, SmolStr>,
800 effect: Effect,
802 principal_constraint: PrincipalConstraint,
806 action_constraint: ActionConstraint,
810 resource_constraint: ResourceConstraint,
814 non_head_constraints: Expr,
819}
820
821impl TemplateBody {
822 pub fn id(&self) -> &PolicyID {
824 &self.id
825 }
826
827 pub fn new_id(&self, id: PolicyID) -> Self {
829 let mut new = self.clone();
830 new.id = id;
831 new
832 }
833
834 pub fn effect(&self) -> Effect {
836 self.effect
837 }
838
839 pub fn annotation(&self, key: &Id) -> Option<&SmolStr> {
841 self.annotations.get(key)
842 }
843
844 pub fn annotations(&self) -> impl Iterator<Item = (&Id, &SmolStr)> {
846 self.annotations.iter()
847 }
848
849 pub fn principal_constraint(&self) -> &PrincipalConstraint {
851 &self.principal_constraint
852 }
853
854 pub fn principal_constraint_expr(&self) -> Expr {
858 self.principal_constraint.as_expr()
859 }
860
861 pub fn action_constraint(&self) -> &ActionConstraint {
863 &self.action_constraint
864 }
865
866 pub fn action_constraint_expr(&self) -> Expr {
870 self.action_constraint.as_expr()
871 }
872
873 pub fn resource_constraint(&self) -> &ResourceConstraint {
875 &self.resource_constraint
876 }
877
878 pub fn resource_constraint_expr(&self) -> Expr {
882 self.resource_constraint.as_expr()
883 }
884
885 pub fn non_head_constraints(&self) -> &Expr {
890 &self.non_head_constraints
891 }
892
893 pub fn condition(&self) -> Expr {
899 Expr::and(
900 Expr::and(
901 Expr::and(
902 self.principal_constraint_expr(),
903 self.action_constraint_expr(),
904 ),
905 self.resource_constraint_expr(),
906 ),
907 self.non_head_constraints.clone(),
908 )
909 }
910
911 pub fn new(
913 id: PolicyID,
914 annotations: BTreeMap<Id, SmolStr>,
915 effect: Effect,
916 principal_constraint: PrincipalConstraint,
917 action_constraint: ActionConstraint,
918 resource_constraint: ResourceConstraint,
919 non_head_constraints: Expr,
920 ) -> Self {
921 Self {
922 id,
923 annotations,
924 effect,
925 principal_constraint,
926 action_constraint,
927 resource_constraint,
928 non_head_constraints,
929 }
930 }
931}
932
933impl From<StaticPolicy> for TemplateBody {
934 fn from(p: StaticPolicy) -> Self {
935 p.0
936 }
937}
938
939impl std::fmt::Display for TemplateBody {
940 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
941 for (k, v) in &self.annotations {
942 writeln!(f, "@{}(\"{}\")", k, v.escape_debug())?
943 }
944 write!(
945 f,
946 "{}(\n {},\n {},\n {}\n) when {{\n {}\n}};",
947 self.effect(),
948 self.principal_constraint(),
949 self.action_constraint(),
950 self.resource_constraint(),
951 self.non_head_constraints()
952 )
953 }
954}
955
956#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
958pub struct PrincipalConstraint {
959 pub(crate) constraint: PrincipalOrResourceConstraint,
960}
961
962impl PrincipalConstraint {
963 pub fn new(constraint: PrincipalOrResourceConstraint) -> Self {
965 PrincipalConstraint { constraint }
966 }
967
968 pub fn as_inner(&self) -> &PrincipalOrResourceConstraint {
970 &self.constraint
971 }
972
973 pub fn into_inner(self) -> PrincipalOrResourceConstraint {
975 self.constraint
976 }
977
978 pub fn as_expr(&self) -> Expr {
980 self.constraint.as_expr(PrincipalOrResource::Principal)
981 }
982
983 pub fn any() -> Self {
985 PrincipalConstraint {
986 constraint: PrincipalOrResourceConstraint::any(),
987 }
988 }
989
990 pub fn is_eq(euid: EntityUID) -> Self {
992 PrincipalConstraint {
993 constraint: PrincipalOrResourceConstraint::is_eq(euid),
994 }
995 }
996
997 pub fn is_eq_slot() -> Self {
999 Self {
1000 constraint: PrincipalOrResourceConstraint::is_eq_slot(),
1001 }
1002 }
1003
1004 pub fn is_in(euid: EntityUID) -> Self {
1006 PrincipalConstraint {
1007 constraint: PrincipalOrResourceConstraint::is_in(euid),
1008 }
1009 }
1010
1011 pub fn is_in_slot() -> Self {
1013 Self {
1014 constraint: PrincipalOrResourceConstraint::is_in_slot(),
1015 }
1016 }
1017
1018 pub fn is_entity_type_in_slot(entity_type: Name) -> Self {
1020 Self {
1021 constraint: PrincipalOrResourceConstraint::is_entity_type_in_slot(entity_type),
1022 }
1023 }
1024
1025 pub fn is_entity_type_in(entity_type: Name, in_entity: EntityUID) -> Self {
1027 Self {
1028 constraint: PrincipalOrResourceConstraint::is_entity_type_in(entity_type, in_entity),
1029 }
1030 }
1031
1032 pub fn is_entity_type(entity_type: Name) -> Self {
1034 Self {
1035 constraint: PrincipalOrResourceConstraint::is_entity_type(entity_type),
1036 }
1037 }
1038
1039 pub fn with_filled_slot(self, euid: Arc<EntityUID>) -> Self {
1041 match self.constraint {
1042 PrincipalOrResourceConstraint::Eq(EntityReference::Slot) => Self {
1043 constraint: PrincipalOrResourceConstraint::Eq(EntityReference::EUID(euid)),
1044 },
1045 PrincipalOrResourceConstraint::In(EntityReference::Slot) => Self {
1046 constraint: PrincipalOrResourceConstraint::In(EntityReference::EUID(euid)),
1047 },
1048 _ => self,
1049 }
1050 }
1051}
1052
1053impl std::fmt::Display for PrincipalConstraint {
1054 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1055 write!(
1056 f,
1057 "{}",
1058 self.constraint.display(PrincipalOrResource::Principal)
1059 )
1060 }
1061}
1062
1063#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
1065pub struct ResourceConstraint {
1066 pub(crate) constraint: PrincipalOrResourceConstraint,
1067}
1068
1069impl ResourceConstraint {
1070 pub fn new(constraint: PrincipalOrResourceConstraint) -> Self {
1072 ResourceConstraint { constraint }
1073 }
1074
1075 pub fn as_inner(&self) -> &PrincipalOrResourceConstraint {
1077 &self.constraint
1078 }
1079
1080 pub fn into_inner(self) -> PrincipalOrResourceConstraint {
1082 self.constraint
1083 }
1084
1085 pub fn as_expr(&self) -> Expr {
1087 self.constraint.as_expr(PrincipalOrResource::Resource)
1088 }
1089
1090 pub fn any() -> Self {
1092 ResourceConstraint {
1093 constraint: PrincipalOrResourceConstraint::any(),
1094 }
1095 }
1096
1097 pub fn is_eq(euid: EntityUID) -> Self {
1099 ResourceConstraint {
1100 constraint: PrincipalOrResourceConstraint::is_eq(euid),
1101 }
1102 }
1103
1104 pub fn is_eq_slot() -> Self {
1106 Self {
1107 constraint: PrincipalOrResourceConstraint::is_eq_slot(),
1108 }
1109 }
1110
1111 pub fn is_in_slot() -> Self {
1113 Self {
1114 constraint: PrincipalOrResourceConstraint::is_in_slot(),
1115 }
1116 }
1117
1118 pub fn is_in(euid: EntityUID) -> Self {
1120 ResourceConstraint {
1121 constraint: PrincipalOrResourceConstraint::is_in(euid),
1122 }
1123 }
1124
1125 pub fn is_entity_type_in_slot(entity_type: Name) -> Self {
1127 Self {
1128 constraint: PrincipalOrResourceConstraint::is_entity_type_in_slot(entity_type),
1129 }
1130 }
1131
1132 pub fn is_entity_type_in(entity_type: Name, in_entity: EntityUID) -> Self {
1134 Self {
1135 constraint: PrincipalOrResourceConstraint::is_entity_type_in(entity_type, in_entity),
1136 }
1137 }
1138
1139 pub fn is_entity_type(entity_type: Name) -> Self {
1141 Self {
1142 constraint: PrincipalOrResourceConstraint::is_entity_type(entity_type),
1143 }
1144 }
1145
1146 pub fn with_filled_slot(self, euid: Arc<EntityUID>) -> Self {
1148 match self.constraint {
1149 PrincipalOrResourceConstraint::Eq(EntityReference::Slot) => Self {
1150 constraint: PrincipalOrResourceConstraint::Eq(EntityReference::EUID(euid)),
1151 },
1152 PrincipalOrResourceConstraint::In(EntityReference::Slot) => Self {
1153 constraint: PrincipalOrResourceConstraint::In(EntityReference::EUID(euid)),
1154 },
1155 _ => self,
1156 }
1157 }
1158}
1159
1160impl std::fmt::Display for ResourceConstraint {
1161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1162 write!(
1163 f,
1164 "{}",
1165 self.as_inner().display(PrincipalOrResource::Resource)
1166 )
1167 }
1168}
1169
1170#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
1172pub enum EntityReference {
1173 EUID(Arc<EntityUID>),
1175 Slot,
1177}
1178
1179impl EntityReference {
1180 pub fn euid(euid: EntityUID) -> Self {
1182 Self::EUID(Arc::new(euid))
1183 }
1184
1185 pub fn into_euid(self, slot: SlotId) -> Result<Arc<EntityUID>, UnexpectedSlotError> {
1192 match self {
1193 EntityReference::EUID(euid) => Ok(euid),
1194 EntityReference::Slot => Err(UnexpectedSlotError::FoundSlot(slot)),
1195 }
1196 }
1197
1198 pub fn into_expr(&self, slot: SlotId) -> Expr {
1204 match self {
1205 EntityReference::EUID(euid) => Expr::val(euid.clone()),
1206 EntityReference::Slot => Expr::slot(slot),
1207 }
1208 }
1209}
1210
1211#[derive(Debug, Clone, PartialEq, Error)]
1213pub enum UnexpectedSlotError {
1214 #[error("found slot `{0}` where slots are not allowed")]
1216 FoundSlot(SlotId),
1217}
1218
1219impl From<EntityUID> for EntityReference {
1220 fn from(euid: EntityUID) -> Self {
1221 Self::EUID(Arc::new(euid))
1222 }
1223}
1224
1225#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone, Copy)]
1227#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1228pub enum PrincipalOrResource {
1229 Principal,
1231 Resource,
1233}
1234
1235impl std::fmt::Display for PrincipalOrResource {
1236 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1237 let v = Var::from(*self);
1238 write!(f, "{v}")
1239 }
1240}
1241
1242impl TryFrom<Var> for PrincipalOrResource {
1243 type Error = Var;
1244
1245 fn try_from(value: Var) -> Result<Self, Self::Error> {
1246 match value {
1247 Var::Principal => Ok(Self::Principal),
1248 Var::Action => Err(Var::Action),
1249 Var::Resource => Ok(Self::Resource),
1250 Var::Context => Err(Var::Context),
1251 }
1252 }
1253}
1254
1255#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
1258pub enum PrincipalOrResourceConstraint {
1259 Any,
1261 In(EntityReference),
1263 Eq(EntityReference),
1265 Is(Name),
1267 IsIn(Name, EntityReference),
1269}
1270
1271impl PrincipalOrResourceConstraint {
1272 pub fn any() -> Self {
1274 PrincipalOrResourceConstraint::Any
1275 }
1276
1277 pub fn is_eq(euid: EntityUID) -> Self {
1279 PrincipalOrResourceConstraint::Eq(EntityReference::euid(euid))
1280 }
1281
1282 pub fn is_eq_slot() -> Self {
1284 PrincipalOrResourceConstraint::Eq(EntityReference::Slot)
1285 }
1286
1287 pub fn is_in_slot() -> Self {
1289 PrincipalOrResourceConstraint::In(EntityReference::Slot)
1290 }
1291
1292 pub fn is_in(euid: EntityUID) -> Self {
1294 PrincipalOrResourceConstraint::In(EntityReference::euid(euid))
1295 }
1296
1297 pub fn is_entity_type_in_slot(entity_type: Name) -> Self {
1299 PrincipalOrResourceConstraint::IsIn(entity_type, EntityReference::Slot)
1300 }
1301
1302 pub fn is_entity_type_in(entity_type: Name, in_entity: EntityUID) -> Self {
1304 PrincipalOrResourceConstraint::IsIn(entity_type, EntityReference::euid(in_entity))
1305 }
1306
1307 pub fn is_entity_type(entity_type: Name) -> Self {
1309 PrincipalOrResourceConstraint::Is(entity_type)
1310 }
1311
1312 pub fn as_expr(&self, v: PrincipalOrResource) -> Expr {
1316 match self {
1317 PrincipalOrResourceConstraint::Any => Expr::val(true),
1318 PrincipalOrResourceConstraint::Eq(euid) => {
1319 Expr::is_eq(Expr::var(v.into()), euid.into_expr(v.into()))
1320 }
1321 PrincipalOrResourceConstraint::In(euid) => {
1322 Expr::is_in(Expr::var(v.into()), euid.into_expr(v.into()))
1323 }
1324 PrincipalOrResourceConstraint::IsIn(entity_type, euid) => Expr::and(
1325 Expr::is_entity_type(Expr::var(v.into()), entity_type.clone()),
1326 Expr::is_in(Expr::var(v.into()), euid.into_expr(v.into())),
1327 ),
1328 PrincipalOrResourceConstraint::Is(entity_type) => {
1329 Expr::is_entity_type(Expr::var(v.into()), entity_type.clone())
1330 }
1331 }
1332 }
1333
1334 pub fn display(&self, v: PrincipalOrResource) -> String {
1338 match self {
1339 PrincipalOrResourceConstraint::In(euid) => {
1340 format!("{} in {}", v, euid.into_expr(v.into()))
1341 }
1342 PrincipalOrResourceConstraint::Eq(euid) => {
1343 format!("{} == {}", v, euid.into_expr(v.into()))
1344 }
1345 PrincipalOrResourceConstraint::IsIn(entity_type, euid) => {
1346 format!("{} is {} in {}", v, entity_type, euid.into_expr(v.into()))
1347 }
1348 PrincipalOrResourceConstraint::Is(entity_type) => {
1349 format!("{} is {}", v, entity_type)
1350 }
1351 PrincipalOrResourceConstraint::Any => format!("{}", v),
1352 }
1353 }
1354
1355 pub fn iter_euids(&'_ self) -> impl Iterator<Item = &'_ EntityUID> {
1357 match self {
1358 PrincipalOrResourceConstraint::Any => EntityIterator::None,
1359 PrincipalOrResourceConstraint::In(EntityReference::EUID(euid)) => {
1360 EntityIterator::One(euid)
1361 }
1362 PrincipalOrResourceConstraint::In(EntityReference::Slot) => EntityIterator::None,
1363 PrincipalOrResourceConstraint::Eq(EntityReference::EUID(euid)) => {
1364 EntityIterator::One(euid)
1365 }
1366 PrincipalOrResourceConstraint::Eq(EntityReference::Slot) => EntityIterator::None,
1367 PrincipalOrResourceConstraint::IsIn(_, EntityReference::EUID(euid)) => {
1368 EntityIterator::One(euid)
1369 }
1370 PrincipalOrResourceConstraint::IsIn(_, EntityReference::Slot) => EntityIterator::None,
1371 PrincipalOrResourceConstraint::Is(_) => EntityIterator::None,
1372 }
1373 }
1374
1375 pub fn iter_entity_type_names(&self) -> impl Iterator<Item = &'_ Name> {
1379 self.iter_euids()
1380 .filter_map(|euid| match euid.entity_type() {
1381 EntityType::Specified(name) => Some(name),
1382 EntityType::Unspecified => None,
1383 })
1384 .chain(match self {
1385 PrincipalOrResourceConstraint::Is(entity_type)
1386 | PrincipalOrResourceConstraint::IsIn(entity_type, _) => Some(entity_type),
1387 _ => None,
1388 })
1389 }
1390}
1391
1392#[derive(Serialize, Deserialize, Clone, Hash, Eq, PartialEq, Debug)]
1395pub enum ActionConstraint {
1396 Any,
1398 In(Vec<Arc<EntityUID>>),
1400 Eq(Arc<EntityUID>),
1402}
1403
1404impl std::fmt::Display for ActionConstraint {
1405 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1406 let render_euids =
1407 |euids: &Vec<Arc<EntityUID>>| euids.iter().map(|euid| format!("{euid}")).join(",");
1408 match self {
1409 ActionConstraint::Any => write!(f, "action"),
1410 ActionConstraint::In(euids) => {
1411 write!(f, "action in [{}]", render_euids(euids))
1412 }
1413 ActionConstraint::Eq(euid) => write!(f, "action == {}", euid),
1414 }
1415 }
1416}
1417
1418impl ActionConstraint {
1419 pub fn any() -> Self {
1421 ActionConstraint::Any
1422 }
1423
1424 pub fn is_in(euids: impl IntoIterator<Item = EntityUID>) -> Self {
1426 ActionConstraint::In(euids.into_iter().map(Arc::new).collect())
1427 }
1428
1429 pub fn is_eq(euid: EntityUID) -> Self {
1431 ActionConstraint::Eq(Arc::new(euid))
1432 }
1433
1434 fn euids_into_expr(euids: impl IntoIterator<Item = Arc<EntityUID>>) -> Expr {
1435 Expr::set(euids.into_iter().map(Expr::val))
1436 }
1437
1438 pub fn as_expr(&self) -> Expr {
1440 match self {
1441 ActionConstraint::Any => Expr::val(true),
1442 ActionConstraint::In(euids) => Expr::is_in(
1443 Expr::var(Var::Action),
1444 ActionConstraint::euids_into_expr(euids.iter().cloned()),
1445 ),
1446 ActionConstraint::Eq(euid) => {
1447 Expr::is_eq(Expr::var(Var::Action), Expr::val(euid.clone()))
1448 }
1449 }
1450 }
1451
1452 pub fn iter_euids(&self) -> impl Iterator<Item = &'_ EntityUID> {
1454 match self {
1455 ActionConstraint::Any => EntityIterator::None,
1456 ActionConstraint::In(euids) => {
1457 EntityIterator::Bunch(euids.iter().map(Arc::as_ref).collect())
1458 }
1459 ActionConstraint::Eq(euid) => EntityIterator::One(euid),
1460 }
1461 }
1462
1463 pub fn iter_entity_type_names(&self) -> impl Iterator<Item = &'_ Name> {
1467 self.iter_euids()
1468 .filter_map(|euid| match euid.entity_type() {
1469 EntityType::Specified(name) => Some(name),
1470 EntityType::Unspecified => None,
1471 })
1472 }
1473}
1474
1475impl std::fmt::Display for StaticPolicy {
1476 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1477 for (k, v) in &self.0.annotations {
1478 writeln!(f, "@{}(\"{}\")", k, v.escape_debug())?
1479 }
1480 write!(
1481 f,
1482 "{}(\n {},\n {},\n {}\n) when {{\n {}\n}};",
1483 self.effect(),
1484 self.principal_constraint(),
1485 self.action_constraint(),
1486 self.resource_constraint(),
1487 self.non_head_constraints()
1488 )
1489 }
1490}
1491
1492#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Hash)]
1494pub struct PolicyID(SmolStr);
1495
1496impl PolicyID {
1497 pub fn from_string(id: impl AsRef<str>) -> Self {
1499 Self(SmolStr::from(id.as_ref()))
1500 }
1501
1502 pub fn from_smolstr(id: SmolStr) -> Self {
1504 Self(id)
1505 }
1506}
1507
1508impl std::fmt::Display for PolicyID {
1509 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1510 write!(f, "{}", self.0.escape_debug())
1511 }
1512}
1513
1514#[cfg(feature = "arbitrary")]
1515impl<'u> arbitrary::Arbitrary<'u> for PolicyID {
1516 fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<PolicyID> {
1517 let s: String = u.arbitrary()?;
1518 Ok(PolicyID::from_string(s))
1519 }
1520 fn size_hint(depth: usize) -> (usize, Option<usize>) {
1521 <String as arbitrary::Arbitrary>::size_hint(depth)
1522 }
1523}
1524
1525#[derive(Serialize, Deserialize, Hash, Debug, PartialEq, Eq, Clone, Copy)]
1527#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1528pub enum Effect {
1529 #[serde(rename = "permit")]
1531 Permit,
1532 #[serde(rename = "forbid")]
1534 Forbid,
1535}
1536
1537impl std::fmt::Display for Effect {
1538 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1539 match self {
1540 Self::Permit => write!(f, "permit"),
1541 Self::Forbid => write!(f, "forbid"),
1542 }
1543 }
1544}
1545
1546enum EntityIterator<'a> {
1547 None,
1548 One(&'a EntityUID),
1549 Bunch(Vec<&'a EntityUID>),
1550}
1551
1552impl<'a> Iterator for EntityIterator<'a> {
1553 type Item = &'a EntityUID;
1554
1555 fn next(&mut self) -> Option<Self::Item> {
1556 match self {
1557 EntityIterator::None => None,
1558 EntityIterator::One(euid) => {
1559 let eptr = *euid;
1560 let mut ptr = EntityIterator::None;
1561 std::mem::swap(self, &mut ptr);
1562 Some(eptr)
1563 }
1564 EntityIterator::Bunch(v) => v.pop(),
1565 }
1566 }
1567}
1568
1569#[cfg(test)]
1570pub mod test_generators {
1571 use super::*;
1572
1573 pub fn all_por_constraints() -> impl Iterator<Item = PrincipalOrResourceConstraint> {
1574 let euid = EntityUID::with_eid("test");
1575 let v = vec![
1576 PrincipalOrResourceConstraint::any(),
1577 PrincipalOrResourceConstraint::is_eq(euid.clone()),
1578 PrincipalOrResourceConstraint::Eq(EntityReference::Slot),
1579 PrincipalOrResourceConstraint::is_in(euid),
1580 PrincipalOrResourceConstraint::In(EntityReference::Slot),
1581 ];
1582
1583 v.into_iter()
1584 }
1585
1586 pub fn all_principal_constraints() -> impl Iterator<Item = PrincipalConstraint> {
1587 all_por_constraints().map(|constraint| PrincipalConstraint { constraint })
1588 }
1589
1590 pub fn all_resource_constraints() -> impl Iterator<Item = ResourceConstraint> {
1591 all_por_constraints().map(|constraint| ResourceConstraint { constraint })
1592 }
1593
1594 pub fn all_actions_constraints() -> impl Iterator<Item = ActionConstraint> {
1595 let euid: EntityUID = "Action::\"test\""
1596 .parse()
1597 .expect("Invalid action constraint euid");
1598 let v = vec![
1599 ActionConstraint::any(),
1600 ActionConstraint::is_eq(euid.clone()),
1601 ActionConstraint::is_in([euid.clone()]),
1602 ActionConstraint::is_in([euid.clone(), euid]),
1603 ];
1604
1605 v.into_iter()
1606 }
1607
1608 pub fn all_templates() -> impl Iterator<Item = Template> {
1609 let mut buf = vec![];
1610 let permit = PolicyID::from_string("permit");
1611 let forbid = PolicyID::from_string("forbid");
1612 for principal in all_principal_constraints() {
1613 for action in all_actions_constraints() {
1614 for resource in all_resource_constraints() {
1615 let permit = Template::new(
1616 permit.clone(),
1617 BTreeMap::new(),
1618 Effect::Permit,
1619 principal.clone(),
1620 action.clone(),
1621 resource.clone(),
1622 Expr::val(true),
1623 );
1624 let forbid = Template::new(
1625 forbid.clone(),
1626 BTreeMap::new(),
1627 Effect::Forbid,
1628 principal.clone(),
1629 action.clone(),
1630 resource.clone(),
1631 Expr::val(true),
1632 );
1633 buf.push(permit);
1634 buf.push(forbid);
1635 }
1636 }
1637 }
1638 buf.into_iter()
1639 }
1640}
1641
1642#[cfg(test)]
1643#[allow(clippy::indexing_slicing)]
1645#[allow(clippy::panic)]
1647mod test {
1648 use std::collections::HashSet;
1649
1650 use super::{test_generators::*, *};
1651 use crate::{
1652 ast::{entity, name, EntityUID},
1653 parser::{
1654 err::{ParseError, ParseErrors, ToASTError},
1655 parse_policy,
1656 },
1657 };
1658
1659 #[test]
1660 fn literal_and_borrowed() {
1661 for template in all_templates() {
1662 let t = Arc::new(template);
1663 let env = t
1664 .slots()
1665 .map(|slotid| (*slotid, EntityUID::with_eid("eid")))
1666 .collect();
1667 let p =
1668 Template::link(t, PolicyID::from_string("id"), env).expect("Instantiation Failed");
1669
1670 let b_literal = BorrowedLiteralPolicy::from(&p);
1671 let src = serde_json::to_string(&b_literal).expect("ser error");
1672 let literal: LiteralPolicy = serde_json::from_str(&src).expect("de error");
1673
1674 assert_eq!(b_literal.template_id, &literal.template_id);
1675 assert_eq!(b_literal.link_id, literal.link_id.as_ref());
1676 assert_eq!(b_literal.values, &literal.values);
1677 }
1678 }
1679
1680 #[test]
1681 fn template_roundtrip() {
1682 for template in all_templates() {
1683 template.check_invariant();
1684 let json = serde_json::to_string(&template).expect("Serialization Failed");
1685 let t2 = serde_json::from_str::<Template>(&json).expect("Deserialization failed");
1686 t2.check_invariant();
1687 assert_eq!(template, t2);
1688 }
1689 }
1690
1691 #[test]
1692 fn test_template_rebuild() {
1693 for template in all_templates() {
1694 let id = template.id().clone();
1695 let effect = template.effect();
1696 let p = template.principal_constraint().clone();
1697 let a = template.action_constraint().clone();
1698 let r = template.resource_constraint().clone();
1699 let nhc = template.non_head_constraints().clone();
1700 let t2 = Template::new(id, BTreeMap::new(), effect, p, a, r, nhc);
1701 assert_eq!(template, t2);
1702 }
1703 }
1704
1705 #[test]
1706 fn test_inline_policy_rebuild() {
1707 for template in all_templates() {
1708 if let Ok(ip) = StaticPolicy::try_from(template.clone()) {
1709 let id = ip.id().clone();
1710 let e = ip.effect();
1711 let anno = ip
1712 .annotations()
1713 .map(|(k, v)| (k.clone(), v.clone()))
1714 .collect();
1715 let p = ip.principal_constraint().clone();
1716 let a = ip.action_constraint().clone();
1717 let r = ip.resource_constraint().clone();
1718 let nhc = ip.non_head_constraints().clone();
1719 let ip2 =
1720 StaticPolicy::new(id, anno, e, p, a, r, nhc).expect("Policy Creation Failed");
1721 assert_eq!(ip, ip2);
1722 let (t2, inst) = Template::link_static_policy(ip2);
1723 assert!(inst.is_static());
1724 assert_eq!(&template, t2.as_ref());
1725 }
1726 }
1727 }
1728
1729 #[test]
1730 fn ir_binding_too_many() {
1731 let tid = PolicyID::from_string("tid");
1732 let iid = PolicyID::from_string("iid");
1733 let t = Arc::new(Template::new(
1734 tid,
1735 BTreeMap::new(),
1736 Effect::Forbid,
1737 PrincipalConstraint::is_eq_slot(),
1738 ActionConstraint::Any,
1739 ResourceConstraint::any(),
1740 Expr::val(true),
1741 ));
1742 let mut m = HashMap::new();
1743 m.insert(SlotId::resource(), EntityUID::with_eid("eid"));
1744 match Template::link(t, iid, m) {
1745 Ok(_) => panic!("Should fail!"),
1746 Err(LinkingError::ArityError {
1747 unbound_values,
1748 extra_values,
1749 }) => {
1750 assert_eq!(unbound_values.len(), 1);
1751 assert!(unbound_values.contains(&SlotId::principal()));
1752 assert_eq!(extra_values.len(), 1);
1753 assert!(extra_values.contains(&SlotId::resource()));
1754 }
1755 Err(e) => panic!("Wrong error: {e}"),
1756 };
1757 }
1758
1759 #[test]
1760 fn ir_binding_too_few() {
1761 let tid = PolicyID::from_string("tid");
1762 let iid = PolicyID::from_string("iid");
1763 let t = Arc::new(Template::new(
1764 tid,
1765 BTreeMap::new(),
1766 Effect::Forbid,
1767 PrincipalConstraint::is_eq_slot(),
1768 ActionConstraint::Any,
1769 ResourceConstraint::is_in_slot(),
1770 Expr::val(true),
1771 ));
1772 match Template::link(t.clone(), iid.clone(), HashMap::new()) {
1773 Ok(_) => panic!("should have failed!"),
1774 Err(LinkingError::ArityError {
1775 unbound_values,
1776 extra_values,
1777 }) => {
1778 assert_eq!(unbound_values.len(), 2);
1779 assert_eq!(extra_values.len(), 0);
1780 }
1781 Err(e) => panic!("Wrong error: {e}"),
1782 };
1783 let mut m = HashMap::new();
1784 m.insert(SlotId::principal(), EntityUID::with_eid("eid"));
1785 match Template::link(t, iid, m) {
1786 Ok(_) => panic!("should have failed!"),
1787 Err(LinkingError::ArityError {
1788 unbound_values,
1789 extra_values,
1790 }) => {
1791 assert_eq!(unbound_values.len(), 1);
1792 assert!(unbound_values.contains(&SlotId::resource()));
1793 assert_eq!(extra_values.len(), 0);
1794 }
1795 Err(e) => panic!("Wrong error: {e}"),
1796 };
1797 }
1798
1799 #[test]
1800 fn ir_binding() {
1801 let tid = PolicyID::from_string("template");
1802 let iid = PolicyID::from_string("linked");
1803 let t = Arc::new(Template::new(
1804 tid,
1805 BTreeMap::new(),
1806 Effect::Permit,
1807 PrincipalConstraint::is_in_slot(),
1808 ActionConstraint::any(),
1809 ResourceConstraint::is_eq_slot(),
1810 Expr::val(true),
1811 ));
1812
1813 let mut m = HashMap::new();
1814 m.insert(SlotId::principal(), EntityUID::with_eid("theprincipal"));
1815 m.insert(SlotId::resource(), EntityUID::with_eid("theresource"));
1816
1817 let r = Template::link(t, iid.clone(), m).expect("Should Succeed");
1818 assert_eq!(r.id(), &iid);
1819 assert_eq!(
1820 r.env().get(&SlotId::principal()),
1821 Some(&EntityUID::with_eid("theprincipal"))
1822 );
1823 assert_eq!(
1824 r.env().get(&SlotId::resource()),
1825 Some(&EntityUID::with_eid("theresource"))
1826 );
1827 }
1828
1829 #[test]
1830 fn isnt_template_implies_from_succeeds() {
1831 for template in all_templates() {
1832 if template.slots().count() == 0 {
1833 StaticPolicy::try_from(template).expect("Should succeed");
1834 }
1835 }
1836 }
1837
1838 #[test]
1839 fn is_template_implies_from_fails() {
1840 for template in all_templates() {
1841 if template.slots().count() != 0 {
1842 assert!(
1843 StaticPolicy::try_from(template.clone()).is_err(),
1844 "Following template did convert {template}"
1845 );
1846 }
1847 }
1848 }
1849
1850 #[test]
1851 fn non_template_iso() {
1852 for template in all_templates() {
1853 if let Ok(p) = StaticPolicy::try_from(template.clone()) {
1854 let (t2, _) = Template::link_static_policy(p);
1855 assert_eq!(&template, t2.as_ref());
1856 }
1857 }
1858 }
1859
1860 #[test]
1861 fn template_into_expr() {
1862 for template in all_templates() {
1863 if let Ok(p) = StaticPolicy::try_from(template.clone()) {
1864 let t: Template = template;
1865 assert_eq!(p.condition(), t.condition());
1866 assert_eq!(p.effect(), t.effect());
1867 }
1868 }
1869 }
1870
1871 #[test]
1872 fn template_por_iter() {
1873 let e = Arc::new(EntityUID::with_eid("eid"));
1874 assert_eq!(PrincipalOrResourceConstraint::Any.iter_euids().count(), 0);
1875 assert_eq!(
1876 PrincipalOrResourceConstraint::In(EntityReference::EUID(e.clone()))
1877 .iter_euids()
1878 .count(),
1879 1
1880 );
1881 assert_eq!(
1882 PrincipalOrResourceConstraint::In(EntityReference::Slot)
1883 .iter_euids()
1884 .count(),
1885 0
1886 );
1887 assert_eq!(
1888 PrincipalOrResourceConstraint::Eq(EntityReference::EUID(e.clone()))
1889 .iter_euids()
1890 .count(),
1891 1
1892 );
1893 assert_eq!(
1894 PrincipalOrResourceConstraint::Eq(EntityReference::Slot)
1895 .iter_euids()
1896 .count(),
1897 0
1898 );
1899 assert_eq!(
1900 PrincipalOrResourceConstraint::IsIn("T".parse().unwrap(), EntityReference::EUID(e))
1901 .iter_euids()
1902 .count(),
1903 1
1904 );
1905 assert_eq!(
1906 PrincipalOrResourceConstraint::Is("T".parse().unwrap())
1907 .iter_euids()
1908 .count(),
1909 0
1910 );
1911 assert_eq!(
1912 PrincipalOrResourceConstraint::IsIn("T".parse().unwrap(), EntityReference::Slot)
1913 .iter_euids()
1914 .count(),
1915 0
1916 );
1917 }
1918
1919 #[test]
1920 fn action_iter() {
1921 assert_eq!(ActionConstraint::Any.iter_euids().count(), 0);
1922 let a = ActionConstraint::Eq(Arc::new(EntityUID::with_eid("test")));
1923 let v = a.iter_euids().collect::<Vec<_>>();
1924 assert_eq!(vec![&EntityUID::with_eid("test")], v);
1925 let a =
1926 ActionConstraint::is_in([EntityUID::with_eid("test1"), EntityUID::with_eid("test2")]);
1927 let set = a.iter_euids().collect::<HashSet<_>>();
1928 let e1 = EntityUID::with_eid("test1");
1929 let e2 = EntityUID::with_eid("test2");
1930 let correct = vec![&e1, &e2].into_iter().collect::<HashSet<_>>();
1931 assert_eq!(set, correct);
1932 }
1933
1934 #[test]
1935 fn test_iter_none() {
1936 let mut i = EntityIterator::None;
1937 assert_eq!(i.next(), None);
1938 }
1939
1940 #[test]
1941 fn test_iter_once() {
1942 let id = EntityUID::from_components(
1943 name::Name::unqualified_name(name::Id::new_unchecked("s")),
1944 entity::Eid::new("eid"),
1945 );
1946 let mut i = EntityIterator::One(&id);
1947 assert_eq!(i.next(), Some(&id));
1948 assert_eq!(i.next(), None);
1949 }
1950
1951 #[test]
1952 fn test_iter_mult() {
1953 let id1 = EntityUID::from_components(
1954 name::Name::unqualified_name(name::Id::new_unchecked("s")),
1955 entity::Eid::new("eid1"),
1956 );
1957 let id2 = EntityUID::from_components(
1958 name::Name::unqualified_name(name::Id::new_unchecked("s")),
1959 entity::Eid::new("eid2"),
1960 );
1961 let v = vec![&id1, &id2];
1962 let mut i = EntityIterator::Bunch(v);
1963 assert_eq!(i.next(), Some(&id2));
1964 assert_eq!(i.next(), Some(&id1));
1965 assert_eq!(i.next(), None)
1966 }
1967
1968 #[test]
1969 fn euid_into_expr() {
1970 let e = EntityReference::Slot;
1971 assert_eq!(
1972 e.into_expr(SlotId::principal()),
1973 Expr::slot(SlotId::principal())
1974 );
1975 let e = EntityReference::euid(EntityUID::with_eid("eid"));
1976 assert_eq!(
1977 e.into_expr(SlotId::principal()),
1978 Expr::val(EntityUID::with_eid("eid"))
1979 );
1980 }
1981
1982 #[test]
1983 fn por_constraint_display() {
1984 let t = PrincipalOrResourceConstraint::Eq(EntityReference::Slot);
1985 let s = t.display(PrincipalOrResource::Principal);
1986 assert_eq!(s, "principal == ?principal");
1987 let t =
1988 PrincipalOrResourceConstraint::Eq(EntityReference::euid(EntityUID::with_eid("test")));
1989 let s = t.display(PrincipalOrResource::Principal);
1990 assert_eq!(s, "principal == test_entity_type::\"test\"");
1991 }
1992
1993 #[test]
1994 fn unexpected_templates() {
1995 let policy_str = r#"permit(principal == ?principal, action, resource);"#;
1996 let ParseErrors(errs) = parse_policy(Some("id".into()), policy_str).err().unwrap();
1997 assert_eq!(
1998 &errs[0],
1999 &ParseError::ToAST(ToASTError::UnexpectedTemplate {
2000 slot: crate::parser::cst::Slot::Principal
2001 })
2002 );
2003 assert_eq!(errs.len(), 1);
2004 let policy_str =
2005 r#"permit(principal == ?principal, action, resource) when { ?principal == 3 } ;"#;
2006 let ParseErrors(errs) = parse_policy(Some("id".into()), policy_str).err().unwrap();
2007 assert!(
2008 errs.contains(&ParseError::ToAST(ToASTError::UnexpectedTemplate {
2009 slot: crate::parser::cst::Slot::Principal
2010 }))
2011 );
2012 assert_eq!(errs.len(), 2);
2013 }
2014}