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