chore: extract auth-related model funcs to proper module
Build nomnom / Build-NomNom (push) Successful in 3m32s Details

This commit is contained in:
Félix Baylac Jacqué 2023-11-27 12:08:23 +01:00
parent 076dea7ab7
commit 62535ae5cf
2 changed files with 138 additions and 81 deletions

View File

@ -0,0 +1,127 @@
use super::{UserUuid, RegistrationUuid, User, Key};
use anyhow::Result;
use deadpool_postgres::Pool;
use serde_json::Value;
use uuid::Uuid;
use webauthn_rs::prelude::AuthenticationResult;
/**
Generate a new registration uuid for the given `UserUuid` and
saves it to the DB.
*/
pub async fn generate_registration_uuid(db: &Pool, user_id: &UserUuid) -> Result<RegistrationUuid> {
let registration_uuid = Uuid::new_v4();
let mut conn = db.get().await?;
let client = conn.as_mut();
let stmt = client.prepare_cached("INSERT INTO PendingRegistrations (id, user_id) VALUES ($1, $2)").await?;
client.query(&stmt, &[&registration_uuid, &user_id.0]).await?;
Ok(RegistrationUuid(registration_uuid))
}
/**
Retrieves the registred `User` attached to the `RegistrationUuid`.
Returns `Some` `User` if it can find it, `None` if the `RegistrationUuid`
doesn't exist in the registration table.
*/
pub async fn retrieve_registration_user(db: &Pool, registration_id: &RegistrationUuid) -> Result<Option<User>> {
let mut conn = db.get().await?;
let client = conn.as_mut();
let stmt = client.prepare_cached("SELECT u.id, u.user_name FROM Users u INNER JOIN PendingRegistrations r ON r.user_id = u.id WHERE r.id=$1").await?;
let row = client.query_one(&stmt, &[&registration_id.0]).await?;
let usr = User {
uuid: row.get(0),
name: row.get(1),
};
Ok(Some(usr))
}
/**
Deletes the registration `Uuid` attached to the `UserUuid` passed in
parameter.
*/
pub async fn delete_registration_link(db: &Pool, user_id: &RegistrationUuid) -> Result<()> {
let mut conn = db.get().await?;
let client = conn.as_mut();
let stmt = client.prepare_cached("DELETE FROM PendingRegistrations WHERE id=$1").await?;
client.query(&stmt, &[&user_id.0]).await?;
Ok(())
}
pub async fn get_user(db: &Pool, username: &str) -> Result<User> {
let mut conn = db.get().await?;
let client = conn.as_mut();
let stmt = client.prepare_cached("SELECT id, user_name FROM Users WHERE user_name = $1").await?;
let row = client.query_one(&stmt, &[&username]).await?;
Ok(User{ uuid: row.get(0), name: row.get(1) })
}
pub async fn save_user(db: &Pool, user: &User) -> Result<()> {
let mut conn = db.get().await?;
let client = conn.as_mut();
let stmt = client.prepare_cached("INSERT INTO Users (id, user_name) VALUES ($1, $2)").await?;
let _ = client.query(&stmt, &[&user.uuid.0, &user.name]).await?;
Ok(())
}
pub async fn save_user_key(db: &Pool, user_id: &UserUuid, passkey: &Key) -> Result<()> {
let passkey_json: Value = serde_json::to_value(&passkey.key).unwrap();
let mut conn = db.get().await?;
let client = conn.as_mut();
let stmt = client.prepare_cached("INSERT INTO Keys (user_id, name, key_dump) VALUES ($1, $2, $3)").await?;
client.query(&stmt, &[&user_id.0, &passkey.name, &passkey_json]).await?;
Ok(())
}
pub async fn get_user_keys(db: &Pool, user_id: &UserUuid) -> Result<Vec<Key>> {
let mut conn = db.get().await?;
let client = conn.as_mut();
let stmt = client.prepare_cached("SELECT name, key_dump FROM Keys WHERE user_id = $1").await?;
let res = client.query(&stmt, &[&user_id.0]).await?;
let res = res.iter().map(|row| {
let key: Value = row.get(1);
Key {
name: row.get(0),
// We can safely assume the JSON was valid, postgres
// would reject it if it wasn't. We can safely unwrap
// here.
key: serde_json::from_value(key).unwrap()
}}).collect();
Ok(res)
}
/**
Updates the keys for the user `UserUuid` according to the last
successful login `AuthenticationResult`.
*/
pub async fn update_user_keys(db: &Pool, user_id: &UserUuid, auth_res: AuthenticationResult) -> Result<()> {
let mut conn = db.get().await?;
let transaction = conn.as_mut().transaction().await?;
// Retrieve user keys
let stmt = transaction.prepare_cached("SELECT name, key_dump FROM Keys WHERE user_id = $1").await?;
let res = transaction.query(&stmt, &[&user_id.0]).await?;
let mut keys: Vec<Key> = res.iter().map(|row| {
let key = row.get(1);
Key { name: row.get(0), key: serde_json::from_value(key).unwrap() }
}).collect();
// Update keys
keys.iter_mut().for_each(
|key| {
key.key.update_credential(&auth_res);
}
);
// Delete current keys, save updated keys
let stmt = transaction.prepare_cached("DELETE FROM Keys WHERE user_id = $1").await?;
let _ = transaction.execute(&stmt, &[&user_id.0]).await?;
let stmt = transaction.prepare_cached("INSERT INTO Keys (user_id, name, key_dump) VALUES ($1,$2,$3)").await?;
for key in keys {
let passkey_val: Value = serde_json::to_value(&key.key).unwrap();
let _ = transaction.execute(&stmt, &[&user_id.0, &key.name, &passkey_val]).await?;
}
transaction.commit().await?;
Ok(())
}

