diff --git a/matrix-authentication-service/src/handlers/views/mod.rs b/matrix-authentication-service/src/handlers/views/mod.rs index f772825b5..2de4ca507 100644 --- a/matrix-authentication-service/src/handlers/views/mod.rs +++ b/matrix-authentication-service/src/handlers/views/mod.rs @@ -24,11 +24,12 @@ mod index; mod login; mod logout; mod reauth; +mod register; pub use self::login::LoginRequest; use self::{ index::filter as index, login::filter as login, logout::filter as logout, - reauth::filter as reauth, + reauth::filter as reauth, register::filter as register, }; pub(super) fn filter( @@ -39,6 +40,7 @@ pub(super) fn filter( ) -> impl Filter + Clone + Send + Sync + 'static { index(pool, templates, csrf_config, cookies_config) .or(login(pool, templates, csrf_config, cookies_config)) + .or(register(pool, templates, csrf_config, cookies_config)) .or(logout(pool, cookies_config)) .or(reauth(pool, templates, csrf_config, cookies_config)) .boxed() diff --git a/matrix-authentication-service/src/handlers/views/register.rs b/matrix-authentication-service/src/handlers/views/register.rs new file mode 100644 index 000000000..28f85c383 --- /dev/null +++ b/matrix-authentication-service/src/handlers/views/register.rs @@ -0,0 +1,143 @@ +// Copyright 2021 The Matrix.org Foundation C.I.C. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::convert::TryFrom; + +use argon2::Argon2; +use hyper::http::uri::{Parts, PathAndQuery, Uri}; +use serde::{Deserialize, Serialize}; +use sqlx::{pool::PoolConnection, PgPool, Postgres}; +use warp::{reply::html, wrap_fn, Filter, Rejection, Reply}; + +use crate::{ + config::{CookiesConfig, CsrfConfig}, + errors::WrapError, + filters::{ + csrf::{protected_form, save_csrf_token, updated_csrf_token}, + database::with_connection, + session::{save_session, with_optional_session}, + with_templates, CsrfToken, + }, + storage::{register_user, user::start_session, SessionInfo}, + templates::{TemplateContext, Templates}, +}; + +#[derive(Serialize, Deserialize)] +pub struct RegisterRequest { + next: Option, +} + +impl RegisterRequest { + #[allow(dead_code)] + pub fn new(next: Option) -> Self { + Self { next } + } + + #[allow(dead_code)] + pub fn build_uri(&self) -> anyhow::Result { + let qs = serde_urlencoded::to_string(self)?; + let path_and_query = PathAndQuery::try_from(format!("/register?{}", qs))?; + let uri = Uri::from_parts({ + let mut parts = Parts::default(); + parts.path_and_query = Some(path_and_query); + parts + })?; + Ok(uri) + } + + fn redirect(self) -> Result { + let uri: Uri = Uri::from_parts({ + let mut parts = Parts::default(); + parts.path_and_query = Some( + self.next + .map(warp::http::uri::PathAndQuery::try_from) + .transpose() + .wrap_error()? + .unwrap_or_else(|| PathAndQuery::from_static("/")), + ); + parts + }) + .wrap_error()?; + Ok(warp::redirect::see_other(uri)) + } +} + +#[derive(Deserialize)] +struct RegisterForm { + username: String, + password: String, + password_confirm: String, +} + +pub(super) fn filter( + pool: &PgPool, + templates: &Templates, + csrf_config: &CsrfConfig, + cookies_config: &CookiesConfig, +) -> impl Filter + Clone + Send + Sync + 'static { + let get = warp::get() + .and(with_templates(templates)) + .and(updated_csrf_token(cookies_config, csrf_config)) + .and(warp::query()) + .and(with_optional_session(pool, cookies_config)) + .and_then(get) + .untuple_one() + .with(wrap_fn(save_csrf_token(cookies_config))); + + let post = warp::post() + .and(with_connection(pool)) + .and(protected_form(cookies_config)) + .and(warp::query()) + .and_then(post) + .untuple_one() + .with(wrap_fn(save_session(cookies_config))); + + warp::path!("register").and(get.or(post)) +} + +async fn get( + templates: Templates, + csrf_token: CsrfToken, + query: RegisterRequest, + maybe_session: Option, +) -> Result<(CsrfToken, Box), Rejection> { + if maybe_session.is_some() { + Ok((csrf_token, Box::new(query.redirect()?))) + } else { + let ctx = ().with_csrf(&csrf_token); + let content = templates.render_register(&ctx)?; + Ok((csrf_token, Box::new(html(content)))) + } +} + +async fn post( + mut conn: PoolConnection, + form: RegisterForm, + query: RegisterRequest, +) -> Result<(SessionInfo, impl Reply), Rejection> { + if form.password != form.password_confirm { + return Err(anyhow::anyhow!("password mismatch")) + .wrap_error() + .map_err(warp::reject::custom); + } + + let pfh = Argon2::default(); + let user = register_user(&mut conn, pfh, &form.username, &form.password) + .await + .wrap_error()?; + + let session_info = start_session(&mut conn, user).await.wrap_error()?; + + Ok((session_info, query.redirect()?)) +} diff --git a/matrix-authentication-service/src/storage/mod.rs b/matrix-authentication-service/src/storage/mod.rs index 1d1752242..6170bb18a 100644 --- a/matrix-authentication-service/src/storage/mod.rs +++ b/matrix-authentication-service/src/storage/mod.rs @@ -17,7 +17,7 @@ use sqlx::migrate::Migrator; pub mod oauth2; -mod user; +pub mod user; pub use self::user::{login, lookup_active_session, register_user, SessionInfo, User}; diff --git a/matrix-authentication-service/src/templates.rs b/matrix-authentication-service/src/templates.rs index fa75c456f..b5b7f8a18 100644 --- a/matrix-authentication-service/src/templates.rs +++ b/matrix-authentication-service/src/templates.rs @@ -135,6 +135,9 @@ register_templates! { /// Render the login page pub fn render_login(WithCsrf<()>) { "login.html" } + /// Render the registration page + pub fn render_register(WithCsrf<()>) { "register.html" } + /// Render the home page pub fn render_index(WithCsrf>) { "index.html" } diff --git a/matrix-authentication-service/templates/base.html b/matrix-authentication-service/templates/base.html index e2651a155..bfa8c7805 100644 --- a/matrix-authentication-service/templates/base.html +++ b/matrix-authentication-service/templates/base.html @@ -50,6 +50,12 @@ limitations under the License. Log in + + {% endif %} diff --git a/matrix-authentication-service/templates/login.html b/matrix-authentication-service/templates/login.html index 9d6ffd431..d4458c622 100644 --- a/matrix-authentication-service/templates/login.html +++ b/matrix-authentication-service/templates/login.html @@ -38,7 +38,7 @@ limitations under the License.
- +
diff --git a/matrix-authentication-service/templates/register.html b/matrix-authentication-service/templates/register.html new file mode 100644 index 000000000..c6b92a306 --- /dev/null +++ b/matrix-authentication-service/templates/register.html @@ -0,0 +1,55 @@ +{# +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +#} + +{% extends "base.html" %} + +{% block content %} +
+
+
+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+
+
+
+
+
+{% endblock content %}