factoriauth/src/db.rs

221 lines
5.6 KiB
Rust
Raw Normal View History

2024-02-11 16:48:03 +01:00
use std::{fmt::Debug, str::FromStr};
2024-02-10 13:31:23 +01:00
2024-02-11 16:48:03 +01:00
use axum::async_trait;
2024-02-10 13:07:36 +01:00
use secrecy::ExposeSecret;
2024-02-10 13:31:23 +01:00
use sqlx::{query, query_as, sqlite::SqliteConnectOptions, Connection, SqliteConnection};
2024-02-10 13:51:53 +01:00
use tracing::instrument;
2024-02-10 12:06:53 +01:00
2024-02-20 22:57:03 +01:00
use crate::secrets::{ServerHash, ServerPadlock, UserToken};
2024-02-10 12:06:53 +01:00
2024-02-10 14:31:07 +01:00
// TODO: check again if it's possible to pass this as a parameter to query!
// const TABLE_USER_TOKENS: &str = "user_tokens";
2024-02-10 13:07:36 +01:00
pub enum UserTokenEntry {
2024-02-10 14:31:07 +01:00
Valid(
UserToken,
time::OffsetDateTime,
Option<time::OffsetDateTime>,
),
Invalid(
UserToken,
time::OffsetDateTime,
Option<time::OffsetDateTime>,
),
}
2024-02-11 16:48:03 +01:00
#[async_trait]
pub trait Database: Debug {
async fn get_user_token(
&mut self,
username: &str,
) -> Result<Option<UserTokenEntry>, sqlx::Error>;
2024-02-10 13:07:36 +01:00
async fn save_user_token(
&mut self,
username: &str,
token: &UserToken,
) -> Result<(), sqlx::Error>;
2024-02-11 20:00:05 +01:00
async fn update_user_token_last_used(
2024-02-11 20:00:05 +01:00
&mut self,
username: &str,
token: &UserToken,
) -> Result<(), sqlx::Error>;
2024-02-20 22:57:03 +01:00
async fn get_server_padlock(
&mut self,
server_hash: &ServerHash,
) -> Result<Option<ServerPadlock>, sqlx::Error>;
async fn save_server_padlock(
&mut self,
server_hash: &ServerHash,
server_padlock: &ServerPadlock,
) -> Result<(), sqlx::Error>;
2024-02-10 12:06:53 +01:00
}
#[derive(Debug)]
pub struct SqliteDatabase {
conn: SqliteConnection,
}
impl SqliteDatabase {
2024-02-10 13:51:53 +01:00
#[instrument]
2024-02-15 21:14:07 +01:00
pub async fn open(connection_string: &str) -> Result<Self, sqlx::Error> {
let options = SqliteConnectOptions::from_str(connection_string)?.create_if_missing(true);
2024-02-10 13:31:23 +01:00
2024-02-10 12:06:53 +01:00
let mut db = Self {
2024-02-15 21:14:07 +01:00
conn: SqliteConnection::connect_with(&options).await?,
2024-02-10 12:06:53 +01:00
};
2024-02-15 21:14:07 +01:00
db.init().await?;
2024-02-10 12:06:53 +01:00
2024-02-15 21:14:07 +01:00
Ok(db)
2024-02-10 12:06:53 +01:00
}
2024-02-10 13:51:53 +01:00
#[instrument]
2024-02-15 21:14:07 +01:00
async fn init(&mut self) -> Result<(), sqlx::Error> {
2024-02-10 14:31:07 +01:00
query!(
"CREATE TABLE IF NOT EXISTS user_tokens (
2024-02-10 13:07:36 +01:00
id INTEGER PRIMARY KEY AUTOINCREMENT,
2024-02-10 13:18:08 +01:00
username VARCHAR(255) NOT NULL,
token NCHAR(30) NOT NULL,
valid BOOLEAN NOT NULL,
created DATETIME NOT NULL,
last_used DATETIME
2024-02-10 13:31:23 +01:00
)"
2024-02-10 14:31:07 +01:00
)
2024-02-10 12:06:53 +01:00
.execute(&mut self.conn)
2024-02-15 21:14:07 +01:00
.await?;
2024-02-20 22:57:03 +01:00
query!(
"CREATE TABLE IF NOT EXISTS server_padlocks (
hash TEXT NOT NULL UNIQUE,
padlock TEXT NOT NULL
) STRICT"
)
.execute(&mut self.conn)
.await?;
2024-02-15 21:14:07 +01:00
Ok(())
2024-02-10 12:06:53 +01:00
}
}
2024-02-11 16:48:03 +01:00
#[async_trait]
2024-02-10 12:06:53 +01:00
impl Database for SqliteDatabase {
2024-02-10 13:51:53 +01:00
#[instrument]
async fn get_user_token(
&mut self,
username: &str,
) -> Result<Option<UserTokenEntry>, sqlx::Error> {
2024-02-10 14:31:07 +01:00
struct TokenRow {
token: String,
valid: bool,
created: time::OffsetDateTime,
last_used: Option<time::OffsetDateTime>,
}
let row = query_as!(
TokenRow,
"SELECT token, valid, created, last_used
2024-02-10 12:25:46 +01:00
FROM user_tokens
2024-02-10 14:31:07 +01:00
WHERE username = $1
ORDER BY created DESC",
2024-02-10 14:31:07 +01:00
username
2024-02-10 12:25:46 +01:00
)
.fetch_optional(&mut self.conn)
2024-02-10 12:25:46 +01:00
.await?;
2024-02-15 19:34:43 +01:00
#[allow(clippy::match_bool)]
2024-02-10 14:31:07 +01:00
Ok(row.map(
|TokenRow {
token,
valid,
created,
last_used,
}| match valid {
true => UserTokenEntry::Valid(UserToken::from(token), created, last_used),
false => UserTokenEntry::Invalid(UserToken::from(token), created, last_used),
2024-02-10 14:31:07 +01:00
},
))
2024-02-10 12:06:53 +01:00
}
2024-02-10 13:07:36 +01:00
2024-02-10 13:51:53 +01:00
#[instrument]
async fn save_user_token(
&mut self,
username: &str,
token: &UserToken,
) -> Result<(), sqlx::Error> {
2024-02-10 14:31:07 +01:00
let token_inner = token.0.expose_secret();
query!(
"INSERT INTO user_tokens
2024-02-10 13:07:36 +01:00
(username, token, created, valid)
VALUES
2024-02-10 14:31:07 +01:00
($1, $2, DATETIME('NOW'), TRUE)
2024-02-10 13:07:36 +01:00
",
2024-02-10 14:31:07 +01:00
username,
token_inner
)
2024-02-10 13:07:36 +01:00
.execute(&mut self.conn)
.await?;
Ok(())
}
2024-02-11 20:00:05 +01:00
#[instrument]
async fn update_user_token_last_used(
2024-02-11 20:00:05 +01:00
&mut self,
username: &str,
token: &UserToken,
) -> Result<(), sqlx::Error> {
let token_inner = token.0.expose_secret();
query!(
"UPDATE user_tokens
SET last_used = DATETIME('NOW')
WHERE username = $1 AND token = $2",
username,
token_inner
)
.execute(&mut self.conn)
.await?;
Ok(())
}
2024-02-20 22:57:03 +01:00
#[instrument]
async fn get_server_padlock(
&mut self,
server_hash: &ServerHash,
) -> Result<Option<ServerPadlock>, sqlx::Error> {
let server_hash = &server_hash.0;
let padlock = query!(
"SELECT padlock FROM server_padlocks WHERE hash = $1",
server_hash
)
.fetch_optional(&mut self.conn)
.await?;
Ok(padlock.map(|d| ServerPadlock::from(d.padlock)))
}
#[instrument]
async fn save_server_padlock(
&mut self,
server_hash: &ServerHash,
server_padlock: &ServerPadlock,
) -> Result<(), sqlx::Error> {
let server_hash = &server_hash.0;
let server_padlock = server_padlock.0.expose_secret();
query!(
"INSERT INTO server_padlocks (hash, padlock) VALUES ($1, $2)",
server_hash,
server_padlock
)
.execute(&mut self.conn)
.await?;
Ok(())
}
2024-02-10 12:06:53 +01:00
}