diff --git a/crates/handlers/src/views/account/mod.rs b/crates/handlers/src/views/account/mod.rs new file mode 100644 index 00000000..dd546fe6 --- /dev/null +++ b/crates/handlers/src/views/account/mod.rs @@ -0,0 +1,85 @@ +// Copyright 2021-2022 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. + +mod password; + +use mas_config::{CookiesConfig, CsrfConfig}; +use mas_data_model::BrowserSession; +use mas_storage::{ + user::{count_active_sessions, get_user_emails}, + PostgresqlBackend, +}; +use mas_templates::{AccountContext, TemplateContext, Templates}; +use mas_warp_utils::{ + errors::WrapError, + filters::{ + cookies::{encrypted_cookie_saver, EncryptedCookieSaver}, + csrf::updated_csrf_token, + database::connection, + session::session, + with_templates, CsrfToken, + }, +}; +use sqlx::{pool::PoolConnection, PgPool, Postgres}; +use warp::{filters::BoxedFilter, reply::html, Filter, Rejection, Reply}; + +use self::password::filter as password; + +pub(super) fn filter( + pool: &PgPool, + templates: &Templates, + csrf_config: &CsrfConfig, + cookies_config: &CookiesConfig, +) -> BoxedFilter<(Box,)> { + let get = warp::get() + .and(with_templates(templates)) + .and(encrypted_cookie_saver(cookies_config)) + .and(updated_csrf_token(cookies_config, csrf_config)) + .and(session(pool, cookies_config)) + .and(connection(pool)) + .and_then(get); + + let index = warp::path::end().and(get); + let password = password(pool, templates, csrf_config, cookies_config); + + let filter = index.or(password).unify(); + + warp::path::path("account").and(filter).boxed() +} + +async fn get( + templates: Templates, + cookie_saver: EncryptedCookieSaver, + csrf_token: CsrfToken, + session: BrowserSession, + mut conn: PoolConnection, +) -> Result, Rejection> { + let active_sessions = count_active_sessions(&mut conn, &session.user) + .await + .wrap_error()?; + + let emails = get_user_emails(&mut conn, &session.user) + .await + .wrap_error()?; + + let ctx = AccountContext::new(active_sessions, emails) + .with_session(session) + .with_csrf(csrf_token.form_value()); + + let content = templates.render_account_index(&ctx).await?; + let reply = html(content); + let reply = cookie_saver.save_encrypted(&csrf_token, reply)?; + + Ok(Box::new(reply)) +} diff --git a/crates/handlers/src/views/account.rs b/crates/handlers/src/views/account/password.rs similarity index 79% rename from crates/handlers/src/views/account.rs rename to crates/handlers/src/views/account/password.rs index 773d0c99..dd21e52e 100644 --- a/crates/handlers/src/views/account.rs +++ b/crates/handlers/src/views/account/password.rs @@ -1,4 +1,4 @@ -// Copyright 2021 The Matrix.org Foundation C.I.C. +// Copyright 2022 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. @@ -16,10 +16,10 @@ use argon2::Argon2; use mas_config::{CookiesConfig, CsrfConfig}; use mas_data_model::BrowserSession; use mas_storage::{ - user::{authenticate_session, count_active_sessions, get_user_emails, set_password}, + user::{authenticate_session, set_password}, PostgresqlBackend, }; -use mas_templates::{AccountContext, TemplateContext, Templates}; +use mas_templates::{EmptyContext, TemplateContext, Templates}; use mas_warp_utils::{ errors::WrapError, filters::{ @@ -44,7 +44,6 @@ pub(super) fn filter( .and(encrypted_cookie_saver(cookies_config)) .and(updated_csrf_token(cookies_config, csrf_config)) .and(session(pool, cookies_config)) - .and(transaction(pool)) .and_then(get); let post = with_templates(templates) @@ -55,9 +54,11 @@ pub(super) fn filter( .and(protected_form(cookies_config)) .and_then(post); - let filter = warp::get().and(get).or(warp::post().and(post)).unify(); + let get = warp::get().and(get); + let post = warp::post().and(post); + let filter = get.or(post).unify(); - warp::path!("account").and(filter).boxed() + warp::path!("password").and(filter).boxed() } #[derive(Deserialize)] @@ -66,14 +67,14 @@ struct Form { new_password: String, new_password_confirm: String, } + async fn get( templates: Templates, cookie_saver: EncryptedCookieSaver, csrf_token: CsrfToken, session: BrowserSession, - txn: Transaction<'_, Postgres>, ) -> Result, Rejection> { - render(templates, cookie_saver, csrf_token, session, txn).await + render(templates, cookie_saver, csrf_token, session).await } async fn render( @@ -81,23 +82,12 @@ async fn render( cookie_saver: EncryptedCookieSaver, csrf_token: CsrfToken, session: BrowserSession, - mut txn: Transaction<'_, Postgres>, ) -> Result, Rejection> { - let active_sessions = count_active_sessions(&mut txn, &session.user) - .await - .wrap_error()?; - - let emails = get_user_emails(&mut txn, &session.user) - .await - .wrap_error()?; - - txn.commit().await.wrap_error()?; - - let ctx = AccountContext::new(active_sessions, emails) + let ctx = EmptyContext .with_session(session) .with_csrf(csrf_token.form_value()); - let content = templates.render_account(&ctx).await?; + let content = templates.render_account_password(&ctx).await?; let reply = html(content); let reply = cookie_saver.save_encrypted(&csrf_token, reply)?; @@ -126,7 +116,9 @@ async fn post( .await .wrap_error()?; - let reply = render(templates, cookie_saver, csrf_token, session, txn).await?; + let reply = render(templates, cookie_saver, csrf_token, session).await?; + + txn.commit().await.wrap_error()?; Ok(reply) } diff --git a/crates/templates/src/lib.rs b/crates/templates/src/lib.rs index a89b47fe..b4a13706 100644 --- a/crates/templates/src/lib.rs +++ b/crates/templates/src/lib.rs @@ -285,25 +285,28 @@ register_templates! { }; /// Render the login page - pub fn render_login(WithCsrf) { "login.html" } + pub fn render_login(WithCsrf) { "pages/login.html" } /// Render the registration page - pub fn render_register(WithCsrf) { "register.html" } + pub fn render_register(WithCsrf) { "pages/register.html" } /// Render the home page - pub fn render_index(WithCsrf>) { "index.html" } + pub fn render_index(WithCsrf>) { "pages/index.html" } /// Render the account management page - pub fn render_account(WithCsrf>) { "account.html" } + pub fn render_account_index(WithCsrf>) { "pages/account/index.html" } + + /// Render the password change page + pub fn render_account_password(WithCsrf>) { "pages/account/password.html" } /// Render the re-authentication form - pub fn render_reauth(WithCsrf>) { "reauth.html" } + pub fn render_reauth(WithCsrf>) { "pages/reauth.html" } /// Render the form used by the form_post response mode pub fn render_form_post(FormPostContext) { "form_post.html" } /// Render the HTML error page - pub fn render_error(ErrorContext) { "error.html" } + pub fn render_error(ErrorContext) { "pages/error.html" } } impl Templates { @@ -313,7 +316,8 @@ impl Templates { check::render_login(self).await?; check::render_register(self).await?; check::render_index(self).await?; - check::render_account(self).await?; + check::render_account_index(self).await?; + check::render_account_password(self).await?; check::render_reauth(self).await?; check::render_form_post::(self).await?; check::render_error(self).await?; diff --git a/crates/templates/src/res/error.txt b/crates/templates/src/res/error.txt deleted file mode 100644 index c9d47687..00000000 --- a/crates/templates/src/res/error.txt +++ /dev/null @@ -1,27 +0,0 @@ -{# -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. -#} - -{% if code %} -{{- code }} -{% endif %} - -{%- if description %} -{{ description }} -{% endif %} - -{%- if details %} -{{ details }} -{% endif %} diff --git a/crates/templates/src/res/account.html b/crates/templates/src/res/pages/account/index.html similarity index 77% rename from crates/templates/src/res/account.html rename to crates/templates/src/res/pages/account/index.html index 76440a48..fe5ce129 100644 --- a/crates/templates/src/res/account.html +++ b/crates/templates/src/res/pages/account/index.html @@ -1,5 +1,5 @@ {# -Copyright 2021 The Matrix.org Foundation C.I.C. +Copyright 2021-2022 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. @@ -30,15 +30,8 @@ limitations under the License.
Primary email
{{ current_session.user.primary_email.email }}
{% endif %} + {{ button::link_ghost(text="Change password", href="/account/password", class="col-span-2 place-self-end") }} -
-

