mas_handlers/views/register/steps/
finish.rsuse std::sync::Arc;
use anyhow::Context as _;
use axum::{
extract::{Path, State},
response::{Html, IntoResponse, Response},
};
use axum_extra::TypedHeader;
use chrono::Duration;
use mas_axum_utils::{FancyError, SessionInfoExt as _, cookies::CookieJar};
use mas_data_model::UserAgent;
use mas_matrix::HomeserverConnection;
use mas_router::{PostAuthAction, UrlBuilder};
use mas_storage::{
BoxClock, BoxRepository, BoxRng,
queue::{ProvisionUserJob, QueueJobRepositoryExt as _},
user::UserEmailFilter,
};
use mas_templates::{RegisterStepsEmailInUseContext, TemplateContext as _, Templates};
use ulid::Ulid;
use super::super::cookie::UserRegistrationSessions;
use crate::{BoundActivityTracker, PreferredLanguage, views::shared::OptionalPostAuthAction};
#[tracing::instrument(
name = "handlers.views.register.steps.finish.get",
fields(user_registration.id = %id),
skip_all,
err,
)]
pub(crate) async fn get(
mut rng: BoxRng,
clock: BoxClock,
mut repo: BoxRepository,
activity_tracker: BoundActivityTracker,
user_agent: Option<TypedHeader<headers::UserAgent>>,
State(url_builder): State<UrlBuilder>,
State(homeserver): State<Arc<dyn HomeserverConnection>>,
State(templates): State<Templates>,
PreferredLanguage(lang): PreferredLanguage,
cookie_jar: CookieJar,
Path(id): Path<Ulid>,
) -> Result<Response, FancyError> {
let user_agent = user_agent.map(|ua| UserAgent::parse(ua.as_str().to_owned()));
let registration = repo
.user_registration()
.lookup(id)
.await?
.context("User registration not found")?;
if registration.completed_at.is_some() {
let post_auth_action: Option<PostAuthAction> = registration
.post_auth_action
.map(serde_json::from_value)
.transpose()?;
return Ok((
cookie_jar,
OptionalPostAuthAction::from(post_auth_action).go_next(&url_builder),
)
.into_response());
}
if clock.now() - registration.created_at > Duration::hours(1) {
return Err(FancyError::from(anyhow::anyhow!(
"Registration session has expired"
)));
}
let registrations = UserRegistrationSessions::load(&cookie_jar);
if !registrations.contains(®istration) {
return Err(FancyError::from(anyhow::anyhow!(
"Could not find the registration in the browser cookies"
)));
}
if repo.user().exists(®istration.username).await? {
return Err(FancyError::from(anyhow::anyhow!(
"Username is already taken"
)));
}
if !homeserver
.is_localpart_available(®istration.username)
.await?
{
return Err(FancyError::from(anyhow::anyhow!(
"Username is not available"
)));
}
let email_authentication_id = registration
.email_authentication_id
.context("No email authentication started for this registration")?;
let email_authentication = repo
.user_email()
.lookup_authentication(email_authentication_id)
.await?
.context("Could not load the email authentication")?;
if email_authentication.completed_at.is_none() {
return Ok((
cookie_jar,
url_builder.redirect(&mas_router::RegisterVerifyEmail::new(id)),
)
.into_response());
}
if repo
.user_email()
.count(UserEmailFilter::new().for_email(&email_authentication.email))
.await?
> 0
{
let action = registration
.post_auth_action
.map(serde_json::from_value)
.transpose()?;
let ctx = RegisterStepsEmailInUseContext::new(email_authentication.email, action)
.with_language(lang);
return Ok((
cookie_jar,
Html(templates.render_register_steps_email_in_use(&ctx)?),
)
.into_response());
}
if registration.display_name.is_none() {
return Ok((
cookie_jar,
url_builder.redirect(&mas_router::RegisterDisplayName::new(registration.id)),
)
.into_response());
}
let registration = repo
.user_registration()
.complete(&clock, registration)
.await?;
let cookie_jar = registrations
.consume_session(®istration)?
.save(cookie_jar, &clock);
let user = repo
.user()
.add(&mut rng, &clock, registration.username)
.await?;
let user_session = repo
.browser_session()
.add(&mut rng, &clock, &user, user_agent)
.await?;
repo.user_email()
.add(&mut rng, &clock, &user, email_authentication.email)
.await?;
if let Some(password) = registration.password {
let user_password = repo
.user_password()
.add(
&mut rng,
&clock,
&user,
password.version,
password.hashed_password,
None,
)
.await?;
repo.browser_session()
.authenticate_with_password(&mut rng, &clock, &user_session, &user_password)
.await?;
}
if let Some(terms_url) = registration.terms_url {
repo.user_terms()
.accept_terms(&mut rng, &clock, &user, terms_url)
.await?;
}
let mut job = ProvisionUserJob::new(&user);
if let Some(display_name) = registration.display_name {
job = job.set_display_name(display_name);
}
repo.queue_job().schedule_job(&mut rng, &clock, job).await?;
repo.save().await?;
activity_tracker
.record_browser_session(&clock, &user_session)
.await;
let post_auth_action: Option<PostAuthAction> = registration
.post_auth_action
.map(serde_json::from_value)
.transpose()?;
let cookie_jar = cookie_jar.set_session(&user_session);
return Ok((
cookie_jar,
OptionalPostAuthAction::from(post_auth_action).go_next(&url_builder),
)
.into_response());
}