factoriauth/src/db.rs

107 lines
3 KiB
Rust
Raw Normal View History

2024-02-10 13:31:23 +01:00
use std::str::FromStr;
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-10 12:32:01 +01:00
use crate::secrets::UserToken;
2024-02-10 12:06:53 +01:00
// TODO: allow configuring this via envar
const DB_URI_DEFAULT: &str = "sqlite://sqlite.db";
2024-02-10 13:07:36 +01:00
const TABLE_USER_TOKENS: &str = "user_tokens";
pub enum UserTokenEntry {
Valid(UserToken, time::Time, time::Time),
Invalid(UserToken, time::Time, time::Time),
}
2024-02-10 12:06:53 +01:00
pub trait Database {
async fn get_token(&mut self, username: &str) -> Result<Option<UserTokenEntry>, sqlx::Error>;
2024-02-10 13:07:36 +01:00
async fn save_token(&mut self, username: &str, token: &UserToken) -> 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-10 12:06:53 +01:00
pub async fn open() -> Self {
2024-02-10 13:31:23 +01:00
let options = SqliteConnectOptions::from_str(DB_URI_DEFAULT)
.expect("Invalid database URI")
.create_if_missing(true);
2024-02-10 12:06:53 +01:00
let mut db = Self {
2024-02-10 13:31:23 +01:00
conn: SqliteConnection::connect_with(&options)
2024-02-10 12:06:53 +01:00
.await
.expect("Failed to open SQLite database"),
};
db.init().await;
db
}
2024-02-10 13:51:53 +01:00
#[instrument]
2024-02-10 12:06:53 +01:00
pub async fn init(&mut self) {
2024-02-10 13:31:23 +01:00
query(&format!(
2024-02-10 13:07:36 +01:00
"CREATE TABLE IF NOT EXISTS {TABLE_USER_TOKENS} (
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 12:06:53 +01:00
.execute(&mut self.conn)
.await
.expect("Failed to initialize table user_tokens");
}
}
impl Database for SqliteDatabase {
2024-02-10 13:51:53 +01:00
#[instrument]
async fn get_token(&mut self, username: &str) -> Result<Option<UserTokenEntry>, sqlx::Error> {
let row: Option<(String, bool, time::Time, time::Time)> = query_as(
"SELECT token, valid, created, last_used
2024-02-10 12:25:46 +01:00
FROM user_tokens
WHERE username = ?
ORDER BY created DESC",
2024-02-10 12:25:46 +01:00
)
.bind(username)
.fetch_optional(&mut self.conn)
2024-02-10 12:25:46 +01:00
.await?;
Ok(row.map(|row| match row {
(token_str, true, created, last_used) => {
UserTokenEntry::Valid(UserToken::from(token_str), created, last_used)
}
(token_str, false, created, last_used) => {
UserTokenEntry::Invalid(UserToken::from(token_str), created, last_used)
}
}))
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]
2024-02-10 13:07:36 +01:00
async fn save_token(&mut self, username: &str, token: &UserToken) -> Result<(), sqlx::Error> {
2024-02-10 13:31:23 +01:00
query(&format!(
2024-02-10 13:07:36 +01:00
"INSERT INTO {TABLE_USER_TOKENS}
(username, token, created, valid)
VALUES
(?, ?, DATETIME('NOW'), TRUE)
",
2024-02-10 13:31:23 +01:00
))
2024-02-10 13:07:36 +01:00
.bind(username)
.bind(token.0.expose_secret())
.execute(&mut self.conn)
.await?;
Ok(())
}
2024-02-10 12:06:53 +01:00
}