From 62104b417a0da702291a95c7cbe20447317557bf Mon Sep 17 00:00:00 2001 From: Valentin Tolmer Date: Mon, 13 Feb 2023 20:09:24 +0100 Subject: [PATCH] app: probe for password reset support --- app/src/components/app.rs | 66 +++++++++++++++++++++++++++++++------ app/src/components/login.rs | 20 +++++++---- app/src/infra/api.rs | 21 ++++++++++-- 3 files changed, 88 insertions(+), 19 deletions(-) diff --git a/app/src/components/app.rs b/app/src/components/app.rs index 7f35c43..a41346f 100644 --- a/app/src/components/app.rs +++ b/app/src/components/app.rs @@ -13,10 +13,13 @@ use crate::{ user_details::UserDetails, user_table::UserTable, }, - infra::cookies::get_cookie, + infra::{api::HostService, cookies::get_cookie}, +}; + +use yew::{ + prelude::*, + services::{fetch::FetchTask, ConsoleService}, }; -use yew::prelude::*; -use yew::services::ConsoleService; use yew_router::{ agent::{RouteAgentDispatcher, RouteRequest}, route::Route, @@ -29,11 +32,14 @@ pub struct App { user_info: Option<(String, bool)>, redirect_to: Option, route_dispatcher: RouteAgentDispatcher, + password_reset_enabled: bool, + task: Option, } pub enum Msg { Login((String, bool)), Logout, + PasswordResetProbeFinished(anyhow::Result), } impl Component for App { @@ -58,7 +64,15 @@ impl Component for App { }), redirect_to: Self::get_redirect_route(), route_dispatcher: RouteAgentDispatcher::new(), + password_reset_enabled: false, + task: None, }; + app.task = Some( + HostService::probe_password_reset( + app.link.callback_once(Msg::PasswordResetProbeFinished), + ) + .unwrap(), + ); app.apply_initial_redirections(); app } @@ -82,6 +96,16 @@ impl Component for App { self.user_info = None; self.redirect_to = None; } + Msg::PasswordResetProbeFinished(Ok(enabled)) => { + self.task = None; + self.password_reset_enabled = enabled; + } + Msg::PasswordResetProbeFinished(Err(err)) => { + self.task = None; + ConsoleService::error(&format!( + "Could not probe for password reset support: {err:#}" + )); + } } if self.user_info.is_none() { self.route_dispatcher @@ -97,6 +121,7 @@ impl Component for App { fn view(&self) -> Html { let link = self.link.clone(); let is_admin = self.is_admin(); + let password_reset_enabled = self.password_reset_enabled; html! {
{self.view_banner()} @@ -104,7 +129,7 @@ impl Component for App {
- render = Router::render(move |s| Self::dispatch_route(s, &link, is_admin)) + render = Router::render(move |s| Self::dispatch_route(s, &link, is_admin, password_reset_enabled)) />
@@ -135,6 +160,10 @@ impl App { let route_service = RouteService::<()>::new(); let current_route = route_service.get_path(); if current_route.contains("reset-password") { + if !self.password_reset_enabled { + self.route_dispatcher + .send(RouteRequest::ReplaceRoute(Route::from(AppRoute::Login))); + } return; } match &self.user_info { @@ -162,10 +191,15 @@ impl App { } } - fn dispatch_route(switch: AppRoute, link: &ComponentLink, is_admin: bool) -> Html { + fn dispatch_route( + switch: AppRoute, + link: &ComponentLink, + is_admin: bool, + password_reset_enabled: bool, + ) -> Html { match switch { AppRoute::Login => html! { - + }, AppRoute::CreateUser => html! { @@ -200,11 +234,23 @@ impl App { AppRoute::ChangePassword(username) => html! { }, - AppRoute::StartResetPassword => html! { - - }, + AppRoute::StartResetPassword => { + if password_reset_enabled { + html! { + + } + } else { + App::dispatch_route(AppRoute::Login, link, is_admin, password_reset_enabled) + } + } AppRoute::FinishResetPassword(token) => html! { - + if password_reset_enabled { + html! { + + } + } else { + App::dispatch_route(AppRoute::Login, link, is_admin, password_reset_enabled) + } }, } } diff --git a/app/src/components/login.rs b/app/src/components/login.rs index 0503e95..06adc6e 100644 --- a/app/src/components/login.rs +++ b/app/src/components/login.rs @@ -30,6 +30,7 @@ pub struct FormModel { #[derive(Clone, PartialEq, Properties)] pub struct Props { pub on_logged_in: Callback<(String, bool)>, + pub password_reset_enabled: bool, } pub enum Msg { @@ -147,6 +148,7 @@ impl Component for LoginForm { fn view(&self) -> Html { type Field = yew_form::Field; + let password_reset_enabled = self.common.password_reset_enabled; if self.refreshing { html! {
@@ -198,12 +200,18 @@ impl Component for LoginForm { {"Login"} - - {"Forgot your password?"} - + { if password_reset_enabled { + html! { + + {"Forgot your password?"} + + } + } else { + html!{} + }}
{ if let Some(e) = &self.common.error { diff --git a/app/src/infra/api.rs b/app/src/infra/api.rs index a210b9a..49e1b0e 100644 --- a/app/src/infra/api.rs +++ b/app/src/infra/api.rs @@ -3,9 +3,11 @@ use anyhow::{anyhow, Context, Result}; use graphql_client::GraphQLQuery; use lldap_auth::{login, registration, JWTClaims}; -use yew::callback::Callback; -use yew::format::Json; -use yew::services::fetch::{Credentials, FetchOptions, FetchService, FetchTask, Request, Response}; +use yew::{ + callback::Callback, + format::Json, + services::fetch::{Credentials, FetchOptions, FetchService, FetchTask, Request, Response}, +}; #[derive(Default)] pub struct HostService {} @@ -286,4 +288,17 @@ impl HostService { "Could not validate token", ) } + + pub fn probe_password_reset(callback: Callback>) -> Result { + let request = Request::get("/auth/reset/step1/lldap_unlikely_very_long_user_name") + .header("Content-Type", "application/json") + .body(yew::format::Nothing)?; + FetchService::fetch_with_options( + request, + get_default_options(), + create_handler(callback, move |status: http::StatusCode, _data: String| { + Ok(status != http::StatusCode::NOT_FOUND) + }), + ) + } }