View File

@ -9,12 +9,14 @@ use anyhow::{Result, Context};
use deadpool_postgres::{Manager, ManagerConfig, Pool, RecyclingMethod};
use handlebars::Handlebars;
use serde::{Deserialize, Serialize};
use serde_json::{self, Value};
use serde_json;
use tokio::sync::RwLock;
use tokio_postgres::NoTls;
use webauthn_rs::prelude::{Uuid, PasskeyRegistration, Passkey, PasskeyAuthentication, AuthenticationResult};
use webauthn_rs::{Webauthn, WebauthnBuilder};
mod authentication;
#[derive(Deserialize, Debug, Clone)]
pub struct Configuration {
pub url: String,
@ -115,12 +117,7 @@ impl AppState<'_> {
saves it to the DB.
*/
pub async fn generate_registration_uuid(&self, user_id: &UserUuid) -> Result<RegistrationUuid> {
let registration_uuid = Uuid::new_v4();
let mut conn = self.db.get().await?;
let client = conn.deref_mut();
let stmt = client.prepare_cached("INSERT INTO PendingRegistrations (id, user_id) VALUES ($1, $2)").await?;
client.query(&stmt, &[&registration_uuid, &user_id.0]).await?;
Ok(RegistrationUuid(registration_uuid))
authentication::generate_registration_uuid(&self.db, user_id).await
}
/**
@ -130,15 +127,7 @@ impl AppState<'_> {
doesn't exist in the registration table.
*/
pub async fn retrieve_registration_user(&self, registration_id: &RegistrationUuid) -> Result<Option<User>> {
let mut conn = self.db.get().await?;
let client = conn.deref_mut();
let stmt = client.prepare_cached("SELECT u.id, u.user_name FROM Users u INNER JOIN PendingRegistrations r ON r.user_id = u.id WHERE r.id=$1").await?;
let row = client.query_one(&stmt, &[&registration_id.0]).await?;
let usr = User {
uuid: row.get(0),
name: row.get(1),
};
Ok(Some(usr))
authentication::retrieve_registration_user(&self.db, registration_id).await
}
/**
@ -146,11 +135,7 @@ impl AppState<'_> {
parameter.
*/
pub async fn delete_registration_link(&self, user_id: &RegistrationUuid) -> Result<()> {
let mut conn = self.db.get().await?;
let client = conn.deref_mut();
let stmt = client.prepare_cached("DELETE FROM PendingRegistrations WHERE id=$1").await?;
client.query(&stmt, &[&user_id.0]).await?;
Ok(())
authentication::delete_registration_link(&self.db, user_id).await
}
pub async fn run_migrations(&self) -> Result<()> {
@ -161,47 +146,20 @@ impl AppState<'_> {
Ok(())
}
pub async fn get_user(&self, username: &str) -> Result<User> {
let mut conn = self.db.get().await?;
let client = conn.deref_mut();
let stmt = client.prepare_cached("SELECT id, user_name FROM Users WHERE user_name = $1").await?;
let row = client.query_one(&stmt, &[&username]).await?;
Ok(User{ uuid: row.get(0), name: row.get(1) })
authentication::get_user(&self.db, username).await
}
pub async fn save_user(&self, user: &User) -> Result<()> {
let mut conn = self.db.get().await?;
let client = conn.deref_mut();
let stmt = client.prepare_cached("INSERT INTO Users (id, user_name) VALUES ($1, $2)").await?;
let _ = client.query(&stmt, &[&user.uuid.0, &user.name]).await?;
Ok(())
authentication::save_user(&self.db, user).await
}
pub async fn save_user_key(&self, user_id: &UserUuid, passkey: &Key) -> Result<()> {
let passkey_json: Value = serde_json::to_value(&passkey.key).unwrap();
let mut conn = self.db.get().await?;
let client = conn.deref_mut();
let stmt = client.prepare_cached("INSERT INTO Keys (user_id, name, key_dump) VALUES ($1, $2, $3)").await?;
client.query(&stmt, &[&user_id.0, &passkey.name, &passkey_json]).await?;
Ok(())
authentication::save_user_key(&self.db, user_id, passkey).await
}
pub async fn get_user_keys(&self, user_id: &UserUuid) -> Result<Vec<Key>> {
let mut conn = self.db.get().await?;
let client = conn.deref_mut();
let stmt = client.prepare_cached("SELECT name, key_dump FROM Keys WHERE user_id = $1").await?;
let res = client.query(&stmt, &[&user_id.0]).await?;
let res = res.iter().map(|row| {
let key: Value = row.get(1);
Key {
name: row.get(0),
// We can safely assume the JSON was valid, postgres
// would reject it if it wasn't. We can safely unwrap
// here.
key: serde_json::from_value(key).unwrap()
}}).collect();
Ok(res)
authentication::get_user_keys(&self.db, user_id).await
}
/**
@ -209,34 +167,6 @@ impl AppState<'_> {
successful login `AuthenticationResult`.
*/
pub async fn update_user_keys(&self, user_id: &UserUuid, auth_res: AuthenticationResult) -> Result<()> {
let mut conn = self.db.get().await?;
let transaction = conn.deref_mut().transaction().await?;
// Retrieve user keys
let stmt = transaction.prepare_cached("SELECT name, key_dump FROM Keys WHERE user_id = $1").await?;
let res = transaction.query(&stmt, &[&user_id.0]).await?;
let mut keys: Vec<Key> = res.iter().map(|row| {
let key = row.get(1);
Key { name: row.get(0), key: serde_json::from_value(key).unwrap() }
}).collect();
// Update keys
keys.iter_mut().for_each(
|key| {
key.key.update_credential(&auth_res);
}
);
// Delete current keys, save updated keys
let stmt = transaction.prepare_cached("DELETE FROM Keys WHERE user_id = $1").await?;
let _ = transaction.execute(&stmt, &[&user_id.0]).await?;
let stmt = transaction.prepare_cached("INSERT INTO Keys (user_id, name, key_dump) VALUES ($1,$2,$3)").await?;
for key in keys {
let passkey_val: Value = serde_json::to_value(&key.key).unwrap();
let _ = transaction.execute(&stmt, &[&user_id.0, &key.name, &passkey_val]).await?;
}
transaction.commit().await?;
Ok(())
authentication::update_user_keys(&self.db, user_id, auth_res).await
}
}