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

ambee/giterated

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

Base protocol refactor complete

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨079d544

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