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