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

ambee/giterated

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

Fix settings serialization

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨a70c63a

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