use crate::{ components::router::AppRoute, infra::{ api::HostService, common_component::{CommonComponent, CommonComponentParts}, }, }; use anyhow::{bail, Context, Result}; use lldap_auth::{ opaque::client::registration as opaque_registration, password_reset::ServerPasswordResetResponse, registration, }; use validator_derive::Validate; use yew::prelude::*; use yew_form::Form; use yew_form_derive::Model; use yew_router::{ agent::{RouteAgentDispatcher, RouteRequest}, route::Route, }; /// The fields of the form, with the constraints. #[derive(Model, Validate, PartialEq, Clone, Default)] pub struct FormModel { #[validate(length(min = 8, message = "Invalid password. Min length: 8"))] password: String, #[validate(must_match(other = "password", message = "Passwords must match"))] confirm_password: String, } pub struct ResetPasswordStep2Form { common: CommonComponentParts, form: Form, username: Option, opaque_data: Option, route_dispatcher: RouteAgentDispatcher, } #[derive(Clone, PartialEq, Properties)] pub struct Props { pub token: String, } pub enum Msg { ValidateTokenResponse(Result), FormUpdate, Submit, RegistrationStartResponse(Result>), RegistrationFinishResponse(Result<()>), } impl CommonComponent for ResetPasswordStep2Form { fn handle_msg(&mut self, msg: ::Message) -> Result { match msg { Msg::ValidateTokenResponse(response) => { self.username = Some(response?.user_id); self.common.cancel_task(); Ok(true) } Msg::FormUpdate => Ok(true), Msg::Submit => { if !self.form.validate() { bail!("Check the form for errors"); } let mut rng = rand::rngs::OsRng; let new_password = self.form.model().password; let registration_start_request = opaque_registration::start_registration(&new_password, &mut rng) .context("Could not initiate password change")?; let req = registration::ClientRegistrationStartRequest { username: self.username.clone().unwrap(), registration_start_request: registration_start_request.message, }; self.opaque_data = Some(registration_start_request.state); self.common.call_backend( HostService::register_start, req, Msg::RegistrationStartResponse, )?; Ok(true) } Msg::RegistrationStartResponse(res) => { let res = res.context("Could not initiate password change")?; let registration = self.opaque_data.take().expect("Missing registration data"); let mut rng = rand::rngs::OsRng; let registration_finish = opaque_registration::finish_registration( registration, res.registration_response, &mut rng, ) .context("Error during password change")?; let req = registration::ClientRegistrationFinishRequest { server_data: res.server_data, registration_upload: registration_finish.message, }; self.common.call_backend( HostService::register_finish, req, Msg::RegistrationFinishResponse, )?; Ok(false) } Msg::RegistrationFinishResponse(response) => { self.common.cancel_task(); if response.is_ok() { self.route_dispatcher .send(RouteRequest::ChangeRoute(Route::from(AppRoute::Login))); } response?; Ok(true) } } } fn mut_common(&mut self) -> &mut CommonComponentParts { &mut self.common } } impl Component for ResetPasswordStep2Form { type Message = Msg; type Properties = Props; fn create(props: Self::Properties, link: ComponentLink) -> Self { let mut component = ResetPasswordStep2Form { common: CommonComponentParts::::create(props, link), form: yew_form::Form::::new(FormModel::default()), opaque_data: None, route_dispatcher: RouteAgentDispatcher::new(), username: None, }; let token = component.common.token.clone(); component .common .call_backend( HostService::reset_password_step2, &token, Msg::ValidateTokenResponse, ) .unwrap(); component } fn update(&mut self, msg: Self::Message) -> ShouldRender { CommonComponentParts::::update(self, msg) } fn change(&mut self, props: Self::Properties) -> ShouldRender { self.common.change(props) } fn view(&self) -> Html { match (&self.username, &self.common.error) { (None, None) => { return html! { {"Validating token"} } } (None, Some(e)) => { return html! {
{e.to_string() }
} } _ => (), }; type Field = yew_form::Field; html! { <>

{"Reset your password"}

{&self.form.field_message("password")}
{&self.form.field_message("confirm_password")}
{ if let Some(e) = &self.common.error { html! {
{e.to_string() }
} } else { html! {} } } } } }