surrealdb_core/sql/statements/define/
access.rs1use crate::ctx::Context;
2use crate::dbs::Options;
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::iam::{Action, ResourceKind};
6use crate::sql::statements::info::InfoStructure;
7use crate::sql::{access::AccessDuration, AccessType, Base, Ident, Strand, Value};
8
9use rand::distributions::Alphanumeric;
10use rand::Rng;
11use revision::revisioned;
12use serde::{Deserialize, Serialize};
13use std::fmt::{self, Display};
14
15#[revisioned(revision = 3)]
16#[derive(Clone, Default, Debug, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Hash)]
17#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
18#[non_exhaustive]
19pub struct DefineAccessStatement {
20 pub name: Ident,
21 pub base: Base,
22 pub kind: AccessType,
23 #[revision(start = 2)]
24 pub authenticate: Option<Value>,
25 pub duration: AccessDuration,
26 pub comment: Option<Strand>,
27 pub if_not_exists: bool,
28 #[revision(start = 3)]
29 pub overwrite: bool,
30}
31
32impl DefineAccessStatement {
33 pub(crate) fn random_key() -> String {
37 rand::thread_rng().sample_iter(&Alphanumeric).take(128).map(char::from).collect::<String>()
38 }
39
40 pub fn redacted(&self) -> DefineAccessStatement {
44 let mut das = self.clone();
45 das.kind = match das.kind {
46 AccessType::Jwt(ac) => AccessType::Jwt(ac.redacted()),
47 AccessType::Record(mut ac) => {
48 ac.jwt = ac.jwt.redacted();
49 AccessType::Record(ac)
50 }
51 AccessType::Bearer(mut ac) => {
52 ac.jwt = ac.jwt.redacted();
53 AccessType::Bearer(ac)
54 }
55 };
56 das
57 }
58}
59
60impl DefineAccessStatement {
61 pub(crate) async fn compute(
63 &self,
64 ctx: &Context,
65 opt: &Options,
66 _doc: Option<&CursorDoc>,
67 ) -> Result<Value, Error> {
68 opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?;
70 match &self.base {
72 Base::Root => {
73 let txn = ctx.tx();
75 if txn.get_root_access(&self.name).await.is_ok() {
77 if self.if_not_exists {
78 return Ok(Value::None);
79 } else if !self.overwrite {
80 return Err(Error::AccessRootAlreadyExists {
81 ac: self.name.to_string(),
82 });
83 }
84 }
85 let key = crate::key::root::ac::new(&self.name);
87 txn.set(
88 key,
89 revision::to_vec(&DefineAccessStatement {
90 if_not_exists: false,
92 overwrite: false,
93 ..self.clone()
94 })?,
95 None,
96 )
97 .await?;
98 txn.clear();
100 Ok(Value::None)
102 }
103 Base::Ns => {
104 let txn = ctx.tx();
106 if txn.get_ns_access(opt.ns()?, &self.name).await.is_ok() {
108 if self.if_not_exists {
109 return Ok(Value::None);
110 } else if !self.overwrite {
111 return Err(Error::AccessNsAlreadyExists {
112 ac: self.name.to_string(),
113 ns: opt.ns()?.into(),
114 });
115 }
116 }
117 let key = crate::key::namespace::ac::new(opt.ns()?, &self.name);
119 txn.get_or_add_ns(opt.ns()?, opt.strict).await?;
120 txn.set(
121 key,
122 revision::to_vec(&DefineAccessStatement {
123 if_not_exists: false,
125 overwrite: false,
126 ..self.clone()
127 })?,
128 None,
129 )
130 .await?;
131 txn.clear();
133 Ok(Value::None)
135 }
136 Base::Db => {
137 let txn = ctx.tx();
139 let (ns, db) = opt.ns_db()?;
141 if txn.get_db_access(ns, db, &self.name).await.is_ok() {
142 if self.if_not_exists {
143 return Ok(Value::None);
144 } else if !self.overwrite {
145 return Err(Error::AccessDbAlreadyExists {
146 ac: self.name.to_string(),
147 ns: ns.into(),
148 db: db.into(),
149 });
150 }
151 }
152 let key = crate::key::database::ac::new(ns, db, &self.name);
154 txn.get_or_add_ns(ns, opt.strict).await?;
155 txn.get_or_add_db(ns, db, opt.strict).await?;
156 txn.set(
157 key,
158 revision::to_vec(&DefineAccessStatement {
159 if_not_exists: false,
161 overwrite: false,
162 ..self.clone()
163 })?,
164 None,
165 )
166 .await?;
167 txn.clear();
169 Ok(Value::None)
171 }
172 _ => Err(Error::InvalidLevel(self.base.to_string())),
174 }
175 }
176}
177
178impl Display for DefineAccessStatement {
179 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
180 write!(f, "DEFINE ACCESS",)?;
181 if self.if_not_exists {
182 write!(f, " IF NOT EXISTS")?
183 }
184 if self.overwrite {
185 write!(f, " OVERWRITE")?
186 }
187 write!(f, " {} ON {} TYPE {}", self.name, self.base, self.kind)?;
189 if let Some(ref v) = self.authenticate {
191 write!(f, " AUTHENTICATE {v}")?
192 }
193 write!(f, " DURATION")?;
197 if self.kind.can_issue_grants() {
198 write!(
199 f,
200 " FOR GRANT {},",
201 match self.duration.grant {
202 Some(dur) => format!("{}", dur),
203 None => "NONE".to_string(),
204 }
205 )?;
206 }
207 if self.kind.can_issue_tokens() {
208 write!(
209 f,
210 " FOR TOKEN {},",
211 match self.duration.token {
212 Some(dur) => format!("{}", dur),
213 None => "NONE".to_string(),
214 }
215 )?;
216 }
217 write!(
218 f,
219 " FOR SESSION {}",
220 match self.duration.session {
221 Some(dur) => format!("{}", dur),
222 None => "NONE".to_string(),
223 }
224 )?;
225 if let Some(ref v) = self.comment {
226 write!(f, " COMMENT {v}")?
227 }
228 Ok(())
229 }
230}
231
232impl InfoStructure for DefineAccessStatement {
233 fn structure(self) -> Value {
234 Value::from(map! {
235 "name".to_string() => self.name.structure(),
236 "base".to_string() => self.base.structure(),
237 "authenticate".to_string(), if let Some(v) = self.authenticate => v.structure(),
238 "duration".to_string() => Value::from(map!{
239 "session".to_string() => self.duration.session.into(),
240 "grant".to_string(), if self.kind.can_issue_grants() => self.duration.grant.into(),
241 "token".to_string(), if self.kind.can_issue_tokens() => self.duration.token.into(),
242 }),
243 "kind".to_string() => self.kind.structure(),
244 "comment".to_string(), if let Some(v) = self.comment => v.into(),
245 })
246 }
247}