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

ambee/giterated

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

Automatically populate the target instance field from the request using MessageTarget trait

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨37da513

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