Change my password

- - {{ field::input(label="Current password", name="current_password", type="password", class="xl:col-span-2") }} - {{ field::input(label="New password", name="new_password", type="password") }} - {{ field::input(label="Confirm password", name="new_password_confirm", type="password") }} - {{ button::button(text="Change password", type="password", class="xl:col-span-2 place-self-end") }} -

Current session

Started at
diff --git a/crates/templates/src/res/pages/account/password.html b/crates/templates/src/res/pages/account/password.html new file mode 100644 index 00000000..ce44e928 --- /dev/null +++ b/crates/templates/src/res/pages/account/password.html @@ -0,0 +1,31 @@ +{# +Copyright 2022 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 %} +
+
+

Change my password

+ + {{ field::input(label="Current password", name="current_password", type="password", class="xl:col-span-2") }} + {{ field::input(label="New password", name="new_password", type="password") }} + {{ field::input(label="Confirm password", name="new_password_confirm", type="password") }} + {{ button::button(text="Change password", type="password", class="xl:col-span-2 place-self-end") }} +
+
+{% endblock content %} + diff --git a/crates/templates/src/res/error.html b/crates/templates/src/res/pages/error.html similarity index 100% rename from crates/templates/src/res/error.html rename to crates/templates/src/res/pages/error.html diff --git a/crates/templates/src/res/index.html b/crates/templates/src/res/pages/index.html similarity index 100% rename from crates/templates/src/res/index.html rename to crates/templates/src/res/pages/index.html diff --git a/crates/templates/src/res/login.html b/crates/templates/src/res/pages/login.html similarity index 100% rename from crates/templates/src/res/login.html rename to crates/templates/src/res/pages/login.html diff --git a/crates/templates/src/res/reauth.html b/crates/templates/src/res/pages/reauth.html similarity index 100% rename from crates/templates/src/res/reauth.html rename to crates/templates/src/res/pages/reauth.html diff --git a/crates/templates/src/res/register.html b/crates/templates/src/res/pages/register.html similarity index 100% rename from crates/templates/src/res/register.html rename to crates/templates/src/res/pages/register.html