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