[WEB-3673] fix: password change form (#6839)

* fix: change password

* fix: added store action for change password

* fix: type

* fix: store refactor
This commit is contained in:
Akshita Goyal 2025-03-28 13:35:42 +05:30 committed by GitHub
parent 99dba80d19
commit e8779511ad
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 101 additions and 41 deletions

View file

@ -44,10 +44,21 @@ class ChangePasswordEndpoint(APIView):
def post(self, request):
user = User.objects.get(pk=request.user.id)
old_password = request.data.get("old_password", False)
# If the user password is not autoset then we need to check the old passwords
if not user.is_password_autoset:
old_password = request.data.get("old_password", False)
if not old_password:
exc = AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["MISSING_PASSWORD"],
error_message="MISSING_PASSWORD",
payload={"error": "Old password is missing"},
)
return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST)
# Get the new password
new_password = request.data.get("new_password", False)
if not old_password or not new_password:
if not new_password:
exc = AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["MISSING_PASSWORD"],
error_message="MISSING_PASSWORD",
@ -55,7 +66,9 @@ class ChangePasswordEndpoint(APIView):
)
return Response(exc.get_error_dict(), status=status.HTTP_400_BAD_REQUEST)
if not user.check_password(old_password):
# If the user password is not autoset then we need to check the old passwords
if not user.is_password_autoset and not user.check_password(old_password):
exc = AuthenticationException(
error_code=AUTHENTICATION_ERROR_CODES["INCORRECT_OLD_PASSWORD"],
error_message="INCORRECT_OLD_PASSWORD",

View file

@ -499,6 +499,7 @@
"re_generate_key": "Znovu generovat klíč",
"export": "Exportovat",
"member": "{count, plural, one{# člen} few{# členové} other{# členů}}",
"new_password_must_be_different_from_old_password": "Nové heslo musí být odlišné od starého hesla",
"project_view": {
"sort_by": {

View file

@ -499,6 +499,8 @@
"re_generate_key": "Schlüssel neu generieren",
"export": "Exportieren",
"member": "{count, plural, one{# Mitglied} few{# Mitglieder} other{# Mitglieder}}",
"new_password_must_be_different_from_old_password": "Das neue Passwort muss von dem alten Passwort abweichen",
"project_view": {
"sort_by": {
"created_at": "Erstellt am",

View file

@ -331,6 +331,7 @@
"re_generate_key": "Re-generate key",
"export": "Export",
"member": "{count, plural, one{# member} other{# members}}",
"new_password_must_be_different_from_old_password": "New password must be different from old password",
"edited": "edited",
"bot": "Bot",

View file

@ -503,6 +503,7 @@
"re_generate_key": "Regenerar clave",
"export": "Exportar",
"member": "{count, plural, one{# miembro} other{# miembros}}",
"new_password_must_be_different_from_old_password": "La nueva contraseña debe ser diferente a la contraseña anterior",
"edited": "Modificado",
"bot": "Bot",

View file

@ -501,6 +501,7 @@
"re_generate_key": "Régénérer la clé",
"export": "Exporter",
"member": "{count, plural, one{# membre} other{# membres}}",
"new_password_must_be_different_from_old_password": "Le nouveau mot de passe doit être différent du mot de passe précédent",
"edited": "Modifié",
"bot": "Bot",

View file

@ -501,6 +501,7 @@
"re_generate_key": "Hasilkan kembali kunci",
"export": "Ekspor",
"member": "{count, plural, one{# anggota} other{# anggota}}",
"new_password_must_be_different_from_old_password": "Kata sandi baru harus berbeda dari kata sandi lama",
"project_view": {
"sort_by": {

View file

@ -500,6 +500,8 @@
"re_generate_key": "Rigenera chiave",
"export": "Esporta",
"member": "{count, plural, one {# membro} other {# membri}}",
"new_password_must_be_different_from_old_password": "La nuova password deve essere diversa dalla password precedente",
"edited": "Modificato",
"bot": "Bot",

View file

@ -501,6 +501,7 @@
"re_generate_key": "キーを再生成",
"export": "エクスポート",
"member": "{count, plural, other{# メンバー}}",
"new_password_must_be_different_from_old_password": "新しいパスワードは古いパスワードと異なる必要があります",
"edited": "編集済み",
"bot": "ボット",

View file

@ -501,6 +501,7 @@
"re_generate_key": "키 다시 생성",
"export": "내보내기",
"member": "{count, plural, one{# 멤버} other{# 멤버}}",
"new_password_must_be_different_from_old_password": "새 비밀번호는 이전 비밀번호와 다르게 설정해야 합니다",
"edited": "수정됨",
"bot": "봇",

View file

@ -499,6 +499,8 @@
"re_generate_key": "Wygeneruj klucz ponownie",
"export": "Eksportuj",
"member": "{count, plural, one{# członek} few{# członkowie} other{# członków}}",
"new_password_must_be_different_from_old_password": "Nowe hasło musi być innym niż stare hasło",
"edited": "Edytowano",
"bot": "Bot",

View file

@ -501,6 +501,7 @@
"re_generate_key": "Regenerează cheia",
"export": "Exportă",
"member": "{count, plural, one{# membru} other{# membri}}",
"new_password_must_be_different_from_old_password": "Parola nouă trebuie să fie diferită de parola veche",
"project_view": {
"sort_by": {

View file

@ -499,6 +499,7 @@
"re_generate_key": "Перегенерировать ключ",
"export": "Экспорт",
"member": "{count, plural, one{# участник} few{# участника} other{# участников}}",
"new_password_must_be_different_from_old_password": "Новое пароль должен отличаться от старого пароля",
"edited": "Редактировано",
"bot": "Бот",

View file

@ -499,6 +499,7 @@
"re_generate_key": "Znova generovať kľúč",
"export": "Exportovať",
"member": "{count, plural, one{# člen} few{# členovia} other{# členov}}",
"new_password_must_be_different_from_old_password": "Nové heslo musí byť odlišné od starého hesla",
"edited": "Upravené",
"bot": "Bot",

View file

@ -499,6 +499,7 @@
"re_generate_key": "Повторно згенерувати ключ",
"export": "Експортувати",
"member": "{count, plural, one{# учасник} few{# учасники} other{# учасників}}",
"new_password_must_be_different_from_old_password": "Новий пароль повинен бути відмінним від старого пароля",
"edited": "Редагувано",
"bot": "Бот",

View file

@ -501,6 +501,7 @@
"re_generate_key": "重新生成密钥",
"export": "导出",
"member": "{count, plural, other{# 成员}}",
"new_password_must_be_different_from_old_password": "新密码必须不同于旧密码",
"edited": "已编辑",
"bot": "机器人",

View file

@ -501,6 +501,7 @@
"re_generate_key": "重新產生金鑰",
"export": "匯出",
"member": "{count, plural, one{# 位成員} other{# 位成員}}",
"new_password_must_be_different_from_old_password": "新密碼必須與舊密碼不同",
"edited": "已編輯",
"bot": "機器人",

View file

@ -14,9 +14,10 @@ import { ProfileSettingContentHeader, ProfileSettingContentWrapper } from "@/com
// helpers
import { authErrorHandler } from "@/helpers/authentication.helper";
import { E_PASSWORD_STRENGTH, getPasswordStrength } from "@/helpers/password.helper";
// hooks
import { useUser } from "@/hooks/store";
// services
import { AuthService } from "@/services/auth.service";
import { UserService } from "@/services/user.service";
export interface FormValues {
old_password: string;
@ -30,7 +31,6 @@ const defaultValues: FormValues = {
confirm_password: "",
};
const userService = new UserService();
const authService = new AuthService();
const defaultShowPassword = {
@ -40,10 +40,13 @@ const defaultShowPassword = {
};
const SecurityPage = observer(() => {
// store
const { data: currentUser, changePassword } = useUser();
// states
const [showPassword, setShowPassword] = useState(defaultShowPassword);
const [isPasswordInputFocused, setIsPasswordInputFocused] = useState(false);
const [isRetryPasswordInputFocused, setIsRetryPasswordInputFocused] = useState(false);
// use form
const {
control,
@ -56,6 +59,7 @@ const SecurityPage = observer(() => {
const oldPassword = watch("old_password");
const password = watch("new_password");
const confirmPassword = watch("confirm_password");
const oldPasswordRequired = !currentUser?.is_password_autoset;
// i18n
const { t } = useTranslation();
@ -70,8 +74,8 @@ const SecurityPage = observer(() => {
const csrfToken = await authService.requestCSRFToken().then((data) => data?.csrf_token);
if (!csrfToken) throw new Error("csrf token not found");
await userService.changePassword(csrfToken, {
old_password,
await changePassword(csrfToken, {
...(oldPasswordRequired && { old_password }),
new_password,
});
@ -95,7 +99,7 @@ const SecurityPage = observer(() => {
const isButtonDisabled =
getPasswordStrength(password) != E_PASSWORD_STRENGTH.STRENGTH_VALID ||
oldPassword.trim() === "" ||
(oldPasswordRequired && oldPassword.trim() === "") ||
password.trim() === "" ||
confirmPassword.trim() === "" ||
password !== confirmPassword ||
@ -115,41 +119,43 @@ const SecurityPage = observer(() => {
<ProfileSettingContentHeader title={t("auth.common.password.change_password.label.default")} />
<form onSubmit={handleSubmit(handleChangePassword)} className="flex flex-col gap-8 py-6">
<div className="flex flex-col gap-10 w-full max-w-96">
<div className="space-y-1">
<h4 className="text-sm">{t("auth.common.password.current_password.label")}</h4>
<div className="relative flex items-center rounded-md">
<Controller
control={control}
name="old_password"
rules={{
required: t("common.errors.required"),
}}
render={({ field: { value, onChange } }) => (
<Input
id="old_password"
type={showPassword?.oldPassword ? "text" : "password"}
value={value}
onChange={onChange}
placeholder={t("old_password")}
className="w-full"
hasError={Boolean(errors.old_password)}
{oldPasswordRequired && (
<div className="space-y-1">
<h4 className="text-sm">{t("auth.common.password.current_password.label")}</h4>
<div className="relative flex items-center rounded-md">
<Controller
control={control}
name="old_password"
rules={{
required: t("common.errors.required"),
}}
render={({ field: { value, onChange } }) => (
<Input
id="old_password"
type={showPassword?.oldPassword ? "text" : "password"}
value={value}
onChange={onChange}
placeholder={t("old_password")}
className="w-full"
hasError={Boolean(errors.old_password)}
/>
)}
/>
{showPassword?.oldPassword ? (
<EyeOff
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
onClick={() => handleShowPassword("oldPassword")}
/>
) : (
<Eye
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
onClick={() => handleShowPassword("oldPassword")}
/>
)}
/>
{showPassword?.oldPassword ? (
<EyeOff
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
onClick={() => handleShowPassword("oldPassword")}
/>
) : (
<Eye
className="absolute right-3 h-5 w-5 stroke-custom-text-400 hover:cursor-pointer"
onClick={() => handleShowPassword("oldPassword")}
/>
)}
</div>
{errors.old_password && <span className="text-xs text-red-500">{errors.old_password.message}</span>}
</div>
{errors.old_password && <span className="text-xs text-red-500">{errors.old_password.message}</span>}
</div>
)}
<div className="space-y-1">
<h4 className="text-sm">{t("auth.common.password.new_password.label")}</h4>
<div className="relative flex items-center rounded-md">

View file

@ -145,7 +145,7 @@ export class UserService extends APIService {
});
}
async changePassword(token: string, data: { old_password: string; new_password: string }): Promise<any> {
async changePassword(token: string, data: { old_password?: string; new_password: string }): Promise<any> {
return this.post(`/auth/change-password/`, data, {
headers: {
"X-CSRFTOKEN": token,

View file

@ -41,6 +41,10 @@ export interface IUserStore {
updateCurrentUser: (data: Partial<IUser>) => Promise<IUser | undefined>;
handleSetPassword: (csrfToken: string, data: { password: string }) => Promise<IUser | undefined>;
deactivateAccount: () => Promise<void>;
changePassword: (
csrfToken: string,
payload: { old_password?: string; new_password: string }
) => Promise<IUser | undefined>;
reset: () => void;
signOut: () => Promise<void>;
// computed
@ -89,6 +93,7 @@ export class UserStore implements IUserStore {
updateCurrentUser: action,
handleSetPassword: action,
deactivateAccount: action,
changePassword: action,
reset: action,
signOut: action,
// computed
@ -200,6 +205,23 @@ export class UserStore implements IUserStore {
}
};
changePassword = async (
csrfToken: string,
payload: {
old_password?: string;
new_password: string;
}
): Promise<IUser | undefined> => {
try {
const user = await this.userService.changePassword(csrfToken, payload);
if (this.data) set(this.data, ["is_password_autoset"], false);
return user;
} catch (error) {
console.log(error);
throw error;
}
};
/**
* @description deactivates the current user
* @returns {Promise<void>}