cedar_policy_validator/cedar_schema/
ast.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
17use std::iter::once;
18
19use cedar_policy_core::{
20    ast::{Id, InternalName},
21    parser::{Loc, Node},
22};
23use itertools::{Either, Itertools};
24use nonempty::NonEmpty;
25use smol_str::SmolStr;
26// We don't need this import on macOS but CI fails without it
27#[allow(unused_imports)]
28use smol_str::ToSmolStr;
29
30use crate::json_schema;
31
32pub const BUILTIN_TYPES: [&str; 3] = ["Long", "String", "Bool"];
33
34pub(super) const CEDAR_NAMESPACE: &str = "__cedar";
35
36pub type Schema = Vec<Node<Namespace>>;
37
38/// A path is a non empty list of identifiers that forms a namespace + type
39#[derive(Debug, Clone, PartialEq, Eq, Hash)]
40pub struct Path(Node<PathInternal>);
41impl Path {
42    /// Create a [`Path`] with a single entry
43    pub fn single(basename: Id, loc: Loc) -> Self {
44        Self(Node::with_source_loc(
45            PathInternal {
46                basename,
47                namespace: vec![],
48            },
49            loc,
50        ))
51    }
52
53    /// Create [`Path`] with a head and an iterator. Most significant name first.
54    pub fn new(basename: Id, namespace: impl IntoIterator<Item = Id>, loc: Loc) -> Self {
55        let namespace = namespace.into_iter().collect();
56        Self(Node::with_source_loc(
57            PathInternal {
58                basename,
59                namespace,
60            },
61            loc,
62        ))
63    }
64
65    /// Borrowed iteration of the [`Path`]'s elements. Most significant name first
66    pub fn iter(&self) -> impl Iterator<Item = &Id> {
67        self.0.node.iter()
68    }
69
70    /// Source [`Loc`] of this [`Path`]
71    pub fn loc(&self) -> &Loc {
72        &self.0.loc
73    }
74
75    /// Consume the [`Path`] and get an owned iterator over the elements. Most significant name first
76    pub fn into_iter(self) -> impl Iterator<Item = Node<Id>> {
77        let loc = self.0.loc;
78        self.0
79            .node
80            .into_iter()
81            .map(move |x| Node::with_source_loc(x, loc.clone()))
82    }
83
84    /// Get the base type name as well as the (potentially empty) prefix
85    pub fn split_last(self) -> (Vec<Id>, Id) {
86        (self.0.node.namespace, self.0.node.basename)
87    }
88
89    /// Is this referring to a name in the `__cedar` namespace (eg: `__cedar::Bool`)
90    pub fn is_in_cedar(&self) -> bool {
91        self.0.node.is_in_cedar()
92    }
93}
94
95impl From<Path> for InternalName {
96    fn from(value: Path) -> Self {
97        InternalName::new(
98            value.0.node.basename,
99            value.0.node.namespace,
100            Some(value.0.loc),
101        )
102    }
103}
104
105impl std::fmt::Display for Path {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        write!(f, "{}", self.0.node)
108    }
109}
110
111#[derive(Debug, Clone, PartialEq, Eq, Hash)]
112struct PathInternal {
113    basename: Id,
114    namespace: Vec<Id>,
115}
116
117impl PathInternal {
118    fn iter(&self) -> impl Iterator<Item = &Id> {
119        self.namespace.iter().chain(once(&self.basename))
120    }
121
122    fn into_iter(self) -> impl Iterator<Item = Id> {
123        self.namespace.into_iter().chain(once(self.basename))
124    }
125
126    /// Is this referring to a name _in_ the `__cedar` namespace (ex: `__cedar::Bool`)
127    fn is_in_cedar(&self) -> bool {
128        match self.namespace.as_slice() {
129            [id] => id.as_ref() == CEDAR_NAMESPACE,
130            _ => false,
131        }
132    }
133}
134
135impl std::fmt::Display for PathInternal {
136    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
137        if self.namespace.is_empty() {
138            write!(f, "{}", self.basename)
139        } else {
140            let namespace = self.namespace.iter().map(|id| id.as_ref()).join("::");
141            write!(f, "{namespace}::{}", self.basename)
142        }
143    }
144}
145
146/// This struct represents Entity Uids in the Schema Syntax
147#[derive(Debug, Clone)]
148pub struct QualName {
149    pub path: Option<Path>,
150    pub eid: SmolStr,
151}
152
153impl QualName {
154    pub fn unqualified(eid: SmolStr) -> Self {
155        Self { path: None, eid }
156    }
157
158    pub fn qualified(path: Path, eid: SmolStr) -> Self {
159        Self {
160            path: Some(path),
161            eid,
162        }
163    }
164}
165
166/// A [`Namespace`] has a name and a collection declaration
167/// A schema is made up of a series of fragments
168/// A fragment is a series of namespaces
169#[derive(Debug, Clone)]
170pub struct Namespace {
171    /// The name of this namespace. If [`None`], then this is the unqualified namespace
172    pub name: Option<Node<Path>>,
173    /// The [`Declaration`]s contained in this namespace
174    pub decls: Vec<Node<Declaration>>,
175}
176
177impl Namespace {
178    /// Is this [`Namespace`] unqualfied?
179    pub fn is_unqualified(&self) -> bool {
180        self.name.is_none()
181    }
182}
183
184pub trait Decl {
185    fn names(&self) -> Vec<Node<SmolStr>>;
186}
187
188/// Schema Declarations,
189/// Defines either entity types, action types, or common types
190#[derive(Debug, Clone)]
191pub enum Declaration {
192    Entity(EntityDecl),
193    Action(ActionDecl),
194    Type(TypeDecl),
195}
196
197#[derive(Debug, Clone)]
198pub struct TypeDecl {
199    pub name: Node<Id>,
200    pub def: Node<Type>,
201}
202
203impl Decl for TypeDecl {
204    fn names(&self) -> Vec<Node<SmolStr>> {
205        vec![self.name.clone().map(|id| id.to_smolstr())]
206    }
207}
208
209/// Declaration of an entity type
210#[derive(Debug, Clone)]
211pub struct EntityDecl {
212    /// Entity Type Names bound by this declaration.
213    /// More than one name can be bound if they have the same definition, for convenience
214    pub names: Vec<Node<Id>>,
215    /// Entity Types this type is allowed to be related to via the `in` relation
216    pub member_of_types: Vec<Path>,
217    /// Attributes this entity has
218    pub attrs: Vec<Node<AttrDecl>>,
219    /// Tag type for this entity (`None` means no tags on this entity)
220    pub tags: Option<Node<Type>>,
221}
222
223/// Type definitions
224#[derive(Debug, Clone)]
225pub enum Type {
226    /// A set of types
227    Set(Box<Node<Type>>),
228    /// A [`Path`] that could either refer to a Common Type or an Entity Type
229    Ident(Path),
230    /// A Record
231    Record(Vec<Node<AttrDecl>>),
232}
233
234/// Primitive Type Definitions
235#[derive(Debug, Clone)]
236pub enum PrimitiveType {
237    /// Cedar Longs
238    Long,
239    /// Cedar Strings
240    String,
241    /// Cedar booleans
242    Bool,
243}
244
245impl<N> From<PrimitiveType> for json_schema::TypeVariant<N> {
246    fn from(value: PrimitiveType) -> Self {
247        match value {
248            PrimitiveType::Long => json_schema::TypeVariant::Long,
249            PrimitiveType::String => json_schema::TypeVariant::String,
250            PrimitiveType::Bool => json_schema::TypeVariant::Boolean,
251        }
252    }
253}
254
255/// Attribute declarations, used in records and entity types.
256/// One [`AttrDecl`] is one key-value pair.
257#[derive(Debug, Clone)]
258pub struct AttrDecl {
259    /// Name of this attribute
260    pub name: Node<SmolStr>,
261    /// Whether or not it is a required attribute (default `true`)
262    pub required: bool,
263    /// The type of this attribute
264    pub ty: Node<Type>,
265}
266
267/// The target of a [`PRAppDecl`]
268#[derive(Debug, Clone, Copy, PartialEq, Eq)]
269pub enum PR {
270    /// Applies to the `principal` variable
271    Principal,
272    /// Applies to the `resource` variable
273    Resource,
274}
275
276impl std::fmt::Display for PR {
277    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
278        match self {
279            PR::Principal => write!(f, "principal"),
280            PR::Resource => write!(f, "resource"),
281        }
282    }
283}
284
285/// A declaration that defines what kind of entities this action can be applied against
286#[derive(Debug, Clone)]
287pub struct PRAppDecl {
288    /// Is this constraining the `principal` or the `resource`
289    pub kind: Node<PR>,
290    /// What entity types are allowed?
291    pub entity_tys: NonEmpty<Path>,
292}
293
294/// A declaration of constraints on an action type
295#[derive(Debug, Clone)]
296pub enum AppDecl {
297    /// Constraints on the `principal` or `resource`
298    PR(PRAppDecl),
299    /// Constraints on the `context`
300    Context(Either<Path, Vec<Node<AttrDecl>>>),
301}
302
303/// An action declaration
304#[derive(Debug, Clone)]
305pub struct ActionDecl {
306    /// The names this declaration is binding.
307    /// More than one name can be bound if they have the same definition, for convenience.
308    pub names: NonEmpty<Node<SmolStr>>,
309    /// The parents of this action
310    pub parents: Option<NonEmpty<Node<QualName>>>,
311    /// The constraining clauses in this declarations
312    pub app_decls: Option<Node<NonEmpty<Node<AppDecl>>>>,
313}
314
315impl Decl for ActionDecl {
316    fn names(&self) -> Vec<Node<SmolStr>> {
317        self.names.iter().cloned().collect()
318    }
319}
320
321#[cfg(test)]
322mod test {
323    use std::sync::Arc;
324
325    use super::*;
326
327    fn loc() -> Loc {
328        Loc::new((1, 1), Arc::from("foo"))
329    }
330
331    // Ensure the iterators over [`Path`]s return most significant names first
332    #[test]
333    fn path_iter() {
334        let p = Path::new(
335            "baz".parse().unwrap(),
336            ["foo".parse().unwrap(), "bar".parse().unwrap()],
337            loc(),
338        );
339
340        let expected: Vec<Id> = vec![
341            "foo".parse().unwrap(),
342            "bar".parse().unwrap(),
343            "baz".parse().unwrap(),
344        ];
345
346        let expected_borrowed = expected.iter().collect::<Vec<_>>();
347
348        let borrowed = p.iter().collect::<Vec<_>>();
349        assert_eq!(borrowed, expected_borrowed);
350        let moved = p.into_iter().map(|n| n.node).collect::<Vec<_>>();
351        assert_eq!(moved, expected);
352    }
353}