11 changed files with 148 additions and 0 deletions
@ -0,0 +1 @@ |
|||
DROP TABLE twofactor_duo_ctx; |
@ -0,0 +1,8 @@ |
|||
CREATE TABLE twofactor_duo_ctx ( |
|||
state VARCHAR(1024) NOT NULL, |
|||
user_email VARCHAR(255) NOT NULL, |
|||
nonce VARCHAR(1024) NOT NULL, |
|||
exp BIGINT NOT NULL, |
|||
|
|||
PRIMARY KEY (state) |
|||
); |
@ -0,0 +1 @@ |
|||
DROP TABLE twofactor_duo_ctx; |
@ -0,0 +1,8 @@ |
|||
CREATE TABLE twofactor_duo_ctx ( |
|||
state VARCHAR(1024) NOT NULL, |
|||
user_email VARCHAR(255) NOT NULL, |
|||
nonce VARCHAR(1024) NOT NULL, |
|||
exp BIGINT NOT NULL, |
|||
|
|||
PRIMARY KEY (state) |
|||
); |
@ -0,0 +1 @@ |
|||
DROP TABLE twofactor_duo_ctx; |
@ -0,0 +1,8 @@ |
|||
CREATE TABLE twofactor_duo_ctx ( |
|||
state TEXT NOT NULL, |
|||
user_email TEXT NOT NULL, |
|||
nonce TEXT NOT NULL, |
|||
exp INTEGER NOT NULL, |
|||
|
|||
PRIMARY KEY (state) |
|||
); |
@ -0,0 +1,92 @@ |
|||
use chrono::Utc; |
|||
|
|||
use crate::{api::EmptyResult, db::DbConn, error::MapResult}; |
|||
|
|||
db_object! { |
|||
#[derive(Identifiable, Queryable, Insertable, AsChangeset)] |
|||
#[diesel(table_name = twofactor_duo_ctx)] |
|||
#[diesel(primary_key(state))] |
|||
pub struct TwoFactorDuoContext { |
|||
pub state: String, |
|||
pub user_email: String, |
|||
pub nonce: String, |
|||
pub exp: i64, |
|||
} |
|||
} |
|||
|
|||
impl TwoFactorDuoContext { |
|||
pub async fn find_by_state(state: &str, conn: &mut DbConn) -> Option<Self> { |
|||
db_run! { |
|||
conn: { |
|||
twofactor_duo_ctx::table |
|||
.filter(twofactor_duo_ctx::state.eq(state)) |
|||
.first::<TwoFactorDuoContextDb>(conn) |
|||
.ok() |
|||
.from_db() |
|||
} |
|||
} |
|||
} |
|||
|
|||
pub async fn save( |
|||
state: &str, |
|||
user_email: &str, |
|||
nonce: &str, |
|||
ttl: i64, |
|||
conn: &mut DbConn, |
|||
) -> EmptyResult { |
|||
// A saved context should never be changed, only created or deleted.
|
|||
let exists = Self::find_by_state(state, conn).await; |
|||
if exists.is_some() { |
|||
return Ok(()) |
|||
}; |
|||
|
|||
let exp = Utc::now().timestamp() + ttl; |
|||
|
|||
db_run! { |
|||
conn: { |
|||
diesel::insert_into(twofactor_duo_ctx::table) |
|||
.values(( |
|||
twofactor_duo_ctx::state.eq(state), |
|||
twofactor_duo_ctx::user_email.eq(user_email), |
|||
twofactor_duo_ctx::nonce.eq(nonce), |
|||
twofactor_duo_ctx::exp.eq(exp) |
|||
)) |
|||
.execute(conn) |
|||
.map_res("Error saving context to twofactor_duo_ctx") |
|||
} |
|||
} |
|||
} |
|||
|
|||
pub async fn find_expired(conn: &mut DbConn) -> Vec<Self> { |
|||
let now = Utc::now().timestamp(); |
|||
db_run! { |
|||
conn: { |
|||
twofactor_duo_ctx::table |
|||
.filter(twofactor_duo_ctx::exp.lt(now)) |
|||
.load::<TwoFactorDuoContextDb>(conn) |
|||
.expect("Error finding expired contexts in twofactor_duo_ctx") |
|||
.from_db() |
|||
} |
|||
} |
|||
} |
|||
|
|||
pub async fn delete(&self, conn: &mut DbConn) -> EmptyResult { |
|||
db_run! { |
|||
conn: { |
|||
diesel::delete( |
|||
twofactor_duo_ctx::table |
|||
.filter(twofactor_duo_ctx::state.eq(&self.state))) |
|||
.execute(conn) |
|||
.map_res("Error deleting from twofactor_duo_ctx") |
|||
} |
|||
} |
|||
} |
|||
|
|||
pub async fn purge_expired_duo_contexts(conn: &mut DbConn) { |
|||
for context in Self::find_expired(conn).await { |
|||
if context.exp < Utc::now().timestamp() { |
|||
context.delete(conn).await.ok(); |
|||
} |
|||
} |
|||
} |
|||
} |
Loading…
Reference in new issue