cedar_policy_validator/schema/
action.rs1use cedar_policy_core::{
20 ast::{EntityType, EntityUID, PartialValueSerializedAsExpr},
21 transitive_closure::TCNode,
22};
23use serde::Serialize;
24use smol_str::SmolStr;
25use std::collections::{BTreeMap, HashSet};
26
27use crate::types::{Attributes, Type};
28
29#[derive(Clone, Debug, Serialize)]
33pub struct ValidatorActionId {
34 pub(crate) name: EntityUID,
36
37 #[serde(rename = "appliesTo")]
39 pub(crate) applies_to: ValidatorApplySpec,
40
41 pub(crate) descendants: HashSet<EntityUID>,
46
47 pub(crate) context: Type,
49
50 pub(crate) attribute_types: Attributes,
52
53 pub(crate) attributes: BTreeMap<SmolStr, PartialValueSerializedAsExpr>,
60}
61
62impl ValidatorActionId {
63 pub fn principals(&self) -> impl Iterator<Item = &EntityType> {
65 self.applies_to.principal_apply_spec.iter()
66 }
67
68 pub fn resources(&self) -> impl Iterator<Item = &EntityType> {
70 self.applies_to.resource_apply_spec.iter()
71 }
72
73 pub fn context_type(&self) -> Type {
77 self.context.clone()
78 }
79
80 pub fn applies_to_principals(&self) -> impl Iterator<Item = &EntityType> {
82 self.applies_to.principal_apply_spec.iter()
83 }
84
85 pub fn applies_to_resources(&self) -> impl Iterator<Item = &EntityType> {
87 self.applies_to.resource_apply_spec.iter()
88 }
89}
90
91impl TCNode<EntityUID> for ValidatorActionId {
92 fn get_key(&self) -> EntityUID {
93 self.name.clone()
94 }
95
96 fn add_edge_to(&mut self, k: EntityUID) {
97 self.descendants.insert(k);
98 }
99
100 fn out_edges(&self) -> Box<dyn Iterator<Item = &EntityUID> + '_> {
101 Box::new(self.descendants.iter())
102 }
103
104 fn has_edge_to(&self, e: &EntityUID) -> bool {
105 self.descendants.contains(e)
106 }
107}
108
109#[derive(Clone, Debug, Serialize)]
111pub(crate) struct ValidatorApplySpec {
112 #[serde(rename = "principalApplySpec")]
119 principal_apply_spec: HashSet<EntityType>,
120
121 #[serde(rename = "resourceApplySpec")]
124 resource_apply_spec: HashSet<EntityType>,
125}
126
127impl ValidatorApplySpec {
128 pub fn new(
131 principal_apply_spec: HashSet<EntityType>,
132 resource_apply_spec: HashSet<EntityType>,
133 ) -> Self {
134 Self {
135 principal_apply_spec,
136 resource_apply_spec,
137 }
138 }
139
140 pub fn is_applicable_principal_type(&self, ty: &EntityType) -> bool {
142 self.principal_apply_spec.contains(ty)
143 }
144
145 pub fn applicable_principal_types(&self) -> impl Iterator<Item = &EntityType> {
147 self.principal_apply_spec.iter()
148 }
149
150 pub fn is_applicable_resource_type(&self, ty: &EntityType) -> bool {
152 self.resource_apply_spec.contains(ty)
153 }
154
155 pub fn applicable_resource_types(&self) -> impl Iterator<Item = &EntityType> {
157 self.resource_apply_spec.iter()
158 }
159}
160
161#[cfg(test)]
162mod test {
163 use super::*;
164
165 fn make_action() -> ValidatorActionId {
166 ValidatorActionId {
167 name: r#"Action::"foo""#.parse().unwrap(),
168 applies_to: ValidatorApplySpec {
169 principal_apply_spec: HashSet::from([
170 EntityType::Specified("User".parse().unwrap()),
172 EntityType::Specified("User".parse().unwrap()),
173 ]),
174 resource_apply_spec: HashSet::from([
175 EntityType::Specified("App".parse().unwrap()),
176 EntityType::Specified("File".parse().unwrap()),
177 ]),
178 },
179 descendants: HashSet::new(),
180 context: Type::any_record(),
181 attribute_types: Attributes::default(),
182 attributes: BTreeMap::default(),
183 }
184 }
185
186 #[test]
187 fn test_resources() {
188 let a = make_action();
189 let got = a.resources().cloned().collect::<HashSet<EntityType>>();
190 let expected = HashSet::from([
191 EntityType::Specified("App".parse().unwrap()),
192 EntityType::Specified("File".parse().unwrap()),
193 ]);
194 assert_eq!(got, expected);
195 }
196
197 #[test]
198 fn test_principals() {
199 let a = make_action();
200 let got = a.principals().cloned().collect::<Vec<EntityType>>();
201 let expected: [EntityType; 1] = [EntityType::Specified("User".parse().unwrap())];
202 assert_eq!(got, &expected);
203 }
204}