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

ambee/giterated

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

Revert authentication payload change

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨16b4bc2

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