Skip to content

Commit 0bedaf3

Browse files
committed
Make the password registration create a user_registration
1 parent a294b37 commit 0bedaf3

File tree

6 files changed

+114
-53
lines changed

6 files changed

+114
-53
lines changed

crates/handlers/src/views/register/password.rs

+65-41
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ use mas_matrix::BoxHomeserverConnection;
2424
use mas_policy::Policy;
2525
use mas_router::UrlBuilder;
2626
use mas_storage::{
27-
queue::{ProvisionUserJob, QueueJobRepositoryExt as _},
28-
user::{BrowserSessionRepository, UserEmailRepository, UserPasswordRepository, UserRepository},
27+
queue::{QueueJobRepositoryExt as _, SendEmailAuthenticationCodeJob},
28+
user::{UserEmailRepository, UserRepository},
2929
BoxClock, BoxRepository, BoxRng, RepositoryAccess,
3030
};
3131
use mas_templates::{
@@ -141,6 +141,8 @@ pub(crate) async fn post(
141141
Form(form): Form<ProtectedForm<RegisterForm>>,
142142
) -> Result<Response, FancyError> {
143143
let user_agent = user_agent.map(|ua| UserAgent::parse(ua.as_str().to_owned()));
144+
145+
let ip_address = activity_tracker.ip();
144146
if !site_config.password_registration_enabled {
145147
return Ok(StatusCode::METHOD_NOT_ALLOWED.into_response());
146148
}
@@ -296,49 +298,64 @@ pub(crate) async fn post(
296298
return Ok((cookie_jar, Html(content)).into_response());
297299
}
298300

299-
let user = repo.user().add(&mut rng, &clock, form.username).await?;
300-
301-
if let Some(tos_uri) = &site_config.tos_uri {
302-
repo.user_terms()
303-
.accept_terms(&mut rng, &clock, &user, tos_uri.clone())
304-
.await?;
305-
}
306-
307-
let password = Zeroizing::new(form.password.into_bytes());
308-
let (version, hashed_password) = password_manager.hash(&mut rng, password).await?;
309-
let user_password = repo
310-
.user_password()
311-
.add(&mut rng, &clock, &user, version, hashed_password, None)
301+
let post_auth_action = query
302+
.post_auth_action
303+
.map(serde_json::to_value)
304+
.transpose()?;
305+
let registration = repo
306+
.user_registration()
307+
.add(
308+
&mut rng,
309+
&clock,
310+
form.username,
311+
ip_address,
312+
user_agent,
313+
post_auth_action,
314+
)
312315
.await?;
313316

314-
let user_email = repo
317+
let registration = if let Some(tos_uri) = &site_config.tos_uri {
318+
repo.user_registration()
319+
.set_terms_url(registration, tos_uri.clone())
320+
.await?
321+
} else {
322+
registration
323+
};
324+
325+
// Create a new user email authentication session
326+
let user_email_authentication = repo
315327
.user_email()
316-
.add(&mut rng, &clock, &user, form.email)
328+
.add_authentication_for_registration(&mut rng, &clock, form.email, &registration)
317329
.await?;
318330

319-
let next = mas_router::AccountVerifyEmail::new(user_email.id).and_maybe(query.post_auth_action);
320-
321-
let session = repo
322-
.browser_session()
323-
.add(&mut rng, &clock, &user, user_agent)
331+
// Schedule a job to verify the email
332+
repo.queue_job()
333+
.schedule_job(
334+
&mut rng,
335+
&clock,
336+
SendEmailAuthenticationCodeJob::new(&user_email_authentication, locale.to_string()),
337+
)
324338
.await?;
325339

326-
repo.browser_session()
327-
.authenticate_with_password(&mut rng, &clock, &session, &user_password)
340+
let registration = repo
341+
.user_registration()
342+
.set_email_authentication(registration, &user_email_authentication)
328343
.await?;
329344

330-
repo.queue_job()
331-
.schedule_job(&mut rng, &clock, ProvisionUserJob::new(&user))
345+
// Hash the password
346+
let password = Zeroizing::new(form.password.into_bytes());
347+
let (version, hashed_password) = password_manager.hash(&mut rng, password).await?;
348+
349+
// Add the password to the registration
350+
let registration = repo
351+
.user_registration()
352+
.set_password(registration, hashed_password, version)
332353
.await?;
333354

334355
repo.save().await?;
335356

336-
activity_tracker
337-
.record_browser_session(&clock, &session)
338-
.await;
339-
340-
let cookie_jar = cookie_jar.set_session(&session);
341-
Ok((cookie_jar, url_builder.redirect(&next)).into_response())
357+
// TODO: redirect to the next step on the registration
358+
Ok(format!("{}", registration.id).into_response())
342359
}
343360

344361
async fn render(
@@ -451,16 +468,23 @@ mod tests {
451468
let request = cookies.with_cookies(request);
452469
let response = state.request(request).await;
453470
cookies.save_cookies(&response);
454-
response.assert_status(StatusCode::SEE_OTHER);
455-
456-
// Now if we get to the home page, we should see the user's username
457-
let request = Request::get("/").empty();
458-
let request = cookies.with_cookies(request);
459-
let response = state.request(request).await;
460-
cookies.save_cookies(&response);
461471
response.assert_status(StatusCode::OK);
462-
response.assert_header_value(CONTENT_TYPE, "text/html; charset=utf-8");
463-
assert!(response.body().contains("john"));
472+
473+
// The handler gives us the registration ID in the body for now
474+
let id = response.body().parse().unwrap();
475+
// There should be a new registration in the database
476+
let mut repo = state.repository().await.unwrap();
477+
let registration = repo.user_registration().lookup(id).await.unwrap().unwrap();
478+
assert_eq!(registration.username, "john".to_owned());
479+
assert!(registration.password.is_some());
480+
481+
let email_authentication = repo
482+
.user_email()
483+
.lookup_authentication(registration.email_authentication_id.unwrap())
484+
.await
485+
.unwrap()
486+
.unwrap();
487+
assert_eq!(email_authentication.email, "[email protected]");
464488
}
465489

466490
/// When the two password fields mismatch, it should give an error

crates/tasks/src/email.rs

+21-2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,22 @@ impl RunnableJob for SendEmailAuthenticationCodeJob {
7878
None
7979
};
8080

81+
// Load the registration, if any
82+
let registration =
83+
if let Some(registration_id) = user_email_authentication.user_registration_id {
84+
Some(
85+
repo.user_registration()
86+
.lookup(registration_id)
87+
.await
88+
.map_err(JobError::retry)?
89+
.ok_or(JobError::fail(anyhow::anyhow!(
90+
"Failed to load user registration"
91+
)))?,
92+
)
93+
} else {
94+
None
95+
};
96+
8197
// Generate a new 6-digit authentication code
8298
let range = Uniform::<u32>::from(0..1_000_000);
8399
let code = rng.sample(range);
@@ -98,14 +114,17 @@ impl RunnableJob for SendEmailAuthenticationCodeJob {
98114
.email
99115
.parse()
100116
.map_err(JobError::fail)?;
101-
let username = browser_session.as_ref().map(|s| s.user.username.clone());
117+
let username_from_session = browser_session.as_ref().map(|s| s.user.username.clone());
118+
let username_from_registration = registration.as_ref().map(|r| r.username.clone());
119+
let username = username_from_registration.or(username_from_session);
102120
let mailbox = Mailbox::new(username, address);
103121

104122
info!("Sending email verification code to {}", mailbox);
105123

106124
let language = self.language().parse().map_err(JobError::fail)?;
107125

108-
let context = EmailVerificationContext::new(code, browser_session).with_language(language);
126+
let context = EmailVerificationContext::new(code, browser_session, registration)
127+
.with_language(language);
109128
mailer
110129
.send_verification_email(mailbox, &context)
111130
.await

crates/templates/src/context.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use mas_data_model::{
2323
DeviceCodeGrant, UpstreamOAuthLink, UpstreamOAuthProvider, UpstreamOAuthProviderClaimsImports,
2424
UpstreamOAuthProviderDiscoveryMode, UpstreamOAuthProviderPkceMode,
2525
UpstreamOAuthProviderTokenAuthMethod, User, UserAgent, UserEmail, UserEmailAuthenticationCode,
26-
UserRecoverySession,
26+
UserRecoverySession, UserRegistration,
2727
};
2828
use mas_i18n::DataLocale;
2929
use mas_iana::jose::JsonWebSignatureAlg;
@@ -878,7 +878,10 @@ impl TemplateContext for EmailRecoveryContext {
878878
/// Context used by the `emails/verification.{txt,html,subject}` templates
879879
#[derive(Serialize)]
880880
pub struct EmailVerificationContext {
881+
#[serde(skip_serializing_if = "Option::is_none")]
881882
browser_session: Option<BrowserSession>,
883+
#[serde(skip_serializing_if = "Option::is_none")]
884+
user_registration: Option<UserRegistration>,
882885
authentication_code: UserEmailAuthenticationCode,
883886
}
884887

@@ -888,9 +891,11 @@ impl EmailVerificationContext {
888891
pub fn new(
889892
authentication_code: UserEmailAuthenticationCode,
890893
browser_session: Option<BrowserSession>,
894+
user_registration: Option<UserRegistration>,
891895
) -> Self {
892896
Self {
893897
browser_session,
898+
user_registration,
894899
authentication_code,
895900
}
896901
}
@@ -926,6 +931,7 @@ impl TemplateContext for EmailVerificationContext {
926931

927932
Self {
928933
browser_session: Some(browser_session),
934+
user_registration: None,
929935
authentication_code,
930936
}
931937
})

templates/emails/verification.html

+7-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88

99
{%- set _ = translator(lang) -%}
1010

11-
{{ _("mas.emails.greeting", username=browser_session.user.username | default("user")) }}<br />
11+
{%- if browser_session is defined -%}
12+
{%- set username = browser_session.user.username -%}
13+
{%- elif user_registration is defined -%}
14+
{%- set username = user_registration.username -%}
15+
{%- endif -%}
16+
17+
{{ _("mas.emails.greeting", username=(username|default("user"))) }}<br />
1218
<br />
1319
{{ _("mas.emails.verify.body_html", code=authentication_code.code) }}<br />

templates/emails/verification.txt

+7-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ Please see LICENSE in the repository root for full details.
88

99
{%- set _ = translator(lang) -%}
1010

11-
{{ _("mas.emails.greeting", username=browser_session.user.username | default("user")) }}
11+
{%- if browser_session is defined -%}
12+
{%- set username = browser_session.user.username -%}
13+
{%- elif user_registration is defined -%}
14+
{%- set username = user_registration.username -%}
15+
{%- endif -%}
16+
17+
{{ _("mas.emails.greeting", username=(username|default("user"))) }}
1218

1319
{{ _("mas.emails.verify.body_text", code=authentication_code.code) }}

translations/en.json

+7-7
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@
219219
"emails": {
220220
"greeting": "Hello %(username)s,",
221221
"@greeting": {
222-
"context": "emails/verification.html:11:3-85, emails/verification.txt:11:3-85",
222+
"context": "emails/verification.html:17:3-64, emails/verification.txt:17:3-64",
223223
"description": "Greeting at the top of emails sent to the user"
224224
},
225225
"recovery": {
@@ -251,12 +251,12 @@
251251
"verify": {
252252
"body_html": "Your verification code to confirm this email address is: <strong>%(code)s</strong>",
253253
"@body_html": {
254-
"context": "emails/verification.html:13:3-66",
254+
"context": "emails/verification.html:19:3-66",
255255
"description": "The body of the email sent to verify an email address (HTML)"
256256
},
257257
"body_text": "Your verification code to confirm this email address is: %(code)s",
258258
"@body_text": {
259-
"context": "emails/verification.txt:13:3-66",
259+
"context": "emails/verification.txt:19:3-66",
260260
"description": "The body of the email sent to verify an email address (text)"
261261
},
262262
"subject": "Your email verification code is: %(code)s",
@@ -327,7 +327,7 @@
327327
},
328328
"continue_with_provider": "Continue with %(provider)s",
329329
"@continue_with_provider": {
330-
"context": "pages/login.html:75:15-67, pages/register/index.html:49:15-67",
330+
"context": "pages/login.html:75:15-67, pages/register/index.html:53:15-67",
331331
"description": "Button to log in with an upstream provider"
332332
},
333333
"description": "Please sign in to continue:",
@@ -513,12 +513,12 @@
513513
"register": {
514514
"call_to_login": "Already have an account?",
515515
"@call_to_login": {
516-
"context": "pages/register/index.html:55:35-66, pages/register/password.html:77:33-64",
516+
"context": "pages/register/index.html:59:35-66, pages/register/password.html:77:33-64",
517517
"description": "Displayed on the registration page to suggest to log in instead"
518518
},
519519
"continue_with_email": "Continue with email address",
520520
"@continue_with_email": {
521-
"context": "pages/register/index.html:40:30-67"
521+
"context": "pages/register/index.html:44:30-67"
522522
},
523523
"create_account": {
524524
"description": "Choose a username to continue.",
@@ -682,4 +682,4 @@
682682
}
683683
}
684684
}
685-
}
685+
}

0 commit comments

Comments
 (0)