cedar_policy/proto/
validator.rs

1/*
2 * Copyright Cedar Contributors
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      https://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#![allow(clippy::use_self)]
18
19use super::models;
20use cedar_policy_core::{ast, evaluator, extensions};
21use cedar_policy_validator::types;
22
23impl From<&cedar_policy_validator::ValidatorSchema> for models::ValidatorSchema {
24    fn from(v: &cedar_policy_validator::ValidatorSchema) -> Self {
25        Self {
26            entity_types: v
27                .entity_types()
28                .map(|ety| models::EntityTypeWithTypesMap {
29                    key: Some(models::EntityType::from(ety.name())),
30                    value: Some(models::ValidatorEntityType::from(ety)),
31                })
32                .collect(),
33            action_ids: v
34                .action_ids()
35                .map(|id| models::EntityUidWithActionIdsMap {
36                    key: Some(models::EntityUid::from(id.name())),
37                    value: Some(models::ValidatorActionId::from(id)),
38                })
39                .collect(),
40        }
41    }
42}
43
44impl From<&models::ValidatorSchema> for cedar_policy_validator::ValidatorSchema {
45    // PANIC SAFETY: experimental feature
46    #[allow(clippy::expect_used)]
47    fn from(v: &models::ValidatorSchema) -> Self {
48        Self::new(
49            v.entity_types
50                .iter()
51                .map(|models::EntityTypeWithTypesMap { key, value }| {
52                    let key = key.as_ref().expect("key field should exist");
53                    let value = value.as_ref().expect("value field should exist");
54                    assert_eq!(key, value.name.as_ref().expect("name field should exist"));
55                    cedar_policy_validator::ValidatorEntityType::from(value)
56                }),
57            v.action_ids
58                .iter()
59                .map(|models::EntityUidWithActionIdsMap { key, value }| {
60                    let key = key.as_ref().expect("key field should exist");
61                    let value = value.as_ref().expect("value field should exist");
62                    assert_eq!(key, value.name.as_ref().expect("name field should exist"));
63                    cedar_policy_validator::ValidatorActionId::from(value)
64                }),
65        )
66    }
67}
68
69impl From<&cedar_policy_validator::ValidationMode> for models::ValidationMode {
70    // PANIC SAFETY: experimental feature
71    #[allow(clippy::unimplemented)]
72    fn from(v: &cedar_policy_validator::ValidationMode) -> Self {
73        match v {
74            cedar_policy_validator::ValidationMode::Strict => models::ValidationMode::Strict,
75            cedar_policy_validator::ValidationMode::Permissive => {
76                models::ValidationMode::Permissive
77            }
78            #[cfg(feature = "partial-validate")]
79            cedar_policy_validator::ValidationMode::Partial => unimplemented!(),
80        }
81    }
82}
83
84impl From<&models::ValidationMode> for cedar_policy_validator::ValidationMode {
85    fn from(v: &models::ValidationMode) -> Self {
86        match v {
87            models::ValidationMode::Strict => cedar_policy_validator::ValidationMode::Strict,
88            models::ValidationMode::Permissive => {
89                cedar_policy_validator::ValidationMode::Permissive
90            }
91        }
92    }
93}
94
95impl From<&cedar_policy_validator::ValidatorActionId> for models::ValidatorActionId {
96    fn from(v: &cedar_policy_validator::ValidatorActionId) -> Self {
97        Self {
98            name: Some(models::EntityUid::from(v.name())),
99            applies_to: Some(models::ValidatorApplySpec {
100                principal_apply_spec: v
101                    .applies_to_principals()
102                    .map(models::EntityType::from)
103                    .collect(),
104                resource_apply_spec: v
105                    .applies_to_resources()
106                    .map(models::EntityType::from)
107                    .collect(),
108            }),
109            descendants: v.descendants().map(models::EntityUid::from).collect(),
110            context: Some(models::Type::from(v.context())),
111            attribute_types: Some(models::Attributes::from(v.attribute_types())),
112            attributes: v
113                .attributes()
114                .map(|(k, v)| {
115                    let value =
116                        models::Expr::from(&ast::Expr::from(ast::PartialValue::from(v.to_owned())));
117                    (k.to_string(), value)
118                })
119                .collect(),
120        }
121    }
122}
123
124impl From<&models::ValidatorActionId> for cedar_policy_validator::ValidatorActionId {
125    // PANIC SAFETY: experimental feature
126    #[allow(clippy::expect_used)]
127    fn from(v: &models::ValidatorActionId) -> Self {
128        let extensions_none = extensions::Extensions::none();
129        let eval = evaluator::RestrictedEvaluator::new(extensions_none);
130        Self::new(
131            ast::EntityUID::from(v.name.as_ref().expect("name field should exist")),
132            v.applies_to
133                .as_ref()
134                .expect("applies_to field should exist")
135                .principal_apply_spec
136                .iter()
137                .map(ast::EntityType::from),
138            v.applies_to
139                .as_ref()
140                .expect("applies_to field should exist")
141                .resource_apply_spec
142                .iter()
143                .map(ast::EntityType::from),
144            v.descendants.iter().map(ast::EntityUID::from),
145            types::Type::from(v.context.as_ref().expect("context field should exist")),
146            types::Attributes::from(
147                v.attribute_types
148                    .as_ref()
149                    .expect("attribute_types field should exist"),
150            ),
151            v.attributes
152                .iter()
153                .map(|(k, v)| {
154                    let pval = eval
155                        .partial_interpret(
156                            ast::BorrowedRestrictedExpr::new(&ast::Expr::from(v))
157                                .expect("RestrictedExpr"),
158                        )
159                        .expect("interpret on RestrictedExpr");
160                    (k.into(), pval.into())
161                })
162                .collect(),
163        )
164    }
165}
166
167impl From<&cedar_policy_validator::ValidatorEntityType> for models::ValidatorEntityType {
168    fn from(v: &cedar_policy_validator::ValidatorEntityType) -> Self {
169        Self {
170            name: Some(models::EntityType::from(v.name())),
171            descendants: v.descendants.iter().map(models::EntityType::from).collect(),
172            attributes: Some(models::Attributes::from(v.attributes())),
173            open_attributes: models::OpenTag::from(&v.open_attributes()).into(),
174            tags: v.tag_type().map(|tags| models::Tag {
175                optional_type: Some(models::Type::from(tags)),
176            }),
177        }
178    }
179}
180
181impl From<&models::ValidatorEntityType> for cedar_policy_validator::ValidatorEntityType {
182    // PANIC SAFETY: experimental feature
183    #[allow(clippy::expect_used)]
184    fn from(v: &models::ValidatorEntityType) -> Self {
185        Self::new(
186            ast::EntityType::from(v.name.as_ref().expect("name field should exist")),
187            v.descendants.iter().map(ast::EntityType::from),
188            types::Attributes::from(
189                v.attributes
190                    .as_ref()
191                    .expect("attributes field should exist"),
192            ),
193            types::OpenTag::from(
194                &models::OpenTag::try_from(v.open_attributes).expect("decode should succeed"),
195            ),
196            v.tags
197                .as_ref()
198                .and_then(|tags| tags.optional_type.as_ref().map(types::Type::from)),
199        )
200    }
201}
202
203impl From<&models::Type> for types::Type {
204    // PANIC SAFETY: experimental feature
205    #[allow(clippy::expect_used)]
206    fn from(v: &models::Type) -> Self {
207        match v.data.as_ref().expect("data field should exist") {
208            models::r#type::Data::Ty(vt) => {
209                match models::r#type::Ty::try_from(vt.to_owned()).expect("decode should succeed") {
210                    models::r#type::Ty::Never => types::Type::Never,
211                    models::r#type::Ty::True => types::Type::True,
212                    models::r#type::Ty::False => types::Type::False,
213                    models::r#type::Ty::EmptySetType => types::Type::Set { element_type: None },
214                    models::r#type::Ty::Bool => types::Type::primitive_boolean(),
215                    models::r#type::Ty::String => types::Type::primitive_string(),
216                    models::r#type::Ty::Long => types::Type::primitive_long(),
217                }
218            }
219            models::r#type::Data::SetType(tt) => types::Type::Set {
220                element_type: Some(Box::new(types::Type::from(tt.as_ref()))),
221            },
222            models::r#type::Data::EntityOrRecord(er) => {
223                types::Type::EntityOrRecord(types::EntityRecordKind::from(er))
224            }
225            models::r#type::Data::Name(name) => types::Type::ExtensionType {
226                name: ast::Name::from(name),
227            },
228        }
229    }
230}
231
232impl From<&types::Type> for models::Type {
233    // PANIC SAFETY: experimental feature
234    #[allow(clippy::expect_used)]
235    fn from(v: &types::Type) -> Self {
236        match v {
237            types::Type::Never => Self {
238                data: Some(models::r#type::Data::Ty(models::r#type::Ty::Never.into())),
239            },
240            types::Type::True => Self {
241                data: Some(models::r#type::Data::Ty(models::r#type::Ty::True.into())),
242            },
243
244            types::Type::False => Self {
245                data: Some(models::r#type::Data::Ty(models::r#type::Ty::False.into())),
246            },
247            types::Type::Primitive { primitive_type } => match primitive_type {
248                types::Primitive::Bool => Self {
249                    data: Some(models::r#type::Data::Ty(models::r#type::Ty::Bool.into())),
250                },
251                types::Primitive::Long => Self {
252                    data: Some(models::r#type::Data::Ty(models::r#type::Ty::Long.into())),
253                },
254                types::Primitive::String => Self {
255                    data: Some(models::r#type::Data::Ty(models::r#type::Ty::String.into())),
256                },
257            },
258            types::Type::Set { element_type } => Self {
259                data: Some(models::r#type::Data::SetType(Box::new(models::Type::from(
260                    element_type
261                        .as_ref()
262                        .expect("element_type field should exist")
263                        .as_ref(),
264                )))),
265            },
266            types::Type::EntityOrRecord(er) => Self {
267                data: Some(models::r#type::Data::EntityOrRecord(
268                    models::EntityRecordKind::from(er),
269                )),
270            },
271            types::Type::ExtensionType { name } => Self {
272                data: Some(models::r#type::Data::Name(models::Name::from(name))),
273            },
274        }
275    }
276}
277
278impl From<&models::Attributes> for types::Attributes {
279    fn from(v: &models::Attributes) -> Self {
280        Self::with_attributes(
281            v.attrs
282                .iter()
283                .map(|(k, v)| (k.into(), types::AttributeType::from(v))),
284        )
285    }
286}
287
288impl From<&types::Attributes> for models::Attributes {
289    fn from(v: &types::Attributes) -> Self {
290        Self {
291            attrs: v
292                .iter()
293                .map(|(k, v)| (k.to_string(), models::AttributeType::from(v)))
294                .collect(),
295        }
296    }
297}
298
299impl From<&models::OpenTag> for types::OpenTag {
300    fn from(v: &models::OpenTag) -> Self {
301        match v {
302            models::OpenTag::OpenAttributes => types::OpenTag::OpenAttributes,
303            models::OpenTag::ClosedAttributes => types::OpenTag::ClosedAttributes,
304        }
305    }
306}
307
308impl From<&types::OpenTag> for models::OpenTag {
309    fn from(v: &types::OpenTag) -> Self {
310        match v {
311            types::OpenTag::OpenAttributes => models::OpenTag::OpenAttributes,
312            types::OpenTag::ClosedAttributes => models::OpenTag::ClosedAttributes,
313        }
314    }
315}
316
317impl From<&models::EntityRecordKind> for types::EntityRecordKind {
318    // PANIC SAFETY: experimental feature
319    #[allow(clippy::expect_used)]
320    fn from(v: &models::EntityRecordKind) -> Self {
321        match v.data.as_ref().expect("data field should exist") {
322            models::entity_record_kind::Data::Ty(ty) => {
323                match models::entity_record_kind::Ty::try_from(ty.to_owned())
324                    .expect("decode should succeed")
325                {
326                    models::entity_record_kind::Ty::AnyEntity => Self::AnyEntity,
327                }
328            }
329            models::entity_record_kind::Data::Record(p_record) => Self::Record {
330                attrs: types::Attributes::from(
331                    p_record.attrs.as_ref().expect("attrs field should exist"),
332                ),
333                open_attributes: types::OpenTag::from(
334                    &models::OpenTag::try_from(p_record.open_attributes)
335                        .expect("decode should succeed"),
336                ),
337            },
338            models::entity_record_kind::Data::Entity(p_entity) => {
339                Self::Entity(types::EntityLUB::single_entity(ast::EntityType::from(
340                    p_entity.e.as_ref().expect("e field should exist"),
341                )))
342            }
343            models::entity_record_kind::Data::ActionEntity(p_action_entity) => Self::ActionEntity {
344                name: ast::EntityType::from(
345                    p_action_entity
346                        .name
347                        .as_ref()
348                        .expect("name field should exist"),
349                ),
350                attrs: types::Attributes::from(
351                    p_action_entity
352                        .attrs
353                        .as_ref()
354                        .expect("attrs field should exist"),
355                ),
356            },
357        }
358    }
359}
360
361impl From<&types::EntityRecordKind> for models::EntityRecordKind {
362    // PANIC SAFETY: experimental feature
363    #[allow(clippy::expect_used)]
364    fn from(v: &types::EntityRecordKind) -> Self {
365        let data = match v {
366            types::EntityRecordKind::Record {
367                attrs,
368                open_attributes,
369            } => models::entity_record_kind::Data::Record(models::entity_record_kind::Record {
370                attrs: Some(models::Attributes::from(attrs)),
371                open_attributes: models::OpenTag::from(open_attributes).into(),
372            }),
373            types::EntityRecordKind::AnyEntity => models::entity_record_kind::Data::Ty(
374                models::entity_record_kind::Ty::AnyEntity.into(),
375            ),
376            types::EntityRecordKind::Entity(e) => {
377                models::entity_record_kind::Data::Entity(models::entity_record_kind::Entity {
378                    e: Some(models::EntityType::from(
379                        &e.clone()
380                            .into_single_entity()
381                            .expect("will be single EntityType"),
382                    )),
383                })
384            }
385            types::EntityRecordKind::ActionEntity { name, attrs } => {
386                models::entity_record_kind::Data::ActionEntity(
387                    models::entity_record_kind::ActionEntity {
388                        name: Some(models::EntityType::from(name)),
389                        attrs: Some(models::Attributes::from(attrs)),
390                    },
391                )
392            }
393        };
394        Self { data: Some(data) }
395    }
396}
397
398impl From<&models::AttributeType> for types::AttributeType {
399    // PANIC SAFETY: experimental feature
400    #[allow(clippy::expect_used)]
401    fn from(v: &models::AttributeType) -> Self {
402        Self {
403            attr_type: types::Type::from(
404                v.attr_type.as_ref().expect("attr_type field should exist"),
405            ),
406            is_required: v.is_required,
407        }
408    }
409}
410
411impl From<&types::AttributeType> for models::AttributeType {
412    fn from(v: &types::AttributeType) -> Self {
413        Self {
414            attr_type: Some(models::Type::from(&v.attr_type)),
415            is_required: v.is_required,
416        }
417    }
418}