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

ambee/giterated

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

Begin new protocol refactor

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨26651b1

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