surrealdb_core/sql/statements/define/
user.rs1use std::fmt::{self, Display};
2
3use argon2::Argon2;
4use argon2::password_hash::{PasswordHasher, SaltString};
5use rand::Rng;
6use rand::distributions::Alphanumeric;
7use rand::rngs::OsRng;
8
9use super::DefineKind;
10use crate::sql::escape::QuoteStr;
11use crate::sql::fmt::Fmt;
12use crate::sql::{Base, Ident};
13use crate::val::{Duration, Strand};
14
15#[derive(Clone, Debug, Default, Eq, PartialEq)]
16#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
17pub enum PassType {
18 #[default]
19 Unset,
20 Hash(String),
21 Password(String),
22}
23
24#[derive(Clone, Debug, Default, PartialEq, Eq)]
25#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
26pub struct DefineUserStatement {
27 pub kind: DefineKind,
28 pub name: Ident,
29 pub base: Base,
30 pub pass_type: PassType,
31 pub roles: Vec<Ident>,
32 pub token_duration: Option<Duration>,
33 pub session_duration: Option<Duration>,
34
35 pub comment: Option<Strand>,
36}
37
38impl Display for DefineUserStatement {
39 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40 write!(f, "DEFINE USER")?;
41 match self.kind {
42 DefineKind::Default => {}
43 DefineKind::Overwrite => write!(f, " OVERWRITE")?,
44 DefineKind::IfNotExists => write!(f, " IF NOT EXISTS")?,
45 }
46
47 write!(f, " {} ON {}", self.name, self.base,)?;
48
49 match self.pass_type {
50 PassType::Unset => write!(f, " PASSHASH \"\" ")?,
51 PassType::Hash(ref x) => write!(f, " PASSHASH {}", QuoteStr(x))?,
52 PassType::Password(ref x) => write!(f, " PASSWORD {}", QuoteStr(x))?,
53 }
54
55 write!(
56 f,
57 " ROLES {}",
58 Fmt::comma_separated(
59 &self.roles.iter().map(|r| r.to_string().to_uppercase()).collect::<Vec<String>>()
60 ),
61 )?;
62 write!(f, " DURATION")?;
66 write!(
67 f,
68 " FOR TOKEN {},",
69 match self.token_duration {
70 Some(dur) => format!("{}", dur),
71 None => "NONE".to_string(),
72 }
73 )?;
74 write!(
75 f,
76 " FOR SESSION {}",
77 match self.session_duration {
78 Some(dur) => format!("{}", dur),
79 None => "NONE".to_string(),
80 }
81 )?;
82 if let Some(ref v) = self.comment {
83 write!(f, " COMMENT {v}")?
84 }
85 Ok(())
86 }
87}
88
89#[allow(clippy::fallible_impl_from)]
90impl From<DefineUserStatement> for crate::expr::statements::DefineUserStatement {
91 fn from(v: DefineUserStatement) -> Self {
92 let hash = match v.pass_type {
93 PassType::Unset => String::new(),
94 PassType::Hash(x) => x,
95 PassType::Password(p) => Argon2::default()
97 .hash_password(p.as_bytes(), &SaltString::generate(&mut OsRng))
98 .unwrap()
99 .to_string(),
100 };
101
102 let code = rand::thread_rng()
103 .sample_iter(&Alphanumeric)
104 .take(128)
105 .map(char::from)
106 .collect::<String>();
107
108 Self {
109 kind: v.kind.into(),
110 name: v.name.into(),
111 base: v.base.into(),
112 hash,
113 code,
114 roles: v.roles.into_iter().map(Into::into).collect(),
115 duration: crate::expr::user::UserDuration {
116 token: v.token_duration,
117 session: v.session_duration,
118 },
119 comment: v.comment,
120 }
121 }
122}
123
124impl From<crate::expr::statements::DefineUserStatement> for DefineUserStatement {
125 fn from(v: crate::expr::statements::DefineUserStatement) -> Self {
126 Self {
127 kind: v.kind.into(),
128 name: v.name.into(),
129 base: v.base.into(),
130 pass_type: PassType::Hash(v.hash),
131 roles: v.roles.into_iter().map(Into::into).collect(),
132 token_duration: v.duration.token,
133 session_duration: v.duration.session,
134 comment: v.comment,
135 }
136 }
137}