1use super::{
18 EntityUID, LinkingError, LiteralPolicy, Policy, PolicyID, ReificationError, SlotId,
19 StaticPolicy, Template,
20};
21use itertools::Itertools;
22use miette::Diagnostic;
23use serde::{Deserialize, Serialize};
24use std::collections::{hash_map::Entry, HashMap, HashSet};
25use std::{borrow::Borrow, sync::Arc};
26use thiserror::Error;
27
28#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
30#[serde(try_from = "LiteralPolicySet")]
31#[serde(into = "LiteralPolicySet")]
32pub struct PolicySet {
33 templates: HashMap<PolicyID, Arc<Template>>,
39 links: HashMap<PolicyID, Policy>,
45
46 template_to_links_map: HashMap<PolicyID, HashSet<PolicyID>>,
50}
51
52impl TryFrom<LiteralPolicySet> for PolicySet {
55 type Error = ReificationError;
56 fn try_from(pset: LiteralPolicySet) -> Result<Self, Self::Error> {
57 let templates = pset
59 .templates
60 .into_iter()
61 .map(|(id, template)| (id, Arc::new(template)))
62 .collect();
63 let links = pset
64 .links
65 .into_iter()
66 .map(|(id, literal)| literal.reify(&templates).map(|linked| (id, linked)))
67 .collect::<Result<HashMap<PolicyID, Policy>, ReificationError>>()?;
68
69 let mut template_to_links_map = HashMap::new();
70 for template in &templates {
71 template_to_links_map.insert(template.0.clone(), HashSet::new());
72 }
73 for (link_id, link) in &links {
74 let template = link.template().id();
75 match template_to_links_map.entry(template.clone()) {
76 Entry::Occupied(t) => t.into_mut().insert(link_id.clone()),
77 Entry::Vacant(_) => return Err(ReificationError::NoSuchTemplate(template.clone())),
78 };
79 }
80
81 Ok(Self {
82 templates,
83 links,
84 template_to_links_map,
85 })
86 }
87}
88
89#[derive(Debug, Serialize, Deserialize)]
91struct LiteralPolicySet {
92 templates: HashMap<PolicyID, Template>,
93 links: HashMap<PolicyID, LiteralPolicy>,
94}
95
96impl From<PolicySet> for LiteralPolicySet {
97 fn from(pset: PolicySet) -> Self {
98 let templates = pset
99 .templates
100 .into_iter()
101 .map(|(id, template)| (id, template.as_ref().clone()))
102 .collect();
103 let links = pset
104 .links
105 .into_iter()
106 .map(|(id, p)| (id, p.into()))
107 .collect();
108 Self { templates, links }
109 }
110}
111
112#[derive(Debug, Diagnostic, Error)]
114pub enum PolicySetError {
115 #[error("duplicate template or policy id `{id}`")]
118 Occupied {
119 id: PolicyID,
121 },
122}
123
124#[derive(Debug, Diagnostic, Error)]
126pub enum PolicySetGetLinksError {
127 #[error("No template `{0}`")]
129 MissingTemplate(PolicyID),
130}
131
132#[derive(Debug, Diagnostic, Error)]
134pub enum PolicySetUnlinkError {
135 #[error("unable to unlink policy id `{0}` because it does not exist")]
137 UnlinkingError(PolicyID),
138 #[error("unable to remove link with policy id `{0}` because it is a static policy")]
140 NotLinkError(PolicyID),
141}
142
143#[derive(Debug, Diagnostic, Error)]
145pub enum PolicySetTemplateRemovalError {
146 #[error("unable to remove template id `{0}` from template list because it does not exist")]
148 RemovePolicyNoTemplateError(PolicyID),
149 #[error(
151 "unable to remove template id `{0}` from template list because it still has active links"
152 )]
153 RemoveTemplateWithLinksError(PolicyID),
154 #[error("unable to remove template with policy id `{0}` because it is a static policy")]
156 NotTemplateError(PolicyID),
157}
158
159#[derive(Debug, Diagnostic, Error)]
161pub enum PolicySetPolicyRemovalError {
162 #[error("unable to remove static policy id `{0}` from link list because it does not exist")]
164 RemovePolicyNoLinkError(PolicyID),
165 #[error(
167 "unable to remove static policy id `{0}` from template list because it does not exist"
168 )]
169 RemovePolicyNoTemplateError(PolicyID),
170}
171
172impl PolicySet {
175 pub fn new() -> Self {
177 Self {
178 templates: HashMap::new(),
179 links: HashMap::new(),
180 template_to_links_map: HashMap::new(),
181 }
182 }
183
184 pub fn add(&mut self, policy: Policy) -> Result<(), PolicySetError> {
186 let t = policy.template_arc();
187
188 let template_ventry = match self.templates.entry(t.id().clone()) {
193 Entry::Vacant(ventry) => Some(ventry),
194 Entry::Occupied(oentry) => {
195 if oentry.get() != &t {
196 return Err(PolicySetError::Occupied {
197 id: oentry.key().clone(),
198 });
199 }
200 None
201 }
202 };
203
204 let link_ventry = match self.links.entry(policy.id().clone()) {
205 Entry::Vacant(ventry) => Some(ventry),
206 Entry::Occupied(oentry) => {
207 return Err(PolicySetError::Occupied {
208 id: oentry.key().clone(),
209 });
210 }
211 };
212
213 if let Some(ventry) = template_ventry {
216 self.template_to_links_map.insert(
217 t.id().clone(),
218 vec![policy.id().clone()]
219 .into_iter()
220 .collect::<HashSet<PolicyID>>(),
221 );
222 ventry.insert(t);
223 } else {
224 self.template_to_links_map
226 .entry(t.id().clone())
227 .or_default()
228 .insert(policy.id().clone());
229 }
230 if let Some(ventry) = link_ventry {
231 ventry.insert(policy);
232 }
233
234 Ok(())
235 }
236
237 pub fn remove_static(
239 &mut self,
240 policy_id: &PolicyID,
241 ) -> Result<Policy, PolicySetPolicyRemovalError> {
242 let policy = match self.links.remove(policy_id) {
245 Some(p) => p,
246 None => {
247 return Err(PolicySetPolicyRemovalError::RemovePolicyNoLinkError(
248 policy_id.clone(),
249 ))
250 }
251 };
252 match self.templates.remove(policy_id) {
254 Some(_) => {
255 self.template_to_links_map.remove(policy_id);
256 Ok(policy)
257 }
258 None => {
259 self.links.insert(policy_id.clone(), policy);
262 Err(PolicySetPolicyRemovalError::RemovePolicyNoTemplateError(
263 policy_id.clone(),
264 ))
265 }
266 }
267 }
268
269 pub fn add_static(&mut self, policy: StaticPolicy) -> Result<(), PolicySetError> {
271 let (t, p) = Template::link_static_policy(policy);
272
273 match (
274 self.templates.entry(t.id().clone()),
275 self.links.entry(t.id().clone()),
276 ) {
277 (Entry::Vacant(templates_entry), Entry::Vacant(links_entry)) => {
278 self.template_to_links_map.insert(
279 t.id().clone(),
280 vec![p.id().clone()]
281 .into_iter()
282 .collect::<HashSet<PolicyID>>(),
283 );
284 templates_entry.insert(t);
285 links_entry.insert(p);
286 Ok(())
287 }
288 (Entry::Occupied(oentry), _) => Err(PolicySetError::Occupied {
289 id: oentry.key().clone(),
290 }),
291 (_, Entry::Occupied(oentry)) => Err(PolicySetError::Occupied {
292 id: oentry.key().clone(),
293 }),
294 }
295 }
296
297 pub fn add_template(&mut self, t: Template) -> Result<(), PolicySetError> {
300 if self.links.contains_key(t.id()) {
301 return Err(PolicySetError::Occupied { id: t.id().clone() });
302 }
303
304 match self.templates.entry(t.id().clone()) {
305 Entry::Occupied(oentry) => Err(PolicySetError::Occupied {
306 id: oentry.key().clone(),
307 }),
308 Entry::Vacant(ventry) => {
309 self.template_to_links_map
310 .insert(t.id().clone(), HashSet::new());
311 ventry.insert(Arc::new(t));
312 Ok(())
313 }
314 }
315 }
316
317 pub fn remove_template(
321 &mut self,
322 policy_id: &PolicyID,
323 ) -> Result<Template, PolicySetTemplateRemovalError> {
324 if self.links.contains_key(policy_id) {
326 return Err(PolicySetTemplateRemovalError::NotTemplateError(
327 policy_id.clone(),
328 ));
329 }
330
331 match self.template_to_links_map.get(policy_id) {
332 Some(map) => {
333 if !map.is_empty() {
334 return Err(PolicySetTemplateRemovalError::RemoveTemplateWithLinksError(
335 policy_id.clone(),
336 ));
337 }
338 }
339 None => {
340 return Err(PolicySetTemplateRemovalError::RemovePolicyNoTemplateError(
341 policy_id.clone(),
342 ))
343 }
344 };
345
346 #[allow(clippy::panic)]
348 match self.templates.remove(policy_id) {
349 Some(t) => {
350 self.template_to_links_map.remove(policy_id);
351 Ok((*t).clone())
352 }
353 None => panic!("Found in template_to_links_map but not in templates"),
354 }
355 }
356
357 pub fn get_linked_policies(
360 &self,
361 template_id: &PolicyID,
362 ) -> Result<impl Iterator<Item = &PolicyID>, PolicySetGetLinksError> {
363 match self.template_to_links_map.get(template_id) {
364 Some(s) => Ok(s.iter()),
365 None => Err(PolicySetGetLinksError::MissingTemplate(template_id.clone())),
366 }
367 }
368
369 pub fn link(
377 &mut self,
378 template_id: PolicyID,
379 new_id: PolicyID,
380 values: HashMap<SlotId, EntityUID>,
381 ) -> Result<&Policy, LinkingError> {
382 let t =
383 self.get_template_arc(&template_id)
384 .ok_or_else(|| LinkingError::NoSuchTemplate {
385 id: template_id.clone(),
386 })?;
387 let r = Template::link(t, new_id.clone(), values)?;
388
389 match (
391 self.links.entry(new_id.clone()),
392 self.templates.entry(new_id.clone()),
393 ) {
394 (Entry::Vacant(links_entry), Entry::Vacant(_)) => {
395 self.template_to_links_map
397 .entry(template_id)
398 .or_default()
399 .insert(new_id);
400 Ok(links_entry.insert(r))
401 }
402 (Entry::Occupied(oentry), _) => Err(LinkingError::PolicyIdConflict {
403 id: oentry.key().clone(),
404 }),
405 (_, Entry::Occupied(oentry)) => Err(LinkingError::PolicyIdConflict {
406 id: oentry.key().clone(),
407 }),
408 }
409 }
410
411 pub fn unlink(&mut self, policy_id: &PolicyID) -> Result<Policy, PolicySetUnlinkError> {
414 if self.templates.contains_key(policy_id) {
416 return Err(PolicySetUnlinkError::NotLinkError(policy_id.clone()));
417 }
418 match self.links.remove(policy_id) {
419 Some(p) => {
420 #[allow(clippy::panic)]
422 match self.template_to_links_map.entry(p.template().id().clone()) {
423 Entry::Occupied(t) => t.into_mut().remove(policy_id),
424 Entry::Vacant(_) => {
425 panic!("No template found for linked policy")
426 }
427 };
428 Ok(p)
429 }
430 None => Err(PolicySetUnlinkError::UnlinkingError(policy_id.clone())),
431 }
432 }
433
434 pub fn policies(&self) -> impl Iterator<Item = &Policy> {
436 self.links.values()
437 }
438
439 pub fn all_templates(&self) -> impl Iterator<Item = &Template> {
442 self.templates.values().map(|t| t.borrow())
443 }
444
445 pub fn templates(&self) -> impl Iterator<Item = &Template> {
447 self.all_templates().filter(|t| t.slots().count() != 0)
448 }
449
450 pub fn static_policies(&self) -> impl Iterator<Item = &Policy> {
452 self.policies().filter(|p| p.is_static())
453 }
454
455 pub fn is_empty(&self) -> bool {
457 self.templates.is_empty() && self.links.is_empty()
458 }
459
460 pub fn get_template_arc(&self, id: &PolicyID) -> Option<Arc<Template>> {
462 self.templates.get(id).cloned()
463 }
464
465 pub fn get_template(&self, id: &PolicyID) -> Option<&Template> {
467 self.templates.get(id).map(AsRef::as_ref)
468 }
469
470 pub fn get(&self, id: &PolicyID) -> Option<&Policy> {
472 self.links.get(id)
473 }
474
475 pub fn try_from_iter<T: IntoIterator<Item = Policy>>(iter: T) -> Result<Self, PolicySetError> {
477 let mut set = Self::new();
478 for p in iter {
479 set.add(p)?;
480 }
481 Ok(set)
482 }
483}
484
485impl std::fmt::Display for PolicySet {
486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487 if self.is_empty() {
489 write!(f, "<empty policyset>")
490 } else {
491 write!(
492 f,
493 "Templates:\n{}, Template Linked Policies:\n{}",
494 self.all_templates().join("\n"),
495 self.policies().join("\n")
496 )
497 }
498 }
499}
500
501#[allow(clippy::panic)]
503#[allow(clippy::indexing_slicing)]
505#[cfg(test)]
506mod test {
507 use super::*;
508 use crate::{
509 ast::{
510 ActionConstraint, Annotations, Effect, Expr, PrincipalConstraint, ResourceConstraint,
511 },
512 parser,
513 };
514 use std::collections::HashMap;
515
516 #[test]
517 fn link_conflicts() {
518 let mut pset = PolicySet::new();
519 let p1 = parser::parse_policy(
520 Some(PolicyID::from_string("id")),
521 "permit(principal,action,resource);",
522 )
523 .expect("Failed to parse");
524 pset.add_static(p1).expect("Failed to add!");
525 let template = parser::parse_policy_or_template(
526 Some(PolicyID::from_string("t")),
527 "permit(principal == ?principal, action, resource);",
528 )
529 .expect("Failed to parse");
530 pset.add_template(template).expect("Add failed");
531
532 let env: HashMap<SlotId, EntityUID> = [(
533 SlotId::principal(),
534 r#"Test::"test""#.parse().expect("Failed to parse"),
535 )]
536 .into_iter()
537 .collect();
538
539 let r = pset.link(PolicyID::from_string("t"), PolicyID::from_string("id"), env);
540
541 match r {
542 Ok(_) => panic!("Should have failed due to conflict"),
543 Err(LinkingError::PolicyIdConflict { id }) => {
544 assert_eq!(id, PolicyID::from_string("id"))
545 }
546 Err(e) => panic!("Incorrect error: {e}"),
547 };
548 }
549
550 #[test]
553 fn policyset_add() {
554 let mut pset = PolicySet::new();
555 let static_policy = parser::parse_policy(
556 Some(PolicyID::from_string("id")),
557 "permit(principal,action,resource);",
558 )
559 .expect("Failed to parse");
560 let static_policy: Policy = static_policy.into();
561 pset.add(static_policy)
562 .expect("Adding static policy in Policy form should succeed");
563
564 let template = Arc::new(
565 parser::parse_policy_or_template(
566 Some(PolicyID::from_string("t")),
567 "permit(principal == ?principal, action, resource);",
568 )
569 .expect("Failed to parse"),
570 );
571 let env1: HashMap<SlotId, EntityUID> = [(
572 SlotId::principal(),
573 r#"Test::"test1""#.parse().expect("Failed to parse"),
574 )]
575 .into_iter()
576 .collect();
577
578 let p1 = Template::link(Arc::clone(&template), PolicyID::from_string("link"), env1)
579 .expect("Failed to link");
580 pset.add(p1).expect(
581 "Adding link should succeed, even though the template wasn't previously in the pset",
582 );
583 assert!(
584 pset.get_template_arc(&PolicyID::from_string("t")).is_some(),
585 "Adding link should implicitly add the template"
586 );
587
588 let env2: HashMap<SlotId, EntityUID> = [(
589 SlotId::principal(),
590 r#"Test::"test2""#.parse().expect("Failed to parse"),
591 )]
592 .into_iter()
593 .collect();
594
595 let p2 = Template::link(
596 Arc::clone(&template),
597 PolicyID::from_string("link"),
598 env2.clone(),
599 )
600 .expect("Failed to link");
601 match pset.add(p2) {
602 Ok(_) => panic!("Should have failed due to conflict with existing link id"),
603 Err(PolicySetError::Occupied { id }) => assert_eq!(id, PolicyID::from_string("link")),
604 }
605
606 let p3 = Template::link(Arc::clone(&template), PolicyID::from_string("link2"), env2)
607 .expect("Failed to link");
608 pset.add(p3).expect(
609 "Adding link should succeed, even though the template already existed in the pset",
610 );
611
612 let template2 = Arc::new(
613 parser::parse_policy_or_template(
614 Some(PolicyID::from_string("t")),
615 "forbid(principal, action, resource == ?resource);",
616 )
617 .expect("Failed to parse"),
618 );
619 let env3: HashMap<SlotId, EntityUID> = [(
620 SlotId::resource(),
621 r#"Test::"test3""#.parse().expect("Failed to parse"),
622 )]
623 .into_iter()
624 .collect();
625
626 let p4 = Template::link(
627 Arc::clone(&template2),
628 PolicyID::from_string("unique3"),
629 env3,
630 )
631 .expect("Failed to link");
632 match pset.add(p4) {
633 Ok(_) => panic!("Should have failed due to conflict on template id"),
634 Err(PolicySetError::Occupied { id }) => {
635 assert_eq!(id, PolicyID::from_string("t"))
636 }
637 }
638 }
639
640 #[test]
641 fn policy_conflicts() {
642 let mut pset = PolicySet::new();
643 let p1 = parser::parse_policy(
644 Some(PolicyID::from_string("id")),
645 "permit(principal,action,resource);",
646 )
647 .expect("Failed to parse");
648 let p2 = parser::parse_policy(
649 Some(PolicyID::from_string("id")),
650 "permit(principal,action,resource) when { false };",
651 )
652 .expect("Failed to parse");
653 pset.add_static(p1).expect("Failed to add!");
654 match pset.add_static(p2) {
655 Ok(_) => panic!("Should have failed to due name conflict"),
656 Err(PolicySetError::Occupied { id }) => assert_eq!(id, PolicyID::from_string("id")),
657 }
658 }
659
660 #[test]
661 fn template_filtering() {
662 let template = parser::parse_policy_or_template(
663 Some(PolicyID::from_string("template")),
664 "permit(principal == ?principal, action, resource);",
665 )
666 .expect("Template Parse Failure");
667 let static_policy = parser::parse_policy(
668 Some(PolicyID::from_string("static")),
669 "permit(principal, action, resource);",
670 )
671 .expect("Static parse failure");
672 let mut set = PolicySet::new();
673 set.add_template(template).unwrap();
674 set.add_static(static_policy).unwrap();
675
676 assert_eq!(set.all_templates().count(), 2);
677 assert_eq!(set.templates().count(), 1);
678 assert_eq!(set.static_policies().count(), 1);
679 assert_eq!(set.policies().count(), 1);
680 set.link(
681 PolicyID::from_string("template"),
682 PolicyID::from_string("id"),
683 [(SlotId::principal(), EntityUID::with_eid("eid"))]
684 .into_iter()
685 .collect(),
686 )
687 .expect("Linking failed!");
688 assert_eq!(set.static_policies().count(), 1);
689 assert_eq!(set.policies().count(), 2);
690 }
691
692 #[test]
693 fn linking_missing_template() {
694 let tid = PolicyID::from_string("template");
695 let lid = PolicyID::from_string("link");
696 let t = Template::new(
697 tid.clone(),
698 None,
699 Annotations::new(),
700 Effect::Permit,
701 PrincipalConstraint::any(),
702 ActionConstraint::any(),
703 ResourceConstraint::any(),
704 Expr::val(true),
705 );
706
707 let mut s = PolicySet::new();
708 let e = s
709 .link(tid.clone(), lid.clone(), HashMap::new())
710 .expect_err("Should fail");
711
712 match e {
713 LinkingError::NoSuchTemplate { id } => assert_eq!(tid, id),
714 e => panic!("Wrong error {e}"),
715 };
716
717 s.add_template(t).unwrap();
718 s.link(tid, lid, HashMap::new()).expect("Should succeed");
719 }
720
721 #[test]
722 fn linkinv_valid_link() {
723 let tid = PolicyID::from_string("template");
724 let lid = PolicyID::from_string("link");
725 let t = Template::new(
726 tid.clone(),
727 None,
728 Annotations::new(),
729 Effect::Permit,
730 PrincipalConstraint::is_eq_slot(),
731 ActionConstraint::any(),
732 ResourceConstraint::is_in_slot(),
733 Expr::val(true),
734 );
735
736 let mut s = PolicySet::new();
737 s.add_template(t).unwrap();
738
739 let mut vals = HashMap::new();
740 vals.insert(SlotId::principal(), EntityUID::with_eid("p"));
741 vals.insert(SlotId::resource(), EntityUID::with_eid("a"));
742
743 s.link(tid.clone(), lid.clone(), vals).expect("Should link");
744
745 let v: Vec<_> = s.policies().collect();
746
747 assert_eq!(v[0].id(), &lid);
748 assert_eq!(v[0].template().id(), &tid);
749 }
750
751 #[test]
752 fn linking_empty_set() {
753 let s = PolicySet::new();
754 assert_eq!(s.policies().count(), 0);
755 }
756
757 #[test]
758 fn linking_raw_policy() {
759 let mut s = PolicySet::new();
760 let id = PolicyID::from_string("id");
761 let p = StaticPolicy::new(
762 id.clone(),
763 None,
764 Annotations::new(),
765 Effect::Forbid,
766 PrincipalConstraint::any(),
767 ActionConstraint::any(),
768 ResourceConstraint::any(),
769 Expr::val(true),
770 )
771 .expect("Policy Creation Failed");
772 s.add_static(p).unwrap();
773
774 let mut iter = s.policies();
775 match iter.next() {
776 Some(pol) => {
777 assert_eq!(pol.id(), &id);
778 assert_eq!(pol.effect(), Effect::Forbid);
779 assert!(pol.env().is_empty())
780 }
781 None => panic!("Linked Record Not Present"),
782 };
783 }
784
785 #[test]
786 fn link_slotmap() {
787 let mut s = PolicySet::new();
788 let template_id = PolicyID::from_string("template");
789 let link_id = PolicyID::from_string("link");
790 let t = Template::new(
791 template_id.clone(),
792 None,
793 Annotations::new(),
794 Effect::Forbid,
795 PrincipalConstraint::is_eq_slot(),
796 ActionConstraint::any(),
797 ResourceConstraint::any(),
798 Expr::val(true),
799 );
800 s.add_template(t).unwrap();
801
802 let mut v = HashMap::new();
803 let entity = EntityUID::with_eid("eid");
804 v.insert(SlotId::principal(), entity.clone());
805 s.link(template_id.clone(), link_id.clone(), v)
806 .expect("Linking failed!");
807
808 let link = s.get(&link_id).expect("Link should exist");
809 assert_eq!(&link_id, link.id());
810 assert_eq!(&template_id, link.template().id());
811 assert_eq!(
812 &entity,
813 link.env()
814 .get(&SlotId::principal())
815 .expect("Mapping was incorrect")
816 );
817 }
818
819 #[test]
820 fn policy_sets() {
821 let mut pset = PolicySet::new();
822 assert!(pset.is_empty());
823 let id1 = PolicyID::from_string("id1");
824 let tid1 = PolicyID::from_string("template");
825 let policy1 = StaticPolicy::new(
826 id1.clone(),
827 None,
828 Annotations::new(),
829 Effect::Permit,
830 PrincipalConstraint::any(),
831 ActionConstraint::any(),
832 ResourceConstraint::any(),
833 Expr::val(true),
834 )
835 .expect("Policy Creation Failed");
836 let template1 = Template::new(
837 tid1.clone(),
838 None,
839 Annotations::new(),
840 Effect::Permit,
841 PrincipalConstraint::any(),
842 ActionConstraint::any(),
843 ResourceConstraint::any(),
844 Expr::val(true),
845 );
846 let added = pset.add_static(policy1.clone()).is_ok();
847 assert!(added);
848 let added = pset.add_static(policy1).is_ok();
849 assert!(!added);
850 let added = pset.add_template(template1.clone()).is_ok();
851 assert!(added);
852 let added = pset.add_template(template1).is_ok();
853 assert!(!added);
854 assert!(!pset.is_empty());
855 let id2 = PolicyID::from_string("id2");
856 let policy2 = StaticPolicy::new(
857 id2.clone(),
858 None,
859 Annotations::new(),
860 Effect::Forbid,
861 PrincipalConstraint::is_eq(Arc::new(EntityUID::with_eid("jane"))),
862 ActionConstraint::any(),
863 ResourceConstraint::any(),
864 Expr::val(true),
865 )
866 .expect("Policy Creation Failed");
867 let added = pset.add_static(policy2).is_ok();
868 assert!(added);
869
870 let tid2 = PolicyID::from_string("template2");
871 let template2 = Template::new(
872 tid2.clone(),
873 None,
874 Annotations::new(),
875 Effect::Permit,
876 PrincipalConstraint::is_eq_slot(),
877 ActionConstraint::any(),
878 ResourceConstraint::any(),
879 Expr::val(true),
880 );
881 let id3 = PolicyID::from_string("link");
882 let added = pset.add_template(template2).is_ok();
883 assert!(added);
884
885 let r = pset.link(
886 tid2.clone(),
887 id3.clone(),
888 HashMap::from([(SlotId::principal(), EntityUID::with_eid("example"))]),
889 );
890 r.expect("Linking failed");
891
892 assert_eq!(pset.get(&id1).expect("should find the policy").id(), &id1);
893 assert_eq!(pset.get(&id2).expect("should find the policy").id(), &id2);
894 assert_eq!(pset.get(&id3).expect("should find link").id(), &id3);
895 assert_eq!(
896 pset.get(&id3).expect("should find link").template().id(),
897 &tid2
898 );
899 assert!(pset.get(&tid2).is_none());
900 assert!(pset.get_template_arc(&id1).is_some()); assert!(pset.get_template_arc(&id2).is_some()); assert!(pset.get_template_arc(&tid2).is_some());
903 assert_eq!(pset.policies().count(), 3);
904
905 assert_eq!(
906 pset.get_template_arc(&tid1)
907 .expect("should find the template")
908 .id(),
909 &tid1
910 );
911 assert!(pset.get(&tid1).is_none());
912 assert_eq!(pset.all_templates().count(), 4);
913 }
914}