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

ambee/giterated

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

Expose errors

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨6b2125c

⁨src/backend/user.rs⁩ - ⁨6032⁩ 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
100 Ok(UserBioResponse { bio: db_row.bio })
101 }
102
103 async fn exists(&mut self, user: &User) -> Result<bool, Error> {
104 Ok(sqlx::query_as!(
105 UserRow,
106 r#"SELECT * FROM users WHERE username = $1"#,
107 user.username
108 )
109 .fetch_one(&self.pg_pool.clone())
110 .await
111 .is_err())
112 }
113 }
114
115 #[async_trait::async_trait]
116 impl AuthBackend for UserAuth {
117 async fn register(
118 &mut self,
119 request: RegisterAccountRequest,
120 ) -> Result<RegisterAccountResponse, Error> {
121 const BITS: usize = 2048;
122
123 let private_key = RsaPrivateKey::new(&mut OsRng, BITS).unwrap();
124 let public_key = RsaPublicKey::from(&private_key);
125
126 let key = {
127 let mut target: [u8; 32] = [0; 32];
128
129 let mut index = 0;
130 let mut iterator = request.password.as_bytes().iter();
131 while index < 32 {
132 if let Some(next) = iterator.next() {
133 target[index] = *next;
134 index += 1;
135 } else {
136 iterator = request.password.as_bytes().iter();
137 }
138 }
139
140 target
141 };
142
143 let key: &Key<Aes256Gcm> = &key.into();
144 let cipher = Aes256Gcm::new(key);
145 let nonce = Aes256Gcm::generate_nonce(&mut OsRng);
146 let ciphertext = cipher
147 .encrypt(&nonce, private_key.to_pkcs8_der().unwrap().as_bytes())
148 .unwrap();
149
150 let private_key_enc = format!("{}#{}", STANDARD.encode(nonce), STANDARD.encode(ciphertext));
151
152 let salt = SaltString::generate(&mut OsRng);
153
154 let argon2 = Argon2::default();
155
156 let password_hash = argon2
157 .hash_password(request.password.as_bytes(), &salt)
158 .unwrap()
159 .to_string();
160
161 let user = match sqlx::query_as!(
162 UserRow,
163 r#"INSERT INTO users VALUES ($1, null, $2, null, null, $3, $4, $5) returning *"#,
164 request.username,
165 "example.com",
166 password_hash,
167 public_key
168 .to_public_key_pem(rsa::pkcs8::LineEnding::LF)
169 .unwrap(),
170 private_key_enc
171 )
172 .fetch_one(&self.pg_pool)
173 .await
174 {
175 Ok(user) => user,
176 Err(err) => {
177 error!("Failed inserting into the database! {:?}", err);
178
179 return Err(err.into());
180 }
181 };
182
183 let mut granter = self.auth_granter.lock().await;
184 let token = granter
185 .create_token_for(
186 &User {
187 username: user.username,
188 instance: self.this_instance.clone(),
189 },
190 &self.this_instance,
191 )
192 .await;
193
194 Ok(RegisterAccountResponse { token })
195 }
196
197 async fn login(
198 &mut self,
199 _request: AuthenticationTokenRequest,
200 ) -> Result<AuthenticationTokenResponse, Error> {
201 todo!()
202 }
203 }
204
205 #[allow(unused)]
206 #[derive(Debug, sqlx::FromRow)]
207 struct UserRow {
208 pub username: String,
209 pub image_url: Option<String>,
210 pub display_name: Option<String>,
211 pub bio: Option<String>,
212 pub email: Option<String>,
213 pub password: String,
214 pub public_key: String,
215 pub enc_private_key: Vec<u8>,
216 }
217