cedar_policy_core/entities/json/
value.rs

1/*
2 * Copyright 2022-2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
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 super::{
18    JsonDeserializationError, JsonDeserializationErrorContext, JsonSerializationError, SchemaType,
19};
20use crate::ast::{
21    BorrowedRestrictedExpr, Eid, EntityUID, ExprConstructionError, ExprKind, Literal, Name,
22    RestrictedExpr, Unknown, Value,
23};
24use crate::entities::{
25    schematype_of_restricted_expr, unwrap_or_clone, EntitySchemaConformanceError, EscapeKind,
26    GetSchemaTypeError, TypeMismatchError,
27};
28use crate::extensions::Extensions;
29use crate::FromNormalizedStr;
30use either::Either;
31use serde::{Deserialize, Serialize};
32use serde_with::serde_as;
33use smol_str::SmolStr;
34use std::collections::{BTreeMap, HashSet};
35
36/// The canonical JSON representation of a Cedar value.
37/// Many Cedar values have a natural one-to-one mapping to and from JSON values.
38/// Cedar values of some types, like entity references or extension values,
39/// cannot easily be represented in JSON and thus are represented using the
40/// `__entity`, or `__extn` escapes.
41///
42/// For example, this is the JSON format for attribute values expected by
43/// `EntityJsonParser`, when schema-based parsing is not used.
44#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
45#[serde(untagged)]
46pub enum CedarValueJson {
47    /// The `__expr` escape has been removed, but is still reserved in order to throw meaningful errors.
48    ExprEscape {
49        /// Contents, will be ignored and an error is thrown when attempting to parse this
50        __expr: SmolStr,
51    },
52    /// Special JSON object with single reserved "__entity" key:
53    /// the following item should be a JSON object of the form
54    /// `{ "type": "xxx", "id": "yyy" }`.
55    /// This escape is necessary for entity references.
56    //
57    // listed before `Record` so that it takes priority: otherwise, the escape
58    // would be interpreted as a Record with a key "__entity". see docs on
59    // `serde(untagged)`
60    EntityEscape {
61        /// JSON object containing the entity type and ID
62        __entity: TypeAndId,
63    },
64    /// Special JSON object with single reserved "__extn" key:
65    /// the following item should be a JSON object of the form
66    /// `{ "fn": "xxx", "arg": "yyy" }`.
67    /// This escape is necessary for extension values.
68    //
69    // listed before `Record` so that it takes priority: otherwise, the escape
70    // would be interpreted as a Record with a key "__extn". see docs on
71    // `serde(untagged)`
72    ExtnEscape {
73        /// JSON object containing the extension-constructor call
74        __extn: FnAndArg,
75    },
76    /// JSON bool => Cedar bool
77    Bool(bool),
78    /// JSON int => Cedar long (64-bit signed integer)
79    Long(i64),
80    /// JSON string => Cedar string
81    String(SmolStr),
82    /// JSON list => Cedar set; can contain any `CedarValueJson`s, even
83    /// heterogeneously
84    Set(Vec<CedarValueJson>),
85    /// JSON object => Cedar record; must have string keys, but values
86    /// can be any `CedarValueJson`s, even heterogeneously
87    Record(JsonRecord),
88}
89
90/// Structure representing a Cedar record in JSON
91#[serde_as]
92#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
93pub struct JsonRecord {
94    /// Cedar records must have string keys, but values can be any
95    /// `CedarValueJson`s, even heterogeneously
96    #[serde_as(as = "serde_with::MapPreventDuplicates<_, _>")]
97    #[serde(flatten)]
98    values: BTreeMap<SmolStr, CedarValueJson>,
99}
100
101impl IntoIterator for JsonRecord {
102    type Item = (SmolStr, CedarValueJson);
103    type IntoIter = <BTreeMap<SmolStr, CedarValueJson> as IntoIterator>::IntoIter;
104    fn into_iter(self) -> Self::IntoIter {
105        self.values.into_iter()
106    }
107}
108
109impl<'a> IntoIterator for &'a JsonRecord {
110    type Item = (&'a SmolStr, &'a CedarValueJson);
111    type IntoIter = <&'a BTreeMap<SmolStr, CedarValueJson> as IntoIterator>::IntoIter;
112    fn into_iter(self) -> Self::IntoIter {
113        self.values.iter()
114    }
115}
116
117// At this time, this doesn't check for duplicate keys upon constructing a
118// `JsonRecord` from an iterator.
119// As of this writing, we only construct `JsonRecord` from an iterator during
120// _serialization_, not _deserialization_, and we can assume that values being
121// serialized (i.e., coming from the Cedar engine itself) are already free of
122// duplicate keys.
123impl FromIterator<(SmolStr, CedarValueJson)> for JsonRecord {
124    fn from_iter<T: IntoIterator<Item = (SmolStr, CedarValueJson)>>(iter: T) -> Self {
125        Self {
126            values: BTreeMap::from_iter(iter),
127        }
128    }
129}
130
131impl JsonRecord {
132    /// Iterate over the (k, v) pairs in the record
133    pub fn iter(&self) -> impl Iterator<Item = (&'_ SmolStr, &'_ CedarValueJson)> {
134        self.values.iter()
135    }
136
137    /// Get the number of attributes in the record
138    pub fn len(&self) -> usize {
139        self.values.len()
140    }
141
142    /// Is the record empty (no attributes)
143    pub fn is_empty(&self) -> bool {
144        self.values.is_empty()
145    }
146}
147
148/// Structure expected by the `__entity` escape
149#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
150pub struct TypeAndId {
151    /// Entity typename
152    #[serde(rename = "type")]
153    entity_type: SmolStr,
154    /// Entity id
155    id: SmolStr,
156}
157
158impl From<EntityUID> for TypeAndId {
159    fn from(euid: EntityUID) -> TypeAndId {
160        let (entity_type, eid) = euid.components();
161        TypeAndId {
162            entity_type: entity_type.to_string().into(),
163            id: AsRef::<str>::as_ref(&eid).into(),
164        }
165    }
166}
167
168impl From<&EntityUID> for TypeAndId {
169    fn from(euid: &EntityUID) -> TypeAndId {
170        TypeAndId {
171            entity_type: euid.entity_type().to_string().into(),
172            id: AsRef::<str>::as_ref(&euid.eid()).into(),
173        }
174    }
175}
176
177impl TryFrom<TypeAndId> for EntityUID {
178    type Error = crate::parser::err::ParseErrors;
179
180    fn try_from(e: TypeAndId) -> Result<EntityUID, Self::Error> {
181        Ok(EntityUID::from_components(
182            Name::from_normalized_str(&e.entity_type)?,
183            Eid::new(e.id),
184        ))
185    }
186}
187
188/// Structure expected by the `__extn` escape
189#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
190pub struct FnAndArg {
191    /// Extension constructor function
192    #[serde(rename = "fn")]
193    pub(crate) ext_fn: SmolStr,
194    /// Argument to that constructor
195    pub(crate) arg: Box<CedarValueJson>,
196}
197
198impl CedarValueJson {
199    /// Encode the given `EntityUID` as a `CedarValueJson`
200    pub fn uid(euid: &EntityUID) -> Self {
201        Self::EntityEscape {
202            __entity: TypeAndId::from(euid.clone()),
203        }
204    }
205
206    /// Convert this `CedarValueJson` into a Cedar "restricted expression"
207    pub fn into_expr(
208        self,
209        ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
210    ) -> Result<RestrictedExpr, JsonDeserializationError> {
211        match self {
212            Self::Bool(b) => Ok(RestrictedExpr::val(b)),
213            Self::Long(i) => Ok(RestrictedExpr::val(i)),
214            Self::String(s) => Ok(RestrictedExpr::val(s)),
215            Self::Set(vals) => Ok(RestrictedExpr::set(
216                vals.into_iter()
217                    .map(|v| v.into_expr(ctx.clone()))
218                    .collect::<Result<Vec<_>, _>>()?,
219            )),
220            Self::Record(map) => Ok(RestrictedExpr::record(
221                map.into_iter()
222                    .map(|(k, v)| Ok((k, v.into_expr(ctx.clone())?)))
223                    .collect::<Result<Vec<_>, JsonDeserializationError>>()?,
224            )
225            .map_err(|e| match e {
226                ExprConstructionError::DuplicateKeyInRecordLiteral { key } => {
227                    JsonDeserializationError::DuplicateKeyInRecordLiteral {
228                        ctx: Box::new(ctx()),
229                        key,
230                    }
231                }
232            })?),
233            Self::EntityEscape { __entity: entity } => Ok(RestrictedExpr::val(
234                EntityUID::try_from(entity.clone()).map_err(|errs| {
235                    JsonDeserializationError::ParseEscape {
236                        kind: EscapeKind::Entity,
237                        value: serde_json::to_string_pretty(&entity)
238                            .unwrap_or_else(|_| format!("{:?}", &entity)),
239                        errs,
240                    }
241                })?,
242            )),
243            Self::ExtnEscape { __extn: extn } => extn.into_expr(ctx),
244            Self::ExprEscape { .. } => Err(JsonDeserializationError::ExprTag(Box::new(
245                JsonDeserializationErrorContext::Context,
246            ))),
247        }
248    }
249
250    /// Convert a Cedar "restricted expression" into a `CedarValueJson`.
251    pub fn from_expr(expr: BorrowedRestrictedExpr<'_>) -> Result<Self, JsonSerializationError> {
252        match expr.as_ref().expr_kind() {
253            ExprKind::Lit(lit) => Ok(Self::from_lit(lit.clone())),
254            ExprKind::ExtensionFunctionApp { fn_name, args } => match args.len() {
255                0 => Err(JsonSerializationError::ExtnCall0Arguments {
256                    func: fn_name.clone(),
257                }),
258                // PANIC SAFETY. We've checked that `args` is of length 1, fine to index at 0
259                #[allow(clippy::indexing_slicing)]
260                1 => Ok(Self::ExtnEscape {
261                    __extn: FnAndArg {
262                        ext_fn: fn_name.to_string().into(),
263                        arg: Box::new(CedarValueJson::from_expr(
264                            // assuming the invariant holds for `expr`, it must also hold here
265                            BorrowedRestrictedExpr::new_unchecked(
266                                &args[0], // checked above that |args| == 1
267                            ),
268                        )?),
269                    },
270                }),
271                _ => Err(JsonSerializationError::ExtnCall2OrMoreArguments {
272                    func: fn_name.clone(),
273                }),
274            },
275            ExprKind::Set(exprs) => Ok(Self::Set(
276                exprs
277                    .iter()
278                    .map(BorrowedRestrictedExpr::new_unchecked) // assuming the invariant holds for `expr`, it must also hold here
279                    .map(CedarValueJson::from_expr)
280                    .collect::<Result<_, JsonSerializationError>>()?,
281            )),
282            ExprKind::Record(map) => {
283                // if `map` contains a key which collides with one of our JSON
284                // escapes, then we have a problem because it would be interpreted
285                // as an escape when being read back in.
286                check_for_reserved_keys(map.keys())?;
287                Ok(Self::Record(
288                    map.iter()
289                        .map(|(k, v)| {
290                            Ok((
291                                k.clone(),
292                                CedarValueJson::from_expr(
293                                    // assuming the invariant holds for `expr`, it must also hold here
294                                    BorrowedRestrictedExpr::new_unchecked(v),
295                                )?,
296                            ))
297                        })
298                        .collect::<Result<_, JsonSerializationError>>()?,
299                ))
300            }
301            kind => {
302                Err(JsonSerializationError::UnexpectedRestrictedExprKind { kind: kind.clone() })
303            }
304        }
305    }
306
307    /// Convert a Cedar value into a `CedarValueJson`.
308    ///
309    /// Only throws errors in two cases:
310    /// 1. `value` is (or contains) a record with a reserved key such as
311    ///     "__entity"
312    /// 2. `value` is (or contains) an extension value, and the argument to the
313    ///     extension constructor that produced that extension value can't
314    ///     itself be converted to `CedarJsonValue`. (Either because that
315    ///     argument falls into one of these two cases itself, or because the
316    ///     argument is a nontrivial residual.)
317    pub fn from_value(value: Value) -> Result<Self, JsonSerializationError> {
318        match value {
319            Value::Lit(lit) => Ok(Self::from_lit(lit)),
320            Value::Set(set) => Ok(Self::Set(
321                set.iter()
322                    .cloned()
323                    .map(Self::from_value)
324                    .collect::<Result<_, _>>()?,
325            )),
326            Value::Record(map) => {
327                // if `map` contains a key which collides with one of our JSON
328                // escapes, then we have a problem because it would be interpreted
329                // as an escape when being read back in.
330                check_for_reserved_keys(map.keys())?;
331                Ok(Self::Record(
332                    map.iter()
333                        .map(|(k, v)| Ok((k.clone(), Self::from_value(v.clone())?)))
334                        .collect::<Result<JsonRecord, JsonSerializationError>>()?,
335                ))
336            }
337            Value::ExtensionValue(ev) => {
338                let ext_fn: &Name = &ev.constructor;
339                Ok(Self::ExtnEscape {
340                    __extn: FnAndArg {
341                        ext_fn: ext_fn.to_string().into(),
342                        arg: match ev.args.as_slice() {
343                            [ref expr] => Box::new(Self::from_expr(expr.as_borrowed())?),
344                            [] => {
345                                return Err(JsonSerializationError::ExtnCall0Arguments {
346                                    func: ext_fn.clone(),
347                                })
348                            }
349                            _ => {
350                                return Err(JsonSerializationError::ExtnCall2OrMoreArguments {
351                                    func: ext_fn.clone(),
352                                })
353                            }
354                        },
355                    },
356                })
357            }
358        }
359    }
360
361    /// Convert a Cedar literal into a `CedarValueJson`.
362    pub fn from_lit(lit: Literal) -> Self {
363        match lit {
364            Literal::Bool(b) => Self::Bool(b),
365            Literal::Long(i) => Self::Long(i),
366            Literal::String(s) => Self::String(s),
367            Literal::EntityUID(euid) => Self::EntityEscape {
368                __entity: unwrap_or_clone(euid).into(),
369            },
370        }
371    }
372}
373
374/// helper function to check if the given keys contain any reserved keys,
375/// throwing an appropriate `JsonSerializationError` if so
376fn check_for_reserved_keys<'a>(
377    mut keys: impl Iterator<Item = &'a SmolStr>,
378) -> Result<(), JsonSerializationError> {
379    // We could be a little more permissive here, but to be
380    // conservative, we throw an error for any record that contains
381    // any key with a reserved name, not just single-key records
382    // with the reserved names.
383    let reserved_keys: HashSet<&str> = HashSet::from_iter(["__entity", "__extn", "__expr"]);
384    let collision = keys.find(|k| reserved_keys.contains(k.as_str()));
385    match collision {
386        Some(collision) => Err(JsonSerializationError::ReservedKey {
387            key: collision.clone(),
388        }),
389        None => Ok(()),
390    }
391}
392
393impl FnAndArg {
394    /// Convert this `FnAndArg` into a Cedar "restricted expression" (which will be a call to an extension constructor)
395    pub fn into_expr(
396        self,
397        ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
398    ) -> Result<RestrictedExpr, JsonDeserializationError> {
399        Ok(RestrictedExpr::call_extension_fn(
400            Name::from_normalized_str(&self.ext_fn).map_err(|errs| {
401                JsonDeserializationError::ParseEscape {
402                    kind: EscapeKind::Extension,
403                    value: self.ext_fn.to_string(),
404                    errs,
405                }
406            })?,
407            vec![CedarValueJson::into_expr(*self.arg, ctx)?],
408        ))
409    }
410}
411
412/// Struct used to parse Cedar values from JSON.
413#[derive(Debug, Clone)]
414pub struct ValueParser<'e> {
415    /// Extensions which are active for the JSON parsing.
416    extensions: Extensions<'e>,
417}
418
419impl<'e> ValueParser<'e> {
420    /// Create a new `ValueParser`.
421    pub fn new(extensions: Extensions<'e>) -> Self {
422        Self { extensions }
423    }
424
425    /// internal function that converts a Cedar value (in JSON) into a
426    /// `RestrictedExpr`. Performs schema-based parsing if `expected_ty` is
427    /// provided. This does not mean that this function fully validates the
428    /// value against `expected_ty` -- it does not.
429    pub fn val_into_restricted_expr(
430        &self,
431        val: serde_json::Value,
432        expected_ty: Option<&SchemaType>,
433        ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
434    ) -> Result<RestrictedExpr, JsonDeserializationError> {
435        // First we have to check if we've been given an Unknown. This is valid
436        // regardless of the expected type (see #418).
437        let parse_as_unknown = |val: serde_json::Value| {
438            let extjson: ExtnValueJson = serde_json::from_value(val).ok()?;
439            match extjson {
440                ExtnValueJson::ExplicitExtnEscape {
441                    __extn: FnAndArg { ext_fn, arg },
442                } if ext_fn == "unknown" => {
443                    let arg = arg.into_expr(ctx.clone()).ok()?;
444                    let name = arg.as_string()?;
445                    Some(RestrictedExpr::unknown(Unknown::new_untyped(name.clone())))
446                }
447                _ => None, // only explicit `__extn` escape is valid for this purpose. For instance, if we allowed `ImplicitConstructor` here, then all strings would parse as calls to `unknown()`, which is clearly not what we want.
448            }
449        };
450        if let Some(rexpr) = parse_as_unknown(val.clone()) {
451            return Ok(rexpr);
452        }
453        // otherwise, we do normal schema-based parsing based on the expected type.
454        match expected_ty {
455            // The expected type is an entity reference. Special parsing rules
456            // apply: for instance, the `__entity` escape can optionally be omitted.
457            // What this means is that we parse the contents as `EntityUidJson`, and
458            // then convert that into an entity reference `RestrictedExpr`
459            Some(SchemaType::Entity { .. }) => {
460                let uidjson: EntityUidJson = serde_json::from_value(val)?;
461                Ok(RestrictedExpr::val(uidjson.into_euid(ctx)?))
462            }
463            // The expected type is an extension type. Special parsing rules apply:
464            // for instance, the `__extn` escape can optionally be omitted. What
465            // this means is that we parse the contents as `ExtnValueJson`, and then
466            // convert that into an extension-function-call `RestrictedExpr`
467            Some(SchemaType::Extension { ref name, .. }) => {
468                let extjson: ExtnValueJson = serde_json::from_value(val)?;
469                self.extn_value_json_into_rexpr(extjson, name.clone(), ctx)
470            }
471            // The expected type is a set type. No special parsing rules apply, but
472            // we need to parse the elements according to the expected element type
473            Some(expected_ty @ SchemaType::Set { element_ty }) => match val {
474                serde_json::Value::Array(elements) => Ok(RestrictedExpr::set(
475                    elements
476                        .into_iter()
477                        .map(|element| {
478                            self.val_into_restricted_expr(element, Some(element_ty), ctx.clone())
479                        })
480                        .collect::<Result<Vec<RestrictedExpr>, JsonDeserializationError>>()?,
481                )),
482                val => {
483                    let actual_val = {
484                        let jvalue: CedarValueJson = serde_json::from_value(val)?;
485                        jvalue.into_expr(ctx.clone())?
486                    };
487                    let err = TypeMismatchError {
488                        expected: Box::new(expected_ty.clone()),
489                        actual_ty: match schematype_of_restricted_expr(
490                            actual_val.as_borrowed(),
491                            self.extensions,
492                        ) {
493                            Ok(actual_ty) => Some(Box::new(actual_ty)),
494                            Err(_) => None, // just don't report the type if there was an error computing it
495                        },
496                        actual_val: Either::Right(Box::new(actual_val)),
497                    };
498                    match ctx() {
499                        JsonDeserializationErrorContext::EntityAttribute { uid, attr } => {
500                            Err(JsonDeserializationError::EntitySchemaConformance(
501                                EntitySchemaConformanceError::TypeMismatch { uid, attr, err },
502                            ))
503                        }
504                        ctx => Err(JsonDeserializationError::TypeMismatch {
505                            ctx: Box::new(ctx),
506                            err,
507                        }),
508                    }
509                }
510            },
511            // The expected type is a record type. No special parsing rules
512            // apply, but we need to parse the attribute values according to
513            // their expected element types
514            Some(
515                expected_ty @ SchemaType::Record {
516                    attrs: expected_attrs,
517                    open_attrs,
518                },
519            ) => match val {
520                serde_json::Value::Object(mut actual_attrs) => {
521                    let ctx2 = ctx.clone(); // for borrow-check, so the original `ctx` can be moved into the closure below
522                    let mut_actual_attrs = &mut actual_attrs; // for borrow-check, so only a mut ref gets moved into the closure, and we retain ownership of `actual_attrs`
523                    let rexpr_pairs = expected_attrs
524                        .iter()
525                        .filter_map(move |(k, expected_attr_ty)| {
526                            match mut_actual_attrs.remove(k.as_str()) {
527                                Some(actual_attr) => {
528                                    match self.val_into_restricted_expr(actual_attr, Some(expected_attr_ty.schema_type()), ctx.clone()) {
529                                        Ok(actual_attr) => Some(Ok((k.clone(), actual_attr))),
530                                        Err(e) => Some(Err(e)),
531                                    }
532                                }
533                                None if expected_attr_ty.is_required() => Some(Err(JsonDeserializationError::MissingRequiredRecordAttr {
534                                    ctx: Box::new(ctx()),
535                                    record_attr: k.clone(),
536                                })),
537                                None => None,
538                            }
539                        })
540                        .collect::<Result<Vec<(SmolStr, RestrictedExpr)>, JsonDeserializationError>>()?;
541
542                    if !open_attrs {
543                        // we've now checked that all expected attrs exist, and removed them from `actual_attrs`.
544                        // we still need to verify that we didn't have any unexpected attrs.
545                        if let Some((record_attr, _)) = actual_attrs.into_iter().next() {
546                            return Err(JsonDeserializationError::UnexpectedRecordAttr {
547                                ctx: Box::new(ctx2()),
548                                record_attr: record_attr.into(),
549                            });
550                        }
551                    }
552
553                    // having duplicate keys should be impossible here (because
554                    // neither `actual_attrs` nor `expected_attrs` can have
555                    // duplicate keys; they're both maps), but we can still throw
556                    // the error properly in the case that it somehow happens
557                    RestrictedExpr::record(rexpr_pairs).map_err(|e| match e {
558                        ExprConstructionError::DuplicateKeyInRecordLiteral { key } => {
559                            JsonDeserializationError::DuplicateKeyInRecordLiteral {
560                                ctx: Box::new(ctx2()),
561                                key,
562                            }
563                        }
564                    })
565                }
566                val => {
567                    let actual_val = {
568                        let jvalue: CedarValueJson = serde_json::from_value(val)?;
569                        jvalue.into_expr(ctx.clone())?
570                    };
571                    let err = TypeMismatchError {
572                        expected: Box::new(expected_ty.clone()),
573                        actual_ty: match schematype_of_restricted_expr(
574                            actual_val.as_borrowed(),
575                            self.extensions,
576                        ) {
577                            Ok(actual_ty) => Some(Box::new(actual_ty)),
578                            Err(_) => None, // just don't report the type if there was an error computing it
579                        },
580                        actual_val: Either::Right(Box::new(actual_val)),
581                    };
582                    match ctx() {
583                        JsonDeserializationErrorContext::EntityAttribute { uid, attr } => {
584                            Err(JsonDeserializationError::EntitySchemaConformance(
585                                EntitySchemaConformanceError::TypeMismatch { uid, attr, err },
586                            ))
587                        }
588                        ctx => Err(JsonDeserializationError::TypeMismatch {
589                            ctx: Box::new(ctx),
590                            err,
591                        }),
592                    }
593                }
594            },
595            // The expected type is any other type, or we don't have an expected type.
596            // No special parsing rules apply; we do ordinary, non-schema-based parsing.
597            Some(_) | None => {
598                // Everything is parsed as `CedarValueJson`, and converted into
599                // `RestrictedExpr` from that.
600                let jvalue: CedarValueJson = serde_json::from_value(val)?;
601                Ok(jvalue.into_expr(ctx)?)
602            }
603        }
604    }
605
606    /// internal function that converts an `ExtnValueJson` into a
607    /// `RestrictedExpr`, which will be an extension constructor call.
608    ///
609    /// `expected_typename`: Specific extension type that is expected.
610    fn extn_value_json_into_rexpr(
611        &self,
612        extnjson: ExtnValueJson,
613        expected_typename: Name,
614        ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
615    ) -> Result<RestrictedExpr, JsonDeserializationError> {
616        match extnjson {
617            ExtnValueJson::ExplicitExprEscape { __expr } => {
618                Err(JsonDeserializationError::ExprTag(Box::new(ctx())))
619            }
620            ExtnValueJson::ExplicitExtnEscape { __extn }
621            | ExtnValueJson::ImplicitExtnEscape(__extn) => {
622                // reuse the same logic that parses CedarValueJson
623                let jvalue = CedarValueJson::ExtnEscape { __extn };
624                let expr = jvalue.into_expr(ctx.clone())?;
625                match expr.expr_kind() {
626                    ExprKind::ExtensionFunctionApp { .. } => Ok(expr),
627                    _ => Err(JsonDeserializationError::ExpectedExtnValue {
628                        ctx: Box::new(ctx()),
629                        got: Box::new(Either::Right(expr.clone().into())),
630                    }),
631                }
632            }
633            ExtnValueJson::ImplicitConstructor(val) => {
634                let arg = val.into_expr(ctx.clone())?;
635                let argty = schematype_of_restricted_expr(arg.as_borrowed(), self.extensions)
636                    .map_err(|e| match e {
637                        GetSchemaTypeError::HeterogeneousSet(err) => match ctx() {
638                            JsonDeserializationErrorContext::EntityAttribute { uid, attr } => {
639                                JsonDeserializationError::EntitySchemaConformance(
640                                    EntitySchemaConformanceError::HeterogeneousSet {
641                                        uid,
642                                        attr,
643                                        err,
644                                    },
645                                )
646                            }
647                            ctx => JsonDeserializationError::HeterogeneousSet {
648                                ctx: Box::new(ctx),
649                                err,
650                            },
651                        },
652                        GetSchemaTypeError::ExtensionFunctionLookup(err) => match ctx() {
653                            JsonDeserializationErrorContext::EntityAttribute { uid, attr } => {
654                                JsonDeserializationError::EntitySchemaConformance(
655                                    EntitySchemaConformanceError::ExtensionFunctionLookup {
656                                        uid,
657                                        attr,
658                                        err,
659                                    },
660                                )
661                            }
662                            ctx => JsonDeserializationError::ExtensionFunctionLookup {
663                                ctx: Box::new(ctx),
664                                err,
665                            },
666                        },
667                        GetSchemaTypeError::UnknownInsufficientTypeInfo { .. }
668                        | GetSchemaTypeError::NontrivialResidual { .. } => {
669                            JsonDeserializationError::UnknownInImplicitConstructorArg {
670                                ctx: Box::new(ctx()),
671                                arg: Box::new(arg.clone()),
672                            }
673                        }
674                    })?;
675                let func = self
676                    .extensions
677                    .lookup_single_arg_constructor(
678                        &SchemaType::Extension {
679                            name: expected_typename.clone(),
680                        },
681                        &argty,
682                    )
683                    .map_err(|err| JsonDeserializationError::ExtensionFunctionLookup {
684                        ctx: Box::new(ctx()),
685                        err,
686                    })?
687                    .ok_or_else(|| JsonDeserializationError::MissingImpliedConstructor {
688                        ctx: Box::new(ctx()),
689                        return_type: Box::new(SchemaType::Extension {
690                            name: expected_typename,
691                        }),
692                        arg_type: Box::new(argty.clone()),
693                    })?;
694                Ok(RestrictedExpr::call_extension_fn(
695                    func.name().clone(),
696                    vec![arg],
697                ))
698            }
699        }
700    }
701}
702
703/// Serde JSON format for Cedar values where we know we're expecting an entity
704/// reference
705#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
706#[serde(untagged)]
707pub enum EntityUidJson {
708    /// This was removed in 3.0 and is only here for generating nice error messages.
709    ExplicitExprEscape {
710        /// Contents are ignored.
711        __expr: String,
712    },
713    /// Explicit `__entity` escape; see notes on `CedarValueJson::EntityEscape`
714    ExplicitEntityEscape {
715        /// JSON object containing the entity type and ID
716        __entity: TypeAndId,
717    },
718    /// Implicit `__entity` escape, in which case we'll see just the TypeAndId
719    /// structure
720    ImplicitEntityEscape(TypeAndId),
721
722    /// Implicit catch-all case for error handling
723    FoundValue(serde_json::Value),
724}
725
726impl EntityUidJson {
727    /// Construct an `EntityUidJson` from entity type name and eid.
728    ///
729    /// This will use the `ImplicitEntityEscape` form, if it matters.
730    pub fn new(entity_type: impl Into<SmolStr>, id: impl Into<SmolStr>) -> Self {
731        Self::ImplicitEntityEscape(TypeAndId {
732            entity_type: entity_type.into(),
733            id: id.into(),
734        })
735    }
736
737    /// Convert this `EntityUidJson` into an `EntityUID`
738    pub fn into_euid(
739        self,
740        ctx: impl Fn() -> JsonDeserializationErrorContext + Clone,
741    ) -> Result<EntityUID, JsonDeserializationError> {
742        match self {
743            Self::ExplicitEntityEscape { __entity } | Self::ImplicitEntityEscape(__entity) => {
744                // reuse the same logic that parses CedarValueJson
745                let jvalue = CedarValueJson::EntityEscape { __entity };
746                let expr = jvalue.into_expr(ctx.clone())?;
747                match expr.expr_kind() {
748                    ExprKind::Lit(Literal::EntityUID(euid)) => Ok((**euid).clone()),
749                    _ => Err(JsonDeserializationError::ExpectedLiteralEntityRef {
750                        ctx: Box::new(ctx()),
751                        got: Box::new(Either::Right(expr.clone().into())),
752                    }),
753                }
754            }
755            Self::FoundValue(v) => Err(JsonDeserializationError::ExpectedLiteralEntityRef {
756                ctx: Box::new(ctx()),
757                got: Box::new(Either::Left(v)),
758            }),
759            Self::ExplicitExprEscape { __expr } => {
760                Err(JsonDeserializationError::ExprTag(Box::new(ctx())))
761            }
762        }
763    }
764}
765
766/// Convert an `EntityUID` to `EntityUidJson`, using the `ExplicitEntityEscape` option
767impl From<EntityUID> for EntityUidJson {
768    fn from(uid: EntityUID) -> EntityUidJson {
769        EntityUidJson::ExplicitEntityEscape {
770            __entity: uid.into(),
771        }
772    }
773}
774
775/// Convert an `EntityUID` to `EntityUidJson`, using the `ExplicitEntityEscape` option
776impl From<&EntityUID> for EntityUidJson {
777    fn from(uid: &EntityUID) -> EntityUidJson {
778        EntityUidJson::ExplicitEntityEscape {
779            __entity: uid.into(),
780        }
781    }
782}
783
784/// Serde JSON format for Cedar values where we know we're expecting an
785/// extension value
786#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
787#[serde(untagged)]
788pub enum ExtnValueJson {
789    /// This was removed in 3.0 and is only here for generating nice error messages.
790    ExplicitExprEscape {
791        /// Contents are ignored.
792        __expr: String,
793    },
794    /// Explicit `__extn` escape; see notes on `CedarValueJson::ExtnEscape`
795    ExplicitExtnEscape {
796        /// JSON object containing the extension-constructor call
797        __extn: FnAndArg,
798    },
799    /// Implicit `__extn` escape, in which case we'll just see the `FnAndArg`
800    /// directly
801    ImplicitExtnEscape(FnAndArg),
802    /// Implicit `__extn` escape and constructor. Constructor is implicitly
803    /// selected based on the argument type and the expected type.
804    //
805    // This is listed last so that it has lowest priority when deserializing.
806    // If one of the above forms fits, we use that.
807    ImplicitConstructor(CedarValueJson),
808}