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::{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	#[doc(hidden)]
51	pub async fn compute(
52		&self,
53		_ctx: &Context<'_>,
54		opt: &Options,
55		txn: &Transaction,
56		_doc: Option<&CursorDoc<'_>>,
57	) -> Result<Value, Error> {
58		// Allowed to run?
59		opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?;
60
61		match self.base {
62			Base::Root => {
63				// Claim transaction
64				let mut run = txn.lock().await;
65				// Clear the cache
66				run.clear_cache();
67				// Process the statement
68				let key = crate::key::root::us::new(&self.name);
69				run.set(key, self).await?;
70				// Ok all good
71				Ok(Value::None)
72			}
73			Base::Ns => {
74				// Claim transaction
75				let mut run = txn.lock().await;
76				// Clear the cache
77				run.clear_cache();
78				// Process the statement
79				let key = crate::key::namespace::us::new(opt.ns(), &self.name);
80				run.add_ns(opt.ns(), opt.strict).await?;
81				run.set(key, self).await?;
82				// Ok all good
83				Ok(Value::None)
84			}
85			Base::Db => {
86				// Claim transaction
87				let mut run = txn.lock().await;
88				// Clear the cache
89				run.clear_cache();
90				// Process the statement
91				let key = crate::key::database::us::new(opt.ns(), opt.db(), &self.name);
92				run.add_ns(opt.ns(), opt.strict).await?;
93				run.add_db(opt.ns(), opt.db(), opt.strict).await?;
94				run.set(key, self).await?;
95				// Ok all good
96				Ok(Value::None)
97			}
98			// Other levels are not supported
99			_ => Err(Error::InvalidLevel(self.base.to_string())),
100		}
101	}
102}
103
104impl Display for DefineUserStatement {
105	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
106		write!(
107			f,
108			"DEFINE USER {} ON {} PASSHASH {} ROLES {}",
109			self.name,
110			self.base,
111			quote_str(&self.hash),
112			Fmt::comma_separated(
113				&self.roles.iter().map(|r| r.to_string().to_uppercase()).collect::<Vec<String>>()
114			)
115		)?;
116		if let Some(ref v) = self.comment {
117			write!(f, " COMMENT {v}")?
118		}
119		Ok(())
120	}
121}