refactor: Use more consistent terminology for email validation pages

This commit is contained in:
Ginger
2026-05-26 14:27:09 -04:00
parent baf76cd4dc
commit 5ca1341bf7
12 changed files with 168 additions and 165 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ use std::{
use askama::Template; use askama::Template;
use async_trait::async_trait; use async_trait::async_trait;
use conduwuit::{Result, info, utils::ReadyExt}; use conduwuit::{Result, info, utils::ReadyExt};
use futures::{FutureExt, StreamExt}; use futures::StreamExt;
use ruma::{UserId, events::room::message::RoomMessageEventContent}; use ruma::{UserId, events::room::message::RoomMessageEventContent};
use crate::{ use crate::{
+17 -17
View File
@@ -25,13 +25,13 @@ use crate::{
pub(crate) fn build() -> Router<crate::State> { pub(crate) fn build() -> Router<crate::State> {
Router::new() Router::new()
.route("/change/", on(GET_POST, route_change_email_request)) .route("/change/", on(GET_POST, route_change_email))
.route("/change/validate", get(get_change_email)) .route("/change/validate", get(get_change_email_validate))
.route("/change/delete", post(post_delete_email)) .route("/change/delete", post(post_delete_email))
} }
template! { template! {
struct ChangeEmailRequest use "change_email_request.html.j2" { struct ChangeEmail use "change_email.html.j2" {
user_card: UserCard, user_card: UserCard,
email: Option<String>, email: Option<String>,
form: Form<'static>, form: Form<'static>,
@@ -40,7 +40,7 @@ template! {
} }
form! { form! {
struct ChangeEmailRequestForm { struct ChangeEmailForm {
email: Address where { email: Address where {
input_type: "email", input_type: "email",
label: "Email address" label: "Email address"
@@ -51,9 +51,9 @@ form! {
} }
template! { template! {
struct ChangeEmail use "change_email.html.j2" { struct ChangeEmailValidate use "change_email_validate.html.j2" {
user_card: UserCard, user_card: UserCard,
body: ChangeEmailBody body: ChangeEmailValidateBody
} }
} }
@@ -64,7 +64,7 @@ template! {
} }
#[derive(Debug)] #[derive(Debug)]
enum ChangeEmailBody { enum ChangeEmailValidateBody {
ValidationPending { ValidationPending {
session_id: OwnedSessionId, session_id: OwnedSessionId,
client_secret: OwnedClientSecret, client_secret: OwnedClientSecret,
@@ -73,16 +73,16 @@ enum ChangeEmailBody {
Success, Success,
} }
async fn route_change_email_request( async fn route_change_email(
State(services): State<crate::State>, State(services): State<crate::State>,
Extension(context): Extension<TemplateContext>, Extension(context): Extension<TemplateContext>,
user: User, user: User,
PostForm(form): PostForm<ChangeEmailRequestForm>, PostForm(form): PostForm<ChangeEmailForm>,
) -> Result { ) -> Result {
let user_id = user.expect_recent(LoginTarget::ChangeEmail)?; let user_id = user.expect_recent(LoginTarget::ChangeEmail)?;
let Some(form) = form else { let Some(form) = form else {
return response!(ChangeEmailRequest::new( return response!(ChangeEmail::new(
context.clone(), context.clone(),
UserCard::for_local_user(&services, user_id.clone()).await, UserCard::for_local_user(&services, user_id.clone()).await,
services services
@@ -90,7 +90,7 @@ async fn route_change_email_request(
.get_email_for_localpart(user_id.localpart()) .get_email_for_localpart(user_id.localpart())
.await .await
.map(|address| address.to_string()), .map(|address| address.to_string()),
ChangeEmailRequestForm::build(context), ChangeEmailForm::build(context),
services.threepid.email_requirement().may_remove(), services.threepid.email_requirement().may_remove(),
)); ));
}; };
@@ -128,10 +128,10 @@ async fn route_change_email_request(
} }
}; };
response!(ChangeEmail::new( response!(ChangeEmailValidate::new(
context, context,
UserCard::for_local_user(&services, user_id).await, UserCard::for_local_user(&services, user_id).await,
ChangeEmailBody::ValidationPending { ChangeEmailValidateBody::ValidationPending {
session_id, session_id,
client_secret, client_secret,
validation_error: false validation_error: false
@@ -145,7 +145,7 @@ struct ChangeEmailQuery {
threepid: ThreepidQuery, threepid: ThreepidQuery,
} }
async fn get_change_email( async fn get_change_email_validate(
State(services): State<crate::State>, State(services): State<crate::State>,
Extension(context): Extension<TemplateContext>, Extension(context): Extension<TemplateContext>,
Expect(Query(ChangeEmailQuery { Expect(Query(ChangeEmailQuery {
@@ -165,10 +165,10 @@ async fn get_change_email(
.get_valid_session(&session_id, &client_secret) .get_valid_session(&session_id, &client_secret)
.await .await
else { else {
return response!(ChangeEmail::new( return response!(ChangeEmailValidate::new(
context, context,
user_card, user_card,
ChangeEmailBody::ValidationPending { ChangeEmailValidateBody::ValidationPending {
session_id, session_id,
client_secret, client_secret,
validation_error: true validation_error: true
@@ -186,7 +186,7 @@ async fn get_change_email(
return response!(BadRequest(err.message())); return response!(BadRequest(err.message()));
} }
response!(ChangeEmail::new(context, user_card, ChangeEmailBody::Success)) response!(ChangeEmailValidate::new(context, user_card, ChangeEmailValidateBody::Success))
} }
async fn post_delete_email( async fn post_delete_email(
+24 -24
View File
@@ -28,18 +28,18 @@ use crate::{
pub(crate) fn build() -> Router<crate::State> { pub(crate) fn build() -> Router<crate::State> {
Router::new() Router::new()
.route("/", on(GET_POST, route_reset_password_request)) .route("/", on(GET_POST, route_reset_password))
.route("/validate", on(GET_POST, route_reset_password)) .route("/validate", on(GET_POST, route_reset_password_validate))
} }
template! { template! {
struct ResetPasswordRequest use "reset_password_request.html.j2" { struct ResetPassword use "reset_password.html.j2" {
body: ResetPasswordRequestBody body: ResetPasswordBody
} }
} }
#[derive(Debug)] #[derive(Debug)]
enum ResetPasswordRequestBody { enum ResetPasswordBody {
Form(Form<'static>), Form(Form<'static>),
Unavailable, Unavailable,
} }
@@ -55,24 +55,21 @@ form! {
} }
} }
async fn route_reset_password_request( async fn route_reset_password(
State(services): State<crate::State>, State(services): State<crate::State>,
Extension(context): Extension<TemplateContext>, Extension(context): Extension<TemplateContext>,
PostForm(form): PostForm<ResetPasswordRequestForm>, PostForm(form): PostForm<ResetPasswordRequestForm>,
) -> Result { ) -> Result {
// Check if SMTP is configured // Check if SMTP is configured
if services.mailer.mailer().is_none() { if services.mailer.mailer().is_none() {
return response!(ResetPasswordRequest::new( return response!(ResetPassword::new(context, ResetPasswordBody::Unavailable));
context,
ResetPasswordRequestBody::Unavailable
));
} }
let Some(form) = form else { let Some(form) = form else {
// For GET requests return the reset request form // For GET requests return the reset request form
return response!(ResetPasswordRequest::new( return response!(ResetPassword::new(
context.clone(), context.clone(),
ResetPasswordRequestBody::Form(ResetPasswordRequestForm::build(context)) ResetPasswordBody::Form(ResetPasswordRequestForm::build(context))
)); ));
}; };
@@ -118,21 +115,24 @@ async fn route_reset_password_request(
ValidationSessions::generate_session_id() ValidationSessions::generate_session_id()
}); });
response!(ResetPassword::new(context, ResetPasswordBody::ValidationPending { response!(ResetPasswordValidate::new(
context,
ResetPasswordValidateBody::ValidationPending {
client_secret, client_secret,
session_id, session_id,
validation_error: false validation_error: false
})) }
))
} }
template! { template! {
struct ResetPassword use "reset_password.html.j2" { struct ResetPasswordValidate use "reset_password_validate.html.j2" {
body: ResetPasswordBody body: ResetPasswordValidateBody
} }
} }
#[derive(Debug)] #[derive(Debug)]
enum ResetPasswordBody { enum ResetPasswordValidateBody {
ValidationPending { ValidationPending {
session_id: OwnedSessionId, session_id: OwnedSessionId,
client_secret: OwnedClientSecret, client_secret: OwnedClientSecret,
@@ -173,7 +173,7 @@ struct ResetPasswordQuery {
threepid: ThreepidQuery, threepid: ThreepidQuery,
} }
async fn route_reset_password( async fn route_reset_password_validate(
State(services): State<crate::State>, State(services): State<crate::State>,
Extension(context): Extension<TemplateContext>, Extension(context): Extension<TemplateContext>,
Expect(Query(query)): Expect<Query<ResetPasswordQuery>>, Expect(Query(query)): Expect<Query<ResetPasswordQuery>>,
@@ -203,7 +203,7 @@ async fn route_reset_password(
if let Some(form) = form { if let Some(form) = form {
if let Err(err) = form.validate() { if let Err(err) = form.validate() {
ResetPasswordBody::ValidationSuccess { ResetPasswordValidateBody::ValidationSuccess {
user_card, user_card,
form: ResetPasswordForm::with_errors(context.clone(), err), form: ResetPasswordForm::with_errors(context.clone(), err),
} }
@@ -214,7 +214,7 @@ async fn route_reset_password(
services.users.set_password(&user_id, Some(hash)); services.users.set_password(&user_id, Some(hash));
ResetPasswordBody::ResetSuccess { user_card } ResetPasswordValidateBody::ResetSuccess { user_card }
}, },
| Err(err) => { | Err(err) => {
let mut errors = ValidationErrors::new(); let mut errors = ValidationErrors::new();
@@ -225,7 +225,7 @@ async fn route_reset_password(
.with_message(err.message().into()), .with_message(err.message().into()),
); );
ResetPasswordBody::ValidationSuccess { ResetPasswordValidateBody::ValidationSuccess {
user_card, user_card,
form: ResetPasswordForm::with_errors(context.clone(), errors), form: ResetPasswordForm::with_errors(context.clone(), errors),
} }
@@ -233,18 +233,18 @@ async fn route_reset_password(
} }
} }
} else { } else {
ResetPasswordBody::ValidationSuccess { ResetPasswordValidateBody::ValidationSuccess {
user_card, user_card,
form: ResetPasswordForm::build(context.clone()), form: ResetPasswordForm::build(context.clone()),
} }
} }
}, },
| Err(_) => ResetPasswordBody::ValidationPending { | Err(_) => ResetPasswordValidateBody::ValidationPending {
session_id: query.threepid.session_id, session_id: query.threepid.session_id,
client_secret: query.threepid.client_secret, client_secret: query.threepid.client_secret,
validation_error: true, validation_error: true,
}, },
}; };
response!(ResetPassword::new(context, body)) response!(ResetPasswordValidate::new(context, body))
} }
+10 -8
View File
@@ -31,7 +31,7 @@ const COMPLETED_REGISTRATION_KEY: &str = "completed_registration";
pub(crate) fn build() -> Router<crate::State> { pub(crate) fn build() -> Router<crate::State> {
Router::new() Router::new()
.route("/", on(GET_POST, route_register)) .route("/", on(GET_POST, route_register))
.route("/validate", get(get_register_confirm_email)) .route("/validate", get(get_register_email_validate))
} }
template! { template! {
@@ -225,7 +225,7 @@ async fn route_register(
} }
template! { template! {
struct RegisterConfirmEmail use "register_confirm_email.html.j2" { struct RegisterEmailValidate use "register_email_validate.html.j2" {
session_id: OwnedSessionId, session_id: OwnedSessionId,
client_secret: OwnedClientSecret, client_secret: OwnedClientSecret,
validation_error: bool validation_error: bool
@@ -233,18 +233,18 @@ template! {
} }
#[derive(Deserialize, Serialize)] #[derive(Deserialize, Serialize)]
struct RegisterConfirmEmailQuery { struct RegisterEmailValidateQuery {
#[serde(flatten)] #[serde(flatten)]
threepid: ThreepidQuery, threepid: ThreepidQuery,
} }
async fn get_register_confirm_email( async fn get_register_email_validate(
State(services): State<crate::State>, State(services): State<crate::State>,
Extension(context): Extension<TemplateContext>, Extension(context): Extension<TemplateContext>,
session_store: Session, session_store: Session,
Expect(Query(RegisterConfirmEmailQuery { Expect(Query(RegisterEmailValidateQuery {
threepid: ThreepidQuery { client_secret, session_id }, threepid: ThreepidQuery { client_secret, session_id },
})): Expect<Query<RegisterConfirmEmailQuery>>, })): Expect<Query<RegisterEmailValidateQuery>>,
) -> Result { ) -> Result {
let Some(completed_registration) = session_store let Some(completed_registration) = session_store
.get::<CompletedRegistration>(COMPLETED_REGISTRATION_KEY) .get::<CompletedRegistration>(COMPLETED_REGISTRATION_KEY)
@@ -261,7 +261,7 @@ async fn get_register_confirm_email(
.get_valid_session(&session_id, &client_secret) .get_valid_session(&session_id, &client_secret)
.await .await
else { else {
return response!(RegisterConfirmEmail::new(context, session_id, client_secret, true,)); return response!(RegisterEmailValidate::new(context, session_id, client_secret, true,));
}; };
let email = session.consume(); let email = session.consume();
@@ -451,7 +451,9 @@ async fn begin_registration(
.await .await
.expect("should have been able to serialize completed registration"); .expect("should have been able to serialize completed registration");
Ok(response!(RegisterConfirmEmail::new(context, session_id, client_secret, false,))) Ok(response!(
RegisterEmailValidate::new(context, session_id, client_secret, false,)
))
} else { } else {
// If email isn't required we can immediately complete registration // If email isn't required we can immediately complete registration
complete_registration(services, session_store, completed_registration, None).await; complete_registration(services, session_store, completed_registration, None).await;
+22 -20
View File
@@ -5,29 +5,31 @@ Change your email
{%- endblock -%} {%- endblock -%}
{%- block content -%} {%- block content -%}
<div class="panel narrow"> <div class="panel">
<h1>Change your email</h1> <h1>Change your email <a class="back" href="{{ crate::ROUTE_PREFIX }}/account/">Back</a></h1>
{{ user_card }} {{ user_card }}
{% match body %}
{% when ChangeEmailBody::ValidationPending { session_id, client_secret, validation_error } %}
<p> <p>
A message has been sent to your new email address with a validation link. If you do not receive the email: Your email address will be used for automated emails, such as password reset requests. It is also
<ul> visible to your homeserver's administrator, who may use it to contact you directly.
<li>Check your spam filter.</li>
</ul>
</p> </p>
{% if validation_error %} <p>
<small class="error">Validation failed. Have you clicked the link in the email that was sent to you?</small> {% if let Some(email) = email %}
Your account's associated email address is <code>{{ email }}</code>.
To change your email address, enter your new address below.
{% else %}
Your account has no associated email address. To add an email address, enter it below.
{% endif %} {% endif %}
<form method="get" action="validate">
<input type="hidden" name="session_id" value="{{ session_id }}">
<input type="hidden" name="client_secret" value="{{ client_secret }}">
<button type="submit">Continue</button>
</form>
{% when ChangeEmailBody::Success %}
<p>
Your email address has been changed successfully. <a href="{{ crate::ROUTE_PREFIX }}/account/">Back</a>
</p> </p>
{% endmatch %} {{ form }}
{% if may_remove %}
<p>
You may remove your email address. Note that, if your account has no email address,
you will not be able to reset your password if you forget it.
</p>
<form method="post" action="delete">
<button type="submit">Remove your email address</button>
</form>
{% endif %}
</div> </div>
{%- endblock -%} {% endblock %}
@@ -1,35 +0,0 @@
{% extends "_layout.html.j2" %}
{%- block title -%}
Change your email
{%- endblock -%}
{%- block content -%}
<div class="panel">
<h1>Change your email <a class="back" href="{{ crate::ROUTE_PREFIX }}/account/">Back</a></h1>
{{ user_card }}
<p>
Your email address will be used for automated emails, such as password reset requests. It is also
visible to your homeserver's administrator, who may use it to contact you directly.
</p>
<p>
{% if let Some(email) = email %}
Your account's associated email address is <code>{{ email }}</code>.
To change your email address, enter your new address below.
{% else %}
Your account has no associated email address. To add an email address, enter it below.
{% endif %}
</p>
{{ form }}
{% if may_remove %}
<p>
You may remove your email address. Note that, if your account has no email address,
you will not be able to reset your password if you forget it.
</p>
<form method="post" action="delete">
<button type="submit">Remove your email address</button>
</form>
{% endif %}
</div>
{% endblock %}
@@ -0,0 +1,33 @@
{% extends "_layout.html.j2" %}
{%- block title -%}
Change your email
{%- endblock -%}
{%- block content -%}
<div class="panel narrow">
<h1>Change your email</h1>
{{ user_card }}
{% match body %}
{% when ChangeEmailValidateBody::ValidationPending { session_id, client_secret, validation_error } %}
<p>
A message has been sent to your new email address with a validation link. If you do not receive the email:
<ul>
<li>Check your spam filter.</li>
</ul>
</p>
{% if validation_error %}
<small class="error">Validation failed. Have you clicked the link in the email that was sent to you?</small>
{% endif %}
<form method="get" action="validate">
<input type="hidden" name="session_id" value="{{ session_id }}">
<input type="hidden" name="client_secret" value="{{ client_secret }}">
<button type="submit">Continue</button>
</form>
{% when ChangeEmailValidateBody::Success %}
<p>
Your email address has been changed successfully. <a href="{{ crate::ROUTE_PREFIX }}/account/">Back</a>
</p>
{% endmatch %}
</div>
{%- endblock -%}
+1
View File
@@ -85,6 +85,7 @@ Sign up
<span>:{{ server_name }}</span> <span>:{{ server_name }}</span>
</span> </span>
{{ form::errors(field_errors, std::borrow::Cow::Borrowed("username")) }} {{ form::errors(field_errors, std::borrow::Cow::Borrowed("username")) }}
<small><b>Note:</b> Your username cannot be changed after you create your account.</small>
</p> </p>
<p> <p>
Just a few more details to finish creating your account. Just a few more details to finish creating your account.
+17 -21
View File
@@ -5,32 +5,28 @@ Reset your password
{%- endblock -%} {%- endblock -%}
{%- block content -%} {%- block content -%}
<div class="panel narrow"> {% match body %}
{% when ResetPasswordBody::Form(_) %}
<div class="panel">
{% when ResetPasswordBody::Unavailable %}
<div class="panel middle"/>
{% endmatch %}
<h1>Reset your password</h1> <h1>Reset your password</h1>
{% match body %} {% match body %}
{% when ResetPasswordBody::ValidationPending { session_id, client_secret, validation_error } %} {% when ResetPasswordBody::Form(form) %}
<p> <p>
Check your inbox for the validation email. If you do not receive the email: To reset your password, enter your email below. If your Matrix account has an associated email address,
<ul> you will receive an email with a link to reset your password.
<li>Check your spam filter.</li> </p>
<li>Your Matrix account may not be associated with an email address. Contact your homeserver's <p>
administrator for assistance.</li> If your Matrix account does not have an associated email address, contact your homeserver's administrator
</ul> to reset your password.
</p> </p>
{% if validation_error %}
<small class="error">Validation failed. Have you clicked the link in the email that was sent to you?</small>
{% endif %}
<form method="get" action="validate">
<input type="hidden" name="session_id" value="{{ session_id }}">
<input type="hidden" name="client_secret" value="{{ client_secret }}">
<button type="submit">Continue</button>
</form>
{% when ResetPasswordBody::ValidationSuccess { user_card, form } %}
{{ user_card }}
{{ form }} {{ form }}
{% when ResetPasswordBody::ResetSuccess { user_card } %} {% when ResetPasswordBody::Unavailable %}
{{ user_card }} <p>
<p>Your password has been reset successfully.</p> To reset your password, contact your homeserver's administrator.
</p>
{% endmatch %} {% endmatch %}
</div> </div>
{%- endblock -%} {%- endblock -%}
@@ -1,32 +0,0 @@
{% extends "_layout.html.j2" %}
{%- block title -%}
Reset your password
{%- endblock -%}
{%- block content -%}
{% match body %}
{% when ResetPasswordRequestBody::Form(_) %}
<div class="panel">
{% when ResetPasswordRequestBody::Unavailable %}
<div class="panel middle"/>
{% endmatch %}
<h1>Reset your password</h1>
{% match body %}
{% when ResetPasswordRequestBody::Form(form) %}
<p>
To reset your password, enter your email below. If your Matrix account has an associated email address,
you will receive an email with a link to reset your password.
</p>
<p>
If your Matrix account does not have an associated email address, contact your homeserver's administrator
to reset your password.
</p>
{{ form }}
{% when ResetPasswordRequestBody::Unavailable %}
<p>
To reset your password, contact your homeserver's administrator.
</p>
{% endmatch %}
</div>
{%- endblock -%}
@@ -0,0 +1,36 @@
{% extends "_layout.html.j2" %}
{%- block title -%}
Reset your password
{%- endblock -%}
{%- block content -%}
<div class="panel narrow">
<h1>Reset your password</h1>
{% match body %}
{% when ResetPasswordValidateBody::ValidationPending { session_id, client_secret, validation_error } %}
<p>
Check your inbox for the validation email. If you do not receive the email:
<ul>
<li>Check your spam filter.</li>
<li>Your Matrix account may not be associated with an email address. Contact your homeserver's
administrator for assistance.</li>
</ul>
</p>
{% if validation_error %}
<small class="error">Validation failed. Have you clicked the link in the email that was sent to you?</small>
{% endif %}
<form method="get" action="validate">
<input type="hidden" name="session_id" value="{{ session_id }}">
<input type="hidden" name="client_secret" value="{{ client_secret }}">
<button type="submit">Continue</button>
</form>
{% when ResetPasswordValidateBody::ValidationSuccess { user_card, form } %}
{{ user_card }}
{{ form }}
{% when ResetPasswordValidateBody::ResetSuccess { user_card } %}
{{ user_card }}
<p>Your password has been reset successfully.</p>
{% endmatch %}
</div>
{%- endblock -%}