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

ambee/giterated

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

Move user settings to user settings

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨fd846f6

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