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

ambee/giterated

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

Major post-refactor cleanup

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨f90d7fb

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