61 lines
1.8 KiB
Rust
61 lines
1.8 KiB
Rust
|
use anyhow::{Context, Result};
|
||
|
use chrono::{TimeDelta, Utc};
|
||
|
use deadpool_diesel::postgres::Pool;
|
||
|
use diesel::prelude::*;
|
||
|
use uuid::Uuid;
|
||
|
|
||
|
use crate::{app_error::AppError, models::CsrfToken, schema};
|
||
|
|
||
|
const TOKEN_PREFIX: &'static str = "csrf__";
|
||
|
const TTL_SEC: i64 = 60 * 60 * 24 * 7;
|
||
|
|
||
|
pub async fn generate_csrf_token_for_user(
|
||
|
db_pool: &Pool,
|
||
|
uid: Option<Uuid>,
|
||
|
) -> Result<String, AppError> {
|
||
|
let id = Uuid::new_v4();
|
||
|
let expires_at =
|
||
|
Utc::now() + TimeDelta::new(TTL_SEC, 0).context("Failed to generate TimeDelta")?;
|
||
|
db_pool
|
||
|
.get()
|
||
|
.await?
|
||
|
.interact(move |conn| {
|
||
|
diesel::insert_into(schema::csrf_tokens::table)
|
||
|
.values((
|
||
|
schema::csrf_tokens::id.eq(id),
|
||
|
schema::csrf_tokens::user_id.eq(uid),
|
||
|
schema::csrf_tokens::expires_at.eq(expires_at),
|
||
|
))
|
||
|
.execute(conn)
|
||
|
})
|
||
|
.await
|
||
|
.unwrap()?;
|
||
|
Ok(format!("{}{}", TOKEN_PREFIX, id.hyphenated().to_string()))
|
||
|
}
|
||
|
|
||
|
pub async fn validate_csrf_token_for_user(
|
||
|
db_pool: &Pool,
|
||
|
token: &str,
|
||
|
uid: Option<Uuid>,
|
||
|
) -> Result<bool, AppError> {
|
||
|
let id = match Uuid::try_parse(&token[TOKEN_PREFIX.len()..]) {
|
||
|
Ok(id) => id,
|
||
|
Err(_) => return Ok(false),
|
||
|
};
|
||
|
let row = db_pool
|
||
|
.get()
|
||
|
.await?
|
||
|
.interact(move |conn| {
|
||
|
schema::csrf_tokens::table
|
||
|
.select(CsrfToken::as_select())
|
||
|
.filter(schema::csrf_tokens::id.eq(id))
|
||
|
.filter(schema::csrf_tokens::expires_at.gt(Utc::now()))
|
||
|
.filter(schema::csrf_tokens::user_id.is_not_distinct_from(uid))
|
||
|
.first(conn)
|
||
|
.optional()
|
||
|
})
|
||
|
.await
|
||
|
.unwrap()?;
|
||
|
Ok(row.is_some())
|
||
|
}
|