made most of the sql, now to do the UI
This commit is contained in:
parent
a8cab92fd5
commit
b37867b018
6 changed files with 61 additions and 25 deletions
|
@ -1,17 +1,41 @@
|
|||
use uuid::Uuid;
|
||||
|
||||
use super::schema::DbHook;
|
||||
use super::schema::{DbHook, OTP};
|
||||
|
||||
const METHOD_TOTP: &'static str = "totp";
|
||||
pub const METHOD_TOTP: &'static str = "totp";
|
||||
|
||||
/// Checks that the provided user has at least one OTP method enabled
|
||||
pub async fn has_otp(db: &mut DbHook, id: &Uuid) -> sqlx::Result<bool> {
|
||||
sqlx::query_scalar!("select count(otp_method) from otp where user_id = $1", id)
|
||||
.fetch_one(&mut **db)
|
||||
.await
|
||||
.map(|count| count.unwrap_or(0) > 0)
|
||||
/// Checks that the provided user has the specified OTP method
|
||||
pub async fn has_otp(db: &mut DbHook, id: &Uuid, method: &str) -> sqlx::Result<bool> {
|
||||
Ok(sqlx::query!(
|
||||
"select otp_method from otp where user_id = $1 and otp_method = $2",
|
||||
id,
|
||||
method
|
||||
)
|
||||
.fetch_optional(&mut **db)
|
||||
.await?
|
||||
.is_some())
|
||||
}
|
||||
|
||||
/// Lists the OTP methods the user has enabled
|
||||
pub async fn list_enabled_methods(db: &mut DbHook, id: &Uuid) -> sqlx::Result<Vec<String>> {
|
||||
sqlx::query_scalar!("select otp_method from otp where user_id = $1", id)
|
||||
.fetch_all(&mut **db)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Gets the requested OTP method config, if set
|
||||
pub async fn get_otp_method(db: &mut DbHook, id: &Uuid, method: &str) -> sqlx::Result<Option<OTP>> {
|
||||
sqlx::query_as!(
|
||||
OTP,
|
||||
"select * from otp where user_id = $1 and otp_method = $2",
|
||||
id,
|
||||
method
|
||||
)
|
||||
.fetch_optional(&mut **db)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Adds a new otp method with the provided config; will fail without check if one is already set
|
||||
pub async fn add_otp_method(
|
||||
db: &mut DbHook,
|
||||
id: &Uuid,
|
||||
|
|
|
@ -98,6 +98,18 @@ pub struct User {
|
|||
pub is_admin: bool,
|
||||
}
|
||||
|
||||
/// A user's OTP config for a given OTP scheme.
|
||||
/// WAT? why does it work without a serialize impl.?
|
||||
#[derive(Debug)]
|
||||
pub struct OTP {
|
||||
pub user_id: Uuid,
|
||||
pub created_at: chrono::DateTime<Utc>,
|
||||
|
||||
pub otp_method: String,
|
||||
pub secret_seed: String,
|
||||
pub recovery_key: String,
|
||||
}
|
||||
|
||||
/// The service status aggregate
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
|
|
|
@ -82,8 +82,8 @@ fn rocket() -> _ {
|
|||
account::common::ask_terminate_account,
|
||||
account::common::confirm_terminate_account,
|
||||
account::common::export_data,
|
||||
account::otp::show_otp_enable_start,
|
||||
account::otp::handle_otp_enable_start,
|
||||
account::otp::show_totp_enable_start,
|
||||
account::otp::handle_totp_enable_start,
|
||||
],
|
||||
)
|
||||
.mount(
|
||||
|
|
|
@ -6,21 +6,21 @@ use crate::{
|
|||
auth,
|
||||
db::{
|
||||
self,
|
||||
otp::METHOD_TOTP,
|
||||
schema::{DollTagsDb, User},
|
||||
},
|
||||
pages::CommonTemplateState,
|
||||
routes,
|
||||
routes::error_handlers::PageResult,
|
||||
routes::{self, error_handlers::PageResult},
|
||||
};
|
||||
|
||||
#[get("/settings/otp")]
|
||||
pub async fn show_otp_enable_start(
|
||||
#[get("/settings/totp")]
|
||||
pub async fn show_totp_enable_start(
|
||||
mut db: DollTagsDb,
|
||||
user: User,
|
||||
cookies: &CookieJar<'_>,
|
||||
meta: CommonTemplateState,
|
||||
) -> PageResult {
|
||||
if db::otp::has_otp(&mut *db, &user.id).await? {
|
||||
if db::otp::has_otp(&mut *db, &user.id, METHOD_TOTP).await? {
|
||||
return Ok(Redirect::to(uri!("/account", routes::account::settings::show_settings)).into());
|
||||
}
|
||||
let totp_secret = Secret::generate_secret();
|
||||
|
@ -46,20 +46,20 @@ pub struct OtpEnableForm {
|
|||
pub otp_code: String,
|
||||
}
|
||||
|
||||
#[post("/settings/otp", data = "<form>")]
|
||||
pub async fn handle_otp_enable_start(
|
||||
#[post("/settings/totp", data = "<form>")]
|
||||
pub async fn handle_totp_enable_start(
|
||||
mut db: DollTagsDb,
|
||||
form: Form<OtpEnableForm>,
|
||||
user: User,
|
||||
cookies: &CookieJar<'_>,
|
||||
_meta: CommonTemplateState,
|
||||
) -> PageResult {
|
||||
if db::otp::has_otp(&mut *db, &user.id).await? {
|
||||
if db::otp::has_otp(&mut *db, &user.id, METHOD_TOTP).await? {
|
||||
return Ok(Redirect::to(uri!("/account", routes::account::settings::show_settings)).into());
|
||||
}
|
||||
let secret = match auth::otp::get_secret(cookies) {
|
||||
Some(v) => v,
|
||||
None => return Ok(Redirect::to(uri!("/account", show_otp_enable_start)).into()),
|
||||
None => return Ok(Redirect::to(uri!("/account", show_totp_enable_start)).into()),
|
||||
};
|
||||
|
||||
let totp = auth::otp::make_totp(&user.id.to_string(), secret.to_bytes()?)?;
|
||||
|
|
|
@ -24,14 +24,14 @@ pub async fn show_settings(
|
|||
user: User,
|
||||
meta: CommonTemplateState,
|
||||
) -> PageResult {
|
||||
let has_otp = otp::has_otp(&mut *db, &user.id).await?;
|
||||
let enabled_otp_methods = otp::list_enabled_methods(&mut *db, &user.id).await?;
|
||||
|
||||
Ok(Template::render(
|
||||
"account/settings",
|
||||
context! {
|
||||
user,
|
||||
meta,
|
||||
otp: has_otp,
|
||||
enabled_otp_methods,
|
||||
prev_common: form::Context::default(),
|
||||
prev_password: form::Context::default(),
|
||||
},
|
||||
|
|
|
@ -67,8 +67,8 @@
|
|||
</div>
|
||||
|
||||
<section id="otp">
|
||||
{% if otp %}
|
||||
<p>Wow, you already have OTP enabled even though it's not yet implemented.</p>
|
||||
{% for method in enabled_otp_methods %}
|
||||
<p>Wow, you already have {{method}} OTP enabled even though it's not yet implemented.</p>
|
||||
{% else %}
|
||||
<h3>Two-factor authentication</h3>
|
||||
|
||||
|
@ -77,8 +77,8 @@
|
|||
You can add one using your authenticator app of choice by clicking below.
|
||||
</p>
|
||||
|
||||
<a href="/account/settings/otp" class="btn">Enable 2FA with an authenticator</a>
|
||||
{% endif %}
|
||||
<a href="/account/settings/totp" class="btn">Enable 2FA with an authenticator</a>
|
||||
{% endfor %}
|
||||
</section>
|
||||
|
||||
<section id="data-export">
|
||||
|
|
Loading…
Add table
Reference in a new issue