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

ambee/giterated

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

Completely refactor project structure

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨ae8ff44

⁨giterated-daemon/src/backend/user.rs⁩ - ⁨7520⁩ 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, PasswordHasher};
7 use base64::{engine::general_purpose::STANDARD, Engine as _};
8 use futures_util::StreamExt;
9 use giterated_models::{
10 messages::{
11 authentication::{
12 AuthenticationTokenRequest, AuthenticationTokenResponse, RegisterAccountRequest,
13 RegisterAccountResponse,
14 },
15 user::{
16 UserBioRequest, UserBioResponse, UserDisplayImageRequest, UserDisplayImageResponse,
17 UserDisplayNameRequest, UserDisplayNameResponse,
18 },
19 },
20 model::{instance::Instance, user::User},
21 };
22 use rsa::{
23 pkcs8::{EncodePrivateKey, EncodePublicKey},
24 rand_core::OsRng,
25 RsaPrivateKey, RsaPublicKey,
26 };
27 use serde_json::Value;
28 use sqlx::{Either, PgPool};
29 use tokio::sync::Mutex;
30
31 use crate::authentication::AuthenticationTokenGranter;
32
33 use super::{AuthBackend, UserBackend};
34
35 pub struct UserAuth {
36 pub pg_pool: PgPool,
37 pub this_instance: Instance,
38 pub auth_granter: Arc<Mutex<AuthenticationTokenGranter>>,
39 }
40
41 impl UserAuth {
42 pub fn new(
43 pool: PgPool,
44 this_instance: &Instance,
45 granter: Arc<Mutex<AuthenticationTokenGranter>>,
46 ) -> Self {
47 Self {
48 pg_pool: pool,
49 this_instance: this_instance.clone(),
50 auth_granter: granter,
51 }
52 }
53 }
54
55 #[async_trait::async_trait]
56 impl UserBackend for UserAuth {
57 async fn display_name(
58 &mut self,
59 request: UserDisplayNameRequest,
60 ) -> Result<UserDisplayNameResponse, Error> {
61 let db_row = sqlx::query_as!(
62 UserRow,
63 r#"SELECT * FROM users WHERE username = $1"#,
64 request.user.username
65 )
66 .fetch_one(&self.pg_pool.clone())
67 .await
68 .unwrap();
69
70 Ok(UserDisplayNameResponse {
71 display_name: db_row.display_name,
72 })
73 }
74
75 async fn display_image(
76 &mut self,
77 request: UserDisplayImageRequest,
78 ) -> Result<UserDisplayImageResponse, anyhow::Error> {
79 let db_row = sqlx::query_as!(
80 UserRow,
81 r#"SELECT * FROM users WHERE username = $1"#,
82 request.user.username
83 )
84 .fetch_one(&self.pg_pool.clone())
85 .await
86 .unwrap();
87
88 Ok(UserDisplayImageResponse {
89 image_url: db_row.image_url,
90 })
91 }
92
93 async fn bio(&mut self, request: UserBioRequest) -> Result<UserBioResponse, Error> {
94 let db_row = sqlx::query_as!(
95 UserRow,
96 r#"SELECT * FROM users WHERE username = $1"#,
97 request.user.username
98 )
99 .fetch_one(&self.pg_pool.clone())
100 .await?;
101
102 Ok(UserBioResponse { bio: db_row.bio })
103 }
104
105 async fn exists(&mut self, user: &User) -> Result<bool, Error> {
106 Ok(sqlx::query_as!(
107 UserRow,
108 r#"SELECT * FROM users WHERE username = $1"#,
109 user.username
110 )
111 .fetch_one(&self.pg_pool.clone())
112 .await
113 .is_err())
114 }
115
116 async fn settings(&mut self, user: &User) -> Result<Vec<(String, Value)>, Error> {
117 let settings = sqlx::query_as!(
118 UserSettingRow,
119 r#"SELECT * FROM user_settings WHERE username = $1"#,
120 user.username
121 )
122 .fetch_many(&self.pg_pool)
123 .filter_map(|result| async move {
124 if let Ok(Either::Right(row)) = result {
125 Some(row)
126 } else {
127 None
128 }
129 })
130 .filter_map(|row| async move {
131 if let Ok(value) = serde_json::from_str(&row.value) {
132 Some((row.name, value))
133 } else {
134 None
135 }
136 })
137 .collect::<Vec<_>>()
138 .await;
139
140 Ok(settings)
141 }
142
143 async fn write_settings(
144 &mut self,
145 user: &User,
146 settings: &[(String, Value)],
147 ) -> Result<(), Error> {
148 for (name, value) in settings {
149 let serialized = serde_json::to_string(value)?;
150
151 sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3",
152 user.username, name, serialized)
153 .execute(&self.pg_pool).await?;
154 }
155
156 Ok(())
157 }
158 }
159
160 #[async_trait::async_trait]
161 impl AuthBackend for UserAuth {
162 async fn register(
163 &mut self,
164 request: RegisterAccountRequest,
165 ) -> Result<RegisterAccountResponse, Error> {
166 const BITS: usize = 2048;
167
168 let private_key = RsaPrivateKey::new(&mut OsRng, BITS).unwrap();
169 let public_key = RsaPublicKey::from(&private_key);
170
171 let key = {
172 let mut target: [u8; 32] = [0; 32];
173
174 let mut index = 0;
175 let mut iterator = request.password.as_bytes().iter();
176 while index < 32 {
177 if let Some(next) = iterator.next() {
178 target[index] = *next;
179 index += 1;
180 } else {
181 iterator = request.password.as_bytes().iter();
182 }
183 }
184
185 target
186 };
187
188 let key: &Key<Aes256Gcm> = &key.into();
189 let cipher = Aes256Gcm::new(key);
190 let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
191 let ciphertext = cipher
192 .encrypt(&nonce, private_key.to_pkcs8_der().unwrap().as_bytes())
193 .unwrap();
194
195 let private_key_enc = format!("{}#{}", STANDARD.encode(nonce), STANDARD.encode(ciphertext));
196
197 let salt = SaltString::generate(&mut OsRng);
198
199 let argon2 = Argon2::default();
200
201 let password_hash = argon2
202 .hash_password(request.password.as_bytes(), &salt)
203 .unwrap()
204 .to_string();
205
206 let user = match sqlx::query_as!(
207 UserRow,
208 r#"INSERT INTO users VALUES ($1, null, $2, null, null, $3, $4, $5) returning *"#,
209 request.username,
210 "example.com",
211 password_hash,
212 public_key
213 .to_public_key_pem(rsa::pkcs8::LineEnding::LF)
214 .unwrap(),
215 private_key_enc
216 )
217 .fetch_one(&self.pg_pool)
218 .await
219 {
220 Ok(user) => user,
221 Err(err) => {
222 error!("Failed inserting into the database! {:?}", err);
223
224 return Err(err.into());
225 }
226 };
227
228 let mut granter = self.auth_granter.lock().await;
229 let token = granter
230 .create_token_for(
231 &User {
232 username: user.username,
233 instance: self.this_instance.clone(),
234 },
235 &self.this_instance,
236 )
237 .await;
238
239 Ok(RegisterAccountResponse { token })
240 }
241
242 async fn login(
243 &mut self,
244 _request: AuthenticationTokenRequest,
245 ) -> Result<AuthenticationTokenResponse, Error> {
246 todo!()
247 }
248 }
249
250 #[allow(unused)]
251 #[derive(Debug, sqlx::FromRow)]
252 struct UserRow {
253 pub username: String,
254 pub image_url: Option<String>,
255 pub display_name: Option<String>,
256 pub bio: Option<String>,
257 pub email: Option<String>,
258 pub password: String,
259 pub public_key: String,
260 pub enc_private_key: Vec<u8>,
261 }
262
263 #[allow(unused)]
264 #[derive(Debug, sqlx::FromRow)]
265 struct UserSettingRow {
266 pub username: String,
267 pub name: String,
268 pub value: String,
269 }
270