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

ambee/giterated

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

Fixed imports!

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨ef0e853

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