1use rusqlite::{TransactionBehavior, params};
6
7use super::{
8 AuthError, AuthResult, AuthStore, User, clean_name, ensure_org_tx, external, normalize_email,
9};
10use crate::org::OrgId;
11
12impl AuthStore {
13 pub fn setup_needed(&self) -> AuthResult<bool> {
15 let n: i64 = self
16 .db()
17 .query_row("SELECT COUNT(*) FROM users", [], |r| r.get(0))?;
18 Ok(n == 0)
19 }
20
21 pub fn create_first_admin(&self, email: &str, name: &str, password: &str) -> AuthResult<User> {
24 self.first_admin(email, name, Some(password), None)
25 }
26
27 pub fn claim_first_admin(
31 &self,
32 email: &str,
33 name: &str,
34 password: Option<&str>,
35 link: &external::ExternalIdentity,
36 ) -> AuthResult<User> {
37 self.first_admin(email, name, password, Some(link))
38 }
39
40 fn first_admin(
41 &self,
42 email: &str,
43 name: &str,
44 password: Option<&str>,
45 link: Option<&external::ExternalIdentity>,
46 ) -> AuthResult<User> {
47 let email = normalize_email(email)?;
48 let name = clean_name(name)?;
49 let hash = password.map(|p| self.hash_pw(p)).transpose()?;
50 let now = self.now();
51 let mut db = self.db();
52 let tx = db.transaction_with_behavior(TransactionBehavior::Immediate)?;
53 let n: i64 = tx.query_row("SELECT COUNT(*) FROM users", [], |r| r.get(0))?;
54 if n > 0 {
55 return Err(AuthError::Conflict("setup is already done".into()));
56 }
57 tx.execute(
58 "INSERT INTO users (email, name, password_hash, platform_admin, created_at)
59 VALUES (?1, ?2, ?3, 1, ?4)",
60 params![email, name, hash, now],
61 )?;
62 let id = tx.last_insert_rowid();
63 if let Some(ext) = link {
64 let verified = ext.email_verified
65 && ext
66 .email
67 .as_deref()
68 .is_some_and(|e| e.eq_ignore_ascii_case(&email));
69 let shown = ext.email.as_deref().and_then(|e| normalize_email(e).ok());
70 external::insert_identity(&tx, id, ext, shown.as_deref(), verified, now)?;
71 }
72 let org = OrgId::default_org();
73 ensure_org_tx(&tx, &org, now)?;
74 tx.execute(
75 "INSERT INTO memberships (user_id, org, role, created_at) VALUES (?1, ?2, 'owner', ?3)",
76 params![id, org.as_str(), now],
77 )?;
78 tx.commit()?;
79 drop(db);
80 self.user(id)
81 }
82}