surrealdb/sql/statements/define/
user.rs

1use crate::ctx::Context;
2use crate::dbs::{Options, Transaction};
3use crate::doc::CursorDoc;
4use crate::err::Error;
5use crate::iam::{Action, ResourceKind};
6use crate::sql::{escape::quote_str, fmt::Fmt, Base, Ident, Strand, Value};
7use argon2::{
8	password_hash::{PasswordHasher, SaltString},
9	Argon2,
10};
11use derive::Store;
12use rand::{distributions::Alphanumeric, rngs::OsRng, Rng};
13use revision::revisioned;
14use serde::{Deserialize, Serialize};
15use std::fmt::{self, Display};
16
17#[derive(Clone, Debug, Default, Eq, PartialEq, PartialOrd, Serialize, Deserialize, Store, Hash)]
18#[revisioned(revision = 1)]
19pub struct DefineUserStatement {
20	pub name: Ident,
21	pub base: Base,
22	pub hash: String,
23	pub code: String,
24	pub roles: Vec<Ident>,
25	pub comment: Option<Strand>,
26}
27
28impl From<(Base, &str, &str)> for DefineUserStatement {
29	fn from((base, user, pass): (Base, &str, &str)) -> Self {
30		DefineUserStatement {
31			base,
32			name: user.into(),
33			hash: Argon2::default()
34				.hash_password(pass.as_ref(), &SaltString::generate(&mut OsRng))
35				.unwrap()
36				.to_string(),
37			code: rand::thread_rng()
38				.sample_iter(&Alphanumeric)
39				.take(128)
40				.map(char::from)
41				.collect::<String>(),
42			roles: vec!["owner".into()],
43			comment: None,
44		}
45	}
46}
47
48impl DefineUserStatement {
49	/// Process this type returning a computed simple Value
50	pub(crate) async fn compute(
51		&self,
52		_ctx: &Context<'_>,
53		opt: &Options,
54		txn: &Transaction,
55		_doc: Option<&CursorDoc<'_>>,
56	) -> Result<Value, Error> {
57		// Allowed to run?
58		opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?;
59
60		match self.base {
61			Base::Root => {
62				// Claim transaction
63				let mut run = txn.lock().await;
64				// Clear the cache
65				run.clear_cache();
66				// Process the statement
67				let key = crate::key::root::us::new(&self.name);
68				run.set(key, self).await?;
69				// Ok all good
70				Ok(Value::None)
71			}
72			Base::Ns => {
73				// Claim transaction
74				let mut run = txn.lock().await;
75				// Clear the cache
76				run.clear_cache();
77				// Process the statement
78				let key = crate::key::namespace::us::new(opt.ns(), &self.name);
79				run.add_ns(opt.ns(), opt.strict).await?;
80				run.set(key, self).await?;
81				// Ok all good
82				Ok(Value::None)
83			}
84			Base::Db => {
85				// Claim transaction
86				let mut run = txn.lock().await;
87				// Clear the cache
88				run.clear_cache();
89				// Process the statement
90				let key = crate::key::database::us::new(opt.ns(), opt.db(), &self.name);
91				run.add_ns(opt.ns(), opt.strict).await?;
92				run.add_db(opt.ns(), opt.db(), opt.strict).await?;
93				run.set(key, self).await?;
94				// Ok all good
95				Ok(Value::None)
96			}
97			// Other levels are not supported
98			_ => Err(Error::InvalidLevel(self.base.to_string())),
99		}
100	}
101}
102
103impl Display for DefineUserStatement {
104	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105		write!(
106			f,
107			"DEFINE USER {} ON {} PASSHASH {} ROLES {}",
108			self.name,
109			self.base,
110			quote_str(&self.hash),
111			Fmt::comma_separated(
112				&self.roles.iter().map(|r| r.to_string().to_uppercase()).collect::<Vec<String>>()
113			)
114		)?;
115		if let Some(ref v) = self.comment {
116			write!(f, " COMMENT {v}")?
117		}
118		Ok(())
119	}
120}