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};
8use derive::Store;
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, Store, 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::access::ac::new(&self.name);
87 txn.set(
88 key,
89 DefineAccessStatement {
90 if_not_exists: false,
92 overwrite: false,
93 ..self.clone()
94 },
95 )
96 .await?;
97 txn.clear();
99 Ok(Value::None)
101 }
102 Base::Ns => {
103 let txn = ctx.tx();
105 if txn.get_ns_access(opt.ns()?, &self.name).await.is_ok() {
107 if self.if_not_exists {
108 return Ok(Value::None);
109 } else if !self.overwrite {
110 return Err(Error::AccessNsAlreadyExists {
111 ac: self.name.to_string(),
112 ns: opt.ns()?.into(),
113 });
114 }
115 }
116 let key = crate::key::namespace::access::ac::new(opt.ns()?, &self.name);
118 txn.get_or_add_ns(opt.ns()?, opt.strict).await?;
119 txn.set(
120 key,
121 DefineAccessStatement {
122 if_not_exists: false,
124 overwrite: false,
125 ..self.clone()
126 },
127 )
128 .await?;
129 txn.clear();
131 Ok(Value::None)
133 }
134 Base::Db => {
135 let txn = ctx.tx();
137 if txn.get_db_access(opt.ns()?, opt.db()?, &self.name).await.is_ok() {
139 if self.if_not_exists {
140 return Ok(Value::None);
141 } else if !self.overwrite {
142 return Err(Error::AccessDbAlreadyExists {
143 ac: self.name.to_string(),
144 ns: opt.ns()?.into(),
145 db: opt.db()?.into(),
146 });
147 }
148 }
149 let key = crate::key::database::access::ac::new(opt.ns()?, opt.db()?, &self.name);
151 txn.get_or_add_ns(opt.ns()?, opt.strict).await?;
152 txn.get_or_add_db(opt.ns()?, opt.db()?, opt.strict).await?;
153 txn.set(
154 key,
155 DefineAccessStatement {
156 if_not_exists: false,
158 overwrite: false,
159 ..self.clone()
160 },
161 )
162 .await?;
163 txn.clear();
165 Ok(Value::None)
167 }
168 _ => Err(Error::InvalidLevel(self.base.to_string())),
170 }
171 }
172}
173
174impl Display for DefineAccessStatement {
175 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
176 write!(f, "DEFINE ACCESS",)?;
177 if self.if_not_exists {
178 write!(f, " IF NOT EXISTS")?
179 }
180 if self.overwrite {
181 write!(f, " OVERWRITE")?
182 }
183 write!(f, " {} ON {} TYPE {}", self.name, self.base, self.kind)?;
185 if let Some(ref v) = self.authenticate {
187 write!(f, " AUTHENTICATE {v}")?
188 }
189 write!(f, " DURATION")?;
193 if self.kind.can_issue_grants() {
194 write!(
195 f,
196 " FOR GRANT {},",
197 match self.duration.grant {
198 Some(dur) => format!("{}", dur),
199 None => "NONE".to_string(),
200 }
201 )?;
202 }
203 if self.kind.can_issue_tokens() {
204 write!(
205 f,
206 " FOR TOKEN {},",
207 match self.duration.token {
208 Some(dur) => format!("{}", dur),
209 None => "NONE".to_string(),
210 }
211 )?;
212 }
213 write!(
214 f,
215 " FOR SESSION {}",
216 match self.duration.session {
217 Some(dur) => format!("{}", dur),
218 None => "NONE".to_string(),
219 }
220 )?;
221 if let Some(ref v) = self.comment {
222 write!(f, " COMMENT {v}")?
223 }
224 Ok(())
225 }
226}
227
228impl InfoStructure for DefineAccessStatement {
229 fn structure(self) -> Value {
230 Value::from(map! {
231 "name".to_string() => self.name.structure(),
232 "base".to_string() => self.base.structure(),
233 "authenticate".to_string(), if let Some(v) = self.authenticate => v.structure(),
234 "duration".to_string() => Value::from(map!{
235 "session".to_string() => self.duration.session.into(),
236 "grant".to_string(), if self.kind.can_issue_grants() => self.duration.grant.into(),
237 "token".to_string(), if self.kind.can_issue_tokens() => self.duration.token.into(),
238 }),
239 "kind".to_string() => self.kind.structure(),
240 "comment".to_string(), if let Some(v) = self.comment => v.into(),
241 })
242 }
243}