surrealdb_core/iam/entities/resources/
actor.rs

1use revision::revisioned;
2use std::collections::{HashMap, HashSet};
3use std::ops::Deref;
4use std::str::FromStr;
5
6use cedar_policy::{Entity, EntityId, EntityTypeName, EntityUid, RestrictedExpression};
7use serde::{Deserialize, Serialize};
8
9use super::{Level, Resource, ResourceKind};
10use crate::iam::{Error, Role};
11use crate::sql::statements::{DefineAccessStatement, DefineUserStatement};
12
13//
14// User
15//
16#[revisioned(revision = 1)]
17#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Hash, Serialize, Deserialize)]
18#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
19#[non_exhaustive]
20pub struct Actor {
21	res: Resource,
22	roles: Vec<Role>,
23}
24
25impl Default for Actor {
26	fn default() -> Self {
27		Self {
28			res: ResourceKind::Actor.on_level(Level::No),
29			roles: Vec::new(),
30		}
31	}
32}
33
34impl std::fmt::Display for Actor {
35	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36		if self.res.level() == &Level::No {
37			return write!(f, "Actor::Anonymous");
38		}
39
40		write!(
41			f,
42			"{}{}::{}({})",
43			self.res.level(),
44			self.res.kind(),
45			self.res.id(),
46			self.roles.iter().map(|r| format!("{}", r)).collect::<Vec<String>>().join(", ")
47		)
48	}
49}
50
51impl Actor {
52	pub fn new(id: String, roles: Vec<Role>, level: Level) -> Self {
53		Self {
54			res: Resource::new(id, super::ResourceKind::Actor, level),
55			roles,
56		}
57	}
58
59	/// Checks if the actor has the given role.
60	pub fn has_role(&self, role: Role) -> bool {
61		self.roles.contains(&role)
62	}
63
64	/// Checks if the actor has the Owner role.
65	pub fn has_owner_role(&self) -> bool {
66		self.roles.iter().any(|r| r.eq(&Role::Owner))
67	}
68
69	/// Checks if the actor has the Editor role.
70	pub fn has_editor_role(&self) -> bool {
71		self.roles.iter().any(|r| r.eq(&Role::Owner) || r.eq(&Role::Editor))
72	}
73
74	/// Checks if the actor has the Viewer role.
75	pub fn has_viewer_role(&self) -> bool {
76		self.roles.iter().any(|r| r.eq(&Role::Owner) || r.eq(&Role::Editor) || r.eq(&Role::Viewer))
77	}
78
79	// Cedar policy helpers
80	pub fn cedar_attrs(&self) -> HashMap<String, RestrictedExpression> {
81		[
82			("type", self.kind().into()),
83			("level", self.level().into()),
84			("roles", RestrictedExpression::new_set(self.roles.iter().map(|r| r.into()))),
85		]
86		.into_iter()
87		.map(|(x, v)| (x.into(), v))
88		.collect()
89	}
90
91	pub fn cedar_parents(&self) -> HashSet<EntityUid> {
92		let mut parents = HashSet::with_capacity(1);
93		parents.insert(self.res.level().into());
94		parents
95	}
96
97	pub fn cedar_entities(&self) -> Vec<Entity> {
98		let mut entities = Vec::new();
99
100		entities.push(self.into());
101
102		for role in self.roles.iter() {
103			entities.push(role.into());
104		}
105
106		for level in self.res.level().cedar_entities() {
107			entities.push(level);
108		}
109
110		entities
111	}
112}
113
114impl Deref for Actor {
115	type Target = Resource;
116	fn deref(&self) -> &Self::Target {
117		&self.res
118	}
119}
120
121impl std::convert::From<&Actor> for EntityUid {
122	fn from(actor: &Actor) -> Self {
123		EntityUid::from_type_name_and_id(
124			EntityTypeName::from_str("Actor").unwrap(),
125			EntityId::from_str(actor.id()).unwrap(),
126		)
127	}
128}
129
130impl std::convert::From<&Actor> for Entity {
131	fn from(actor: &Actor) -> Self {
132		Entity::new(actor.into(), actor.cedar_attrs(), actor.cedar_parents())
133	}
134}
135
136impl std::convert::TryFrom<(&DefineUserStatement, Level)> for Actor {
137	type Error = Error;
138	fn try_from(val: (&DefineUserStatement, Level)) -> Result<Self, Self::Error> {
139		let roles = val.0.roles.iter().map(Role::try_from).collect::<Result<_, _>>()?;
140		Ok(Self::new(val.0.name.to_string(), roles, val.1))
141	}
142}
143
144impl std::convert::From<(&DefineAccessStatement, Level)> for Actor {
145	fn from(val: (&DefineAccessStatement, Level)) -> Self {
146		Self::new(val.0.name.to_string(), Vec::default(), val.1)
147	}
148}