Make the password registration create a user_registration
This commit is contained in:
@@ -24,8 +24,8 @@ use mas_matrix::BoxHomeserverConnection;
|
||||
use mas_policy::Policy;
|
||||
use mas_router::UrlBuilder;
|
||||
use mas_storage::{
|
||||
queue::{ProvisionUserJob, QueueJobRepositoryExt as _},
|
||||
user::{BrowserSessionRepository, UserEmailRepository, UserPasswordRepository, UserRepository},
|
||||
queue::{QueueJobRepositoryExt as _, SendEmailAuthenticationCodeJob},
|
||||
user::{UserEmailRepository, UserRepository},
|
||||
BoxClock, BoxRepository, BoxRng, RepositoryAccess,
|
||||
};
|
||||
use mas_templates::{
|
||||
@@ -141,6 +141,8 @@ pub(crate) async fn post(
|
||||
Form(form): Form<ProtectedForm<RegisterForm>>,
|
||||
) -> Result<Response, FancyError> {
|
||||
let user_agent = user_agent.map(|ua| UserAgent::parse(ua.as_str().to_owned()));
|
||||
|
||||
let ip_address = activity_tracker.ip();
|
||||
if !site_config.password_registration_enabled {
|
||||
return Ok(StatusCode::METHOD_NOT_ALLOWED.into_response());
|
||||
}
|
||||
@@ -296,49 +298,64 @@ pub(crate) async fn post(
|
||||
return Ok((cookie_jar, Html(content)).into_response());
|
||||
}
|
||||
|
||||
let user = repo.user().add(&mut rng, &clock, form.username).await?;
|
||||
let post_auth_action = query
|
||||
.post_auth_action
|
||||
.map(serde_json::to_value)
|
||||
.transpose()?;
|
||||
let registration = repo
|
||||
.user_registration()
|
||||
.add(
|
||||
&mut rng,
|
||||
&clock,
|
||||
form.username,
|
||||
ip_address,
|
||||
user_agent,
|
||||
post_auth_action,
|
||||
)
|
||||
.await?;
|
||||
|
||||
if let Some(tos_uri) = &site_config.tos_uri {
|
||||
repo.user_terms()
|
||||
.accept_terms(&mut rng, &clock, &user, tos_uri.clone())
|
||||
.await?;
|
||||
}
|
||||
let registration = if let Some(tos_uri) = &site_config.tos_uri {
|
||||
repo.user_registration()
|
||||
.set_terms_url(registration, tos_uri.clone())
|
||||
.await?
|
||||
} else {
|
||||
registration
|
||||
};
|
||||
|
||||
// Create a new user email authentication session
|
||||
let user_email_authentication = repo
|
||||
.user_email()
|
||||
.add_authentication_for_registration(&mut rng, &clock, form.email, ®istration)
|
||||
.await?;
|
||||
|
||||
// Schedule a job to verify the email
|
||||
repo.queue_job()
|
||||
.schedule_job(
|
||||
&mut rng,
|
||||
&clock,
|
||||
SendEmailAuthenticationCodeJob::new(&user_email_authentication, locale.to_string()),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let registration = repo
|
||||
.user_registration()
|
||||
.set_email_authentication(registration, &user_email_authentication)
|
||||
.await?;
|
||||
|
||||
// Hash the password
|
||||
let password = Zeroizing::new(form.password.into_bytes());
|
||||
let (version, hashed_password) = password_manager.hash(&mut rng, password).await?;
|
||||
let user_password = repo
|
||||
.user_password()
|
||||
.add(&mut rng, &clock, &user, version, hashed_password, None)
|
||||
.await?;
|
||||
|
||||
let user_email = repo
|
||||
.user_email()
|
||||
.add(&mut rng, &clock, &user, form.email)
|
||||
.await?;
|
||||
|
||||
let next = mas_router::AccountVerifyEmail::new(user_email.id).and_maybe(query.post_auth_action);
|
||||
|
||||
let session = repo
|
||||
.browser_session()
|
||||
.add(&mut rng, &clock, &user, user_agent)
|
||||
.await?;
|
||||
|
||||
repo.browser_session()
|
||||
.authenticate_with_password(&mut rng, &clock, &session, &user_password)
|
||||
.await?;
|
||||
|
||||
repo.queue_job()
|
||||
.schedule_job(&mut rng, &clock, ProvisionUserJob::new(&user))
|
||||
// Add the password to the registration
|
||||
let registration = repo
|
||||
.user_registration()
|
||||
.set_password(registration, hashed_password, version)
|
||||
.await?;
|
||||
|
||||
repo.save().await?;
|
||||
|
||||
activity_tracker
|
||||
.record_browser_session(&clock, &session)
|
||||
.await;
|
||||
|
||||
let cookie_jar = cookie_jar.set_session(&session);
|
||||
Ok((cookie_jar, url_builder.redirect(&next)).into_response())
|
||||
// TODO: redirect to the next step on the registration
|
||||
Ok(format!("{}", registration.id).into_response())
|
||||
}
|
||||
|
||||
async fn render(
|
||||
@@ -451,16 +468,23 @@ mod tests {
|
||||
let request = cookies.with_cookies(request);
|
||||
let response = state.request(request).await;
|
||||
cookies.save_cookies(&response);
|
||||
response.assert_status(StatusCode::SEE_OTHER);
|
||||
|
||||
// Now if we get to the home page, we should see the user's username
|
||||
let request = Request::get("/").empty();
|
||||
let request = cookies.with_cookies(request);
|
||||
let response = state.request(request).await;
|
||||
cookies.save_cookies(&response);
|
||||
response.assert_status(StatusCode::OK);
|
||||
response.assert_header_value(CONTENT_TYPE, "text/html; charset=utf-8");
|
||||
assert!(response.body().contains("john"));
|
||||
|
||||
// The handler gives us the registration ID in the body for now
|
||||
let id = response.body().parse().unwrap();
|
||||
// There should be a new registration in the database
|
||||
let mut repo = state.repository().await.unwrap();
|
||||
let registration = repo.user_registration().lookup(id).await.unwrap().unwrap();
|
||||
assert_eq!(registration.username, "john".to_owned());
|
||||
assert!(registration.password.is_some());
|
||||
|
||||
let email_authentication = repo
|
||||
.user_email()
|
||||
.lookup_authentication(registration.email_authentication_id.unwrap())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(email_authentication.email, "john@example.com");
|
||||
}
|
||||
|
||||
/// When the two password fields mismatch, it should give an error
|
||||
|
||||
@@ -78,6 +78,22 @@ impl RunnableJob for SendEmailAuthenticationCodeJob {
|
||||
None
|
||||
};
|
||||
|
||||
// Load the registration, if any
|
||||
let registration =
|
||||
if let Some(registration_id) = user_email_authentication.user_registration_id {
|
||||
Some(
|
||||
repo.user_registration()
|
||||
.lookup(registration_id)
|
||||
.await
|
||||
.map_err(JobError::retry)?
|
||||
.ok_or(JobError::fail(anyhow::anyhow!(
|
||||
"Failed to load user registration"
|
||||
)))?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Generate a new 6-digit authentication code
|
||||
let range = Uniform::<u32>::from(0..1_000_000);
|
||||
let code = rng.sample(range);
|
||||
@@ -98,14 +114,17 @@ impl RunnableJob for SendEmailAuthenticationCodeJob {
|
||||
.email
|
||||
.parse()
|
||||
.map_err(JobError::fail)?;
|
||||
let username = browser_session.as_ref().map(|s| s.user.username.clone());
|
||||
let username_from_session = browser_session.as_ref().map(|s| s.user.username.clone());
|
||||
let username_from_registration = registration.as_ref().map(|r| r.username.clone());
|
||||
let username = username_from_registration.or(username_from_session);
|
||||
let mailbox = Mailbox::new(username, address);
|
||||
|
||||
info!("Sending email verification code to {}", mailbox);
|
||||
|
||||
let language = self.language().parse().map_err(JobError::fail)?;
|
||||
|
||||
let context = EmailVerificationContext::new(code, browser_session).with_language(language);
|
||||
let context = EmailVerificationContext::new(code, browser_session, registration)
|
||||
.with_language(language);
|
||||
mailer
|
||||
.send_verification_email(mailbox, &context)
|
||||
.await
|
||||
|
||||
@@ -23,7 +23,7 @@ use mas_data_model::{
|
||||
DeviceCodeGrant, UpstreamOAuthLink, UpstreamOAuthProvider, UpstreamOAuthProviderClaimsImports,
|
||||
UpstreamOAuthProviderDiscoveryMode, UpstreamOAuthProviderPkceMode,
|
||||
UpstreamOAuthProviderTokenAuthMethod, User, UserAgent, UserEmail, UserEmailAuthenticationCode,
|
||||
UserRecoverySession,
|
||||
UserRecoverySession, UserRegistration,
|
||||
};
|
||||
use mas_i18n::DataLocale;
|
||||
use mas_iana::jose::JsonWebSignatureAlg;
|
||||
@@ -878,7 +878,10 @@ impl TemplateContext for EmailRecoveryContext {
|
||||
/// Context used by the `emails/verification.{txt,html,subject}` templates
|
||||
#[derive(Serialize)]
|
||||
pub struct EmailVerificationContext {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
browser_session: Option<BrowserSession>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
user_registration: Option<UserRegistration>,
|
||||
authentication_code: UserEmailAuthenticationCode,
|
||||
}
|
||||
|
||||
@@ -888,9 +891,11 @@ impl EmailVerificationContext {
|
||||
pub fn new(
|
||||
authentication_code: UserEmailAuthenticationCode,
|
||||
browser_session: Option<BrowserSession>,
|
||||
user_registration: Option<UserRegistration>,
|
||||
) -> Self {
|
||||
Self {
|
||||
browser_session,
|
||||
user_registration,
|
||||
authentication_code,
|
||||
}
|
||||
}
|
||||
@@ -926,6 +931,7 @@ impl TemplateContext for EmailVerificationContext {
|
||||
|
||||
Self {
|
||||
browser_session: Some(browser_session),
|
||||
user_registration: None,
|
||||
authentication_code,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -8,6 +8,12 @@ Please see LICENSE in the repository root for full details.
|
||||
|
||||
{%- set _ = translator(lang) -%}
|
||||
|
||||
{{ _("mas.emails.greeting", username=browser_session.user.username | default("user")) }}<br />
|
||||
{%- if browser_session is defined -%}
|
||||
{%- set username = browser_session.user.username -%}
|
||||
{%- elif user_registration is defined -%}
|
||||
{%- set username = user_registration.username -%}
|
||||
{%- endif -%}
|
||||
|
||||
{{ _("mas.emails.greeting", username=(username|default("user"))) }}<br />
|
||||
<br />
|
||||
{{ _("mas.emails.verify.body_html", code=authentication_code.code) }}<br />
|
||||
|
||||
@@ -8,6 +8,12 @@ Please see LICENSE in the repository root for full details.
|
||||
|
||||
{%- set _ = translator(lang) -%}
|
||||
|
||||
{{ _("mas.emails.greeting", username=browser_session.user.username | default("user")) }}
|
||||
{%- if browser_session is defined -%}
|
||||
{%- set username = browser_session.user.username -%}
|
||||
{%- elif user_registration is defined -%}
|
||||
{%- set username = user_registration.username -%}
|
||||
{%- endif -%}
|
||||
|
||||
{{ _("mas.emails.greeting", username=(username|default("user"))) }}
|
||||
|
||||
{{ _("mas.emails.verify.body_text", code=authentication_code.code) }}
|
||||
|
||||
@@ -219,7 +219,7 @@
|
||||
"emails": {
|
||||
"greeting": "Hello %(username)s,",
|
||||
"@greeting": {
|
||||
"context": "emails/verification.html:11:3-85, emails/verification.txt:11:3-85",
|
||||
"context": "emails/verification.html:17:3-64, emails/verification.txt:17:3-64",
|
||||
"description": "Greeting at the top of emails sent to the user"
|
||||
},
|
||||
"recovery": {
|
||||
@@ -251,12 +251,12 @@
|
||||
"verify": {
|
||||
"body_html": "Your verification code to confirm this email address is: <strong>%(code)s</strong>",
|
||||
"@body_html": {
|
||||
"context": "emails/verification.html:13:3-66",
|
||||
"context": "emails/verification.html:19:3-66",
|
||||
"description": "The body of the email sent to verify an email address (HTML)"
|
||||
},
|
||||
"body_text": "Your verification code to confirm this email address is: %(code)s",
|
||||
"@body_text": {
|
||||
"context": "emails/verification.txt:13:3-66",
|
||||
"context": "emails/verification.txt:19:3-66",
|
||||
"description": "The body of the email sent to verify an email address (text)"
|
||||
},
|
||||
"subject": "Your email verification code is: %(code)s",
|
||||
@@ -327,7 +327,7 @@
|
||||
},
|
||||
"continue_with_provider": "Continue with %(provider)s",
|
||||
"@continue_with_provider": {
|
||||
"context": "pages/login.html:75:15-67, pages/register/index.html:49:15-67",
|
||||
"context": "pages/login.html:75:15-67, pages/register/index.html:53:15-67",
|
||||
"description": "Button to log in with an upstream provider"
|
||||
},
|
||||
"description": "Please sign in to continue:",
|
||||
@@ -513,12 +513,12 @@
|
||||
"register": {
|
||||
"call_to_login": "Already have an account?",
|
||||
"@call_to_login": {
|
||||
"context": "pages/register/index.html:55:35-66, pages/register/password.html:77:33-64",
|
||||
"context": "pages/register/index.html:59:35-66, pages/register/password.html:77:33-64",
|
||||
"description": "Displayed on the registration page to suggest to log in instead"
|
||||
},
|
||||
"continue_with_email": "Continue with email address",
|
||||
"@continue_with_email": {
|
||||
"context": "pages/register/index.html:40:30-67"
|
||||
"context": "pages/register/index.html:44:30-67"
|
||||
},
|
||||
"create_account": {
|
||||
"description": "Choose a username to continue.",
|
||||
@@ -682,4 +682,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user