diff --git a/geneit_app/src/api/UserApi.ts b/geneit_app/src/api/UserApi.ts index 3e49be0..204f3e5 100644 --- a/geneit_app/src/api/UserApi.ts +++ b/geneit_app/src/api/UserApi.ts @@ -11,6 +11,14 @@ export interface User { has_password: boolean; } +export enum ReplacePasswordResponse { + Error, + Success, + InvalidOldPassword, + InvalidNewPassword, + TooManyRequests, +} + export class UserApi { /** * Get current user information @@ -36,4 +44,39 @@ export class UserApi { }, }); } + + /** + * Replace user password + */ + static async ReplacePassword( + oldPwd: string, + newPwd: string + ): Promise { + const res = await APIClient.exec({ + uri: "/user/replace_password", + method: "POST", + jsonData: { + old_password: oldPwd, + new_password: newPwd, + }, + allowFail: true, + }); + + if (res.status >= 200 && res.status < 300) + return ReplacePasswordResponse.Success; + + switch (res.status) { + case 400: + return ReplacePasswordResponse.InvalidNewPassword; + + case 401: + return ReplacePasswordResponse.InvalidOldPassword; + + case 429: + return ReplacePasswordResponse.TooManyRequests; + + default: + return ReplacePasswordResponse.Error; + } + } } diff --git a/geneit_app/src/routes/ProfileRoute.tsx b/geneit_app/src/routes/ProfileRoute.tsx index 06724e7..83ab707 100644 --- a/geneit_app/src/routes/ProfileRoute.tsx +++ b/geneit_app/src/routes/ProfileRoute.tsx @@ -1,6 +1,6 @@ import React, { useRef } from "react"; import { AsyncWidget } from "../widgets/AsyncWidget"; -import { User, UserApi } from "../api/UserApi"; +import { ReplacePasswordResponse, User, UserApi } from "../api/UserApi"; import { Alert, Box, @@ -9,43 +9,26 @@ import { CardActions, CardContent, Checkbox, + CircularProgress, FormControlLabel, TextField, Typography, } from "@mui/material"; import { TimeWidget, formatDate } from "../widgets/TimeWidget"; import { ServerApi } from "../api/ServerApi"; +import { PasswordInput } from "../widgets/PasswordInput"; +import { normalize } from "path/win32"; export function ProfileRoute(): React.ReactElement { const [user, setUser] = React.useState(null); - const [newName, setNewName] = React.useState(""); - - const [error, setError] = React.useState(null); - const [success, setSuccess] = React.useState(null); const load = async () => { const u = await UserApi.GetUserInfo(); setUser(u); - setNewName(u.name); }; const counter = useRef(0); - const updateProfile = async () => { - try { - setSuccess(null); - setError(null); - - await UserApi.UpdateProfile(newName); - - counter.current += 1; - setSuccess("Informations du profil mises à jour avec succès !"); - } catch (e) { - console.error(e); - setError("Echec de la mise à jour du profil !"); - } - }; - return ( Profil - {error && {error}} - {success && {success}} - - - - - Paramètres du compte - - - - - - - - - - - - setNewName(e.target.value)} - inputProps={{ - maxLength: ServerApi.Config.constraints.user_name_len.max, - }} - /> - - } - label="Compte administrateur" - /> - - - - - - + (counter.current += 1)} + /> + {user?.has_password && } )} /> ); } + +function ProfileSettingsCard(p: { user: User; onUpdate: () => {} }) { + const [newName, setNewName] = React.useState(p.user.name); + + const [error, setError] = React.useState(null); + const [success, setSuccess] = React.useState(null); + + const updateProfile = async () => { + try { + setSuccess(null); + setError(null); + + await UserApi.UpdateProfile(newName); + + p.onUpdate(); + setSuccess("Informations du profil enregistrées avec succès !"); + } catch (e) { + console.error(e); + setError("Echec de la mise à jour du profil !"); + } + }; + + return ( + <> + + {error && {error}} + {success && {success}} + + + + Paramètres du compte + + + + + + + + + + + + setNewName(e.target.value)} + inputProps={{ + maxLength: ServerApi.Config.constraints.user_name_len.max, + }} + /> + + } + label="Compte administrateur" + /> + + + + + + + + ); +} + +function ChangePasswordCard(): React.ReactElement { + const [loading, setLoading] = React.useState(false); + const [error, setError] = React.useState(null); + const [success, setSuccess] = React.useState(null); + + const [oldPassword, setOldpassword] = React.useState(""); + const [newPassword, setNewpassword] = React.useState(""); + const [confirmNewPassword, setConfirmNewpassword] = React.useState(""); + + const isValid = + ServerApi.CheckPassword(newPassword) === null && + oldPassword.length > 0 && + confirmNewPassword === newPassword; + + const updatePassword = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!isValid || loading) return; + + setLoading(true); + setSuccess(null); + setError(null); + + try { + const result = await UserApi.ReplacePassword(oldPassword, newPassword); + + switch (result) { + case ReplacePasswordResponse.Error: + setError("Echec du changement de mot de passe !"); + break; + case ReplacePasswordResponse.Success: + setSuccess("Mot de passe changé avec succès !"); + break; + case ReplacePasswordResponse.InvalidOldPassword: + setError("Ancien mot de passe saisi invalide !"); + break; + case ReplacePasswordResponse.InvalidNewPassword: + setError("Nouveau mot de passe saisi invalide !"); + break; + case ReplacePasswordResponse.TooManyRequests: + setError( + "Trop de tentatives de changement de mot de passe, veuillez réessayer ultérieurement !" + ); + break; + } + } catch (e) { + console.error(e); + setError("Echec de la mise à jour du mot de passe !"); + } + + setLoading(false); + }; + + return ( + <> + + {error && {error}} + {success && {success}} + + + + Changement du mot de passe + + + setOldpassword(e.target.value)} + /> + + setNewpassword(n)} + label={"Nouveau mot de passe"} + /> + + setConfirmNewpassword(e.target.value)} + /> + + + + {" "} + + + + ); +} diff --git a/geneit_backend/src/controllers/user_controller.rs b/geneit_backend/src/controllers/user_controller.rs index c4b089e..4c84a2b 100644 --- a/geneit_backend/src/controllers/user_controller.rs +++ b/geneit_backend/src/controllers/user_controller.rs @@ -80,7 +80,7 @@ pub async fn replace_password( .password_len .validate(&q.new_password) { - return Ok(HttpResponse::BadRequest().json("Nouveau mot de passe invalide!")); + return Ok(HttpResponse::BadRequest().json("Invalid new password!")); } let mut user = users_service::get_by_id(token.user_id).await?; @@ -90,7 +90,7 @@ pub async fn replace_password( RatedAction::RequestReplacePasswordSignedIn, ) .await?; - return Ok(HttpResponse::BadRequest().json("Ancien mot de passe invalide !")); + return Ok(HttpResponse::Unauthorized().json("Invalid old password!")); } users_service::change_password(&mut user, &q.new_password).await?;