Add database actions
This commit is contained in:
parent
ba00afab41
commit
e0fae4ede8
@ -2,6 +2,9 @@ use config::SharedAppConfig;
|
||||
use sqlx_core::pool::Pool;
|
||||
use sqlx_core::postgres::Postgres;
|
||||
|
||||
pub mod tokens;
|
||||
pub use tokens::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Database {
|
||||
pub pool: sqlx::PgPool,
|
||||
|
285
crates/token_manager/src/db/tokens.rs
Normal file
285
crates/token_manager/src/db/tokens.rs
Normal file
@ -0,0 +1,285 @@
|
||||
use actix::Message;
|
||||
use model::{AccountId, Audience, Token, TokenId};
|
||||
|
||||
use crate::{db_async_handler, Result};
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("Failed to save new token")]
|
||||
Create,
|
||||
#[error("Failed to find token by jti")]
|
||||
Jti,
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// Find token by JTI field
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use actix::Addr;
|
||||
/// use database_manager::{Database, TokenByJti};
|
||||
///
|
||||
/// async fn find(db: Addr<Database>) {
|
||||
/// match db.send(TokenByJti {
|
||||
/// jti: uuid::Uuid::new_v4()
|
||||
/// }).await {
|
||||
/// Ok(Ok(token)) => { println!("{:?}", token); }
|
||||
/// Ok(Err(db_err)) => { println!("{:?}", db_err); }
|
||||
/// Err(actor_err) => { println!("{:?}", actor_err); }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TokenByJti {
|
||||
pub jti: uuid::Uuid,
|
||||
}
|
||||
|
||||
impl TokenByJti {
|
||||
pub async fn run(self, t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Result<Token> {
|
||||
sqlx::query_as(r#"
|
||||
SELECT id, customer_id, role, issuer, subject, audience, expiration_time, not_before_time, issued_at_time, jwt_id
|
||||
FROM tokens
|
||||
WHERE jwt_id = $1 AND expiration_time > now()
|
||||
"#)
|
||||
.bind(self.jti)
|
||||
.fetch_one(t)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{e:?}");
|
||||
Error::Jti
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CreateToken {
|
||||
pub customer_id: uuid::Uuid,
|
||||
pub role: model::Role,
|
||||
pub subject: AccountId,
|
||||
pub audience: Audience,
|
||||
}
|
||||
|
||||
impl CreateToken {
|
||||
pub async fn run(self, t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Result<Token> {
|
||||
let CreateToken {
|
||||
customer_id,
|
||||
role,
|
||||
subject,
|
||||
audience,
|
||||
} = self;
|
||||
sqlx::query_as(r#"
|
||||
INSERT INTO tokens (customer_id, role, subject, audience)
|
||||
VALUES ($1, $2, $3, $4)
|
||||
RETURNING id, customer_id, role, issuer, subject, audience, expiration_time, not_before_time, issued_at_time, jwt_id
|
||||
"#)
|
||||
.bind(customer_id)
|
||||
.bind(role)
|
||||
.bind(subject)
|
||||
.bind(audience)
|
||||
.fetch_one(t)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{e:?}");
|
||||
Error::Create
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CreateExtendedToken {
|
||||
pub customer_id: uuid::Uuid,
|
||||
pub role: model::Role,
|
||||
pub subject: AccountId,
|
||||
pub audience: Audience,
|
||||
pub expiration_time: chrono::NaiveDateTime,
|
||||
}
|
||||
|
||||
impl CreateExtendedToken {
|
||||
pub async fn run(self, t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Result<Token> {
|
||||
let CreateExtendedToken {
|
||||
customer_id,
|
||||
role,
|
||||
subject,
|
||||
audience,
|
||||
expiration_time,
|
||||
} = self;
|
||||
sqlx::query_as(r#"
|
||||
INSERT INTO tokens (customer_id, role, subject, audience, expiration_time)
|
||||
VALUES ($1, $2, $3, $4, $5)
|
||||
RETURNING id, customer_id, role, issuer, subject, audience, expiration_time, not_before_time, issued_at_time, jwt_id
|
||||
"#)
|
||||
.bind(customer_id)
|
||||
.bind(role)
|
||||
.bind(subject)
|
||||
.bind(audience)
|
||||
.bind(expiration_time)
|
||||
.fetch_one(t)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{e:?}");
|
||||
Error::Create
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct DeleteToken {
|
||||
pub token_id: TokenId,
|
||||
}
|
||||
|
||||
impl DeleteToken {
|
||||
pub async fn run(self, t: &mut sqlx::Transaction<'_, sqlx::Postgres>) -> Result<Option<Token>> {
|
||||
sqlx::query_as(r#"
|
||||
DELETE FROM tokens
|
||||
WHERE id = $1
|
||||
RETURNING id, customer_id, role, issuer, subject, audience, expiration_time, not_before_time, issued_at_time, jwt_id
|
||||
"#)
|
||||
.bind(self.token_id)
|
||||
.fetch_optional(t)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!("{e:?}");
|
||||
Error::Jti
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(test)]
|
||||
// mod tests {
|
||||
// use config::UpdateConfig;
|
||||
// use fake::Fake;
|
||||
// use model::*;
|
||||
// use uuid::Uuid;
|
||||
//
|
||||
// use crate::*;
|
||||
//
|
||||
// pub struct NoOpts;
|
||||
//
|
||||
// impl UpdateConfig for NoOpts {}
|
||||
//
|
||||
// async fn test_create_account(t: &mut sqlx::Transaction<'_,
|
||||
// sqlx::Postgres>) -> FullAccount { use fake::faker::internet::en;
|
||||
// let login: String = en::Username().fake();
|
||||
// let email: String = en::FreeEmail().fake();
|
||||
// let hash: String = en::Password(10..20).fake();
|
||||
//
|
||||
// crate::create_account(
|
||||
// CreateAccount {
|
||||
// email: Email::new(email),
|
||||
// login: Login::new(login),
|
||||
// pass_hash: PassHash::new(hash),
|
||||
// role: Role::Admin,
|
||||
// },
|
||||
// t,
|
||||
// )
|
||||
// .await
|
||||
// .unwrap()
|
||||
// }
|
||||
//
|
||||
// async fn test_create_token_extended(
|
||||
// t: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
// customer_id: Option<Uuid>,
|
||||
// role: Option<Role>,
|
||||
// subject: Option<AccountId>,
|
||||
// audience: Option<Audience>,
|
||||
// expiration_time: Option<chrono::NaiveDateTime>,
|
||||
// ) -> Token {
|
||||
// let customer_id = customer_id.unwrap_or_else(Uuid::new_v4);
|
||||
// let role = role.unwrap_or(Role::Admin);
|
||||
// let subject = match subject {
|
||||
// Some(id) => id,
|
||||
// _ => test_create_account(t).await.id,
|
||||
// };
|
||||
// let audience = audience.unwrap_or(Audience::Web);
|
||||
// let expiration_time = expiration_time
|
||||
// .unwrap_or_else(|| (chrono::Utc::now() +
|
||||
// chrono::Duration::days(60)).naive_utc());
|
||||
//
|
||||
// super::create_extended_token(
|
||||
// CreateExtendedToken {
|
||||
// customer_id,
|
||||
// role,
|
||||
// subject,
|
||||
// audience,
|
||||
// expiration_time,
|
||||
// },
|
||||
// t,
|
||||
// )
|
||||
// .await
|
||||
// .unwrap()
|
||||
// }
|
||||
//
|
||||
// #[actix::test]
|
||||
// async fn create_token() {
|
||||
// testx::db_t_ref!(t);
|
||||
//
|
||||
// super::create_token(
|
||||
// CreateToken {
|
||||
// customer_id: Uuid::new_v4(),
|
||||
// role: Role::Admin,
|
||||
// subject: test_create_account(&mut t).await.id,
|
||||
// audience: Audience::Web,
|
||||
// },
|
||||
// &mut t,
|
||||
// )
|
||||
// .await
|
||||
// .unwrap();
|
||||
// }
|
||||
//
|
||||
// #[actix::test]
|
||||
// async fn create_extended_token() {
|
||||
// testx::db_t_ref!(t);
|
||||
//
|
||||
// test_create_account(&mut t).await;
|
||||
//
|
||||
// testx::db_rollback!(t);
|
||||
// }
|
||||
//
|
||||
// #[actix::test]
|
||||
// async fn find_by_jti() {
|
||||
// testx::db_t_ref!(t);
|
||||
//
|
||||
// let original = test_create_token_extended(&mut t, None, None, None,
|
||||
// None, None).await;
|
||||
//
|
||||
// let found = super::token_by_jti(
|
||||
// TokenByJti {
|
||||
// jti: original.jwt_id,
|
||||
// },
|
||||
// &mut t,
|
||||
// )
|
||||
// .await
|
||||
// .unwrap();
|
||||
//
|
||||
// testx::db_rollback!(t);
|
||||
// assert_eq!(found, original);
|
||||
// }
|
||||
//
|
||||
// #[actix::test]
|
||||
// async fn find_by_jti_expired() {
|
||||
// testx::db_t_ref!(t);
|
||||
//
|
||||
// let original = test_create_token_extended(
|
||||
// &mut t,
|
||||
// None,
|
||||
// None,
|
||||
// None,
|
||||
// None,
|
||||
// Some((chrono::Utc::now() -
|
||||
// chrono::Duration::seconds(1)).naive_utc()), )
|
||||
// .await;
|
||||
//
|
||||
// let found = super::token_by_jti(
|
||||
// TokenByJti {
|
||||
// jti: original.jwt_id,
|
||||
// },
|
||||
// &mut t,
|
||||
// )
|
||||
// .await;
|
||||
//
|
||||
// testx::db_rollback!(t);
|
||||
// assert_eq!(found, Err(crate::Error::Token(super::Error::Jti)));
|
||||
// }
|
||||
// }
|
Loading…
Reference in New Issue
Block a user