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

ambee/giterated

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

Add docs

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨51aad53

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