From 0bbc83f09cc7517dddf16770a15f9e90815f48ba Mon Sep 17 00:00:00 2001 From: Owen Jacobson Date: Sun, 24 Aug 2025 17:03:16 -0400 Subject: Generate tokens in memory and then store them. This is the leading edge of a larger storage refactoring, where repo types stop doing things like generating secrets or deciding whether to carry out an operation. To make this work, there is now a `Token` type that holds the complete state of a token, in memory. --- src/token/repo/token.rs | 61 ++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 34 deletions(-) (limited to 'src/token/repo/token.rs') diff --git a/src/token/repo/token.rs b/src/token/repo/token.rs index 5368fee..afcde53 100644 --- a/src/token/repo/token.rs +++ b/src/token/repo/token.rs @@ -1,12 +1,11 @@ use sqlx::{SqliteConnection, Transaction, sqlite::Sqlite}; -use uuid::Uuid; use crate::{ clock::DateTime, db::NotFound, event::{Instant, Sequence}, name::{self, Name}, - token::{Id, Secret}, + token::{Id, Secret, Token}, user::{self, History, User}, }; @@ -23,33 +22,23 @@ impl Provider for Transaction<'_, Sqlite> { pub struct Tokens<'t>(&'t mut SqliteConnection); impl Tokens<'_> { - // Issue a new token for an existing user. The issued_at timestamp will - // determine the token's initial expiry deadline. - pub async fn issue( - &mut self, - user: &History, - issued_at: &DateTime, - ) -> Result { - let id = Id::generate(); - let secret = Uuid::new_v4().to_string(); - let user = user.id(); - - let secret = sqlx::query_scalar!( + pub async fn create(&mut self, token: &Token, secret: &Secret) -> Result<(), sqlx::Error> { + sqlx::query!( r#" insert into token (id, secret, login, issued_at, last_used_at) - values ($1, $2, $3, $4, $4) - returning secret as "secret!: Secret" + values ($1, $2, $3, $4, $5) "#, - id, + token.id, secret, - user, - issued_at, + token.user, + token.issued_at, + token.last_used_at, ) .fetch_one(&mut *self.0) .await?; - Ok(secret) + Ok(()) } pub async fn require(&mut self, token: &Id) -> Result<(), sqlx::Error> { @@ -67,18 +56,15 @@ impl Tokens<'_> { Ok(()) } - // Revoke a token by its secret. - pub async fn revoke(&mut self, token: &Id) -> Result<(), sqlx::Error> { - sqlx::query_scalar!( + pub async fn revoke(&mut self, token: &Token) -> Result<(), sqlx::Error> { + sqlx::query!( r#" - delete - from token + delete from token where id = $1 - returning id as "id: Id" "#, - token, + token.id, ) - .fetch_one(&mut *self.0) + .execute(&mut *self.0) .await?; Ok(()) @@ -127,24 +113,31 @@ impl Tokens<'_> { &mut self, secret: &Secret, used_at: &DateTime, - ) -> Result<(Id, History), LoadError> { + ) -> Result<(Token, History), LoadError> { // I would use `update … returning` to do this in one query, but // sqlite3, as of this writing, does not allow an update's `returning` // clause to reference columns from tables joined into the update. Two // queries is fine, but it feels untidy. - let (token, user) = sqlx::query!( + let token = sqlx::query!( r#" update token set last_used_at = $1 where secret = $2 returning - id as "token: Id", - login as "login: user::Id" + id as "id: Id", + login as "login: user::Id", + issued_at as "issued_at: DateTime", + last_used_at as "last_used_at: DateTime" "#, used_at, secret, ) - .map(|row| (row.token, row.login)) + .map(|row| Token { + id: row.id, + user: row.login, + issued_at: row.issued_at, + last_used_at: row.last_used_at, + }) .fetch_one(&mut *self.0) .await?; @@ -160,7 +153,7 @@ impl Tokens<'_> { join login using (id) where id = $1 "#, - user, + token.user, ) .map(|row| { Ok::<_, name::Error>(History { -- cgit v1.2.3