From 2f8dce44d3f2be74b5c6ec0a2e7f4ceced715328 Mon Sep 17 00:00:00 2001 From: pennae Date: Wed, 13 Jul 2022 10:33:30 +0200 Subject: initial import --- src/api/auth/email.rs | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 src/api/auth/email.rs (limited to 'src/api/auth/email.rs') diff --git a/src/api/auth/email.rs b/src/api/auth/email.rs new file mode 100644 index 0000000..f206759 --- /dev/null +++ b/src/api/auth/email.rs @@ -0,0 +1,126 @@ +use std::sync::Arc; + +use rocket::{serde::json::Json, State}; +use serde::{Deserialize, Serialize}; + +use crate::{ + api::{ + auth::{self, WithFxaLogin}, + Empty, EMPTY, + }, + auth::Authenticated, + db::{Db, DbConn}, + mailer::Mailer, + push::PushClient, + types::UserID, + utils::DeferAction, +}; + +// MISSING get /recovery_emails +// MISSING post /recovery_email +// MISSING post /recovery_email/destroy +// MISSING post /recovery_email/resend_code +// MISSING post /recovery_email/set_primary +// MISSING post /emails/reminders/cad +// MISSING post /recovery_email/secondary/resend_code +// MISSING post /recovery_email/secondary/verify_code + +#[derive(Debug, Serialize)] +#[allow(non_snake_case)] +pub(crate) struct StatusResp { + email: String, + verified: bool, + sessionVerified: bool, + emailVerified: bool, +} + +// MISSING arg: reason +#[get("/recovery_email/status")] +pub(crate) async fn status( + db: &DbConn, + req: Authenticated<(), WithFxaLogin>, +) -> auth::Result { + let user = db.get_user_by_id(&req.context.uid).await?; + Ok(Json(StatusResp { + email: user.email, + verified: user.verified, + sessionVerified: req.context.verified, + emailVerified: user.verified, + })) +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct VerifyReq { + uid: UserID, + code: String, + // MISSING service + // MISSING reminder + // MISSING type + // MISSING style + // MISSING marketingOptIn + // MISSING newsletters +} + +#[post("/recovery_email/verify_code", data = "")] +pub(crate) async fn verify_code( + db: &DbConn, + db_pool: &Db, + defer: &DeferAction, + pc: &State>, + req: Json, +) -> auth::Result { + let code = match db.try_use_verify_code(&req.uid, &req.code).await? { + Some(code) => code, + None => return Err(auth::Error::InvalidVerificationCode), + }; + db.set_user_verified(&req.uid).await?; + if let Some(sid) = code.session_id { + db.set_session_verified(&sid).await?; + } + match db.get_devices(&req.uid).await { + Ok(devs) => defer.spawn_after_success("api::auth/recovery_email/verify_code(post)", { + let (pc, db) = (Arc::clone(pc), db_pool.clone()); + async move { + let db = db.begin().await?; + pc.account_verified(&db, &devs).await; + db.commit().await?; + Ok(()) + } + }), + Err(e) => warn!("account_verified push failed: {e}"), + } + Ok(EMPTY) +} + +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub(crate) struct ResendReq { + // MISSING email + // MISSING service + // MISSING redirectTo + // MISSING resume + // MISSING style + // MISSING type +} + +// MISSING arg: service +// MISSING arg: type +#[post("/recovery_email/resend_code", data = "")] +pub(crate) async fn resend_code( + db: &DbConn, + mailer: &State>, + req: Authenticated, +) -> auth::Result { + let (email, code) = match db.get_verify_code(&req.context.uid).await { + Ok(v) => v, + Err(_) => return Err(auth::Error::InvalidVerificationCode), + }; + // NOTE we send the email in this context rather than a spawn to signal + // send errors to the client. + mailer.send_account_verify(&req.context.uid, &email, &code.code).await.map_err(|e| { + error!("failed to send email: {e}"); + auth::Error::EmailFailed + })?; + Ok(EMPTY) +} -- cgit v1.2.3