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 = self
383 .get_template(&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(&self, id: &PolicyID) -> Option<Arc<Template>> {
462 self.templates.get(id).map(Arc::clone)
463 }
464
465 pub fn get(&self, id: &PolicyID) -> Option<&Policy> {
467 self.links.get(id)
468 }
469
470 pub fn try_from_iter<T: IntoIterator<Item = Policy>>(iter: T) -> Result<Self, PolicySetError> {
472 let mut set = Self::new();
473 for p in iter {
474 set.add(p)?;
475 }
476 Ok(set)
477 }
478}
479
480impl std::fmt::Display for PolicySet {
481 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
482 if self.is_empty() {
484 write!(f, "<empty policyset>")
485 } else {
486 write!(
487 f,
488 "Templates:\n{}, Template Linked Policies:\n{}",
489 self.all_templates().join("\n"),
490 self.policies().join("\n")
491 )
492 }
493 }
494}
495
496#[allow(clippy::panic)]
498#[allow(clippy::indexing_slicing)]
500#[cfg(test)]
501mod test {
502 use super::*;
503 use crate::{
504 ast::{
505 ActionConstraint, Annotations, Effect, Expr, PrincipalConstraint, ResourceConstraint,
506 },
507 parser,
508 };
509 use std::collections::HashMap;
510
511 #[test]
512 fn link_conflicts() {
513 let mut pset = PolicySet::new();
514 let p1 = parser::parse_policy(Some("id".into()), "permit(principal,action,resource);")
515 .expect("Failed to parse");
516 pset.add_static(p1).expect("Failed to add!");
517 let template = parser::parse_policy_or_template(
518 Some("t".into()),
519 "permit(principal == ?principal, action, resource);",
520 )
521 .expect("Failed to parse");
522 pset.add_template(template).expect("Add failed");
523
524 let env: HashMap<SlotId, EntityUID> = [(
525 SlotId::principal(),
526 r#"Test::"test""#.parse().expect("Failed to parse"),
527 )]
528 .into_iter()
529 .collect();
530
531 let r = pset.link(PolicyID::from_string("t"), PolicyID::from_string("id"), env);
532
533 match r {
534 Ok(_) => panic!("Should have failed due to conflict"),
535 Err(LinkingError::PolicyIdConflict { id }) => {
536 assert_eq!(id, PolicyID::from_string("id"))
537 }
538 Err(e) => panic!("Incorrect error: {e}"),
539 };
540 }
541
542 #[test]
545 fn policyset_add() {
546 let mut pset = PolicySet::new();
547 let static_policy =
548 parser::parse_policy(Some("id".into()), "permit(principal,action,resource);")
549 .expect("Failed to parse");
550 let static_policy: Policy = static_policy.into();
551 pset.add(static_policy)
552 .expect("Adding static policy in Policy form should succeed");
553
554 let template = Arc::new(
555 parser::parse_policy_or_template(
556 Some("t".into()),
557 "permit(principal == ?principal, action, resource);",
558 )
559 .expect("Failed to parse"),
560 );
561 let env1: HashMap<SlotId, EntityUID> = [(
562 SlotId::principal(),
563 r#"Test::"test1""#.parse().expect("Failed to parse"),
564 )]
565 .into_iter()
566 .collect();
567
568 let p1 = Template::link(Arc::clone(&template), PolicyID::from_string("link"), env1)
569 .expect("Failed to link");
570 pset.add(p1).expect(
571 "Adding link should succeed, even though the template wasn't previously in the pset",
572 );
573 assert!(
574 pset.get_template(&PolicyID::from_string("t")).is_some(),
575 "Adding link should implicitly add the template"
576 );
577
578 let env2: HashMap<SlotId, EntityUID> = [(
579 SlotId::principal(),
580 r#"Test::"test2""#.parse().expect("Failed to parse"),
581 )]
582 .into_iter()
583 .collect();
584
585 let p2 = Template::link(
586 Arc::clone(&template),
587 PolicyID::from_string("link"),
588 env2.clone(),
589 )
590 .expect("Failed to link");
591 match pset.add(p2) {
592 Ok(_) => panic!("Should have failed due to conflict with existing link id"),
593 Err(PolicySetError::Occupied { id }) => assert_eq!(id, PolicyID::from_string("link")),
594 }
595
596 let p3 = Template::link(Arc::clone(&template), PolicyID::from_string("link2"), env2)
597 .expect("Failed to link");
598 pset.add(p3).expect(
599 "Adding link should succeed, even though the template already existed in the pset",
600 );
601
602 let template2 = Arc::new(
603 parser::parse_policy_or_template(
604 Some("t".into()),
605 "forbid(principal, action, resource == ?resource);",
606 )
607 .expect("Failed to parse"),
608 );
609 let env3: HashMap<SlotId, EntityUID> = [(
610 SlotId::resource(),
611 r#"Test::"test3""#.parse().expect("Failed to parse"),
612 )]
613 .into_iter()
614 .collect();
615
616 let p4 = Template::link(
617 Arc::clone(&template2),
618 PolicyID::from_string("unique3"),
619 env3,
620 )
621 .expect("Failed to link");
622 match pset.add(p4) {
623 Ok(_) => panic!("Should have failed due to conflict on template id"),
624 Err(PolicySetError::Occupied { id }) => {
625 assert_eq!(id, PolicyID::from_string("t"))
626 }
627 }
628 }
629
630 #[test]
631 fn policy_conflicts() {
632 let mut pset = PolicySet::new();
633 let p1 = parser::parse_policy(Some("id".into()), "permit(principal,action,resource);")
634 .expect("Failed to parse");
635 let p2 = parser::parse_policy(
636 Some("id".into()),
637 "permit(principal,action,resource) when { false };",
638 )
639 .expect("Failed to parse");
640 pset.add_static(p1).expect("Failed to add!");
641 match pset.add_static(p2) {
642 Ok(_) => panic!("Should have failed to due name conflict"),
643 Err(PolicySetError::Occupied { id }) => assert_eq!(id, PolicyID::from_string("id")),
644 }
645 }
646
647 #[test]
648 fn template_filtering() {
649 let template = parser::parse_policy_or_template(
650 Some("template".into()),
651 "permit(principal == ?principal, action, resource);",
652 )
653 .expect("Template Parse Failure");
654 let static_policy = parser::parse_policy(
655 Some("static".into()),
656 "permit(principal, action, resource);",
657 )
658 .expect("Static parse failure");
659 let mut set = PolicySet::new();
660 set.add_template(template).unwrap();
661 set.add_static(static_policy).unwrap();
662
663 assert_eq!(set.all_templates().count(), 2);
664 assert_eq!(set.templates().count(), 1);
665 assert_eq!(set.static_policies().count(), 1);
666 assert_eq!(set.policies().count(), 1);
667 set.link(
668 PolicyID::from_string("template"),
669 PolicyID::from_string("id"),
670 [(SlotId::principal(), EntityUID::with_eid("eid"))]
671 .into_iter()
672 .collect(),
673 )
674 .expect("Linking failed!");
675 assert_eq!(set.static_policies().count(), 1);
676 assert_eq!(set.policies().count(), 2);
677 }
678
679 #[test]
680 fn linking_missing_template() {
681 let tid = PolicyID::from_string("template");
682 let lid = PolicyID::from_string("link");
683 let t = Template::new(
684 tid.clone(),
685 None,
686 Annotations::new(),
687 Effect::Permit,
688 PrincipalConstraint::any(),
689 ActionConstraint::any(),
690 ResourceConstraint::any(),
691 Expr::val(true),
692 );
693
694 let mut s = PolicySet::new();
695 let e = s
696 .link(tid.clone(), lid.clone(), HashMap::new())
697 .expect_err("Should fail");
698
699 match e {
700 LinkingError::NoSuchTemplate { id } => assert_eq!(tid, id),
701 e => panic!("Wrong error {e}"),
702 };
703
704 s.add_template(t).unwrap();
705 s.link(tid, lid, HashMap::new()).expect("Should succeed");
706 }
707
708 #[test]
709 fn linkinv_valid_link() {
710 let tid = PolicyID::from_string("template");
711 let lid = PolicyID::from_string("link");
712 let t = Template::new(
713 tid.clone(),
714 None,
715 Annotations::new(),
716 Effect::Permit,
717 PrincipalConstraint::is_eq_slot(),
718 ActionConstraint::any(),
719 ResourceConstraint::is_in_slot(),
720 Expr::val(true),
721 );
722
723 let mut s = PolicySet::new();
724 s.add_template(t).unwrap();
725
726 let mut vals = HashMap::new();
727 vals.insert(SlotId::principal(), EntityUID::with_eid("p"));
728 vals.insert(SlotId::resource(), EntityUID::with_eid("a"));
729
730 s.link(tid.clone(), lid.clone(), vals).expect("Should link");
731
732 let v: Vec<_> = s.policies().collect();
733
734 assert_eq!(v[0].id(), &lid);
735 assert_eq!(v[0].template().id(), &tid);
736 }
737
738 #[test]
739 fn linking_empty_set() {
740 let s = PolicySet::new();
741 assert_eq!(s.policies().count(), 0);
742 }
743
744 #[test]
745 fn linking_raw_policy() {
746 let mut s = PolicySet::new();
747 let id = PolicyID::from_string("id");
748 let p = StaticPolicy::new(
749 id.clone(),
750 None,
751 Annotations::new(),
752 Effect::Forbid,
753 PrincipalConstraint::any(),
754 ActionConstraint::any(),
755 ResourceConstraint::any(),
756 Expr::val(true),
757 )
758 .expect("Policy Creation Failed");
759 s.add_static(p).unwrap();
760
761 let mut iter = s.policies();
762 match iter.next() {
763 Some(pol) => {
764 assert_eq!(pol.id(), &id);
765 assert_eq!(pol.effect(), Effect::Forbid);
766 assert!(pol.env().is_empty())
767 }
768 None => panic!("Linked Record Not Present"),
769 };
770 }
771
772 #[test]
773 fn link_slotmap() {
774 let mut s = PolicySet::new();
775 let template_id = PolicyID::from_string("template");
776 let link_id = PolicyID::from_string("link");
777 let t = Template::new(
778 template_id.clone(),
779 None,
780 Annotations::new(),
781 Effect::Forbid,
782 PrincipalConstraint::is_eq_slot(),
783 ActionConstraint::any(),
784 ResourceConstraint::any(),
785 Expr::val(true),
786 );
787 s.add_template(t).unwrap();
788
789 let mut v = HashMap::new();
790 let entity = EntityUID::with_eid("eid");
791 v.insert(SlotId::principal(), entity.clone());
792 s.link(template_id.clone(), link_id.clone(), v)
793 .expect("Linking failed!");
794
795 let link = s.get(&link_id).expect("Link should exist");
796 assert_eq!(&link_id, link.id());
797 assert_eq!(&template_id, link.template().id());
798 assert_eq!(
799 &entity,
800 link.env()
801 .get(&SlotId::principal())
802 .expect("Mapping was incorrect")
803 );
804 }
805
806 #[test]
807 fn policy_sets() {
808 let mut pset = PolicySet::new();
809 assert!(pset.is_empty());
810 let id1 = PolicyID::from_string("id1");
811 let tid1 = PolicyID::from_string("template");
812 let policy1 = StaticPolicy::new(
813 id1.clone(),
814 None,
815 Annotations::new(),
816 Effect::Permit,
817 PrincipalConstraint::any(),
818 ActionConstraint::any(),
819 ResourceConstraint::any(),
820 Expr::val(true),
821 )
822 .expect("Policy Creation Failed");
823 let template1 = Template::new(
824 tid1.clone(),
825 None,
826 Annotations::new(),
827 Effect::Permit,
828 PrincipalConstraint::any(),
829 ActionConstraint::any(),
830 ResourceConstraint::any(),
831 Expr::val(true),
832 );
833 let added = pset.add_static(policy1.clone()).is_ok();
834 assert!(added);
835 let added = pset.add_static(policy1).is_ok();
836 assert!(!added);
837 let added = pset.add_template(template1.clone()).is_ok();
838 assert!(added);
839 let added = pset.add_template(template1).is_ok();
840 assert!(!added);
841 assert!(!pset.is_empty());
842 let id2 = PolicyID::from_string("id2");
843 let policy2 = StaticPolicy::new(
844 id2.clone(),
845 None,
846 Annotations::new(),
847 Effect::Forbid,
848 PrincipalConstraint::is_eq(EntityUID::with_eid("jane")),
849 ActionConstraint::any(),
850 ResourceConstraint::any(),
851 Expr::val(true),
852 )
853 .expect("Policy Creation Failed");
854 let added = pset.add_static(policy2).is_ok();
855 assert!(added);
856
857 let tid2 = PolicyID::from_string("template2");
858 let template2 = Template::new(
859 tid2.clone(),
860 None,
861 Annotations::new(),
862 Effect::Permit,
863 PrincipalConstraint::is_eq_slot(),
864 ActionConstraint::any(),
865 ResourceConstraint::any(),
866 Expr::val(true),
867 );
868 let id3 = PolicyID::from_string("link");
869 let added = pset.add_template(template2).is_ok();
870 assert!(added);
871
872 let r = pset.link(
873 tid2.clone(),
874 id3.clone(),
875 HashMap::from([(SlotId::principal(), EntityUID::with_eid("example"))]),
876 );
877 r.expect("Linking failed");
878
879 assert_eq!(pset.get(&id1).expect("should find the policy").id(), &id1);
880 assert_eq!(pset.get(&id2).expect("should find the policy").id(), &id2);
881 assert_eq!(pset.get(&id3).expect("should find link").id(), &id3);
882 assert_eq!(
883 pset.get(&id3).expect("should find link").template().id(),
884 &tid2
885 );
886 assert!(pset.get(&tid2).is_none());
887 assert!(pset.get_template(&id1).is_some()); assert!(pset.get_template(&id2).is_some()); assert!(pset.get_template(&tid2).is_some());
890 assert_eq!(pset.policies().count(), 3);
891
892 assert_eq!(
893 pset.get_template(&tid1)
894 .expect("should find the template")
895 .id(),
896 &tid1
897 );
898 assert!(pset.get(&tid1).is_none());
899 assert_eq!(pset.all_templates().count(), 4);
900 }
901}