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

ambee/giterated

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

Remove image_url from users

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨979c015

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