chore: extract auth-related model funcs to proper module
Build nomnom / Build-NomNom (push) Successful in 3m32s
Details
Build nomnom / Build-NomNom (push) Successful in 3m32s
Details
This commit is contained in:
parent
076dea7ab7
commit
62535ae5cf
|
@ -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, &[®istration_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, &[®istration_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(())
|
||||
}
|
|
@ -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, &[®istration_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, &[®istration_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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue