1use super::{
18 EntityUID, LinkingError, LiteralPolicy, Policy, PolicyID, ReificationError, SlotId,
19 StaticPolicy, Template,
20};
21use itertools::Itertools;
22use miette::Diagnostic;
23use smol_str::format_smolstr;
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)]
30pub struct PolicySet {
31 templates: HashMap<PolicyID, Arc<Template>>,
37 links: HashMap<PolicyID, Policy>,
43
44 template_to_links_map: HashMap<PolicyID, HashSet<PolicyID>>,
48}
49
50#[derive(Debug)]
56pub struct LiteralPolicySet {
57 templates: HashMap<PolicyID, Template>,
59 links: HashMap<PolicyID, LiteralPolicy>,
62}
63
64impl LiteralPolicySet {
65 pub fn new(
68 templates: impl IntoIterator<Item = (PolicyID, Template)>,
69 links: impl IntoIterator<Item = (PolicyID, LiteralPolicy)>,
70 ) -> Self {
71 Self {
72 templates: templates.into_iter().collect(),
73 links: links.into_iter().collect(),
74 }
75 }
76
77 pub fn templates(&self) -> impl Iterator<Item = &Template> {
81 self.templates.values()
82 }
83
84 pub fn policies(&self) -> impl Iterator<Item = &LiteralPolicy> {
87 self.links.values()
88 }
89}
90
91impl TryFrom<LiteralPolicySet> for PolicySet {
94 type Error = ReificationError;
95 fn try_from(pset: LiteralPolicySet) -> Result<Self, Self::Error> {
96 let templates = pset
98 .templates
99 .into_iter()
100 .map(|(id, template)| (id, Arc::new(template)))
101 .collect();
102 let links = pset
103 .links
104 .into_iter()
105 .map(|(id, literal)| literal.reify(&templates).map(|linked| (id, linked)))
106 .collect::<Result<HashMap<PolicyID, Policy>, ReificationError>>()?;
107
108 let mut template_to_links_map = HashMap::new();
109 for template in &templates {
110 template_to_links_map.insert(template.0.clone(), HashSet::new());
111 }
112 for (link_id, link) in &links {
113 let template = link.template().id();
114 match template_to_links_map.entry(template.clone()) {
115 Entry::Occupied(t) => t.into_mut().insert(link_id.clone()),
116 Entry::Vacant(_) => return Err(ReificationError::NoSuchTemplate(template.clone())),
117 };
118 }
119
120 Ok(Self {
121 templates,
122 links,
123 template_to_links_map,
124 })
125 }
126}
127
128impl From<PolicySet> for LiteralPolicySet {
129 fn from(pset: PolicySet) -> Self {
130 let templates = pset
131 .templates
132 .into_iter()
133 .map(|(id, template)| (id, template.as_ref().clone()))
134 .collect();
135 let links = pset
136 .links
137 .into_iter()
138 .map(|(id, p)| (id, p.into()))
139 .collect();
140 Self { templates, links }
141 }
142}
143
144#[derive(Debug, Diagnostic, Error)]
146pub enum PolicySetError {
147 #[error("duplicate template or policy id `{id}`")]
150 Occupied {
151 id: PolicyID,
153 },
154}
155
156#[derive(Debug, Diagnostic, Error)]
158pub enum PolicySetGetLinksError {
159 #[error("No template `{0}`")]
161 MissingTemplate(PolicyID),
162}
163
164#[derive(Debug, Diagnostic, Error)]
166pub enum PolicySetUnlinkError {
167 #[error("unable to unlink policy id `{0}` because it does not exist")]
169 UnlinkingError(PolicyID),
170 #[error("unable to remove link with policy id `{0}` because it is a static policy")]
172 NotLinkError(PolicyID),
173}
174
175#[derive(Debug, Diagnostic, Error)]
177pub enum PolicySetTemplateRemovalError {
178 #[error("unable to remove template id `{0}` from template list because it does not exist")]
180 RemovePolicyNoTemplateError(PolicyID),
181 #[error(
183 "unable to remove template id `{0}` from template list because it still has active links"
184 )]
185 RemoveTemplateWithLinksError(PolicyID),
186 #[error("unable to remove template with policy id `{0}` because it is a static policy")]
188 NotTemplateError(PolicyID),
189}
190
191#[derive(Debug, Diagnostic, Error)]
193pub enum PolicySetPolicyRemovalError {
194 #[error("unable to remove static policy id `{0}` from link list because it does not exist")]
196 RemovePolicyNoLinkError(PolicyID),
197 #[error(
199 "unable to remove static policy id `{0}` from template list because it does not exist"
200 )]
201 RemovePolicyNoTemplateError(PolicyID),
202}
203
204impl PolicySet {
207 pub fn new() -> Self {
209 Self {
210 templates: HashMap::new(),
211 links: HashMap::new(),
212 template_to_links_map: HashMap::new(),
213 }
214 }
215
216 pub fn add(&mut self, policy: Policy) -> Result<(), PolicySetError> {
218 let t = policy.template_arc();
219
220 let template_ventry = match self.templates.entry(t.id().clone()) {
225 Entry::Vacant(ventry) => Some(ventry),
226 Entry::Occupied(oentry) => {
227 if oentry.get() != &t {
228 return Err(PolicySetError::Occupied {
229 id: oentry.key().clone(),
230 });
231 }
232 None
233 }
234 };
235
236 let link_ventry = match self.links.entry(policy.id().clone()) {
237 Entry::Vacant(ventry) => Some(ventry),
238 Entry::Occupied(oentry) => {
239 return Err(PolicySetError::Occupied {
240 id: oentry.key().clone(),
241 });
242 }
243 };
244
245 if let Some(ventry) = template_ventry {
248 self.template_to_links_map.insert(
249 t.id().clone(),
250 vec![policy.id().clone()]
251 .into_iter()
252 .collect::<HashSet<PolicyID>>(),
253 );
254 ventry.insert(t);
255 } else {
256 self.template_to_links_map
258 .entry(t.id().clone())
259 .or_default()
260 .insert(policy.id().clone());
261 }
262 if let Some(ventry) = link_ventry {
263 ventry.insert(policy);
264 }
265
266 Ok(())
267 }
268
269 fn policy_id_is_bound(&self, pid: &PolicyID) -> bool {
272 self.templates.contains_key(pid) || self.links.contains_key(pid)
273 }
274
275 fn update_renaming<T>(
279 &self,
280 this_contents: &HashMap<PolicyID, T>,
281 other: &Self,
282 other_contents: &HashMap<PolicyID, T>,
283 renaming: &mut HashMap<PolicyID, PolicyID>,
284 start_ind: &mut u32,
285 ) where
286 T: PartialEq + Clone,
287 {
288 for (pid, ot) in other_contents {
289 if let Some(tt) = this_contents.get(pid) {
290 if tt != ot {
291 let mut new_pid =
292 PolicyID::from_smolstr(format_smolstr!("policy{}", start_ind));
293 *start_ind += 1;
294 while self.policy_id_is_bound(&new_pid) || other.policy_id_is_bound(&new_pid) {
295 new_pid = PolicyID::from_smolstr(format_smolstr!("policy{}", start_ind));
296 *start_ind += 1;
297 }
298 renaming.insert(pid.clone(), new_pid);
299 }
300 }
301 }
302 }
303
304 pub fn merge_policyset(
319 &mut self,
320 other: &PolicySet,
321 rename_duplicates: bool,
322 ) -> Result<HashMap<PolicyID, PolicyID>, PolicySetError> {
323 let mut min_id = 0;
326 let mut renaming = HashMap::new();
327 self.update_renaming(
328 &self.templates,
329 other,
330 &other.templates,
331 &mut renaming,
332 &mut min_id,
333 );
334 self.update_renaming(&self.links, other, &other.links, &mut renaming, &mut min_id);
335 if !rename_duplicates {
337 if let Some(pid) = renaming.keys().next() {
338 return Err(PolicySetError::Occupied { id: pid.clone() });
339 }
340 }
341 for (pid, other_template) in &other.templates {
344 let pid = renaming.get(pid).unwrap_or(pid);
345 self.templates.insert(pid.clone(), other_template.clone());
346 }
347 for (pid, other_policy) in &other.links {
348 let pid = renaming.get(pid).unwrap_or(pid);
349 self.links.insert(pid.clone(), other_policy.clone());
350 }
351 for (tid, other_template_link_set) in &other.template_to_links_map {
352 let tid = renaming.get(tid).unwrap_or(tid);
353 let mut this_template_link_set =
354 self.template_to_links_map.remove(tid).unwrap_or_default();
355 for pid in other_template_link_set {
356 let pid = renaming.get(pid).unwrap_or(pid);
357 this_template_link_set.insert(pid.clone());
358 }
359 self.template_to_links_map
360 .insert(tid.clone(), this_template_link_set);
361 }
362 Ok(renaming)
363 }
364
365 pub fn remove_static(
367 &mut self,
368 policy_id: &PolicyID,
369 ) -> Result<Policy, PolicySetPolicyRemovalError> {
370 let policy = match self.links.remove(policy_id) {
373 Some(p) => p,
374 None => {
375 return Err(PolicySetPolicyRemovalError::RemovePolicyNoLinkError(
376 policy_id.clone(),
377 ))
378 }
379 };
380 match self.templates.remove(policy_id) {
382 Some(_) => {
383 self.template_to_links_map.remove(policy_id);
384 Ok(policy)
385 }
386 None => {
387 self.links.insert(policy_id.clone(), policy);
390 Err(PolicySetPolicyRemovalError::RemovePolicyNoTemplateError(
391 policy_id.clone(),
392 ))
393 }
394 }
395 }
396
397 pub fn add_static(&mut self, policy: StaticPolicy) -> Result<(), PolicySetError> {
399 let (t, p) = Template::link_static_policy(policy);
400
401 match (
402 self.templates.entry(t.id().clone()),
403 self.links.entry(t.id().clone()),
404 ) {
405 (Entry::Vacant(templates_entry), Entry::Vacant(links_entry)) => {
406 self.template_to_links_map.insert(
407 t.id().clone(),
408 vec![p.id().clone()]
409 .into_iter()
410 .collect::<HashSet<PolicyID>>(),
411 );
412 templates_entry.insert(t);
413 links_entry.insert(p);
414 Ok(())
415 }
416 (Entry::Occupied(oentry), _) => Err(PolicySetError::Occupied {
417 id: oentry.key().clone(),
418 }),
419 (_, Entry::Occupied(oentry)) => Err(PolicySetError::Occupied {
420 id: oentry.key().clone(),
421 }),
422 }
423 }
424
425 pub fn add_template(&mut self, t: Template) -> Result<(), PolicySetError> {
428 if self.links.contains_key(t.id()) {
429 return Err(PolicySetError::Occupied { id: t.id().clone() });
430 }
431
432 match self.templates.entry(t.id().clone()) {
433 Entry::Occupied(oentry) => Err(PolicySetError::Occupied {
434 id: oentry.key().clone(),
435 }),
436 Entry::Vacant(ventry) => {
437 self.template_to_links_map
438 .insert(t.id().clone(), HashSet::new());
439 ventry.insert(Arc::new(t));
440 Ok(())
441 }
442 }
443 }
444
445 pub fn remove_template(
449 &mut self,
450 policy_id: &PolicyID,
451 ) -> Result<Template, PolicySetTemplateRemovalError> {
452 if self.links.contains_key(policy_id) {
454 return Err(PolicySetTemplateRemovalError::NotTemplateError(
455 policy_id.clone(),
456 ));
457 }
458
459 match self.template_to_links_map.get(policy_id) {
460 Some(map) => {
461 if !map.is_empty() {
462 return Err(PolicySetTemplateRemovalError::RemoveTemplateWithLinksError(
463 policy_id.clone(),
464 ));
465 }
466 }
467 None => {
468 return Err(PolicySetTemplateRemovalError::RemovePolicyNoTemplateError(
469 policy_id.clone(),
470 ))
471 }
472 };
473
474 #[allow(clippy::panic)]
476 match self.templates.remove(policy_id) {
477 Some(t) => {
478 self.template_to_links_map.remove(policy_id);
479 Ok(Arc::unwrap_or_clone(t))
480 }
481 None => panic!("Found in template_to_links_map but not in templates"),
482 }
483 }
484
485 pub fn get_linked_policies(
488 &self,
489 template_id: &PolicyID,
490 ) -> Result<impl Iterator<Item = &PolicyID>, PolicySetGetLinksError> {
491 match self.template_to_links_map.get(template_id) {
492 Some(s) => Ok(s.iter()),
493 None => Err(PolicySetGetLinksError::MissingTemplate(template_id.clone())),
494 }
495 }
496
497 pub fn link(
505 &mut self,
506 template_id: PolicyID,
507 new_id: PolicyID,
508 values: HashMap<SlotId, EntityUID>,
509 ) -> Result<&Policy, LinkingError> {
510 let t =
511 self.get_template_arc(&template_id)
512 .ok_or_else(|| LinkingError::NoSuchTemplate {
513 id: template_id.clone(),
514 })?;
515 let r = Template::link(t, new_id.clone(), values)?;
516
517 match (
519 self.links.entry(new_id.clone()),
520 self.templates.entry(new_id.clone()),
521 ) {
522 (Entry::Vacant(links_entry), Entry::Vacant(_)) => {
523 self.template_to_links_map
525 .entry(template_id)
526 .or_default()
527 .insert(new_id);
528 Ok(links_entry.insert(r))
529 }
530 (Entry::Occupied(oentry), _) => Err(LinkingError::PolicyIdConflict {
531 id: oentry.key().clone(),
532 }),
533 (_, Entry::Occupied(oentry)) => Err(LinkingError::PolicyIdConflict {
534 id: oentry.key().clone(),
535 }),
536 }
537 }
538
539 pub fn unlink(&mut self, policy_id: &PolicyID) -> Result<Policy, PolicySetUnlinkError> {
542 if self.templates.contains_key(policy_id) {
544 return Err(PolicySetUnlinkError::NotLinkError(policy_id.clone()));
545 }
546 match self.links.remove(policy_id) {
547 Some(p) => {
548 #[allow(clippy::panic)]
550 match self.template_to_links_map.entry(p.template().id().clone()) {
551 Entry::Occupied(t) => t.into_mut().remove(policy_id),
552 Entry::Vacant(_) => {
553 panic!("No template found for linked policy")
554 }
555 };
556 Ok(p)
557 }
558 None => Err(PolicySetUnlinkError::UnlinkingError(policy_id.clone())),
559 }
560 }
561
562 pub fn policies(&self) -> impl Iterator<Item = &Policy> {
564 self.links.values()
565 }
566
567 pub fn into_policies(self) -> impl Iterator<Item = Policy> {
569 self.links.into_values()
570 }
571
572 pub fn all_templates(&self) -> impl Iterator<Item = &Template> {
575 self.templates.values().map(|t| t.borrow())
576 }
577
578 pub fn templates(&self) -> impl Iterator<Item = &Template> {
580 self.all_templates().filter(|t| t.slots().count() != 0)
581 }
582
583 pub fn static_policies(&self) -> impl Iterator<Item = &Policy> {
585 self.policies().filter(|p| p.is_static())
586 }
587
588 pub fn is_empty(&self) -> bool {
590 self.templates.is_empty() && self.links.is_empty()
591 }
592
593 pub fn get_template_arc(&self, id: &PolicyID) -> Option<Arc<Template>> {
595 self.templates.get(id).cloned()
596 }
597
598 pub fn get_template(&self, id: &PolicyID) -> Option<&Template> {
600 self.templates.get(id).map(AsRef::as_ref)
601 }
602
603 pub fn get(&self, id: &PolicyID) -> Option<&Policy> {
605 self.links.get(id)
606 }
607
608 pub fn try_from_iter<T: IntoIterator<Item = Policy>>(iter: T) -> Result<Self, PolicySetError> {
610 let mut set = Self::new();
611 for p in iter {
612 set.add(p)?;
613 }
614 Ok(set)
615 }
616}
617
618impl std::fmt::Display for PolicySet {
619 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
620 if self.is_empty() {
622 write!(f, "<empty policyset>")
623 } else {
624 write!(
625 f,
626 "Templates:\n{}, Template Linked Policies:\n{}",
627 self.all_templates().join("\n"),
628 self.policies().join("\n")
629 )
630 }
631 }
632}
633
634#[allow(clippy::panic)]
636#[allow(clippy::indexing_slicing)]
638#[cfg(test)]
639mod test {
640 use super::*;
641 use crate::{
642 ast::{
643 annotation::Annotations, ActionConstraint, Effect, Expr, PrincipalConstraint,
644 ResourceConstraint,
645 },
646 parser,
647 };
648
649 use std::collections::HashMap;
650
651 #[test]
652 fn link_conflicts() {
653 let mut pset = PolicySet::new();
654 let p1 = parser::parse_policy(
655 Some(PolicyID::from_string("id")),
656 "permit(principal,action,resource);",
657 )
658 .expect("Failed to parse");
659 pset.add_static(p1).expect("Failed to add!");
660 let template = parser::parse_policy_or_template(
661 Some(PolicyID::from_string("t")),
662 "permit(principal == ?principal, action, resource);",
663 )
664 .expect("Failed to parse");
665 pset.add_template(template).expect("Add failed");
666
667 let env: HashMap<SlotId, EntityUID> = HashMap::from([(
668 SlotId::principal(),
669 r#"Test::"test""#.parse().expect("Failed to parse"),
670 )]);
671
672 let r = pset.link(PolicyID::from_string("t"), PolicyID::from_string("id"), env);
673
674 match r {
675 Ok(_) => panic!("Should have failed due to conflict"),
676 Err(LinkingError::PolicyIdConflict { id }) => {
677 assert_eq!(id, PolicyID::from_string("id"))
678 }
679 Err(e) => panic!("Incorrect error: {e}"),
680 };
681 }
682
683 #[test]
686 fn policyset_add() {
687 let mut pset = PolicySet::new();
688 let static_policy = parser::parse_policy(
689 Some(PolicyID::from_string("id")),
690 "permit(principal,action,resource);",
691 )
692 .expect("Failed to parse");
693 let static_policy: Policy = static_policy.into();
694 pset.add(static_policy)
695 .expect("Adding static policy in Policy form should succeed");
696
697 let template = Arc::new(
698 parser::parse_policy_or_template(
699 Some(PolicyID::from_string("t")),
700 "permit(principal == ?principal, action, resource);",
701 )
702 .expect("Failed to parse"),
703 );
704 let env1: HashMap<SlotId, EntityUID> = HashMap::from([(
705 SlotId::principal(),
706 r#"Test::"test1""#.parse().expect("Failed to parse"),
707 )]);
708
709 let p1 = Template::link(Arc::clone(&template), PolicyID::from_string("link"), env1)
710 .expect("Failed to link");
711 pset.add(p1).expect(
712 "Adding link should succeed, even though the template wasn't previously in the pset",
713 );
714 assert!(
715 pset.get_template_arc(&PolicyID::from_string("t")).is_some(),
716 "Adding link should implicitly add the template"
717 );
718
719 let env2: HashMap<SlotId, EntityUID> = HashMap::from([(
720 SlotId::principal(),
721 r#"Test::"test2""#.parse().expect("Failed to parse"),
722 )]);
723
724 let p2 = Template::link(
725 Arc::clone(&template),
726 PolicyID::from_string("link"),
727 env2.clone(),
728 )
729 .expect("Failed to link");
730 match pset.add(p2) {
731 Ok(_) => panic!("Should have failed due to conflict with existing link id"),
732 Err(PolicySetError::Occupied { id }) => assert_eq!(id, PolicyID::from_string("link")),
733 }
734
735 let p3 = Template::link(Arc::clone(&template), PolicyID::from_string("link2"), env2)
736 .expect("Failed to link");
737 pset.add(p3).expect(
738 "Adding link should succeed, even though the template already existed in the pset",
739 );
740
741 let template2 = Arc::new(
742 parser::parse_policy_or_template(
743 Some(PolicyID::from_string("t")),
744 "forbid(principal, action, resource == ?resource);",
745 )
746 .expect("Failed to parse"),
747 );
748 let env3: HashMap<SlotId, EntityUID> = HashMap::from([(
749 SlotId::resource(),
750 r#"Test::"test3""#.parse().expect("Failed to parse"),
751 )]);
752
753 let p4 = Template::link(
754 Arc::clone(&template2),
755 PolicyID::from_string("unique3"),
756 env3,
757 )
758 .expect("Failed to link");
759 match pset.add(p4) {
760 Ok(_) => panic!("Should have failed due to conflict on template id"),
761 Err(PolicySetError::Occupied { id }) => {
762 assert_eq!(id, PolicyID::from_string("t"))
763 }
764 }
765 }
766
767 #[test]
768 fn policy_merge_no_conflicts() {
769 let p1 = parser::parse_policy(
770 Some(PolicyID::from_string("policy0")),
771 "permit(principal,action,resource);",
772 )
773 .expect("Failed to parse");
774 let p2 = parser::parse_policy(
775 Some(PolicyID::from_string("policy1")),
776 "permit(principal,action,resource) when { false };",
777 )
778 .expect("Failed to parse");
779 let p3 = parser::parse_policy(
780 Some(PolicyID::from_string("policy0")),
781 "permit(principal,action,resource);",
782 )
783 .expect("Failed to parse");
784 let p4 = parser::parse_policy(
785 Some(PolicyID::from_string("policy2")),
786 "permit(principal,action,resource) when { true };",
787 )
788 .expect("Failed to parse");
789 let mut pset1 = PolicySet::new();
790 let mut pset2 = PolicySet::new();
791 pset1.add_static(p1).expect("Failed to add!");
792 pset1.add_static(p2).expect("Failed to add!");
793 pset2.add_static(p3).expect("Failed to add!");
794 pset2.add_static(p4).expect("Failed to add!");
795 match pset1.merge_policyset(&pset2, false) {
797 Ok(_) => (),
798 Err(PolicySetError::Occupied { id }) => panic!(
799 "There should not have been an error! Unexpected conflict for id {}",
800 id
801 ),
802 }
803 }
804
805 #[test]
806 fn policy_merge_with_conflicts() {
807 let pid0 = PolicyID::from_string("policy0");
808 let pid1 = PolicyID::from_string("policy1");
809 let pid2 = PolicyID::from_string("policy2");
810 let p1 = parser::parse_policy(Some(pid0.clone()), "permit(principal,action,resource);")
811 .expect("Failed to parse");
812 let p2 = parser::parse_policy(
813 Some(pid1.clone()),
814 "permit(principal,action,resource) when { false };",
815 )
816 .expect("Failed to parse");
817 let p3 = parser::parse_policy(Some(pid1.clone()), "permit(principal,action,resource);")
818 .expect("Failed to parse");
819 let p4 = parser::parse_policy(
820 Some(pid2.clone()),
821 "permit(principal,action,resource) when { true };",
822 )
823 .expect("Failed to parse");
824 let mut pset1 = PolicySet::new();
825 let mut pset2 = PolicySet::new();
826 pset1.add_static(p1.clone()).expect("Failed to add!");
827 pset1.add_static(p2.clone()).expect("Failed to add!");
828 pset2.add_static(p3.clone()).expect("Failed to add!");
829 pset2.add_static(p4.clone()).expect("Failed to add!");
830 match pset1.merge_policyset(&pset2, false) {
832 Ok(_) => panic!("`pset1` and `pset2` should conflict for PolicyID `policy1`"),
833 Err(PolicySetError::Occupied { id }) => {
834 assert_eq!(id, PolicyID::from_string("policy1"));
835 }
836 }
837 match pset1.merge_policyset(&pset2, true) {
839 Ok(renaming) => {
840 let new_pid1 = match renaming.get(&pid1) {
842 Some(new_pid1) => new_pid1,
843 None => panic!("Error: `policy1` is a conflict and should be renamed"),
844 };
845 assert_eq!(renaming.keys().len(), 1);
847 if let Some(new_p1) = pset1.get(&pid0) {
848 assert_eq!(Policy::from(p1), new_p1.clone());
849 }
850 if let Some(new_p2) = pset1.get(&pid1) {
851 assert_eq!(Policy::from(p2), new_p2.clone());
852 }
853 if let Some(new_p3) = pset1.get(new_pid1) {
854 assert_eq!(Policy::from(p3), new_p3.clone());
855 }
856 if let Some(new_p4) = pset1.get(&pid2) {
857 assert_eq!(Policy::from(p4), new_p4.clone());
858 }
859 }
860 Err(PolicySetError::Occupied { id }) => panic!(
861 "There should not have been an error! Unexpected conflict for id {}",
862 id
863 ),
864 }
865 }
866
867 #[test]
868 fn policy_conflicts() {
869 let mut pset = PolicySet::new();
870 let p1 = parser::parse_policy(
871 Some(PolicyID::from_string("id")),
872 "permit(principal,action,resource);",
873 )
874 .expect("Failed to parse");
875 let p2 = parser::parse_policy(
876 Some(PolicyID::from_string("id")),
877 "permit(principal,action,resource) when { false };",
878 )
879 .expect("Failed to parse");
880 pset.add_static(p1).expect("Failed to add!");
881 match pset.add_static(p2) {
882 Ok(_) => panic!("Should have failed to due name conflict"),
883 Err(PolicySetError::Occupied { id }) => assert_eq!(id, PolicyID::from_string("id")),
884 }
885 }
886
887 #[test]
888 fn template_filtering() {
889 let template = parser::parse_policy_or_template(
890 Some(PolicyID::from_string("template")),
891 "permit(principal == ?principal, action, resource);",
892 )
893 .expect("Template Parse Failure");
894 let static_policy = parser::parse_policy(
895 Some(PolicyID::from_string("static")),
896 "permit(principal, action, resource);",
897 )
898 .expect("Static parse failure");
899 let mut set = PolicySet::new();
900 set.add_template(template).unwrap();
901 set.add_static(static_policy).unwrap();
902
903 assert_eq!(set.all_templates().count(), 2);
904 assert_eq!(set.templates().count(), 1);
905 assert_eq!(set.static_policies().count(), 1);
906 assert_eq!(set.policies().count(), 1);
907 set.link(
908 PolicyID::from_string("template"),
909 PolicyID::from_string("id"),
910 HashMap::from([(SlotId::principal(), EntityUID::with_eid("eid"))]),
911 )
912 .expect("Linking failed!");
913 assert_eq!(set.static_policies().count(), 1);
914 assert_eq!(set.policies().count(), 2);
915 }
916
917 #[test]
918 fn linking_missing_template() {
919 let tid = PolicyID::from_string("template");
920 let lid = PolicyID::from_string("link");
921 let t = Template::new(
922 tid.clone(),
923 None,
924 Annotations::new(),
925 Effect::Permit,
926 PrincipalConstraint::any(),
927 ActionConstraint::any(),
928 ResourceConstraint::any(),
929 Expr::val(true),
930 );
931
932 let mut s = PolicySet::new();
933 let e = s
934 .link(tid.clone(), lid.clone(), HashMap::new())
935 .expect_err("Should fail");
936
937 match e {
938 LinkingError::NoSuchTemplate { id } => assert_eq!(tid, id),
939 e => panic!("Wrong error {e}"),
940 };
941
942 s.add_template(t).unwrap();
943 s.link(tid, lid, HashMap::new()).expect("Should succeed");
944 }
945
946 #[test]
947 fn linkinv_valid_link() {
948 let tid = PolicyID::from_string("template");
949 let lid = PolicyID::from_string("link");
950 let t = Template::new(
951 tid.clone(),
952 None,
953 Annotations::new(),
954 Effect::Permit,
955 PrincipalConstraint::is_eq_slot(),
956 ActionConstraint::any(),
957 ResourceConstraint::is_in_slot(),
958 Expr::val(true),
959 );
960
961 let mut s = PolicySet::new();
962 s.add_template(t).unwrap();
963
964 let mut vals = HashMap::new();
965 vals.insert(SlotId::principal(), EntityUID::with_eid("p"));
966 vals.insert(SlotId::resource(), EntityUID::with_eid("a"));
967
968 s.link(tid.clone(), lid.clone(), vals).expect("Should link");
969
970 let v: Vec<_> = s.policies().collect();
971
972 assert_eq!(v[0].id(), &lid);
973 assert_eq!(v[0].template().id(), &tid);
974 }
975
976 #[test]
977 fn linking_empty_set() {
978 let s = PolicySet::new();
979 assert_eq!(s.policies().count(), 0);
980 }
981
982 #[test]
983 fn linking_raw_policy() {
984 let mut s = PolicySet::new();
985 let id = PolicyID::from_string("id");
986 let p = StaticPolicy::new(
987 id.clone(),
988 None,
989 Annotations::new(),
990 Effect::Forbid,
991 PrincipalConstraint::any(),
992 ActionConstraint::any(),
993 ResourceConstraint::any(),
994 Expr::val(true),
995 )
996 .expect("Policy Creation Failed");
997 s.add_static(p).unwrap();
998
999 let mut iter = s.policies();
1000 match iter.next() {
1001 Some(pol) => {
1002 assert_eq!(pol.id(), &id);
1003 assert_eq!(pol.effect(), Effect::Forbid);
1004 assert!(pol.env().is_empty())
1005 }
1006 None => panic!("Linked Record Not Present"),
1007 };
1008 }
1009
1010 #[test]
1011 fn link_slotmap() {
1012 let mut s = PolicySet::new();
1013 let template_id = PolicyID::from_string("template");
1014 let link_id = PolicyID::from_string("link");
1015 let t = Template::new(
1016 template_id.clone(),
1017 None,
1018 Annotations::new(),
1019 Effect::Forbid,
1020 PrincipalConstraint::is_eq_slot(),
1021 ActionConstraint::any(),
1022 ResourceConstraint::any(),
1023 Expr::val(true),
1024 );
1025 s.add_template(t).unwrap();
1026
1027 let mut v = HashMap::new();
1028 let entity = EntityUID::with_eid("eid");
1029 v.insert(SlotId::principal(), entity.clone());
1030 s.link(template_id.clone(), link_id.clone(), v)
1031 .expect("Linking failed!");
1032
1033 let link = s.get(&link_id).expect("Link should exist");
1034 assert_eq!(&link_id, link.id());
1035 assert_eq!(&template_id, link.template().id());
1036 assert_eq!(
1037 &entity,
1038 link.env()
1039 .get(&SlotId::principal())
1040 .expect("Mapping was incorrect")
1041 );
1042 }
1043
1044 #[test]
1045 fn policy_sets() {
1046 let mut pset = PolicySet::new();
1047 assert!(pset.is_empty());
1048 let id1 = PolicyID::from_string("id1");
1049 let tid1 = PolicyID::from_string("template");
1050 let policy1 = StaticPolicy::new(
1051 id1.clone(),
1052 None,
1053 Annotations::new(),
1054 Effect::Permit,
1055 PrincipalConstraint::any(),
1056 ActionConstraint::any(),
1057 ResourceConstraint::any(),
1058 Expr::val(true),
1059 )
1060 .expect("Policy Creation Failed");
1061 let template1 = Template::new(
1062 tid1.clone(),
1063 None,
1064 Annotations::new(),
1065 Effect::Permit,
1066 PrincipalConstraint::any(),
1067 ActionConstraint::any(),
1068 ResourceConstraint::any(),
1069 Expr::val(true),
1070 );
1071 let added = pset.add_static(policy1.clone()).is_ok();
1072 assert!(added);
1073 let added = pset.add_static(policy1).is_ok();
1074 assert!(!added);
1075 let added = pset.add_template(template1.clone()).is_ok();
1076 assert!(added);
1077 let added = pset.add_template(template1).is_ok();
1078 assert!(!added);
1079 assert!(!pset.is_empty());
1080 let id2 = PolicyID::from_string("id2");
1081 let policy2 = StaticPolicy::new(
1082 id2.clone(),
1083 None,
1084 Annotations::new(),
1085 Effect::Forbid,
1086 PrincipalConstraint::is_eq(Arc::new(EntityUID::with_eid("jane"))),
1087 ActionConstraint::any(),
1088 ResourceConstraint::any(),
1089 Expr::val(true),
1090 )
1091 .expect("Policy Creation Failed");
1092 let added = pset.add_static(policy2).is_ok();
1093 assert!(added);
1094
1095 let tid2 = PolicyID::from_string("template2");
1096 let template2 = Template::new(
1097 tid2.clone(),
1098 None,
1099 Annotations::new(),
1100 Effect::Permit,
1101 PrincipalConstraint::is_eq_slot(),
1102 ActionConstraint::any(),
1103 ResourceConstraint::any(),
1104 Expr::val(true),
1105 );
1106 let id3 = PolicyID::from_string("link");
1107 let added = pset.add_template(template2).is_ok();
1108 assert!(added);
1109
1110 let r = pset.link(
1111 tid2.clone(),
1112 id3.clone(),
1113 HashMap::from([(SlotId::principal(), EntityUID::with_eid("example"))]),
1114 );
1115 r.expect("Linking failed");
1116
1117 assert_eq!(pset.get(&id1).expect("should find the policy").id(), &id1);
1118 assert_eq!(pset.get(&id2).expect("should find the policy").id(), &id2);
1119 assert_eq!(pset.get(&id3).expect("should find link").id(), &id3);
1120 assert_eq!(
1121 pset.get(&id3).expect("should find link").template().id(),
1122 &tid2
1123 );
1124 assert!(pset.get(&tid2).is_none());
1125 assert!(pset.get_template_arc(&id1).is_some()); assert!(pset.get_template_arc(&id2).is_some()); assert!(pset.get_template_arc(&tid2).is_some());
1128 assert_eq!(pset.policies().count(), 3);
1129
1130 assert_eq!(
1131 pset.get_template_arc(&tid1)
1132 .expect("should find the template")
1133 .id(),
1134 &tid1
1135 );
1136 assert!(pset.get(&tid1).is_none());
1137 assert_eq!(pset.all_templates().count(), 4);
1138 }
1139}