1use super::{
18 err::{JsonDeserializationError, JsonDeserializationErrorContext, JsonSerializationError},
19 CedarValueJson, EntityTypeDescription, EntityUidJson, NoEntitiesSchema, Schema, TypeAndId,
20 ValueParser,
21};
22use crate::ast::{BorrowedRestrictedExpr, Entity, EntityUID, PartialValue, RestrictedExpr};
23use crate::entities::conformance::EntitySchemaConformanceChecker;
24use crate::entities::{
25 conformance::err::{EntitySchemaConformanceError, UnexpectedEntityTypeError},
26 Entities, EntitiesError, TCComputation,
27};
28use crate::extensions::Extensions;
29use crate::jsonvalue::JsonValueWithNoDuplicateKeys;
30use serde::{Deserialize, Serialize};
31use serde_with::serde_as;
32use smol_str::SmolStr;
33use std::sync::Arc;
34use std::{
35 collections::{HashMap, HashSet},
36 io::Read,
37};
38
39#[cfg(feature = "wasm")]
40extern crate tsify;
41
42#[serde_as]
44#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
45#[cfg_attr(feature = "wasm", derive(tsify::Tsify))]
46#[cfg_attr(feature = "wasm", tsify(into_wasm_abi, from_wasm_abi))]
47pub struct EntityJson {
48 uid: EntityUidJson,
50 #[serde_as(as = "serde_with::MapPreventDuplicates<_,_>")]
57 #[cfg_attr(feature = "wasm", tsify(type = "Record<string, CedarValueJson>"))]
58 attrs: HashMap<SmolStr, JsonValueWithNoDuplicateKeys>,
60 parents: Vec<EntityUidJson>,
62 #[serde_as(as = "serde_with::MapPreventDuplicates<_,_>")]
63 #[serde(default)]
64 #[serde(skip_serializing_if = "HashMap::is_empty")]
65 #[cfg_attr(feature = "wasm", tsify(type = "Record<string, CedarValueJson>"))]
66 tags: HashMap<SmolStr, JsonValueWithNoDuplicateKeys>,
68}
69
70#[derive(Debug, Clone)]
72pub struct EntityJsonParser<'e, 's, S = NoEntitiesSchema> {
73 schema: Option<&'s S>,
79
80 extensions: &'e Extensions<'e>,
82
83 tc_computation: TCComputation,
86}
87
88#[derive(Debug)]
90enum EntitySchemaInfo<E> {
91 NoSchema,
95 NonAction(E),
98}
99
100impl<'e, 's, S> EntityJsonParser<'e, 's, S> {
101 pub fn new(
121 schema: Option<&'s S>,
122 extensions: &'e Extensions<'e>,
123 tc_computation: TCComputation,
124 ) -> Self {
125 Self {
126 schema,
127 extensions,
128 tc_computation,
129 }
130 }
131}
132
133impl<S: Schema> EntityJsonParser<'_, '_, S> {
134 pub fn from_json_str(&self, json: &str) -> Result<Entities, EntitiesError> {
139 let ejsons: Vec<EntityJson> =
140 serde_json::from_str(json).map_err(JsonDeserializationError::from)?;
141 self.parse_ejsons(ejsons)
142 }
143
144 pub fn from_json_value(&self, json: serde_json::Value) -> Result<Entities, EntitiesError> {
149 let ejsons: Vec<EntityJson> =
150 serde_json::from_value(json).map_err(JsonDeserializationError::from)?;
151 self.parse_ejsons(ejsons)
152 }
153
154 pub fn from_json_file(&self, json: impl std::io::Read) -> Result<Entities, EntitiesError> {
159 let ejsons: Vec<EntityJson> =
160 serde_json::from_reader(json).map_err(JsonDeserializationError::from)?;
161 self.parse_ejsons(ejsons)
162 }
163
164 pub fn iter_from_json_str(
169 &self,
170 json: &str,
171 ) -> Result<impl Iterator<Item = Entity> + '_, EntitiesError> {
172 let ejsons: Vec<EntityJson> =
173 serde_json::from_str(json).map_err(JsonDeserializationError::from)?;
174 self.iter_ejson_to_iter_entity(ejsons)
175 }
176
177 pub fn iter_from_json_value(
182 &self,
183 json: serde_json::Value,
184 ) -> Result<impl Iterator<Item = Entity> + '_, EntitiesError> {
185 let ejsons: Vec<EntityJson> =
186 serde_json::from_value(json).map_err(JsonDeserializationError::from)?;
187 self.iter_ejson_to_iter_entity(ejsons)
188 }
189
190 pub fn iter_from_json_file(
195 &self,
196 json: impl std::io::Read,
197 ) -> Result<impl Iterator<Item = Entity> + '_, EntitiesError> {
198 let ejsons: Vec<EntityJson> =
199 serde_json::from_reader(json).map_err(JsonDeserializationError::from)?;
200 self.iter_ejson_to_iter_entity(ejsons)
201 }
202
203 fn iter_ejson_to_iter_entity(
207 &self,
208 ejsons: impl IntoIterator<Item = EntityJson>,
209 ) -> Result<impl Iterator<Item = Entity> + '_, EntitiesError> {
210 let mut entities: Vec<Entity> = ejsons
211 .into_iter()
212 .map(|ejson| self.parse_ejson(ejson).map_err(EntitiesError::from))
213 .collect::<Result<_, _>>()?;
214 if let Some(schema) = &self.schema {
215 entities.extend(
216 schema
217 .action_entities()
218 .into_iter()
219 .map(Arc::unwrap_or_clone),
220 );
221 }
222 Ok(entities.into_iter())
223 }
224
225 pub fn single_from_json_value(
227 &self,
228 value: serde_json::Value,
229 ) -> Result<Entity, EntitiesError> {
230 let ejson = serde_json::from_value(value).map_err(JsonDeserializationError::from)?;
231 self.single_from_ejson(ejson)
232 }
233
234 pub fn single_from_json_str(&self, src: impl AsRef<str>) -> Result<Entity, EntitiesError> {
236 let ejson = serde_json::from_str(src.as_ref()).map_err(JsonDeserializationError::from)?;
237 self.single_from_ejson(ejson)
238 }
239
240 pub fn single_from_json_file(&self, r: impl Read) -> Result<Entity, EntitiesError> {
242 let ejson = serde_json::from_reader(r).map_err(JsonDeserializationError::from)?;
243 self.single_from_ejson(ejson)
244 }
245
246 fn single_from_ejson(&self, ejson: EntityJson) -> Result<Entity, EntitiesError> {
247 let entity = self.parse_ejson(ejson)?;
248 match self.schema {
249 None => Ok(entity),
250 Some(schema) => {
251 let checker = EntitySchemaConformanceChecker::new(schema, self.extensions);
252 checker.validate_entity(&entity)?;
253 Ok(entity)
254 }
255 }
256 }
257
258 fn parse_ejsons(
264 &self,
265 ejsons: impl IntoIterator<Item = EntityJson>,
266 ) -> Result<Entities, EntitiesError> {
267 let entities: Vec<Entity> = ejsons
268 .into_iter()
269 .map(|ejson| self.parse_ejson(ejson))
270 .collect::<Result<_, _>>()?;
271 Entities::from_entities(entities, self.schema, self.tc_computation, self.extensions)
272 }
273
274 fn parse_ejson(&self, ejson: EntityJson) -> Result<Entity, JsonDeserializationError> {
279 let uid = ejson
280 .uid
281 .into_euid(|| JsonDeserializationErrorContext::EntityUid)?;
282 let etype = uid.entity_type();
283 let entity_schema_info = match &self.schema {
284 None => EntitySchemaInfo::NoSchema,
285 Some(schema) => {
286 if etype.is_action() {
287 EntitySchemaInfo::NoSchema
289 } else {
290 EntitySchemaInfo::NonAction(schema.entity_type(etype).ok_or_else(|| {
291 let suggested_types = schema
292 .entity_types_with_basename(&etype.name().basename())
293 .collect();
294 JsonDeserializationError::EntitySchemaConformance(
295 UnexpectedEntityTypeError {
296 uid: uid.clone(),
297 suggested_types,
298 }
299 .into(),
300 )
301 })?)
302 }
303 }
304 };
305 let vparser = ValueParser::new(self.extensions);
306 let attrs: HashMap<SmolStr, RestrictedExpr> = ejson
307 .attrs
308 .into_iter()
309 .map(|(k, v)| match &entity_schema_info {
310 EntitySchemaInfo::NoSchema => Ok((
311 k.clone(),
312 vparser.val_into_restricted_expr(v.into(), None, || {
313 JsonDeserializationErrorContext::EntityAttribute {
314 uid: uid.clone(),
315 attr: k.clone(),
316 }
317 })?,
318 )),
319 EntitySchemaInfo::NonAction(desc) => {
320 let rexpr = match desc.attr_type(&k) {
323 None => {
326 if desc.open_attributes() {
327 vparser.val_into_restricted_expr(v.into(), None, || {
328 JsonDeserializationErrorContext::EntityAttribute {
329 uid: uid.clone(),
330 attr: k.clone(),
331 }
332 })?
333 } else {
334 return Err(JsonDeserializationError::EntitySchemaConformance(
335 EntitySchemaConformanceError::unexpected_entity_attr(
336 uid.clone(),
337 k,
338 ),
339 ));
340 }
341 }
342 Some(expected_ty) => vparser.val_into_restricted_expr(
343 v.into(),
344 Some(&expected_ty),
345 || JsonDeserializationErrorContext::EntityAttribute {
346 uid: uid.clone(),
347 attr: k.clone(),
348 },
349 )?,
350 };
351 Ok((k, rexpr))
352 }
353 })
354 .collect::<Result<_, JsonDeserializationError>>()?;
355 let tags: HashMap<SmolStr, RestrictedExpr> = ejson
356 .tags
357 .into_iter()
358 .map(|(k, v)| match &entity_schema_info {
359 EntitySchemaInfo::NoSchema => Ok((
360 k.clone(),
361 vparser.val_into_restricted_expr(v.into(), None, || {
362 JsonDeserializationErrorContext::EntityTag {
363 uid: uid.clone(),
364 tag: k.clone(),
365 }
366 })?,
367 )),
368 EntitySchemaInfo::NonAction(desc) => {
369 let rexpr = match desc.tag_type() {
372 None => {
375 return Err(JsonDeserializationError::EntitySchemaConformance(
376 EntitySchemaConformanceError::unexpected_entity_tag(uid.clone(), k),
377 ));
378 }
379 Some(expected_ty) => vparser.val_into_restricted_expr(
380 v.into(),
381 Some(&expected_ty),
382 || JsonDeserializationErrorContext::EntityTag {
383 uid: uid.clone(),
384 tag: k.clone(),
385 },
386 )?,
387 };
388 Ok((k, rexpr))
389 }
390 })
391 .collect::<Result<_, JsonDeserializationError>>()?;
392 let is_parent_allowed = |parent_euid: &EntityUID| {
393 if etype.is_action() {
397 if parent_euid.is_action() {
398 Ok(())
399 } else {
400 Err(JsonDeserializationError::action_parent_is_not_action(
401 uid.clone(),
402 parent_euid.clone(),
403 ))
404 }
405 } else {
406 Ok(()) }
408 };
409 let parents = ejson
410 .parents
411 .into_iter()
412 .map(|parent| {
413 parent.into_euid(|| JsonDeserializationErrorContext::EntityParents {
414 uid: uid.clone(),
415 })
416 })
417 .map(|res| {
418 res.and_then(|parent_euid| {
419 is_parent_allowed(&parent_euid)?;
420 Ok(parent_euid)
421 })
422 })
423 .collect::<Result<_, JsonDeserializationError>>()?;
424 Ok(Entity::new(
425 uid,
426 attrs,
427 HashSet::new(),
428 parents,
429 tags,
430 self.extensions,
431 )?)
432 }
433}
434
435impl EntityJson {
436 pub fn from_entity(entity: &Entity) -> Result<Self, JsonSerializationError> {
440 let serialize_kpvalue = |(k, pvalue): (&SmolStr, &PartialValue)| -> Result<_, _> {
441 match pvalue {
442 PartialValue::Value(value) => {
443 let cedarvaluejson = CedarValueJson::from_value(value.clone())?;
444 Ok((k.clone(), serde_json::to_value(cedarvaluejson)?.into()))
445 }
446 PartialValue::Residual(expr) => match BorrowedRestrictedExpr::new(expr) {
447 Ok(expr) => {
448 let cedarvaluejson = CedarValueJson::from_expr(expr)?;
449 Ok((k.clone(), serde_json::to_value(cedarvaluejson)?.into()))
450 }
451 Err(_) => Err(JsonSerializationError::residual(expr.clone())),
452 },
453 }
454 };
455 Ok(Self {
456 uid: EntityUidJson::ImplicitEntityEscape(TypeAndId::from(entity.uid())),
458 attrs: entity
459 .attrs()
460 .map(serialize_kpvalue)
461 .collect::<Result<_, JsonSerializationError>>()?,
462 parents: entity
463 .ancestors()
464 .map(|euid| EntityUidJson::ImplicitEntityEscape(TypeAndId::from(euid.clone())))
465 .collect(),
466 tags: entity
467 .tags()
468 .map(serialize_kpvalue)
469 .collect::<Result<_, JsonSerializationError>>()?,
470 })
471 }
472}
473
474#[allow(clippy::panic)]
476#[cfg(test)]
477mod test {
478 use super::*;
479 use cool_asserts::assert_matches;
480
481 #[test]
482 fn reject_inconsistent_duplicates() {
483 let json = serde_json::json!([
484 {
485 "uid" : {
486 "type" : "User",
487 "id" : "alice"
488 },
489 "attrs" : {},
490 "parents": []
491 },
492 {
493 "uid" : {
494 "type" : "User",
495 "id" : "alice"
496 },
497 "attrs" : {"location": "Greenland"},
498 "parents": []
499 }
500 ]);
501 let eparser: EntityJsonParser<'_, '_, NoEntitiesSchema> =
502 EntityJsonParser::new(None, Extensions::all_available(), TCComputation::ComputeNow);
503 let e = eparser.from_json_value(json);
504 let bad_euid: EntityUID = r#"User::"alice""#.parse().unwrap();
505 assert_matches!(e, Err(EntitiesError::Duplicate(euid)) => {
506 assert_eq!(&bad_euid, euid.euid(), r#"Returned euid should be User::"alice""#);
507 });
508 }
509
510 #[test]
511 fn simple() {
512 let test = serde_json::json!({
513 "uid" : { "type" : "A", "id" : "b" },
514 "attrs" : {},
515 "parents" : []
516 });
517 let x: Result<EntityJson, _> = serde_json::from_value(test);
518 x.unwrap();
519 }
520}