nom-nom-nix-gc/src/models/authentication.rs
Félix Baylac Jacqué 62535ae5cf
All checks were successful
Build nomnom / Build-NomNom (push) Successful in 3m32s
chore: extract auth-related model funcs to proper module
2023-11-27 12:08:23 +01:00

128 lines
4.8 KiB
Rust

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(())
}