Skip to main content

surrealdb_sql/
permission.rs

1use surrealdb_types::{SqlFormat, ToSql, write_sql};
2
3use crate::{CoverStmts, Expr};
4
5#[derive(Clone, Debug, Default, PartialEq, Eq)]
6#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
7pub struct Permissions {
8	pub select: Permission,
9	pub create: Permission,
10	pub update: Permission,
11	pub delete: Permission,
12}
13
14impl Permissions {
15	pub fn none() -> Self {
16		Permissions {
17			select: Permission::None,
18			create: Permission::None,
19			update: Permission::None,
20			delete: Permission::None,
21		}
22	}
23
24	pub fn full() -> Self {
25		Permissions {
26			select: Permission::Full,
27			create: Permission::Full,
28			update: Permission::Full,
29			delete: Permission::Full,
30		}
31	}
32
33	pub fn is_none(&self) -> bool {
34		matches!(self.select, Permission::None)
35			&& matches!(self.create, Permission::None)
36			&& matches!(self.update, Permission::None)
37			&& matches!(self.delete, Permission::None)
38	}
39
40	pub fn is_full(&self) -> bool {
41		matches!(self.select, Permission::Full)
42			&& matches!(self.create, Permission::Full)
43			&& matches!(self.update, Permission::Full)
44			&& matches!(self.delete, Permission::Full)
45	}
46}
47
48#[derive(Clone, Copy, Eq, PartialEq, Debug)]
49pub enum PermissionKind {
50	Select,
51	Create,
52	Update,
53	Delete,
54}
55
56impl PermissionKind {
57	fn as_str(&self) -> &str {
58		match self {
59			PermissionKind::Select => "select",
60			PermissionKind::Create => "create",
61			PermissionKind::Update => "update",
62			PermissionKind::Delete => "delete",
63		}
64	}
65}
66
67/// Renders the three clauses a field can carry, omitting `delete`.
68///
69/// `DEFINE FIELD` rejects a `FOR delete` clause and the catalog does not track
70/// one, so a field's `delete` is a placeholder that must never reach the
71/// output. Tables render through [`Permissions`] itself and must emit all four:
72/// an action left out of a table's `PERMISSIONS FOR …` list reparses as `NONE`,
73/// so dropping a clause revokes it.
74pub struct FieldPermissions<'a>(pub &'a Permissions);
75
76impl surrealdb_types::ToSql for FieldPermissions<'_> {
77	fn fmt_sql(&self, f: &mut String, fmt: surrealdb_types::SqlFormat) {
78		self.0.fmt_sql_actions(f, fmt, false)
79	}
80}
81
82impl surrealdb_types::ToSql for Permissions {
83	fn fmt_sql(&self, f: &mut String, fmt: surrealdb_types::SqlFormat) {
84		self.fmt_sql_actions(f, fmt, true)
85	}
86}
87
88impl Permissions {
89	/// Writes the `PERMISSIONS` clause, covering `delete` only when the owner
90	/// can express it. See [`FieldPermissions`].
91	fn fmt_sql_actions(&self, f: &mut String, fmt: surrealdb_types::SqlFormat, delete: bool) {
92		f.push_str("PERMISSIONS");
93		if self.is_none() {
94			f.push_str(" NONE");
95			return;
96		}
97		if self.is_full() {
98			f.push_str(" FULL");
99			return;
100		}
101		let mut lines = Vec::<(Vec<PermissionKind>, &Permission)>::new();
102		for (c, permission) in [
103			PermissionKind::Select,
104			PermissionKind::Create,
105			PermissionKind::Update,
106			PermissionKind::Delete,
107		]
108		.into_iter()
109		.zip([&self.select, &self.create, &self.update, &self.delete])
110		{
111			if matches!(c, PermissionKind::Delete) && !delete {
112				continue;
113			}
114
115			if let Some((existing, _)) = lines.iter_mut().find(|(_, p)| *p == permission) {
116				existing.push(c);
117			} else {
118				lines.push((vec![c], permission));
119			}
120		}
121		if fmt.is_pretty() {
122			f.push('\n');
123			let inner_fmt = fmt.increment();
124			inner_fmt.write_indent(f);
125		} else {
126			f.push(' ');
127		}
128		for (i, (kinds, permission)) in lines.into_iter().enumerate() {
129			if i > 0 {
130				if fmt.is_pretty() {
131					f.push(',');
132					f.push('\n');
133					let inner_fmt = fmt.increment();
134					inner_fmt.write_indent(f);
135				} else {
136					f.push_str(", ");
137				}
138			}
139			f.push_str("FOR ");
140			for (i, kind) in kinds.into_iter().enumerate() {
141				if i > 0 {
142					f.push_str(", ");
143				}
144				f.push_str(kind.as_str());
145			}
146			match permission {
147				Permission::Specific(v) if fmt.is_pretty() => {
148					f.push_str(" WHERE ");
149					CoverStmts(v).fmt_sql(f, fmt);
150				}
151				Permission::None => f.push_str(" NONE"),
152				Permission::Full => f.push_str(" FULL"),
153				Permission::Specific(v) => {
154					f.push_str(" WHERE ");
155					CoverStmts(v).fmt_sql(f, fmt);
156				}
157			}
158		}
159	}
160}
161
162#[derive(Clone, Debug, Default, PartialEq, Eq)]
163#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
164pub enum Permission {
165	None,
166	#[default]
167	Full,
168	Specific(Expr),
169}
170
171impl ToSql for Permission {
172	fn fmt_sql(&self, f: &mut String, sql_fmt: SqlFormat) {
173		match self {
174			Self::None => f.push_str("NONE"),
175			Self::Full => f.push_str("FULL"),
176			Self::Specific(v) => write_sql!(f, sql_fmt, "WHERE {}", CoverStmts(v)),
177		}
178	}
179}