surrealdb_core/iam/entities/resources/
resource.rs

1use revision::revisioned;
2use std::{
3	collections::{HashMap, HashSet},
4	str::FromStr,
5};
6
7use super::Level;
8
9use cedar_policy::{Entity, EntityId, EntityTypeName, EntityUid, RestrictedExpression};
10use serde::{Deserialize, Serialize};
11
12#[revisioned(revision = 3)]
13#[derive(Clone, Default, Debug, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize)]
14#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
15#[non_exhaustive]
16pub enum ResourceKind {
17	#[default]
18	Any,
19	Namespace,
20	Database,
21	Record,
22	Table,
23	Document,
24	Option,
25	Function,
26	Analyzer,
27	Parameter,
28	Model,
29	Event,
30	Field,
31	Index,
32	Access,
33	#[revision(start = 2)]
34	Config(ConfigKind),
35	#[revision(start = 3)]
36	Api,
37
38	// IAM
39	Actor,
40}
41
42#[revisioned(revision = 1)]
43#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize)]
44#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
45#[non_exhaustive]
46pub enum ConfigKind {
47	GraphQL,
48	Api,
49}
50
51impl std::fmt::Display for ResourceKind {
52	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53		match self {
54			ResourceKind::Any => write!(f, "Any"),
55			ResourceKind::Namespace => write!(f, "Namespace"),
56			ResourceKind::Database => write!(f, "Database"),
57			ResourceKind::Record => write!(f, "Record"),
58			ResourceKind::Table => write!(f, "Table"),
59			ResourceKind::Document => write!(f, "Document"),
60			ResourceKind::Option => write!(f, "Option"),
61			ResourceKind::Function => write!(f, "Function"),
62			ResourceKind::Api => write!(f, "Api"),
63			ResourceKind::Analyzer => write!(f, "Analyzer"),
64			ResourceKind::Parameter => write!(f, "Parameter"),
65			ResourceKind::Model => write!(f, "Model"),
66			ResourceKind::Event => write!(f, "Event"),
67			ResourceKind::Field => write!(f, "Field"),
68			ResourceKind::Index => write!(f, "Index"),
69			ResourceKind::Access => write!(f, "Access"),
70			ResourceKind::Actor => write!(f, "Actor"),
71			ResourceKind::Config(c) => write!(f, "Config::{c}"),
72		}
73	}
74}
75
76impl std::fmt::Display for ConfigKind {
77	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
78		match self {
79			ConfigKind::GraphQL => write!(f, "GraphQL"),
80			ConfigKind::Api => write!(f, "API"),
81		}
82	}
83}
84
85impl ResourceKind {
86	// Helpers for building default resources for specific levels. Useful for authorization checks.
87	pub fn on_level(self, level: Level) -> Resource {
88		Resource::new("".into(), self, level)
89	}
90
91	pub fn on_root(self) -> Resource {
92		self.on_level(Level::Root)
93	}
94
95	pub fn on_ns(self, ns: &str) -> Resource {
96		self.on_level((ns,).into())
97	}
98
99	pub fn on_db(self, ns: &str, db: &str) -> Resource {
100		self.on_level((ns, db).into())
101	}
102
103	pub fn on_record(self, ns: &str, db: &str, rid: &str) -> Resource {
104		self.on_level((ns, db, rid).into())
105	}
106}
107
108#[revisioned(revision = 1)]
109#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize)]
110#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
111#[non_exhaustive]
112pub struct Resource(String, ResourceKind, Level);
113
114impl std::fmt::Display for Resource {
115	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
116		let Resource(id, kind, level) = self;
117		write!(f, "{}{}:\"{}\"", level, kind, id)
118	}
119}
120
121impl Resource {
122	pub fn new(id: String, kind: ResourceKind, level: Level) -> Self {
123		Self(id, kind, level)
124	}
125
126	pub fn id(&self) -> &str {
127		&self.0
128	}
129
130	pub fn kind(&self) -> &ResourceKind {
131		&self.1
132	}
133
134	pub fn level(&self) -> &Level {
135		&self.2
136	}
137
138	// Cedar policy helpers
139	pub fn cedar_attrs(&self) -> HashMap<String, RestrictedExpression> {
140		[("type", self.kind().into()), ("level", self.level().into())]
141			.into_iter()
142			.map(|(x, v)| (x.into(), v))
143			.collect()
144	}
145
146	pub fn cedar_parents(&self) -> HashSet<EntityUid> {
147		HashSet::from([self.level().into()])
148	}
149
150	pub fn cedar_entities(&self) -> Vec<Entity> {
151		let mut entities = Vec::new();
152
153		entities.push(self.into());
154		entities.extend(self.level().cedar_entities());
155
156		entities
157	}
158}
159
160impl std::convert::From<&Resource> for EntityUid {
161	fn from(res: &Resource) -> Self {
162		EntityUid::from_type_name_and_id(
163			EntityTypeName::from_str(&res.kind().to_string()).unwrap(),
164			EntityId::from_str(res.id()).unwrap(),
165		)
166	}
167}
168
169impl std::convert::From<&Resource> for Entity {
170	fn from(res: &Resource) -> Self {
171		Entity::new(res.into(), res.cedar_attrs(), res.cedar_parents())
172	}
173}
174
175impl std::convert::From<&Resource> for RestrictedExpression {
176	fn from(res: &Resource) -> Self {
177		format!("{}", EntityUid::from(res)).parse().unwrap()
178	}
179}
180
181impl std::convert::From<&ResourceKind> for RestrictedExpression {
182	fn from(kind: &ResourceKind) -> Self {
183		RestrictedExpression::new_string(kind.to_string())
184	}
185}