Skip to content

Commit 1cb4191

Browse files
committed
Bug #236457 [admin portal] Password validations are missing while reseting the password by tenant admin and cohort admin.
1 parent 373e3cc commit 1cb4191

File tree

2 files changed

+120
-19
lines changed

2 files changed

+120
-19
lines changed

public/locales/en/common.json

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,25 @@
151151
"RESET_PASSWORD": "Reset Password",
152152
"NEW_PASSWORD": "New Password",
153153
"CONFIRM_PASSWORD": "Confirm Password",
154-
"PASSWORD_RESET_SUCCESSFUL": "Password Reset Successful!",
155-
"PASSWORD_RESET_FAILED": "Password Reset Failed. Please try again.",
156154
"ENTER_VALID_PASSWORD": "Enter Valid Password",
157-
"ADD_SINGLE_USER":"Add Single user",
158-
"ADD_MULTIPLE_USERS":"Add Multiple users"
155+
"ADD_SINGLE_USER": "Add Single user",
156+
"ADD_MULTIPLE_USERS": "Add Multiple users",
157+
"PASSWORD_RESET_SUCCESSFUL": "Password has been reset successfully.",
158+
"PASSWORD_RESET_FAILED": "Failed to reset password. Please try again.",
159+
"UNEXPECTED_ERROR": "An unexpected error occurred. Please try again later.",
160+
"RESET_PASSWORD_FOR_USER": "Reset password for user:",
161+
"PASSWORDS_DO_NOT_MATCH": "Passwords do not match.",
162+
"PASSWORD_LENGTH_ERROR": "Password must be at least 8 characters long.",
163+
"PASSWORD_UPPERCASE_ERROR": "Password must contain at least one uppercase letter.",
164+
"PASSWORD_LOWERCASE_ERROR": "Password must contain at least one lowercase letter.",
165+
"PASSWORD_NUMBER_ERROR": "Password must contain at least one number.",
166+
"PASSWORD_SPECIAL_CHAR_ERROR": "Password must contain at least one special character.",
167+
"PASSWORD_COMPLEXITY_REQUIREMENTS": "Password must: \n- Be at least 8 characters long\n- Contain uppercase and lowercase letters\n- Include a number\n- Include a special character"
168+
},
169+
"HINT": {
170+
"PASSWORD_STRENGTH": {
171+
"GENERAL": "Tip: Create a strong, unique password to protect your account."
172+
}
159173
},
160174
"LOGIN_PAGE": {
161175
"USERNAME": "Username",

src/components/ResetPassword.tsx

Lines changed: 102 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,16 @@ import {
1313
import { useTranslation } from "next-i18next";
1414
import { resetPassword } from "@/services/LoginService";
1515
import { showToastMessage } from "./Toastify";
16+
import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
1617

1718
interface ResetPasswordModalProps {
1819
open: boolean;
1920
onClose: () => void;
20-
userData: any;
21+
userData: {
22+
tenantId?: string;
23+
username?: string;
24+
name?: string;
25+
};
2126
}
2227

2328
export const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({
@@ -42,15 +47,46 @@ export const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({
4247
onClose();
4348
};
4449

45-
const validatePasswords = () => {
46-
if (newPassword.length < 8) {
47-
setError("Password must be at least 8 characters long");
50+
const validatePasswords = (): boolean => {
51+
// Check if passwords match
52+
if (newPassword !== confirmPassword) {
53+
setError(t("COMMON.PASSWORDS_DO_NOT_MATCH"));
4854
return false;
4955
}
50-
if (newPassword !== confirmPassword) {
51-
setError("Passwords do not match");
56+
57+
// Validate password strength
58+
const hasUpperCase = /[A-Z]/.test(newPassword);
59+
const hasLowerCase = /[a-z]/.test(newPassword);
60+
const hasNumber = /\d/.test(newPassword);
61+
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(newPassword);
62+
const isValidLength = newPassword.length >= 8;
63+
64+
if (!isValidLength) {
65+
setError(t("COMMON.PASSWORD_LENGTH_ERROR"));
66+
return false;
67+
}
68+
69+
if (!hasUpperCase) {
70+
setError(t("COMMON.PASSWORD_UPPERCASE_ERROR"));
71+
return false;
72+
}
73+
74+
if (!hasLowerCase) {
75+
setError(t("COMMON.PASSWORD_LOWERCASE_ERROR"));
5276
return false;
5377
}
78+
79+
if (!hasNumber) {
80+
setError(t("COMMON.PASSWORD_NUMBER_ERROR"));
81+
return false;
82+
}
83+
84+
if (!hasSpecialChar) {
85+
setError(t("COMMON.PASSWORD_SPECIAL_CHAR_ERROR"));
86+
return false;
87+
}
88+
89+
setError("");
5490
return true;
5591
};
5692

@@ -59,34 +95,50 @@ export const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({
5995

6096
setIsLoading(true);
6197
setError("");
62-
const tenantid = userData?.tenantId;
98+
6399
const userObj = {
64100
userName: userData?.username,
65101
newPassword: newPassword,
66102
};
67103

68104
try {
69-
const response = await resetPassword(userObj, tenantid);
105+
const response = await resetPassword(userObj, userData?.tenantId);
70106
if (response?.responseCode !== 200) {
71-
throw new Error("Failed to reset password");
107+
throw new Error(t("COMMON.PASSWORD_RESET_FAILED"));
72108
}
109+
73110
showToastMessage(t("COMMON.PASSWORD_RESET_SUCCESSFUL"), "success");
74111
handleClose();
75112
} catch (err) {
76-
setError(err instanceof Error ? err.message : "An error occurred");
77-
showToastMessage(t("COMMON.PASSWORD_RESET_FAILED"), "error");
113+
const errorMessage =
114+
err instanceof Error ? err.message : t("COMMON.UNEXPECTED_ERROR");
115+
116+
setError(errorMessage);
117+
showToastMessage(errorMessage, "error");
78118
} finally {
79119
setIsLoading(false);
80120
}
81121
};
82122

83123
return (
84-
<Dialog open={open} onClose={handleClose} maxWidth="md" fullWidth>
124+
<Dialog
125+
open={open}
126+
onClose={handleClose}
127+
maxWidth="lg"
128+
sx={{
129+
"& .MuiDialog-paper": {
130+
maxWidth: "400px",
131+
width: "100%",
132+
},
133+
}}
134+
fullWidth
135+
>
85136
<DialogTitle>{t("COMMON.RESET_PASSWORD")}</DialogTitle>
86137
<DialogContent>
87138
<Box sx={{ mt: 2 }}>
88139
<Typography variant="body1" sx={{ mb: 2 }}>
89-
Reset password for user: <strong>{userData?.name}</strong>
140+
{t("COMMON.RESET_PASSWORD_FOR_USER")}{" "}
141+
<strong>{userData?.name}</strong>
90142
</Typography>
91143

92144
<TextField
@@ -97,6 +149,15 @@ export const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({
97149
onChange={(e) => setNewPassword(e.target.value)}
98150
margin="normal"
99151
error={!!error}
152+
helperText={error && newPassword ? error : ""}
153+
aria-describedby="password-requirements"
154+
InputProps={{
155+
sx: {
156+
"& input": {
157+
WebkitTextSecurity: "disc",
158+
},
159+
},
160+
}}
100161
/>
101162

102163
<TextField
@@ -107,8 +168,34 @@ export const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({
107168
onChange={(e) => setConfirmPassword(e.target.value)}
108169
margin="normal"
109170
error={!!error}
110-
helperText={error}
171+
helperText={error && confirmPassword ? error : ""}
172+
aria-describedby="password-requirements"
173+
InputProps={{
174+
sx: {
175+
"& input": {
176+
WebkitTextSecurity: "disc",
177+
},
178+
},
179+
}}
111180
/>
181+
182+
<Box
183+
id="password-requirements"
184+
sx={{
185+
backgroundColor: "rgba(0, 0, 0, 0.05)",
186+
borderRadius: 2,
187+
p: 1,
188+
mt: 2,
189+
display: "flex",
190+
alignItems: "center",
191+
gap: 1,
192+
}}
193+
>
194+
<InfoOutlinedIcon color="primary" />
195+
<Typography variant="body2" color="text.secondary">
196+
{t("COMMON.PASSWORD_COMPLEXITY_REQUIREMENTS")}
197+
</Typography>
198+
</Box>
112199
</Box>
113200
</DialogContent>
114201
<DialogActions sx={{ p: 2 }}>
@@ -118,7 +205,7 @@ export const ResetPasswordModal: React.FC<ResetPasswordModalProps> = ({
118205
<Button
119206
onClick={handleResetConfirm}
120207
variant="contained"
121-
sx={{ color: "white" }}
208+
color="primary"
122209
disabled={isLoading || !newPassword || !confirmPassword}
123210
startIcon={isLoading ? <CircularProgress size={20} /> : null}
124211
>

0 commit comments

Comments
 (0)