JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

Progress on refactor

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨c9f076f

⁨giterated-daemon/src/backend/user.rs⁩ - ⁨6193⁩ bytes
Raw
1 use std::sync::Arc;
2
3 use anyhow::Error;
4
5 use aes_gcm::{aead::Aead, AeadCore, Aes256Gcm, Key, KeyInit};
6 use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
7 use base64::{engine::general_purpose::STANDARD, Engine as _};
8 use giterated_models::{
9 model::{
10 authenticated::UserAuthenticationToken, instance::Instance, settings::AnySetting,
11 user::User,
12 },
13 operation::instance::{AuthenticationTokenRequest, RegisterAccountRequest},
14 values::AnyValue,
15 };
16 use rsa::{
17 pkcs8::{EncodePrivateKey, EncodePublicKey},
18 rand_core::OsRng,
19 RsaPrivateKey, RsaPublicKey,
20 };
21
22 use secrecy::ExposeSecret;
23 use serde_json::Value;
24 use sqlx::PgPool;
25 use tokio::sync::Mutex;
26
27 use crate::authentication::AuthenticationTokenGranter;
28
29 use super::{AuthBackend, SettingsBackend, UserBackend};
30
31 pub struct UserAuth {
32 pub pg_pool: PgPool,
33 pub this_instance: Instance,
34 pub auth_granter: Arc<Mutex<AuthenticationTokenGranter>>,
35 pub settings_provider: Arc<Mutex<dyn SettingsBackend>>,
36 }
37
38 impl UserAuth {
39 pub fn new(
40 pool: PgPool,
41 this_instance: &Instance,
42 granter: Arc<Mutex<AuthenticationTokenGranter>>,
43 settings_provider: Arc<Mutex<dyn SettingsBackend>>,
44 ) -> Self {
45 Self {
46 pg_pool: pool,
47 this_instance: this_instance.clone(),
48 auth_granter: granter,
49 settings_provider,
50 }
51 }
52 }
53
54 #[async_trait::async_trait]
55 impl UserBackend for UserAuth {
56 async fn get_value(&mut self, _user: &User, _name: &str) -> Result<AnyValue<User>, Error> {
57 todo!()
58 }
59 async fn get_setting(&mut self, _user: &User, _name: &str) -> Result<AnySetting, Error> {
60 todo!()
61 }
62 async fn write_setting(
63 &mut self,
64 _user: &User,
65 _name: &str,
66 _setting: &Value,
67 ) -> Result<(), Error> {
68 todo!()
69 }
70 async fn exists(&mut self, user: &User) -> Result<bool, Error> {
71 Ok(sqlx::query_as!(
72 UserRow,
73 r#"SELECT * FROM users WHERE username = $1"#,
74 user.username
75 )
76 .fetch_one(&self.pg_pool.clone())
77 .await
78 .is_ok())
79 }
80 }
81
82 #[async_trait::async_trait]
83 impl AuthBackend for UserAuth {
84 async fn register(
85 &mut self,
86 request: RegisterAccountRequest,
87 ) -> Result<UserAuthenticationToken, Error> {
88 const BITS: usize = 2048;
89
90 let private_key = RsaPrivateKey::new(&mut OsRng, BITS).unwrap();
91 let public_key = RsaPublicKey::from(&private_key);
92
93 let key = {
94 let mut target: [u8; 32] = [0; 32];
95
96 let mut index = 0;
97 let mut iterator = request.password.expose_secret().0.as_bytes().iter();
98 while index < 32 {
99 if let Some(next) = iterator.next() {
100 target[index] = *next;
101 index += 1;
102 } else {
103 iterator = request.password.expose_secret().0.as_bytes().iter();
104 }
105 }
106
107 target
108 };
109
110 let key: &Key<Aes256Gcm> = &key.into();
111 let cipher = Aes256Gcm::new(key);
112 let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
113 let ciphertext = cipher
114 .encrypt(&nonce, private_key.to_pkcs8_der().unwrap().as_bytes())
115 .unwrap();
116
117 let private_key_enc = format!("{}#{}", STANDARD.encode(nonce), STANDARD.encode(ciphertext));
118
119 let salt = SaltString::generate(&mut OsRng);
120
121 let argon2 = Argon2::default();
122
123 let password_hash = argon2
124 .hash_password(request.password.expose_secret().0.as_bytes(), &salt)
125 .unwrap()
126 .to_string();
127
128 let user = match sqlx::query_as!(
129 UserRow,
130 r#"INSERT INTO users VALUES ($1, $2, $3, $4, $5) returning *"#,
131 request.username,
132 "example.com",
133 password_hash,
134 public_key
135 .to_public_key_pem(rsa::pkcs8::LineEnding::LF)
136 .unwrap(),
137 private_key_enc
138 )
139 .fetch_one(&self.pg_pool)
140 .await
141 {
142 Ok(user) => user,
143 Err(err) => {
144 error!("Failed inserting into the database! {:?}", err);
145
146 return Err(err.into());
147 }
148 };
149
150 let mut granter = self.auth_granter.lock().await;
151 let token = granter
152 .create_token_for(
153 &User {
154 username: user.username,
155 instance: self.this_instance.clone(),
156 },
157 &self.this_instance,
158 )
159 .await;
160
161 Ok(UserAuthenticationToken::from(token))
162 }
163
164 async fn login(
165 &mut self,
166 source: &Instance,
167 request: AuthenticationTokenRequest,
168 ) -> Result<UserAuthenticationToken, Error> {
169 info!("fetching!");
170 let user = sqlx::query_as!(
171 UserRow,
172 r#"SELECT * FROM users WHERE username = $1"#,
173 request.username
174 )
175 .fetch_one(&self.pg_pool)
176 .await?;
177
178 let hash = PasswordHash::new(&user.password).unwrap();
179
180 if Argon2::default()
181 .verify_password(request.password.expose_secret().0.as_bytes(), &hash)
182 .is_err()
183 {
184 info!("invalid password");
185 return Err(Error::from(AuthenticationError::InvalidPassword));
186 }
187
188 let mut granter = self.auth_granter.lock().await;
189 let token = granter
190 .create_token_for(
191 &User {
192 username: user.username,
193 instance: self.this_instance.clone(),
194 },
195 &source,
196 )
197 .await;
198
199 Ok(UserAuthenticationToken::from(token))
200 }
201 }
202
203 #[allow(unused)]
204 #[derive(Debug, sqlx::FromRow)]
205 struct UserRow {
206 pub username: String,
207 pub email: Option<String>,
208 pub password: String,
209 pub public_key: String,
210 pub enc_private_key: Vec<u8>,
211 }
212
213 #[derive(Debug, thiserror::Error)]
214 pub enum AuthenticationError {
215 #[error("invalid password")]
216 InvalidPassword,
217 }
218