1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-08-07 17:03:01 +03:00

Actually send emails for recovery

This commit is contained in:
Quentin Gliech
2024-06-24 17:20:22 +02:00
parent 4a60f5d32f
commit c156a3891e
17 changed files with 337 additions and 14 deletions

View File

@@ -38,6 +38,7 @@ mas-data-model.workspace = true
mas-email.workspace = true
mas-i18n.workspace = true
mas-matrix.workspace = true
mas-router.workspace = true
mas-storage.workspace = true
mas-storage-pg.workspace = true
mas-templates.workspace = true

View File

@@ -17,6 +17,7 @@ use std::sync::Arc;
use apalis_core::{executor::TokioExecutor, layers::extensions::Extension, monitor::Monitor};
use mas_email::Mailer;
use mas_matrix::HomeserverConnection;
use mas_router::UrlBuilder;
use mas_storage::{BoxClock, BoxRepository, Repository, SystemClock};
use mas_storage_pg::{DatabaseError, PgRepository};
use rand::SeedableRng;
@@ -39,6 +40,7 @@ struct State {
mailer: Mailer,
clock: SystemClock,
homeserver: Arc<dyn HomeserverConnection<Error = anyhow::Error>>,
url_builder: UrlBuilder,
}
impl State {
@@ -47,12 +49,14 @@ impl State {
clock: SystemClock,
mailer: Mailer,
homeserver: impl HomeserverConnection<Error = anyhow::Error> + 'static,
url_builder: UrlBuilder,
) -> Self {
Self {
pool,
mailer,
clock,
homeserver: Arc::new(homeserver),
url_builder,
}
}
@@ -90,6 +94,10 @@ impl State {
pub fn matrix_connection(&self) -> &dyn HomeserverConnection<Error = anyhow::Error> {
self.homeserver.as_ref()
}
pub fn url_builder(&self) -> &UrlBuilder {
&self.url_builder
}
}
trait JobContextExt {
@@ -140,12 +148,14 @@ pub async fn init(
pool: &Pool<Postgres>,
mailer: &Mailer,
homeserver: impl HomeserverConnection<Error = anyhow::Error> + 'static,
url_builder: UrlBuilder,
) -> Result<Monitor<TokioExecutor>, sqlx::Error> {
let state = State::new(
pool.clone(),
SystemClock::default(),
mailer.clone(),
homeserver,
url_builder,
);
let factory = PostgresStorageFactory::new(pool.clone());
let monitor = Monitor::new().executor(TokioExecutor::new());

View File

@@ -14,13 +14,16 @@
use anyhow::Context;
use apalis_core::{context::JobContext, executor::TokioExecutor, monitor::Monitor};
use mas_email::{Address, Mailbox};
use mas_i18n::DataLocale;
use mas_storage::{
job::{JobWithSpanContext, SendAccountRecoveryEmailsJob},
user::{UserEmailFilter, UserRecoveryRepository},
Pagination, RepositoryAccess,
};
use mas_templates::{EmailRecoveryContext, TemplateContext};
use rand::distributions::{Alphanumeric, DistString};
use tracing::info;
use tracing::{error, info};
use crate::{storage::PostgresStorageFactory, JobContextExt, State};
@@ -40,6 +43,8 @@ async fn send_account_recovery_email_job(
) -> Result<(), anyhow::Error> {
let state = ctx.state();
let clock = state.clock();
let mailer = state.mailer();
let url_builder = state.url_builder();
let mut rng = state.rng();
let mut repo = state.repository().await?;
@@ -58,6 +63,11 @@ async fn send_account_recovery_email_job(
let mut cursor = Pagination::first(50);
let lang: DataLocale = session
.locale
.parse()
.context("Invalid locale in database on recovery session")?;
loop {
let page = repo
.user_email()
@@ -72,13 +82,39 @@ async fn send_account_recovery_email_job(
for email in page.edges {
let ticket = Alphanumeric.sample_string(&mut rng, 32);
let _ticket = repo
let ticket = repo
.user_recovery()
.add_ticket(&mut rng, &clock, &session, &email, ticket)
.await?;
info!("Sending recovery email to {}", email.email);
// TODO
let user_email = repo
.user_email()
.lookup(email.id)
.await?
.context("User email not found")?;
let user = repo
.user()
.lookup(user_email.user_id)
.await?
.context("User not found")?;
let url = url_builder.account_recovery_link(ticket.ticket);
let address: Address = user_email.email.parse()?;
let mailbox = Mailbox::new(Some(user.username.clone()), address);
info!("Sending recovery email to {}", mailbox);
let context =
EmailRecoveryContext::new(user, session.clone(), url).with_language(lang.clone());
// XXX: we only log if the email fails to send, to avoid stopping the loop
if let Err(e) = mailer.send_recovery_email(mailbox, &context).await {
error!(
error = &e as &dyn std::error::Error,
"Failed to send recovery email"
);
}
cursor = cursor.after(email.id);
}