1use super::{
18 EntityUID, LinkingError, LiteralPolicy, Policy, PolicyID, ReificationError, SlotId,
19 StaticPolicy, Template,
20};
21use itertools::Itertools;
22use linked_hash_map::{Entry, LinkedHashMap};
23use linked_hash_set::LinkedHashSet;
24
25use miette::Diagnostic;
26use smol_str::format_smolstr;
27use std::{borrow::Borrow, collections::HashMap, sync::Arc};
28use thiserror::Error;
29
30#[derive(Debug, Default, Clone, PartialEq, Eq)]
32pub struct PolicySet {
33 templates: LinkedHashMap<PolicyID, Arc<Template>>,
39 links: LinkedHashMap<PolicyID, Policy>,
45
46 template_to_links_map: LinkedHashMap<PolicyID, LinkedHashSet<PolicyID>>,
50}
51
52#[derive(Debug)]
58pub struct LiteralPolicySet {
59 templates: LinkedHashMap<PolicyID, Template>,
61 links: LinkedHashMap<PolicyID, LiteralPolicy>,
64}
65
66impl LiteralPolicySet {
67 pub fn new(
70 templates: impl IntoIterator<Item = (PolicyID, Template)>,
71 links: impl IntoIterator<Item = (PolicyID, LiteralPolicy)>,
72 ) -> Self {
73 Self {
74 templates: templates.into_iter().collect(),
75 links: links.into_iter().collect(),
76 }
77 }
78
79 pub fn templates(&self) -> impl Iterator<Item = &Template> {
83 self.templates.values()
84 }
85
86 pub fn policies(&self) -> impl Iterator<Item = &LiteralPolicy> {
89 self.links.values()
90 }
91}
92
93impl TryFrom<LiteralPolicySet> for PolicySet {
96 type Error = ReificationError;
97 fn try_from(pset: LiteralPolicySet) -> Result<Self, Self::Error> {
98 let templates = pset
100 .templates
101 .into_iter()
102 .map(|(id, template)| (id, Arc::new(template)))
103 .collect::<LinkedHashMap<PolicyID, Arc<Template>>>();
104 let links = pset
105 .links
106 .into_iter()
107 .map(|(id, literal)| literal.reify(&templates).map(|linked| (id, linked)))
108 .collect::<Result<LinkedHashMap<PolicyID, Policy>, ReificationError>>()?;
109
110 let mut template_to_links_map = LinkedHashMap::new();
111 for template in &templates {
112 template_to_links_map.insert(template.0.clone(), LinkedHashSet::new());
113 }
114 for (link_id, link) in &links {
115 let template = link.template().id();
116 match template_to_links_map.entry(template.clone()) {
117 Entry::Occupied(t) => t.into_mut().insert(link_id.clone()),
118 Entry::Vacant(_) => return Err(ReificationError::NoSuchTemplate(template.clone())),
119 };
120 }
121
122 Ok(Self {
123 templates,
124 links,
125 template_to_links_map,
126 })
127 }
128}
129
130impl From<PolicySet> for LiteralPolicySet {
131 fn from(pset: PolicySet) -> Self {
132 let templates = pset
133 .templates
134 .into_iter()
135 .map(|(id, template)| (id, template.as_ref().clone()))
136 .collect();
137 let links = pset
138 .links
139 .into_iter()
140 .map(|(id, p)| (id, p.into()))
141 .collect();
142 Self { templates, links }
143 }
144}
145
146#[derive(Debug, Diagnostic, Error)]
148pub enum PolicySetError {
149 #[error("duplicate template or policy id `{id}`")]
152 Occupied {
153 id: PolicyID,
155 },
156}
157
158#[derive(Debug, Diagnostic, Error)]
160pub enum PolicySetGetLinksError {
161 #[error("No template `{0}`")]
163 MissingTemplate(PolicyID),
164}
165
166#[derive(Debug, Diagnostic, Error)]
168pub enum PolicySetUnlinkError {
169 #[error("unable to unlink policy id `{0}` because it does not exist")]
171 UnlinkingError(PolicyID),
172 #[error("unable to remove link with policy id `{0}` because it is a static policy")]
174 NotLinkError(PolicyID),
175}
176
177#[derive(Debug, Diagnostic, Error)]
179pub enum PolicySetTemplateRemovalError {
180 #[error("unable to remove template id `{0}` from template list because it does not exist")]
182 RemovePolicyNoTemplateError(PolicyID),
183 #[error(
185 "unable to remove template id `{0}` from template list because it still has active links"
186 )]
187 RemoveTemplateWithLinksError(PolicyID),
188 #[error("unable to remove template with policy id `{0}` because it is a static policy")]
190 NotTemplateError(PolicyID),
191}
192
193#[derive(Debug, Diagnostic, Error)]
195pub enum PolicySetPolicyRemovalError {
196 #[error("unable to remove static policy id `{0}` from link list because it does not exist")]
198 RemovePolicyNoLinkError(PolicyID),
199 #[error(
201 "unable to remove static policy id `{0}` from template list because it does not exist"
202 )]
203 RemovePolicyNoTemplateError(PolicyID),
204}
205
206impl PolicySet {
209 pub fn new() -> Self {
211 Self {
212 templates: LinkedHashMap::new(),
213 links: LinkedHashMap::new(),
214 template_to_links_map: LinkedHashMap::new(),
215 }
216 }
217
218 pub fn singleton(p: Policy) -> Self {
220 let t = p.template_arc();
221
222 let templates = std::iter::once((t.id().clone(), t.clone())).collect();
223 let template_to_links_map =
224 std::iter::once((t.id().clone(), std::iter::once(p.id().clone()).collect())).collect();
225 let links = std::iter::once((p.id().clone(), p)).collect();
226
227 Self {
228 templates,
229 links,
230 template_to_links_map,
231 }
232 }
233
234 pub fn add(&mut self, policy: Policy) -> Result<(), PolicySetError> {
236 let t = policy.template_arc();
237
238 let template_ventry = match self.templates.entry(t.id().clone()) {
243 Entry::Vacant(ventry) => Some(ventry),
244 Entry::Occupied(oentry) => {
245 if oentry.get() != &t {
246 return Err(PolicySetError::Occupied {
247 id: oentry.key().clone(),
248 });
249 }
250 None
251 }
252 };
253
254 let link_ventry = match self.links.entry(policy.id().clone()) {
255 Entry::Vacant(ventry) => Some(ventry),
256 Entry::Occupied(oentry) => {
257 return Err(PolicySetError::Occupied {
258 id: oentry.key().clone(),
259 });
260 }
261 };
262
263 if let Some(ventry) = template_ventry {
266 self.template_to_links_map.insert(
267 t.id().clone(),
268 std::iter::once(policy.id().clone()).collect(),
269 );
270 ventry.insert(t);
271 } else {
272 self.template_to_links_map
274 .entry(t.id().clone())
275 .or_default()
276 .insert(policy.id().clone());
277 }
278 if let Some(ventry) = link_ventry {
279 ventry.insert(policy);
280 }
281
282 Ok(())
283 }
284
285 fn policy_id_is_bound(&self, pid: &PolicyID) -> bool {
288 self.templates.contains_key(pid) || self.links.contains_key(pid)
289 }
290
291 fn get_fresh_id(&self, other: &Self, start_ind: &mut u32) -> PolicyID {
293 let mut new_pid = PolicyID::from_smolstr(format_smolstr!("policy{}", start_ind));
294 *start_ind += 1;
295 while self.policy_id_is_bound(&new_pid) || other.policy_id_is_bound(&new_pid) {
296 new_pid = PolicyID::from_smolstr(format_smolstr!("policy{}", start_ind));
297 *start_ind += 1;
298 }
299 new_pid
300 }
301
302 fn update_renaming<T>(
306 &self,
307 this_contents: &LinkedHashMap<PolicyID, T>,
308 other: &Self,
309 other_contents: &LinkedHashMap<PolicyID, T>,
310 renaming: &mut LinkedHashMap<PolicyID, PolicyID>,
311 start_ind: &mut u32,
312 ) where
313 T: PartialEq + Clone,
314 {
315 for (pid, ot) in other_contents {
316 if let Some(tt) = this_contents.get(pid) {
317 if tt != ot && !renaming.contains_key(pid) {
318 let new_pid = self.get_fresh_id(other, start_ind);
319 renaming.insert(pid.clone(), new_pid);
320 }
321 }
322 }
323 }
324
325 pub fn merge_policyset(
340 &mut self,
341 other: &PolicySet,
342 rename_duplicates: bool,
343 ) -> Result<LinkedHashMap<PolicyID, PolicyID>, PolicySetError> {
344 let mut min_id = 0;
347 let mut renaming = LinkedHashMap::new();
348 self.update_renaming(
349 &self.templates,
350 other,
351 &other.templates,
352 &mut renaming,
353 &mut min_id,
354 );
355 self.update_renaming(&self.links, other, &other.links, &mut renaming, &mut min_id);
356 for (pid, template) in &other.templates {
359 if !template.is_static() && self.get(pid).is_some() && !renaming.contains_key(pid) {
360 let new_pid = self.get_fresh_id(other, &mut min_id);
361 renaming.insert(pid.clone(), new_pid);
362 }
363 }
364 for (pid, link) in &other.links {
366 if !link.is_static() && self.get_template(pid).is_some() && !renaming.contains_key(pid)
367 {
368 let new_pid = self.get_fresh_id(other, &mut min_id);
369 renaming.insert(pid.clone(), new_pid);
370 }
371 }
372
373 if !rename_duplicates {
375 if let Some(pid) = renaming.keys().next() {
376 return Err(PolicySetError::Occupied { id: pid.clone() });
377 }
378 }
379 for (pid, other_template) in &other.templates {
382 if let Some(new_pid) = renaming.get(pid) {
383 self.templates.insert(
384 new_pid.clone(),
385 Arc::new(other_template.new_id(new_pid.clone())),
386 );
387 } else {
388 self.templates.insert(pid.clone(), other_template.clone());
389 }
390 }
391 for (pid, other_policy) in &other.links {
392 let (new_pid, other_policy) = if let Some(new_pid) = renaming.get(pid) {
394 (new_pid.clone(), other_policy.new_id(new_pid.clone()))
395 } else {
396 (pid.clone(), other_policy.clone())
397 };
398 let other_policy = match renaming.get(other_policy.template().id()) {
402 #[expect(
403 clippy::unwrap_used,
404 reason = "`if` confirms that `other_policy` is a template link"
405 )]
406 Some(new_tid) if !other_policy.is_static() => {
407 other_policy.new_template_id(new_tid.clone()).unwrap()
408 }
409 _ => other_policy,
410 };
411 self.links.insert(new_pid, other_policy);
412 }
413 for (tid, other_template_link_set) in &other.template_to_links_map {
414 let tid = renaming.get(tid).unwrap_or(tid);
415 let mut this_template_link_set =
416 self.template_to_links_map.remove(tid).unwrap_or_default();
417 for pid in other_template_link_set {
418 let pid = renaming.get(pid).unwrap_or(pid);
419 this_template_link_set.insert(pid.clone());
420 }
421 self.template_to_links_map
422 .insert(tid.clone(), this_template_link_set);
423 }
424 Ok(renaming)
425 }
426
427 pub fn remove_static(
429 &mut self,
430 policy_id: &PolicyID,
431 ) -> Result<Policy, PolicySetPolicyRemovalError> {
432 let policy = match self.links.remove(policy_id) {
435 Some(p) => p,
436 None => {
437 return Err(PolicySetPolicyRemovalError::RemovePolicyNoLinkError(
438 policy_id.clone(),
439 ))
440 }
441 };
442 match self.templates.remove(policy_id) {
444 Some(_) => {
445 self.template_to_links_map.remove(policy_id);
446 Ok(policy)
447 }
448 None => {
449 self.links.insert(policy_id.clone(), policy);
452 Err(PolicySetPolicyRemovalError::RemovePolicyNoTemplateError(
453 policy_id.clone(),
454 ))
455 }
456 }
457 }
458
459 pub fn add_static(&mut self, policy: StaticPolicy) -> Result<(), PolicySetError> {
461 let (t, p) = Template::link_static_policy(policy);
462
463 match (
464 self.templates.entry(t.id().clone()),
465 self.links.entry(t.id().clone()),
466 ) {
467 (Entry::Vacant(templates_entry), Entry::Vacant(links_entry)) => {
468 self.template_to_links_map.insert(
469 t.id().clone(),
470 vec![p.id().clone()]
471 .into_iter()
472 .collect::<LinkedHashSet<PolicyID>>(),
473 );
474 templates_entry.insert(t);
475 links_entry.insert(p);
476 Ok(())
477 }
478 (Entry::Occupied(oentry), _) => Err(PolicySetError::Occupied {
479 id: oentry.key().clone(),
480 }),
481 (_, Entry::Occupied(oentry)) => Err(PolicySetError::Occupied {
482 id: oentry.key().clone(),
483 }),
484 }
485 }
486
487 pub fn add_template(&mut self, t: Template) -> Result<(), PolicySetError> {
490 if self.links.contains_key(t.id()) {
491 return Err(PolicySetError::Occupied { id: t.id().clone() });
492 }
493
494 match self.templates.entry(t.id().clone()) {
495 Entry::Occupied(oentry) => Err(PolicySetError::Occupied {
496 id: oentry.key().clone(),
497 }),
498 Entry::Vacant(ventry) => {
499 self.template_to_links_map
500 .insert(t.id().clone(), LinkedHashSet::new());
501 ventry.insert(Arc::new(t));
502 Ok(())
503 }
504 }
505 }
506
507 pub fn remove_template(
511 &mut self,
512 policy_id: &PolicyID,
513 ) -> Result<Template, PolicySetTemplateRemovalError> {
514 if self.links.contains_key(policy_id) {
516 return Err(PolicySetTemplateRemovalError::NotTemplateError(
517 policy_id.clone(),
518 ));
519 }
520
521 match self.template_to_links_map.get(policy_id) {
522 Some(map) => {
523 if !map.is_empty() {
524 return Err(PolicySetTemplateRemovalError::RemoveTemplateWithLinksError(
525 policy_id.clone(),
526 ));
527 }
528 }
529 None => {
530 return Err(PolicySetTemplateRemovalError::RemovePolicyNoTemplateError(
531 policy_id.clone(),
532 ))
533 }
534 };
535
536 #[expect(clippy::panic, reason = "every linked policy should have a template")]
537 match self.templates.remove(policy_id) {
538 Some(t) => {
539 self.template_to_links_map.remove(policy_id);
540 Ok(Arc::unwrap_or_clone(t))
541 }
542 None => panic!("Found in template_to_links_map but not in templates"),
543 }
544 }
545
546 pub fn get_linked_policies(
549 &self,
550 template_id: &PolicyID,
551 ) -> Result<impl Iterator<Item = &PolicyID>, PolicySetGetLinksError> {
552 match self.template_to_links_map.get(template_id) {
553 Some(s) => Ok(s.iter()),
554 None => Err(PolicySetGetLinksError::MissingTemplate(template_id.clone())),
555 }
556 }
557
558 pub fn link(
566 &mut self,
567 template_id: PolicyID,
568 new_id: PolicyID,
569 values: HashMap<SlotId, EntityUID>,
570 ) -> Result<&Policy, LinkingError> {
571 let t =
572 self.get_template_arc(&template_id)
573 .ok_or_else(|| LinkingError::NoSuchTemplate {
574 id: template_id.clone(),
575 })?;
576 let r = Template::link(t, new_id.clone(), values)?;
577
578 match (
580 self.links.entry(new_id.clone()),
581 self.templates.entry(new_id.clone()),
582 ) {
583 (Entry::Vacant(links_entry), Entry::Vacant(_)) => {
584 self.template_to_links_map
586 .entry(template_id)
587 .or_default()
588 .insert(new_id);
589 Ok(links_entry.insert(r))
590 }
591 (Entry::Occupied(oentry), _) => Err(LinkingError::PolicyIdConflict {
592 id: oentry.key().clone(),
593 }),
594 (_, Entry::Occupied(oentry)) => Err(LinkingError::PolicyIdConflict {
595 id: oentry.key().clone(),
596 }),
597 }
598 }
599
600 pub fn unlink(&mut self, policy_id: &PolicyID) -> Result<Policy, PolicySetUnlinkError> {
603 if self.templates.contains_key(policy_id) {
605 return Err(PolicySetUnlinkError::NotLinkError(policy_id.clone()));
606 }
607 match self.links.remove(policy_id) {
608 Some(p) => {
609 #[expect(clippy::panic, reason = "every linked policy should have a template")]
610 match self.template_to_links_map.entry(p.template().id().clone()) {
611 Entry::Occupied(t) => t.into_mut().remove(policy_id),
612 Entry::Vacant(_) => {
613 panic!("No template found for linked policy")
614 }
615 };
616 Ok(p)
617 }
618 None => Err(PolicySetUnlinkError::UnlinkingError(policy_id.clone())),
619 }
620 }
621
622 pub fn policies(&self) -> impl Iterator<Item = &Policy> {
624 self.links.values()
625 }
626
627 pub fn into_policies(self) -> impl Iterator<Item = Policy> {
629 self.links.into_iter().map(|(_, p)| p)
630 }
631
632 pub fn all_templates(&self) -> impl Iterator<Item = &Template> {
635 self.templates.values().map(|t| t.borrow())
636 }
637
638 pub fn templates(&self) -> impl Iterator<Item = &Template> {
640 self.all_templates().filter(|t| t.slots().count() != 0)
641 }
642
643 pub fn static_policies(&self) -> impl Iterator<Item = &Policy> {
645 self.policies().filter(|p| p.is_static())
646 }
647
648 pub fn is_empty(&self) -> bool {
650 self.templates.is_empty() && self.links.is_empty()
651 }
652
653 pub fn get_template_arc(&self, id: &PolicyID) -> Option<Arc<Template>> {
655 self.templates.get(id).cloned()
656 }
657
658 pub fn get_template(&self, id: &PolicyID) -> Option<&Template> {
660 self.templates.get(id).map(AsRef::as_ref)
661 }
662
663 pub fn get(&self, id: &PolicyID) -> Option<&Policy> {
665 self.links.get(id)
666 }
667
668 pub fn try_from_iter<T: IntoIterator<Item = Policy>>(iter: T) -> Result<Self, PolicySetError> {
670 let mut set = Self::new();
671 for p in iter {
672 set.add(p)?;
673 }
674 Ok(set)
675 }
676}
677
678impl std::fmt::Display for PolicySet {
679 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
680 if self.is_empty() {
682 write!(f, "<empty policyset>")
683 } else {
684 write!(
685 f,
686 "Templates:\n{}, Template Linked Policies:\n{}",
687 self.all_templates().join("\n"),
688 self.policies().join("\n")
689 )
690 }
691 }
692}
693
694#[expect(clippy::panic, clippy::indexing_slicing, reason = "tests")]
695#[cfg(test)]
696mod test {
697 use super::*;
698 use crate::{
699 ast::{
700 annotation::Annotations, ActionConstraint, Effect, PrincipalConstraint,
701 ResourceConstraint,
702 },
703 parser,
704 };
705
706 use similar_asserts::assert_eq;
707 use std::collections::HashMap;
708
709 #[test]
710 fn link_conflicts() {
711 let mut pset = PolicySet::new();
712 let p1 = parser::parse_policy(
713 Some(PolicyID::from_string("id")),
714 "permit(principal,action,resource);",
715 )
716 .expect("Failed to parse");
717 pset.add_static(p1).expect("Failed to add!");
718 let template = parser::parse_policy_or_template(
719 Some(PolicyID::from_string("t")),
720 "permit(principal == ?principal, action, resource);",
721 )
722 .expect("Failed to parse");
723 pset.add_template(template).expect("Add failed");
724
725 let env: HashMap<SlotId, EntityUID> = HashMap::from([(
726 SlotId::principal(),
727 r#"Test::"test""#.parse().expect("Failed to parse"),
728 )]);
729
730 let r = pset.link(PolicyID::from_string("t"), PolicyID::from_string("id"), env);
731
732 match r {
733 Ok(_) => panic!("Should have failed due to conflict"),
734 Err(LinkingError::PolicyIdConflict { id }) => {
735 assert_eq!(id, PolicyID::from_string("id"))
736 }
737 Err(e) => panic!("Incorrect error: {e}"),
738 };
739 }
740
741 #[test]
744 fn policyset_add() {
745 let mut pset = PolicySet::new();
746 let static_policy = parser::parse_policy(
747 Some(PolicyID::from_string("id")),
748 "permit(principal,action,resource);",
749 )
750 .expect("Failed to parse");
751 let static_policy: Policy = static_policy.into();
752 pset.add(static_policy)
753 .expect("Adding static policy in Policy form should succeed");
754
755 let template = Arc::new(
756 parser::parse_policy_or_template(
757 Some(PolicyID::from_string("t")),
758 "permit(principal == ?principal, action, resource);",
759 )
760 .expect("Failed to parse"),
761 );
762 let env1: HashMap<SlotId, EntityUID> = HashMap::from([(
763 SlotId::principal(),
764 r#"Test::"test1""#.parse().expect("Failed to parse"),
765 )]);
766
767 let p1 = Template::link(Arc::clone(&template), PolicyID::from_string("link"), env1)
768 .expect("Failed to link");
769 pset.add(p1).expect(
770 "Adding link should succeed, even though the template wasn't previously in the pset",
771 );
772 assert!(
773 pset.get_template_arc(&PolicyID::from_string("t")).is_some(),
774 "Adding link should implicitly add the template"
775 );
776
777 let env2: HashMap<SlotId, EntityUID> = HashMap::from([(
778 SlotId::principal(),
779 r#"Test::"test2""#.parse().expect("Failed to parse"),
780 )]);
781
782 let p2 = Template::link(
783 Arc::clone(&template),
784 PolicyID::from_string("link"),
785 env2.clone(),
786 )
787 .expect("Failed to link");
788 match pset.add(p2) {
789 Ok(_) => panic!("Should have failed due to conflict with existing link id"),
790 Err(PolicySetError::Occupied { id }) => assert_eq!(id, PolicyID::from_string("link")),
791 }
792
793 let p3 = Template::link(Arc::clone(&template), PolicyID::from_string("link2"), env2)
794 .expect("Failed to link");
795 pset.add(p3).expect(
796 "Adding link should succeed, even though the template already existed in the pset",
797 );
798
799 let template2 = Arc::new(
800 parser::parse_policy_or_template(
801 Some(PolicyID::from_string("t")),
802 "forbid(principal, action, resource == ?resource);",
803 )
804 .expect("Failed to parse"),
805 );
806 let env3: HashMap<SlotId, EntityUID> = HashMap::from([(
807 SlotId::resource(),
808 r#"Test::"test3""#.parse().expect("Failed to parse"),
809 )]);
810
811 let p4 = Template::link(
812 Arc::clone(&template2),
813 PolicyID::from_string("unique3"),
814 env3,
815 )
816 .expect("Failed to link");
817 match pset.add(p4) {
818 Ok(_) => panic!("Should have failed due to conflict on template id"),
819 Err(PolicySetError::Occupied { id }) => {
820 assert_eq!(id, PolicyID::from_string("t"))
821 }
822 }
823 }
824
825 #[test]
826 fn policy_set_singleton_static() {
827 let policy: Policy = parser::parse_policy(
828 Some(PolicyID::from_string("id")),
829 "permit(principal,action,resource);",
830 )
831 .unwrap()
832 .into();
833
834 let pset_singleton = PolicySet::singleton(policy.clone());
835 let mut pset_add = PolicySet::new();
836 pset_add.add(policy).unwrap();
837 assert_eq!(pset_singleton, pset_add);
838 }
839
840 #[test]
841 fn policy_set_singleton_link() {
842 let template = Arc::new(
843 parser::parse_policy_or_template(
844 Some(PolicyID::from_string("t")),
845 "permit(principal == ?principal, action, resource);",
846 )
847 .expect("Failed to parse"),
848 );
849 let env1 = HashMap::from([(
850 SlotId::principal(),
851 r#"Test::"test1""#.parse().expect("Failed to parse"),
852 )]);
853 let policy =
854 Template::link(template, PolicyID::from_string("link"), env1).expect("Failed to link");
855
856 let pset_singleton = PolicySet::singleton(policy.clone());
857 let mut pset_add = PolicySet::new();
858 pset_add.add(policy).unwrap();
859 assert_eq!(pset_singleton, pset_add);
860 }
861
862 #[test]
863 fn policy_merge_no_conflicts() {
864 let p1 = parser::parse_policy(
865 Some(PolicyID::from_string("policy0")),
866 "permit(principal,action,resource);",
867 )
868 .expect("Failed to parse");
869 let p2 = parser::parse_policy(
870 Some(PolicyID::from_string("policy1")),
871 "permit(principal,action,resource) when { false };",
872 )
873 .expect("Failed to parse");
874 let p3 = parser::parse_policy(
875 Some(PolicyID::from_string("policy0")),
876 "permit(principal,action,resource);",
877 )
878 .expect("Failed to parse");
879 let p4 = parser::parse_policy(
880 Some(PolicyID::from_string("policy2")),
881 "permit(principal,action,resource) when { true };",
882 )
883 .expect("Failed to parse");
884 let mut pset1 = PolicySet::new();
885 let mut pset2 = PolicySet::new();
886 pset1.add_static(p1).expect("Failed to add!");
887 pset1.add_static(p2).expect("Failed to add!");
888 pset2.add_static(p3).expect("Failed to add!");
889 pset2.add_static(p4).expect("Failed to add!");
890 match pset1.merge_policyset(&pset2, false) {
892 Ok(_) => (),
893 Err(PolicySetError::Occupied { id }) => {
894 panic!("There should not have been an error! Unexpected conflict for id {id}")
895 }
896 }
897 }
898
899 #[test]
900 fn policy_merge_with_conflicts() {
901 let pid0 = PolicyID::from_string("policy0");
902 let pid1 = PolicyID::from_string("policy1");
903 let pid2 = PolicyID::from_string("policy2");
904 let p1 = parser::parse_policy(Some(pid0.clone()), "permit(principal,action,resource);")
905 .expect("Failed to parse");
906 let p2 = parser::parse_policy(
907 Some(pid1.clone()),
908 "permit(principal,action,resource) when { false };",
909 )
910 .expect("Failed to parse");
911 let p3 = parser::parse_policy(Some(pid1.clone()), "permit(principal,action,resource);")
912 .expect("Failed to parse");
913 let p4 = parser::parse_policy(
914 Some(pid2.clone()),
915 "permit(principal,action,resource) when { true };",
916 )
917 .expect("Failed to parse");
918 let mut pset1 = PolicySet::new();
919 let mut pset2 = PolicySet::new();
920 pset1.add_static(p1.clone()).expect("Failed to add!");
921 pset1.add_static(p2.clone()).expect("Failed to add!");
922 pset2.add_static(p3.clone()).expect("Failed to add!");
923 pset2.add_static(p4.clone()).expect("Failed to add!");
924 match pset1.merge_policyset(&pset2, false) {
926 Ok(_) => panic!("`pset1` and `pset2` should conflict for PolicyID `policy1`"),
927 Err(PolicySetError::Occupied { id }) => {
928 assert_eq!(id, PolicyID::from_string("policy1"));
929 }
930 }
931 match pset1.merge_policyset(&pset2, true) {
933 Ok(renaming) => {
934 let new_pid1 = match renaming.get(&pid1) {
936 Some(new_pid1) => new_pid1,
937 None => panic!("Error: `policy1` is a conflict and should be renamed"),
938 };
939 assert_eq!(renaming.keys().len(), 1);
941 if let Some(new_p1) = pset1.get(&pid0) {
942 assert_eq!(Policy::from(p1), new_p1.clone());
943 }
944 if let Some(new_p2) = pset1.get(&pid1) {
945 assert_eq!(Policy::from(p2), new_p2.clone());
946 }
947 if let Some(new_p3) = pset1.get(new_pid1) {
948 assert_eq!(Policy::from(p3).new_id(new_pid1.clone()), new_p3.clone());
949 }
950 if let Some(new_p4) = pset1.get(&pid2) {
951 assert_eq!(Policy::from(p4), new_p4.clone());
952 }
953 }
954 Err(PolicySetError::Occupied { id }) => {
955 panic!("There should not have been an error! Unexpected conflict for id {id}")
956 }
957 }
958 }
959
960 #[test]
961 fn policy_conflicts() {
962 let mut pset = PolicySet::new();
963 let p1 = parser::parse_policy(
964 Some(PolicyID::from_string("id")),
965 "permit(principal,action,resource);",
966 )
967 .expect("Failed to parse");
968 let p2 = parser::parse_policy(
969 Some(PolicyID::from_string("id")),
970 "permit(principal,action,resource) when { false };",
971 )
972 .expect("Failed to parse");
973 pset.add_static(p1).expect("Failed to add!");
974 match pset.add_static(p2) {
975 Ok(_) => panic!("Should have failed to due name conflict"),
976 Err(PolicySetError::Occupied { id }) => assert_eq!(id, PolicyID::from_string("id")),
977 }
978 }
979
980 #[test]
981 fn template_filtering() {
982 let template = parser::parse_policy_or_template(
983 Some(PolicyID::from_string("template")),
984 "permit(principal == ?principal, action, resource);",
985 )
986 .expect("Template Parse Failure");
987 let static_policy = parser::parse_policy(
988 Some(PolicyID::from_string("static")),
989 "permit(principal, action, resource);",
990 )
991 .expect("Static parse failure");
992 let mut set = PolicySet::new();
993 set.add_template(template).unwrap();
994 set.add_static(static_policy).unwrap();
995
996 assert_eq!(set.all_templates().count(), 2);
997 assert_eq!(set.templates().count(), 1);
998 assert_eq!(set.static_policies().count(), 1);
999 assert_eq!(set.policies().count(), 1);
1000 set.link(
1001 PolicyID::from_string("template"),
1002 PolicyID::from_string("id"),
1003 HashMap::from([(SlotId::principal(), EntityUID::with_eid("eid"))]),
1004 )
1005 .expect("Linking failed!");
1006 assert_eq!(set.static_policies().count(), 1);
1007 assert_eq!(set.policies().count(), 2);
1008 }
1009
1010 #[test]
1011 fn linking_missing_template() {
1012 let tid = PolicyID::from_string("template");
1013 let lid = PolicyID::from_string("link");
1014 let t = Template::new(
1015 tid.clone(),
1016 None,
1017 Annotations::new(),
1018 Effect::Permit,
1019 PrincipalConstraint::any(),
1020 ActionConstraint::any(),
1021 ResourceConstraint::any(),
1022 None,
1023 );
1024
1025 let mut s = PolicySet::new();
1026 let e = s
1027 .link(tid.clone(), lid.clone(), HashMap::new())
1028 .expect_err("Should fail");
1029
1030 match e {
1031 LinkingError::NoSuchTemplate { id } => assert_eq!(tid, id),
1032 e => panic!("Wrong error {e}"),
1033 };
1034
1035 s.add_template(t).unwrap();
1036 s.link(tid, lid, HashMap::new()).expect("Should succeed");
1037 }
1038
1039 #[test]
1040 fn linkinv_valid_link() {
1041 let tid = PolicyID::from_string("template");
1042 let lid = PolicyID::from_string("link");
1043 let t = Template::new(
1044 tid.clone(),
1045 None,
1046 Annotations::new(),
1047 Effect::Permit,
1048 PrincipalConstraint::is_eq_slot(),
1049 ActionConstraint::any(),
1050 ResourceConstraint::is_in_slot(),
1051 None,
1052 );
1053
1054 let mut s = PolicySet::new();
1055 s.add_template(t).unwrap();
1056
1057 let mut vals = HashMap::new();
1058 vals.insert(SlotId::principal(), EntityUID::with_eid("p"));
1059 vals.insert(SlotId::resource(), EntityUID::with_eid("a"));
1060
1061 s.link(tid.clone(), lid.clone(), vals).expect("Should link");
1062
1063 let v: Vec<_> = s.policies().collect();
1064
1065 assert_eq!(v[0].id(), &lid);
1066 assert_eq!(v[0].template().id(), &tid);
1067 }
1068
1069 #[test]
1070 fn linking_empty_set() {
1071 let s = PolicySet::new();
1072 assert_eq!(s.policies().count(), 0);
1073 }
1074
1075 #[test]
1076 fn linking_raw_policy() {
1077 let mut s = PolicySet::new();
1078 let id = PolicyID::from_string("id");
1079 let p = StaticPolicy::new(
1080 id.clone(),
1081 None,
1082 Annotations::new(),
1083 Effect::Forbid,
1084 PrincipalConstraint::any(),
1085 ActionConstraint::any(),
1086 ResourceConstraint::any(),
1087 None,
1088 )
1089 .expect("Policy Creation Failed");
1090 s.add_static(p).unwrap();
1091
1092 let mut iter = s.policies();
1093 match iter.next() {
1094 Some(pol) => {
1095 assert_eq!(pol.id(), &id);
1096 assert_eq!(pol.effect(), Effect::Forbid);
1097 assert!(pol.env().is_empty())
1098 }
1099 None => panic!("Linked Record Not Present"),
1100 };
1101 }
1102
1103 #[test]
1104 fn link_slotmap() {
1105 let mut s = PolicySet::new();
1106 let template_id = PolicyID::from_string("template");
1107 let link_id = PolicyID::from_string("link");
1108 let t = Template::new(
1109 template_id.clone(),
1110 None,
1111 Annotations::new(),
1112 Effect::Forbid,
1113 PrincipalConstraint::is_eq_slot(),
1114 ActionConstraint::any(),
1115 ResourceConstraint::any(),
1116 None,
1117 );
1118 s.add_template(t).unwrap();
1119
1120 let mut v = HashMap::new();
1121 let entity = EntityUID::with_eid("eid");
1122 v.insert(SlotId::principal(), entity.clone());
1123 s.link(template_id.clone(), link_id.clone(), v)
1124 .expect("Linking failed!");
1125
1126 let link = s.get(&link_id).expect("Link should exist");
1127 assert_eq!(&link_id, link.id());
1128 assert_eq!(&template_id, link.template().id());
1129 assert_eq!(
1130 &entity,
1131 link.env()
1132 .get(&SlotId::principal())
1133 .expect("Mapping was incorrect")
1134 );
1135 }
1136
1137 #[test]
1138 fn policy_sets() {
1139 let mut pset = PolicySet::new();
1140 assert!(pset.is_empty());
1141 let id1 = PolicyID::from_string("id1");
1142 let tid1 = PolicyID::from_string("template");
1143 let policy1 = StaticPolicy::new(
1144 id1.clone(),
1145 None,
1146 Annotations::new(),
1147 Effect::Permit,
1148 PrincipalConstraint::any(),
1149 ActionConstraint::any(),
1150 ResourceConstraint::any(),
1151 None,
1152 )
1153 .expect("Policy Creation Failed");
1154 let template1 = Template::new(
1155 tid1.clone(),
1156 None,
1157 Annotations::new(),
1158 Effect::Permit,
1159 PrincipalConstraint::any(),
1160 ActionConstraint::any(),
1161 ResourceConstraint::any(),
1162 None,
1163 );
1164 let added = pset.add_static(policy1.clone()).is_ok();
1165 assert!(added);
1166 let added = pset.add_static(policy1).is_ok();
1167 assert!(!added);
1168 let added = pset.add_template(template1.clone()).is_ok();
1169 assert!(added);
1170 let added = pset.add_template(template1).is_ok();
1171 assert!(!added);
1172 assert!(!pset.is_empty());
1173 let id2 = PolicyID::from_string("id2");
1174 let policy2 = StaticPolicy::new(
1175 id2.clone(),
1176 None,
1177 Annotations::new(),
1178 Effect::Forbid,
1179 PrincipalConstraint::is_eq(Arc::new(EntityUID::with_eid("jane"))),
1180 ActionConstraint::any(),
1181 ResourceConstraint::any(),
1182 None,
1183 )
1184 .expect("Policy Creation Failed");
1185 let added = pset.add_static(policy2).is_ok();
1186 assert!(added);
1187
1188 let tid2 = PolicyID::from_string("template2");
1189 let template2 = Template::new(
1190 tid2.clone(),
1191 None,
1192 Annotations::new(),
1193 Effect::Permit,
1194 PrincipalConstraint::is_eq_slot(),
1195 ActionConstraint::any(),
1196 ResourceConstraint::any(),
1197 None,
1198 );
1199 let id3 = PolicyID::from_string("link");
1200 let added = pset.add_template(template2).is_ok();
1201 assert!(added);
1202
1203 let r = pset.link(
1204 tid2.clone(),
1205 id3.clone(),
1206 HashMap::from([(SlotId::principal(), EntityUID::with_eid("example"))]),
1207 );
1208 r.expect("Linking failed");
1209
1210 assert_eq!(pset.get(&id1).expect("should find the policy").id(), &id1);
1211 assert_eq!(pset.get(&id2).expect("should find the policy").id(), &id2);
1212 assert_eq!(pset.get(&id3).expect("should find link").id(), &id3);
1213 assert_eq!(
1214 pset.get(&id3).expect("should find link").template().id(),
1215 &tid2
1216 );
1217 assert!(pset.get(&tid2).is_none());
1218 assert!(pset.get_template_arc(&id1).is_some()); assert!(pset.get_template_arc(&id2).is_some()); assert!(pset.get_template_arc(&tid2).is_some());
1221 assert_eq!(pset.policies().count(), 3);
1222
1223 assert_eq!(
1224 pset.get_template_arc(&tid1)
1225 .expect("should find the template")
1226 .id(),
1227 &tid1
1228 );
1229 assert!(pset.get(&tid1).is_none());
1230 assert_eq!(pset.all_templates().count(), 4);
1231 }
1232
1233 #[test]
1234 fn policy_set_insertion_order() {
1235 let mut pset = PolicySet::new();
1236 assert!(pset.is_empty());
1237
1238 let src = "permit(principal, action, resource);";
1239 let ids: Vec<PolicyID> = (1..=4)
1240 .map(|i| {
1241 let id = PolicyID::from_string(format!("id{i}"));
1242 let p = parser::parse_policy(Some(id.clone()), src).unwrap();
1243 let added = pset.add(p.into()).is_ok();
1244 assert!(added);
1245 id
1246 })
1247 .collect();
1248
1249 assert_eq!(
1250 pset.into_policies()
1251 .map(|p| p.id().clone())
1252 .collect::<Vec<PolicyID>>(),
1253 ids
1254 );
1255 }
1256}