factoriauth/src/db.rs

86 lines
2.3 KiB
Rust

use secrecy::ExposeSecret;
use sqlx::{query, query_as, Connection, SqliteConnection};
use crate::secrets::UserToken;
// TODO: allow configuring this via envar
const DB_URI_DEFAULT: &str = "sqlite://sqlite.db";
const TABLE_USER_TOKENS: &str = "user_tokens";
pub trait Database {
async fn get_token(&mut self, username: &str) -> Result<UserToken, sqlx::Error>;
async fn save_token(&mut self, username: &str, token: &UserToken) -> Result<(), sqlx::Error>;
}
#[derive(Debug)]
pub struct SqliteDatabase {
conn: SqliteConnection,
}
impl SqliteDatabase {
pub async fn open() -> Self {
let mut db = Self {
conn: SqliteConnection::connect(DB_URI_DEFAULT)
.await
.expect("Failed to open SQLite database"),
};
db.init().await;
db
}
pub async fn init(&mut self) {
query(
"CREATE TABLE IF NOT EXISTS {TABLE_USER_TOKENS} (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username VARCHAR(255) NOT NULL,
token NCHAR(30) NOT NULL,
valid BOOLEAN NOT NULL,
created DATETIME NOT NULL,
last_used DATETIME
)",
)
.execute(&mut self.conn)
.await
.expect("Failed to initialize table user_tokens");
}
}
impl Database for SqliteDatabase {
// TODO: distinguish between invalid token and SQLX error
async fn get_token(&mut self, username: &str) -> Result<UserToken, sqlx::Error> {
let row: (String,) = query_as(
"SELECT token
FROM user_tokens
WHERE
username = ?
AND valid = TRUE
ORDER BY
created DESC",
)
.bind(username)
.fetch_one(&mut self.conn)
.await?;
Ok(UserToken::from(row.0))
}
async fn save_token(&mut self, username: &str, token: &UserToken) -> Result<(), sqlx::Error> {
query(
"INSERT INTO {TABLE_USER_TOKENS}
(username, token, created, valid)
VALUES
(?, ?, DATETIME('NOW'), TRUE)
",
)
.bind(username)
.bind(token.0.expose_secret())
.execute(&mut self.conn)
.await?;
Ok(())
}
}