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 add(&mut self, policy: Policy) -> Result<(), PolicySetError> {
220 let t = policy.template_arc();
221
222 let template_ventry = match self.templates.entry(t.id().clone()) {
227 Entry::Vacant(ventry) => Some(ventry),
228 Entry::Occupied(oentry) => {
229 if oentry.get() != &t {
230 return Err(PolicySetError::Occupied {
231 id: oentry.key().clone(),
232 });
233 }
234 None
235 }
236 };
237
238 let link_ventry = match self.links.entry(policy.id().clone()) {
239 Entry::Vacant(ventry) => Some(ventry),
240 Entry::Occupied(oentry) => {
241 return Err(PolicySetError::Occupied {
242 id: oentry.key().clone(),
243 });
244 }
245 };
246
247 if let Some(ventry) = template_ventry {
250 self.template_to_links_map.insert(
251 t.id().clone(),
252 vec![policy.id().clone()]
253 .into_iter()
254 .collect::<LinkedHashSet<PolicyID>>(),
255 );
256 ventry.insert(t);
257 } else {
258 self.template_to_links_map
260 .entry(t.id().clone())
261 .or_default()
262 .insert(policy.id().clone());
263 }
264 if let Some(ventry) = link_ventry {
265 ventry.insert(policy);
266 }
267
268 Ok(())
269 }
270
271 fn policy_id_is_bound(&self, pid: &PolicyID) -> bool {
274 self.templates.contains_key(pid) || self.links.contains_key(pid)
275 }
276
277 fn get_fresh_id(&self, other: &Self, start_ind: &mut u32) -> PolicyID {
279 let mut new_pid = PolicyID::from_smolstr(format_smolstr!("policy{}", start_ind));
280 *start_ind += 1;
281 while self.policy_id_is_bound(&new_pid) || other.policy_id_is_bound(&new_pid) {
282 new_pid = PolicyID::from_smolstr(format_smolstr!("policy{}", start_ind));
283 *start_ind += 1;
284 }
285 new_pid
286 }
287
288 fn update_renaming<T>(
292 &self,
293 this_contents: &LinkedHashMap<PolicyID, T>,
294 other: &Self,
295 other_contents: &LinkedHashMap<PolicyID, T>,
296 renaming: &mut LinkedHashMap<PolicyID, PolicyID>,
297 start_ind: &mut u32,
298 ) where
299 T: PartialEq + Clone,
300 {
301 for (pid, ot) in other_contents {
302 if let Some(tt) = this_contents.get(pid) {
303 if tt != ot && !renaming.contains_key(pid) {
304 let new_pid = self.get_fresh_id(other, start_ind);
305 renaming.insert(pid.clone(), new_pid);
306 }
307 }
308 }
309 }
310
311 pub fn merge_policyset(
326 &mut self,
327 other: &PolicySet,
328 rename_duplicates: bool,
329 ) -> Result<LinkedHashMap<PolicyID, PolicyID>, PolicySetError> {
330 let mut min_id = 0;
333 let mut renaming = LinkedHashMap::new();
334 self.update_renaming(
335 &self.templates,
336 other,
337 &other.templates,
338 &mut renaming,
339 &mut min_id,
340 );
341 self.update_renaming(&self.links, other, &other.links, &mut renaming, &mut min_id);
342 for (pid, template) in &other.templates {
345 if !template.is_static() && self.get(pid).is_some() && !renaming.contains_key(pid) {
346 let new_pid = self.get_fresh_id(other, &mut min_id);
347 renaming.insert(pid.clone(), new_pid);
348 }
349 }
350 for (pid, link) in &other.links {
352 if !link.is_static() && self.get_template(pid).is_some() && !renaming.contains_key(pid)
353 {
354 let new_pid = self.get_fresh_id(other, &mut min_id);
355 renaming.insert(pid.clone(), new_pid);
356 }
357 }
358
359 if !rename_duplicates {
361 if let Some(pid) = renaming.keys().next() {
362 return Err(PolicySetError::Occupied { id: pid.clone() });
363 }
364 }
365 for (pid, other_template) in &other.templates {
368 if let Some(new_pid) = renaming.get(pid) {
369 self.templates.insert(
370 new_pid.clone(),
371 Arc::new(other_template.new_id(new_pid.clone())),
372 );
373 } else {
374 self.templates.insert(pid.clone(), other_template.clone());
375 }
376 }
377 for (pid, other_policy) in &other.links {
378 let (new_pid, other_policy) = if let Some(new_pid) = renaming.get(pid) {
380 (new_pid.clone(), other_policy.new_id(new_pid.clone()))
381 } else {
382 (pid.clone(), other_policy.clone())
383 };
384 let other_policy = match renaming.get(other_policy.template().id()) {
388 #[allow(clippy::unwrap_used)]
390 Some(new_tid) if !other_policy.is_static() => {
391 other_policy.new_template_id(new_tid.clone()).unwrap()
392 }
393 _ => other_policy,
394 };
395 self.links.insert(new_pid, other_policy);
396 }
397 for (tid, other_template_link_set) in &other.template_to_links_map {
398 let tid = renaming.get(tid).unwrap_or(tid);
399 let mut this_template_link_set =
400 self.template_to_links_map.remove(tid).unwrap_or_default();
401 for pid in other_template_link_set {
402 let pid = renaming.get(pid).unwrap_or(pid);
403 this_template_link_set.insert(pid.clone());
404 }
405 self.template_to_links_map
406 .insert(tid.clone(), this_template_link_set);
407 }
408 Ok(renaming)
409 }
410
411 pub fn remove_static(
413 &mut self,
414 policy_id: &PolicyID,
415 ) -> Result<Policy, PolicySetPolicyRemovalError> {
416 let policy = match self.links.remove(policy_id) {
419 Some(p) => p,
420 None => {
421 return Err(PolicySetPolicyRemovalError::RemovePolicyNoLinkError(
422 policy_id.clone(),
423 ))
424 }
425 };
426 match self.templates.remove(policy_id) {
428 Some(_) => {
429 self.template_to_links_map.remove(policy_id);
430 Ok(policy)
431 }
432 None => {
433 self.links.insert(policy_id.clone(), policy);
436 Err(PolicySetPolicyRemovalError::RemovePolicyNoTemplateError(
437 policy_id.clone(),
438 ))
439 }
440 }
441 }
442
443 pub fn add_static(&mut self, policy: StaticPolicy) -> Result<(), PolicySetError> {
445 let (t, p) = Template::link_static_policy(policy);
446
447 match (
448 self.templates.entry(t.id().clone()),
449 self.links.entry(t.id().clone()),
450 ) {
451 (Entry::Vacant(templates_entry), Entry::Vacant(links_entry)) => {
452 self.template_to_links_map.insert(
453 t.id().clone(),
454 vec![p.id().clone()]
455 .into_iter()
456 .collect::<LinkedHashSet<PolicyID>>(),
457 );
458 templates_entry.insert(t);
459 links_entry.insert(p);
460 Ok(())
461 }
462 (Entry::Occupied(oentry), _) => Err(PolicySetError::Occupied {
463 id: oentry.key().clone(),
464 }),
465 (_, Entry::Occupied(oentry)) => Err(PolicySetError::Occupied {
466 id: oentry.key().clone(),
467 }),
468 }
469 }
470
471 pub fn add_template(&mut self, t: Template) -> Result<(), PolicySetError> {
474 if self.links.contains_key(t.id()) {
475 return Err(PolicySetError::Occupied { id: t.id().clone() });
476 }
477
478 match self.templates.entry(t.id().clone()) {
479 Entry::Occupied(oentry) => Err(PolicySetError::Occupied {
480 id: oentry.key().clone(),
481 }),
482 Entry::Vacant(ventry) => {
483 self.template_to_links_map
484 .insert(t.id().clone(), LinkedHashSet::new());
485 ventry.insert(Arc::new(t));
486 Ok(())
487 }
488 }
489 }
490
491 pub fn remove_template(
495 &mut self,
496 policy_id: &PolicyID,
497 ) -> Result<Template, PolicySetTemplateRemovalError> {
498 if self.links.contains_key(policy_id) {
500 return Err(PolicySetTemplateRemovalError::NotTemplateError(
501 policy_id.clone(),
502 ));
503 }
504
505 match self.template_to_links_map.get(policy_id) {
506 Some(map) => {
507 if !map.is_empty() {
508 return Err(PolicySetTemplateRemovalError::RemoveTemplateWithLinksError(
509 policy_id.clone(),
510 ));
511 }
512 }
513 None => {
514 return Err(PolicySetTemplateRemovalError::RemovePolicyNoTemplateError(
515 policy_id.clone(),
516 ))
517 }
518 };
519
520 #[allow(clippy::panic)]
522 match self.templates.remove(policy_id) {
523 Some(t) => {
524 self.template_to_links_map.remove(policy_id);
525 Ok(Arc::unwrap_or_clone(t))
526 }
527 None => panic!("Found in template_to_links_map but not in templates"),
528 }
529 }
530
531 pub fn get_linked_policies(
534 &self,
535 template_id: &PolicyID,
536 ) -> Result<impl Iterator<Item = &PolicyID>, PolicySetGetLinksError> {
537 match self.template_to_links_map.get(template_id) {
538 Some(s) => Ok(s.iter()),
539 None => Err(PolicySetGetLinksError::MissingTemplate(template_id.clone())),
540 }
541 }
542
543 pub fn link(
551 &mut self,
552 template_id: PolicyID,
553 new_id: PolicyID,
554 values: HashMap<SlotId, EntityUID>,
555 ) -> Result<&Policy, LinkingError> {
556 let t =
557 self.get_template_arc(&template_id)
558 .ok_or_else(|| LinkingError::NoSuchTemplate {
559 id: template_id.clone(),
560 })?;
561 let r = Template::link(t, new_id.clone(), values)?;
562
563 match (
565 self.links.entry(new_id.clone()),
566 self.templates.entry(new_id.clone()),
567 ) {
568 (Entry::Vacant(links_entry), Entry::Vacant(_)) => {
569 self.template_to_links_map
571 .entry(template_id)
572 .or_default()
573 .insert(new_id);
574 Ok(links_entry.insert(r))
575 }
576 (Entry::Occupied(oentry), _) => Err(LinkingError::PolicyIdConflict {
577 id: oentry.key().clone(),
578 }),
579 (_, Entry::Occupied(oentry)) => Err(LinkingError::PolicyIdConflict {
580 id: oentry.key().clone(),
581 }),
582 }
583 }
584
585 pub fn unlink(&mut self, policy_id: &PolicyID) -> Result<Policy, PolicySetUnlinkError> {
588 if self.templates.contains_key(policy_id) {
590 return Err(PolicySetUnlinkError::NotLinkError(policy_id.clone()));
591 }
592 match self.links.remove(policy_id) {
593 Some(p) => {
594 #[allow(clippy::panic)]
596 match self.template_to_links_map.entry(p.template().id().clone()) {
597 Entry::Occupied(t) => t.into_mut().remove(policy_id),
598 Entry::Vacant(_) => {
599 panic!("No template found for linked policy")
600 }
601 };
602 Ok(p)
603 }
604 None => Err(PolicySetUnlinkError::UnlinkingError(policy_id.clone())),
605 }
606 }
607
608 pub fn policies(&self) -> impl Iterator<Item = &Policy> {
610 self.links.values()
611 }
612
613 pub fn into_policies(self) -> impl Iterator<Item = Policy> {
615 self.links.into_iter().map(|(_, p)| p)
616 }
617
618 pub fn all_templates(&self) -> impl Iterator<Item = &Template> {
621 self.templates.values().map(|t| t.borrow())
622 }
623
624 pub fn templates(&self) -> impl Iterator<Item = &Template> {
626 self.all_templates().filter(|t| t.slots().count() != 0)
627 }
628
629 pub fn static_policies(&self) -> impl Iterator<Item = &Policy> {
631 self.policies().filter(|p| p.is_static())
632 }
633
634 pub fn is_empty(&self) -> bool {
636 self.templates.is_empty() && self.links.is_empty()
637 }
638
639 pub fn get_template_arc(&self, id: &PolicyID) -> Option<Arc<Template>> {
641 self.templates.get(id).cloned()
642 }
643
644 pub fn get_template(&self, id: &PolicyID) -> Option<&Template> {
646 self.templates.get(id).map(AsRef::as_ref)
647 }
648
649 pub fn get(&self, id: &PolicyID) -> Option<&Policy> {
651 self.links.get(id)
652 }
653
654 pub fn try_from_iter<T: IntoIterator<Item = Policy>>(iter: T) -> Result<Self, PolicySetError> {
656 let mut set = Self::new();
657 for p in iter {
658 set.add(p)?;
659 }
660 Ok(set)
661 }
662}
663
664impl std::fmt::Display for PolicySet {
665 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
666 if self.is_empty() {
668 write!(f, "<empty policyset>")
669 } else {
670 write!(
671 f,
672 "Templates:\n{}, Template Linked Policies:\n{}",
673 self.all_templates().join("\n"),
674 self.policies().join("\n")
675 )
676 }
677 }
678}
679
680#[allow(clippy::panic)]
682#[allow(clippy::indexing_slicing)]
684#[cfg(test)]
685mod test {
686 use super::*;
687 use crate::{
688 ast::{
689 annotation::Annotations, ActionConstraint, Effect, PrincipalConstraint,
690 ResourceConstraint,
691 },
692 parser,
693 };
694
695 use similar_asserts::assert_eq;
696 use std::collections::HashMap;
697
698 #[test]
699 fn link_conflicts() {
700 let mut pset = PolicySet::new();
701 let p1 = parser::parse_policy(
702 Some(PolicyID::from_string("id")),
703 "permit(principal,action,resource);",
704 )
705 .expect("Failed to parse");
706 pset.add_static(p1).expect("Failed to add!");
707 let template = parser::parse_policy_or_template(
708 Some(PolicyID::from_string("t")),
709 "permit(principal == ?principal, action, resource);",
710 )
711 .expect("Failed to parse");
712 pset.add_template(template).expect("Add failed");
713
714 let env: HashMap<SlotId, EntityUID> = HashMap::from([(
715 SlotId::principal(),
716 r#"Test::"test""#.parse().expect("Failed to parse"),
717 )]);
718
719 let r = pset.link(PolicyID::from_string("t"), PolicyID::from_string("id"), env);
720
721 match r {
722 Ok(_) => panic!("Should have failed due to conflict"),
723 Err(LinkingError::PolicyIdConflict { id }) => {
724 assert_eq!(id, PolicyID::from_string("id"))
725 }
726 Err(e) => panic!("Incorrect error: {e}"),
727 };
728 }
729
730 #[test]
733 fn policyset_add() {
734 let mut pset = PolicySet::new();
735 let static_policy = parser::parse_policy(
736 Some(PolicyID::from_string("id")),
737 "permit(principal,action,resource);",
738 )
739 .expect("Failed to parse");
740 let static_policy: Policy = static_policy.into();
741 pset.add(static_policy)
742 .expect("Adding static policy in Policy form should succeed");
743
744 let template = Arc::new(
745 parser::parse_policy_or_template(
746 Some(PolicyID::from_string("t")),
747 "permit(principal == ?principal, action, resource);",
748 )
749 .expect("Failed to parse"),
750 );
751 let env1: HashMap<SlotId, EntityUID> = HashMap::from([(
752 SlotId::principal(),
753 r#"Test::"test1""#.parse().expect("Failed to parse"),
754 )]);
755
756 let p1 = Template::link(Arc::clone(&template), PolicyID::from_string("link"), env1)
757 .expect("Failed to link");
758 pset.add(p1).expect(
759 "Adding link should succeed, even though the template wasn't previously in the pset",
760 );
761 assert!(
762 pset.get_template_arc(&PolicyID::from_string("t")).is_some(),
763 "Adding link should implicitly add the template"
764 );
765
766 let env2: HashMap<SlotId, EntityUID> = HashMap::from([(
767 SlotId::principal(),
768 r#"Test::"test2""#.parse().expect("Failed to parse"),
769 )]);
770
771 let p2 = Template::link(
772 Arc::clone(&template),
773 PolicyID::from_string("link"),
774 env2.clone(),
775 )
776 .expect("Failed to link");
777 match pset.add(p2) {
778 Ok(_) => panic!("Should have failed due to conflict with existing link id"),
779 Err(PolicySetError::Occupied { id }) => assert_eq!(id, PolicyID::from_string("link")),
780 }
781
782 let p3 = Template::link(Arc::clone(&template), PolicyID::from_string("link2"), env2)
783 .expect("Failed to link");
784 pset.add(p3).expect(
785 "Adding link should succeed, even though the template already existed in the pset",
786 );
787
788 let template2 = Arc::new(
789 parser::parse_policy_or_template(
790 Some(PolicyID::from_string("t")),
791 "forbid(principal, action, resource == ?resource);",
792 )
793 .expect("Failed to parse"),
794 );
795 let env3: HashMap<SlotId, EntityUID> = HashMap::from([(
796 SlotId::resource(),
797 r#"Test::"test3""#.parse().expect("Failed to parse"),
798 )]);
799
800 let p4 = Template::link(
801 Arc::clone(&template2),
802 PolicyID::from_string("unique3"),
803 env3,
804 )
805 .expect("Failed to link");
806 match pset.add(p4) {
807 Ok(_) => panic!("Should have failed due to conflict on template id"),
808 Err(PolicySetError::Occupied { id }) => {
809 assert_eq!(id, PolicyID::from_string("t"))
810 }
811 }
812 }
813
814 #[test]
815 fn policy_merge_no_conflicts() {
816 let p1 = parser::parse_policy(
817 Some(PolicyID::from_string("policy0")),
818 "permit(principal,action,resource);",
819 )
820 .expect("Failed to parse");
821 let p2 = parser::parse_policy(
822 Some(PolicyID::from_string("policy1")),
823 "permit(principal,action,resource) when { false };",
824 )
825 .expect("Failed to parse");
826 let p3 = parser::parse_policy(
827 Some(PolicyID::from_string("policy0")),
828 "permit(principal,action,resource);",
829 )
830 .expect("Failed to parse");
831 let p4 = parser::parse_policy(
832 Some(PolicyID::from_string("policy2")),
833 "permit(principal,action,resource) when { true };",
834 )
835 .expect("Failed to parse");
836 let mut pset1 = PolicySet::new();
837 let mut pset2 = PolicySet::new();
838 pset1.add_static(p1).expect("Failed to add!");
839 pset1.add_static(p2).expect("Failed to add!");
840 pset2.add_static(p3).expect("Failed to add!");
841 pset2.add_static(p4).expect("Failed to add!");
842 match pset1.merge_policyset(&pset2, false) {
844 Ok(_) => (),
845 Err(PolicySetError::Occupied { id }) => {
846 panic!("There should not have been an error! Unexpected conflict for id {id}")
847 }
848 }
849 }
850
851 #[test]
852 fn policy_merge_with_conflicts() {
853 let pid0 = PolicyID::from_string("policy0");
854 let pid1 = PolicyID::from_string("policy1");
855 let pid2 = PolicyID::from_string("policy2");
856 let p1 = parser::parse_policy(Some(pid0.clone()), "permit(principal,action,resource);")
857 .expect("Failed to parse");
858 let p2 = parser::parse_policy(
859 Some(pid1.clone()),
860 "permit(principal,action,resource) when { false };",
861 )
862 .expect("Failed to parse");
863 let p3 = parser::parse_policy(Some(pid1.clone()), "permit(principal,action,resource);")
864 .expect("Failed to parse");
865 let p4 = parser::parse_policy(
866 Some(pid2.clone()),
867 "permit(principal,action,resource) when { true };",
868 )
869 .expect("Failed to parse");
870 let mut pset1 = PolicySet::new();
871 let mut pset2 = PolicySet::new();
872 pset1.add_static(p1.clone()).expect("Failed to add!");
873 pset1.add_static(p2.clone()).expect("Failed to add!");
874 pset2.add_static(p3.clone()).expect("Failed to add!");
875 pset2.add_static(p4.clone()).expect("Failed to add!");
876 match pset1.merge_policyset(&pset2, false) {
878 Ok(_) => panic!("`pset1` and `pset2` should conflict for PolicyID `policy1`"),
879 Err(PolicySetError::Occupied { id }) => {
880 assert_eq!(id, PolicyID::from_string("policy1"));
881 }
882 }
883 match pset1.merge_policyset(&pset2, true) {
885 Ok(renaming) => {
886 let new_pid1 = match renaming.get(&pid1) {
888 Some(new_pid1) => new_pid1,
889 None => panic!("Error: `policy1` is a conflict and should be renamed"),
890 };
891 assert_eq!(renaming.keys().len(), 1);
893 if let Some(new_p1) = pset1.get(&pid0) {
894 assert_eq!(Policy::from(p1), new_p1.clone());
895 }
896 if let Some(new_p2) = pset1.get(&pid1) {
897 assert_eq!(Policy::from(p2), new_p2.clone());
898 }
899 if let Some(new_p3) = pset1.get(new_pid1) {
900 assert_eq!(Policy::from(p3).new_id(new_pid1.clone()), new_p3.clone());
901 }
902 if let Some(new_p4) = pset1.get(&pid2) {
903 assert_eq!(Policy::from(p4), new_p4.clone());
904 }
905 }
906 Err(PolicySetError::Occupied { id }) => {
907 panic!("There should not have been an error! Unexpected conflict for id {id}")
908 }
909 }
910 }
911
912 #[test]
913 fn policy_conflicts() {
914 let mut pset = PolicySet::new();
915 let p1 = parser::parse_policy(
916 Some(PolicyID::from_string("id")),
917 "permit(principal,action,resource);",
918 )
919 .expect("Failed to parse");
920 let p2 = parser::parse_policy(
921 Some(PolicyID::from_string("id")),
922 "permit(principal,action,resource) when { false };",
923 )
924 .expect("Failed to parse");
925 pset.add_static(p1).expect("Failed to add!");
926 match pset.add_static(p2) {
927 Ok(_) => panic!("Should have failed to due name conflict"),
928 Err(PolicySetError::Occupied { id }) => assert_eq!(id, PolicyID::from_string("id")),
929 }
930 }
931
932 #[test]
933 fn template_filtering() {
934 let template = parser::parse_policy_or_template(
935 Some(PolicyID::from_string("template")),
936 "permit(principal == ?principal, action, resource);",
937 )
938 .expect("Template Parse Failure");
939 let static_policy = parser::parse_policy(
940 Some(PolicyID::from_string("static")),
941 "permit(principal, action, resource);",
942 )
943 .expect("Static parse failure");
944 let mut set = PolicySet::new();
945 set.add_template(template).unwrap();
946 set.add_static(static_policy).unwrap();
947
948 assert_eq!(set.all_templates().count(), 2);
949 assert_eq!(set.templates().count(), 1);
950 assert_eq!(set.static_policies().count(), 1);
951 assert_eq!(set.policies().count(), 1);
952 set.link(
953 PolicyID::from_string("template"),
954 PolicyID::from_string("id"),
955 HashMap::from([(SlotId::principal(), EntityUID::with_eid("eid"))]),
956 )
957 .expect("Linking failed!");
958 assert_eq!(set.static_policies().count(), 1);
959 assert_eq!(set.policies().count(), 2);
960 }
961
962 #[test]
963 fn linking_missing_template() {
964 let tid = PolicyID::from_string("template");
965 let lid = PolicyID::from_string("link");
966 let t = Template::new(
967 tid.clone(),
968 None,
969 Annotations::new(),
970 Effect::Permit,
971 PrincipalConstraint::any(),
972 ActionConstraint::any(),
973 ResourceConstraint::any(),
974 None,
975 );
976
977 let mut s = PolicySet::new();
978 let e = s
979 .link(tid.clone(), lid.clone(), HashMap::new())
980 .expect_err("Should fail");
981
982 match e {
983 LinkingError::NoSuchTemplate { id } => assert_eq!(tid, id),
984 e => panic!("Wrong error {e}"),
985 };
986
987 s.add_template(t).unwrap();
988 s.link(tid, lid, HashMap::new()).expect("Should succeed");
989 }
990
991 #[test]
992 fn linkinv_valid_link() {
993 let tid = PolicyID::from_string("template");
994 let lid = PolicyID::from_string("link");
995 let t = Template::new(
996 tid.clone(),
997 None,
998 Annotations::new(),
999 Effect::Permit,
1000 PrincipalConstraint::is_eq_slot(),
1001 ActionConstraint::any(),
1002 ResourceConstraint::is_in_slot(),
1003 None,
1004 );
1005
1006 let mut s = PolicySet::new();
1007 s.add_template(t).unwrap();
1008
1009 let mut vals = HashMap::new();
1010 vals.insert(SlotId::principal(), EntityUID::with_eid("p"));
1011 vals.insert(SlotId::resource(), EntityUID::with_eid("a"));
1012
1013 s.link(tid.clone(), lid.clone(), vals).expect("Should link");
1014
1015 let v: Vec<_> = s.policies().collect();
1016
1017 assert_eq!(v[0].id(), &lid);
1018 assert_eq!(v[0].template().id(), &tid);
1019 }
1020
1021 #[test]
1022 fn linking_empty_set() {
1023 let s = PolicySet::new();
1024 assert_eq!(s.policies().count(), 0);
1025 }
1026
1027 #[test]
1028 fn linking_raw_policy() {
1029 let mut s = PolicySet::new();
1030 let id = PolicyID::from_string("id");
1031 let p = StaticPolicy::new(
1032 id.clone(),
1033 None,
1034 Annotations::new(),
1035 Effect::Forbid,
1036 PrincipalConstraint::any(),
1037 ActionConstraint::any(),
1038 ResourceConstraint::any(),
1039 None,
1040 )
1041 .expect("Policy Creation Failed");
1042 s.add_static(p).unwrap();
1043
1044 let mut iter = s.policies();
1045 match iter.next() {
1046 Some(pol) => {
1047 assert_eq!(pol.id(), &id);
1048 assert_eq!(pol.effect(), Effect::Forbid);
1049 assert!(pol.env().is_empty())
1050 }
1051 None => panic!("Linked Record Not Present"),
1052 };
1053 }
1054
1055 #[test]
1056 fn link_slotmap() {
1057 let mut s = PolicySet::new();
1058 let template_id = PolicyID::from_string("template");
1059 let link_id = PolicyID::from_string("link");
1060 let t = Template::new(
1061 template_id.clone(),
1062 None,
1063 Annotations::new(),
1064 Effect::Forbid,
1065 PrincipalConstraint::is_eq_slot(),
1066 ActionConstraint::any(),
1067 ResourceConstraint::any(),
1068 None,
1069 );
1070 s.add_template(t).unwrap();
1071
1072 let mut v = HashMap::new();
1073 let entity = EntityUID::with_eid("eid");
1074 v.insert(SlotId::principal(), entity.clone());
1075 s.link(template_id.clone(), link_id.clone(), v)
1076 .expect("Linking failed!");
1077
1078 let link = s.get(&link_id).expect("Link should exist");
1079 assert_eq!(&link_id, link.id());
1080 assert_eq!(&template_id, link.template().id());
1081 assert_eq!(
1082 &entity,
1083 link.env()
1084 .get(&SlotId::principal())
1085 .expect("Mapping was incorrect")
1086 );
1087 }
1088
1089 #[test]
1090 fn policy_sets() {
1091 let mut pset = PolicySet::new();
1092 assert!(pset.is_empty());
1093 let id1 = PolicyID::from_string("id1");
1094 let tid1 = PolicyID::from_string("template");
1095 let policy1 = StaticPolicy::new(
1096 id1.clone(),
1097 None,
1098 Annotations::new(),
1099 Effect::Permit,
1100 PrincipalConstraint::any(),
1101 ActionConstraint::any(),
1102 ResourceConstraint::any(),
1103 None,
1104 )
1105 .expect("Policy Creation Failed");
1106 let template1 = Template::new(
1107 tid1.clone(),
1108 None,
1109 Annotations::new(),
1110 Effect::Permit,
1111 PrincipalConstraint::any(),
1112 ActionConstraint::any(),
1113 ResourceConstraint::any(),
1114 None,
1115 );
1116 let added = pset.add_static(policy1.clone()).is_ok();
1117 assert!(added);
1118 let added = pset.add_static(policy1).is_ok();
1119 assert!(!added);
1120 let added = pset.add_template(template1.clone()).is_ok();
1121 assert!(added);
1122 let added = pset.add_template(template1).is_ok();
1123 assert!(!added);
1124 assert!(!pset.is_empty());
1125 let id2 = PolicyID::from_string("id2");
1126 let policy2 = StaticPolicy::new(
1127 id2.clone(),
1128 None,
1129 Annotations::new(),
1130 Effect::Forbid,
1131 PrincipalConstraint::is_eq(Arc::new(EntityUID::with_eid("jane"))),
1132 ActionConstraint::any(),
1133 ResourceConstraint::any(),
1134 None,
1135 )
1136 .expect("Policy Creation Failed");
1137 let added = pset.add_static(policy2).is_ok();
1138 assert!(added);
1139
1140 let tid2 = PolicyID::from_string("template2");
1141 let template2 = Template::new(
1142 tid2.clone(),
1143 None,
1144 Annotations::new(),
1145 Effect::Permit,
1146 PrincipalConstraint::is_eq_slot(),
1147 ActionConstraint::any(),
1148 ResourceConstraint::any(),
1149 None,
1150 );
1151 let id3 = PolicyID::from_string("link");
1152 let added = pset.add_template(template2).is_ok();
1153 assert!(added);
1154
1155 let r = pset.link(
1156 tid2.clone(),
1157 id3.clone(),
1158 HashMap::from([(SlotId::principal(), EntityUID::with_eid("example"))]),
1159 );
1160 r.expect("Linking failed");
1161
1162 assert_eq!(pset.get(&id1).expect("should find the policy").id(), &id1);
1163 assert_eq!(pset.get(&id2).expect("should find the policy").id(), &id2);
1164 assert_eq!(pset.get(&id3).expect("should find link").id(), &id3);
1165 assert_eq!(
1166 pset.get(&id3).expect("should find link").template().id(),
1167 &tid2
1168 );
1169 assert!(pset.get(&tid2).is_none());
1170 assert!(pset.get_template_arc(&id1).is_some()); assert!(pset.get_template_arc(&id2).is_some()); assert!(pset.get_template_arc(&tid2).is_some());
1173 assert_eq!(pset.policies().count(), 3);
1174
1175 assert_eq!(
1176 pset.get_template_arc(&tid1)
1177 .expect("should find the template")
1178 .id(),
1179 &tid1
1180 );
1181 assert!(pset.get(&tid1).is_none());
1182 assert_eq!(pset.all_templates().count(), 4);
1183 }
1184
1185 #[test]
1186 fn policy_set_insertion_order() {
1187 let mut pset = PolicySet::new();
1188 assert!(pset.is_empty());
1189
1190 let src = "permit(principal, action, resource);";
1191 let ids: Vec<PolicyID> = (1..=4)
1192 .map(|i| {
1193 let id = PolicyID::from_string(format!("id{i}"));
1194 let p = parser::parse_policy(Some(id.clone()), src).unwrap();
1195 let added = pset.add(p.into()).is_ok();
1196 assert!(added);
1197 id
1198 })
1199 .collect();
1200
1201 assert_eq!(
1202 pset.into_policies()
1203 .map(|p| p.id().clone())
1204 .collect::<Vec<PolicyID>>(),
1205 ids
1206 );
1207 }
1208}