146 lines
3.9 KiB
Rust
146 lines
3.9 KiB
Rust
use crate::{to_account_page, Authenticated, Error, State};
|
|
use actix_web::{error::BlockingError, web, HttpResponse};
|
|
|
|
pub type UpdatePasswordPageArgs = (Authenticated, web::Data<State>);
|
|
|
|
pub type UpdatePasswordArgs = (
|
|
Authenticated,
|
|
web::Data<State>,
|
|
web::Form<UpdatePasswordForm>,
|
|
);
|
|
|
|
#[derive(Debug, serde::Deserialize)]
|
|
pub struct UpdatePasswordForm {
|
|
new_password: String,
|
|
new_password_confirmation: String,
|
|
password: String,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct UpdatePasswordState {
|
|
state: State,
|
|
form: Option<UpdatePasswordForm>,
|
|
error: Option<UpdatePasswordError>,
|
|
}
|
|
|
|
pub fn update_password_page((_, state): UpdatePasswordPageArgs) -> UpdatePasswordState {
|
|
UpdatePasswordState::empty(&state)
|
|
}
|
|
|
|
pub async fn update_password(
|
|
(authenticated, state, form): UpdatePasswordArgs,
|
|
) -> Result<Result<UpdatePasswordState, HttpResponse>, Error> {
|
|
let form = form.into_inner();
|
|
|
|
match try_update_password(authenticated, &form, &state).await? {
|
|
Ok(res) => Ok(Err(res)),
|
|
Err(e) => Ok(Ok(UpdatePasswordState::new_from_request(&state, form, e))),
|
|
}
|
|
}
|
|
|
|
impl UpdatePasswordState {
|
|
fn empty(state: &State) -> Self {
|
|
UpdatePasswordState {
|
|
state: state.clone(),
|
|
form: None,
|
|
error: None,
|
|
}
|
|
}
|
|
|
|
fn new_from_request(
|
|
state: &State,
|
|
form: UpdatePasswordForm,
|
|
error: UpdatePasswordError,
|
|
) -> Self {
|
|
UpdatePasswordState {
|
|
state: state.clone(),
|
|
form: Some(form),
|
|
error: Some(error),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn update_password_path(&self) -> String {
|
|
self.state.pages.update_password_path()
|
|
}
|
|
|
|
pub(crate) fn new_password(&self) -> Option<String> {
|
|
self.form.as_ref().map(|form| form.new_password.clone())
|
|
}
|
|
|
|
pub(crate) fn new_password_confirmation(&self) -> Option<String> {
|
|
self.form
|
|
.as_ref()
|
|
.map(|form| form.new_password_confirmation.clone())
|
|
}
|
|
|
|
pub(crate) fn new_password_confirmation_error(&self) -> Option<String> {
|
|
self.error
|
|
.as_ref()
|
|
.and_then(|e| e.new_password_confirmation())
|
|
}
|
|
|
|
pub(crate) fn password(&self) -> Option<String> {
|
|
self.form.as_ref().map(|form| form.password.clone())
|
|
}
|
|
|
|
pub(crate) fn password_error(&self) -> Option<String> {
|
|
self.error.as_ref().and_then(|e| e.password())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
enum UpdatePasswordError {
|
|
Match,
|
|
Invalid,
|
|
}
|
|
|
|
impl UpdatePasswordError {
|
|
fn new_password_confirmation(&self) -> Option<String> {
|
|
match self {
|
|
UpdatePasswordError::Match => {
|
|
Some("New password confirmation does not match".to_owned())
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
fn password(&self) -> Option<String> {
|
|
match self {
|
|
UpdatePasswordError::Invalid => Some("Incorrect password supplied".to_owned()),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn try_update_password(
|
|
authenticated: Authenticated,
|
|
form: &UpdatePasswordForm,
|
|
state: &State,
|
|
) -> Result<Result<HttpResponse, UpdatePasswordError>, Error> {
|
|
if form.new_password != form.new_password_confirmation {
|
|
return Ok(Err(UpdatePasswordError::Match));
|
|
}
|
|
|
|
let res = state
|
|
.user_store
|
|
.exec(crate::store::UpdatePassword {
|
|
user: authenticated.user(),
|
|
new_password: form.new_password.clone(),
|
|
password: form.password.clone(),
|
|
})
|
|
.await;
|
|
|
|
match res {
|
|
Ok(_) => (),
|
|
Err(BlockingError::Error(crate::store::StoreError::AuthenticationFailed)) => {
|
|
return Ok(Err(UpdatePasswordError::Invalid))
|
|
}
|
|
Err(BlockingError::Error(crate::store::StoreError::DoubleChange)) => {
|
|
return Ok(Err(UpdatePasswordError::Invalid))
|
|
}
|
|
Err(e) => return Err(e.into()),
|
|
};
|
|
|
|
Ok(Ok(to_account_page(&state)))
|
|
}
|