scufflecloud_core_cedar/
lib.rs1use std::borrow::Borrow;
2use std::collections::BTreeMap;
3
4use ext_traits::ResultExt;
5use serde::ser::SerializeMap;
6
7pub trait CedarEntityId: CedarEntity {
8 type Id<'a>: CedarIdentifiable
9 where
10 Self: 'a;
11
12 fn id(&self) -> impl std::borrow::Borrow<Self::Id<'_>>;
13}
14
15impl<T: CedarEntityId> CedarIdentifiable for T {
16 const ENTITY_TYPE: EntityTypeName = T::Id::ENTITY_TYPE;
17
18 fn entity_id(&self) -> cedar_policy::EntityId {
19 self.id().borrow().entity_id()
20 }
21
22 fn entity_uid(&self) -> JsonEntityUid {
23 self.id().borrow().entity_uid()
24 }
25}
26
27#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub struct JsonEntityUid {
29 pub type_name: EntityTypeName,
30 pub id: cedar_policy::EntityId,
31}
32
33impl From<JsonEntityUid> for cedar_policy::EntityUid {
34 fn from(value: JsonEntityUid) -> Self {
35 cedar_policy::EntityUid::from_type_name_and_id(value.type_name.as_str().parse().unwrap(), value.id)
36 }
37}
38
39impl serde::Serialize for JsonEntityUid {
40 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
41 where
42 S: serde::Serializer,
43 {
44 let mut map = serializer.serialize_map(Some(2))?;
45 map.serialize_entry("type", self.type_name.as_str())?;
46 map.serialize_entry("id", self.id.unescaped())?;
47 map.end()
48 }
49}
50
51pub trait CedarIdentifiable {
52 const ENTITY_TYPE: EntityTypeName;
56
57 fn entity_id(&self) -> cedar_policy::EntityId;
58
59 fn entity_uid(&self) -> JsonEntityUid {
60 JsonEntityUid {
61 type_name: Self::ENTITY_TYPE,
62 id: self.entity_id(),
63 }
64 }
65}
66
67pub trait CedarEntity: CedarIdentifiable + serde::Serialize + Send + Sync {
68 fn parents(
69 &self,
70 global: &impl core_traits::Global,
71 ) -> impl Future<Output = Result<impl IntoIterator<Item = JsonEntityUid>, tonic::Status>> + Send {
72 let _ = global;
73 std::future::ready(Ok([]))
74 }
75
76 fn additional_attributes(
77 &self,
78 global: &impl core_traits::Global,
79 ) -> impl Future<Output = Result<impl serde::Serialize, tonic::Status>> + Send {
80 let _ = global;
81 std::future::ready(Ok(BTreeMap::<(), ()>::new()))
82 }
83
84 fn attributes(
87 &self,
88 global: &impl core_traits::Global,
89 ) -> impl std::future::Future<Output = Result<impl serde::Serialize, tonic::Status>> + Send {
90 #[derive(serde_derive::Serialize)]
91 struct Merged<A, B> {
92 #[serde(flatten)]
93 a: A,
94 #[serde(flatten)]
95 b: B,
96 }
97
98 async move {
99 Ok(Merged {
100 a: self,
101 b: self.additional_attributes(global).await?,
102 })
103 }
104 }
105
106 fn to_entity(
107 &self,
108 global: &impl core_traits::Global,
109 schema: Option<&cedar_policy::Schema>,
110 ) -> impl std::future::Future<Output = Result<cedar_policy::Entity, tonic::Status>> + Send {
111 async move {
112 let value = serde_json::json!({
113 "uid": self.entity_uid(),
114 "attrs": self.attributes(global).await?,
115 "parents": self.parents(global).await?.into_iter().collect::<Vec<_>>(),
116 });
117
118 cedar_policy::Entity::from_json_value(value, schema).into_tonic_internal_err("failed to create cedar entity")
119 }
120 }
121}
122
123pub use entity_type_name::EntityTypeName;
124
125mod entity_type_name;
126mod macros;
127mod models;