surrealdb/sql/statements/define/
user.rs1use 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 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 opt.is_allowed(Action::Edit, ResourceKind::Actor, &self.base)?;
59
60 match self.base {
61 Base::Root => {
62 let mut run = txn.lock().await;
64 run.clear_cache();
66 let key = crate::key::root::us::new(&self.name);
68 run.set(key, self).await?;
69 Ok(Value::None)
71 }
72 Base::Ns => {
73 let mut run = txn.lock().await;
75 run.clear_cache();
77 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(Value::None)
83 }
84 Base::Db => {
85 let mut run = txn.lock().await;
87 run.clear_cache();
89 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(Value::None)
96 }
97 _ => 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}