Skip to content

Commit c68acde

Browse files
authored
PD-5147 add auth challenge to password reset (#7498)
* PD-5147 add auth challenge to password reset * fix test and form logic --------- Co-authored-by: andrej romanov <[email protected]>
1 parent 9bb5358 commit c68acde

3 files changed

Lines changed: 234 additions & 38 deletions

File tree

orcid-web/src/main/java/org/orcid/frontend/web/controllers/PasswordResetController.java

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@
1919
import org.apache.commons.codec.binary.Base64;
2020
import org.jasypt.exceptions.EncryptionOperationNotPossibleException;
2121
import org.orcid.core.constants.OrcidOauth2Constants;
22-
import org.orcid.core.manager.EncryptionManager;
23-
import org.orcid.core.manager.ProfileEntityCacheManager;
24-
import org.orcid.core.manager.RegistrationManager;
22+
import org.orcid.core.manager.*;
2523
import org.orcid.core.manager.v3.ProfileEntityManager;
2624
import org.orcid.core.manager.v3.read_only.EmailManagerReadOnly;
2725
import org.orcid.core.togglz.Features;
@@ -32,7 +30,6 @@
3230
import org.orcid.frontend.spring.web.social.config.SocialSignInUtils;
3331
import org.orcid.frontend.web.forms.OneTimeResetPasswordForm;
3432
import org.orcid.frontend.web.util.CommonPasswords;
35-
import org.orcid.jaxb.model.v3.release.record.Email;
3633
import org.orcid.jaxb.model.v3.release.record.Emails;
3734
import org.orcid.persistence.jpa.entities.ProfileEntity;
3835
import org.orcid.pojo.EmailRequest;
@@ -71,6 +68,12 @@ public class PasswordResetController extends BaseController {
7168
@Resource
7269
private SocialAjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandlerSocial;
7370

71+
@Resource
72+
private TwoFactorAuthenticationManager twoFactorAuthenticationManager;
73+
74+
@Resource
75+
private BackupCodeManager backupCodeManager;
76+
7477
@Resource
7578
private ShibbolethAjaxAuthenticationSuccessHandler ajaxAuthenticationSuccessHandlerShibboleth;
7679

@@ -203,14 +206,14 @@ public ModelAndView resetPasswordEmail(HttpServletRequest request, @PathVariable
203206
String orcid = emailManagerReadOnly.findOrcidIdByEmail(passwordResetToken.getEmail());
204207
emails = emailManager.getEmails(orcid);
205208
}
206-
passwordChecklistValidate(resetPasswordForm.getRetypedPassword(), resetPasswordForm.getPassword(), emails);
209+
passwordChecklistValidate(resetPasswordForm.getRetypedPassword(), resetPasswordForm.getNewPassword(), emails);
207210

208-
if (resetPasswordForm.getRetypedPassword() != null && !resetPasswordForm.getRetypedPassword().equals(resetPasswordForm.getPassword())) {
211+
if (resetPasswordForm.getRetypedPassword() != null && !resetPasswordForm.getRetypedPassword().equals(resetPasswordForm.getNewPassword())) {
209212
setError(resetPasswordForm, "FieldMatch.registrationForm");
210213
}
211214

212-
if (CommonPasswords.passwordIsCommon(resetPasswordForm.getPassword().getValue())) {
213-
setError(resetPasswordForm, "password.too_common", resetPasswordForm.getPassword());
215+
if (CommonPasswords.passwordIsCommon(resetPasswordForm.getNewPassword().getValue())) {
216+
setError(resetPasswordForm, "password.too_common", resetPasswordForm.getNewPassword());
214217
}
215218
return resetPasswordForm;
216219
}
@@ -219,7 +222,7 @@ public ModelAndView resetPasswordEmail(HttpServletRequest request, @PathVariable
219222
public @ResponseBody OneTimeResetPasswordForm getResetPassword() {
220223

221224
OneTimeResetPasswordForm oneTimeResetPasswordForm = new OneTimeResetPasswordForm();
222-
passwordChecklistValidate(oneTimeResetPasswordForm.getRetypedPassword(), oneTimeResetPasswordForm.getPassword());
225+
passwordChecklistValidate(oneTimeResetPasswordForm.getRetypedPassword(), oneTimeResetPasswordForm.getNewPassword());
223226
return oneTimeResetPasswordForm;
224227
}
225228

@@ -239,17 +242,17 @@ public ModelAndView resetPasswordEmail(HttpServletRequest request, @PathVariable
239242
return oneTimeResetPasswordForm;
240243
}
241244

242-
passwordConfirmValidate(oneTimeResetPasswordForm.getRetypedPassword(), oneTimeResetPasswordForm.getPassword());
245+
passwordConfirmValidate(oneTimeResetPasswordForm.getRetypedPassword(), oneTimeResetPasswordForm.getNewPassword());
243246

244247
String orcid = emailManagerReadOnly.findOrcidIdByEmail(passwordResetToken.getEmail());
245248
Emails emails = emailManager.getEmails(orcid);
246249

247-
passwordChecklistValidate(oneTimeResetPasswordForm.getRetypedPassword(), oneTimeResetPasswordForm.getPassword(), emails);
248-
if (!oneTimeResetPasswordForm.getPassword().getErrors().isEmpty() || !oneTimeResetPasswordForm.getRetypedPassword().getErrors().isEmpty()) {
250+
passwordChecklistValidate(oneTimeResetPasswordForm.getRetypedPassword(), oneTimeResetPasswordForm.getNewPassword(), emails);
251+
if (!oneTimeResetPasswordForm.getNewPassword().getErrors().isEmpty() || !oneTimeResetPasswordForm.getRetypedPassword().getErrors().isEmpty()) {
249252
return oneTimeResetPasswordForm;
250253
}
251254

252-
profileEntityManager.updatePassword(orcid, oneTimeResetPasswordForm.getPassword().getValue());
255+
profileEntityManager.updatePassword(orcid, oneTimeResetPasswordForm.getNewPassword().getValue());
253256
//reset the lock fields
254257
profileEntityManager.resetSigninLock(orcid);
255258
profileEntityCacheManager.remove(orcid);
@@ -280,7 +283,7 @@ public ModelAndView resetPasswordEmail(HttpServletRequest request, @PathVariable
280283
return oneTimeResetPasswordForm;
281284
}
282285

283-
passwordConfirmValidate(oneTimeResetPasswordForm.getRetypedPassword(), oneTimeResetPasswordForm.getPassword());
286+
passwordConfirmValidate(oneTimeResetPasswordForm.getRetypedPassword(), oneTimeResetPasswordForm.getNewPassword());
284287

285288
String orcid = null;
286289
//check first if valid orcid as the admin portal can send either and email or an orcid
@@ -299,13 +302,39 @@ public ModelAndView resetPasswordEmail(HttpServletRequest request, @PathVariable
299302
}
300303

301304
Emails emails = emailManager.getEmails(orcid);
302-
303-
passwordChecklistValidate(oneTimeResetPasswordForm.getRetypedPassword(), oneTimeResetPasswordForm.getPassword(), emails);
304-
if (!oneTimeResetPasswordForm.getPassword().getErrors().isEmpty() || !oneTimeResetPasswordForm.getRetypedPassword().getErrors().isEmpty()) {
305+
oneTimeResetPasswordForm.setOrcid(orcid);
306+
307+
passwordChecklistValidate(oneTimeResetPasswordForm.getRetypedPassword(), oneTimeResetPasswordForm.getNewPassword(), emails);
308+
if (!oneTimeResetPasswordForm.getNewPassword().getErrors().isEmpty() || !oneTimeResetPasswordForm.getRetypedPassword().getErrors().isEmpty()) {
305309
return oneTimeResetPasswordForm;
306310
}
307311

308-
profileEntityManager.updatePassword(orcid, oneTimeResetPasswordForm.getPassword().getValue());
312+
if (twoFactorAuthenticationManager.userUsing2FA(orcid)) {
313+
oneTimeResetPasswordForm.setTwoFactorEnabled(true);
314+
315+
if (oneTimeResetPasswordForm.getTwoFactorCode() == null && oneTimeResetPasswordForm.getTwoFactorRecoveryCode() == null) {
316+
return oneTimeResetPasswordForm;
317+
} else {
318+
if (oneTimeResetPasswordForm.getTwoFactorRecoveryCode() != null && !oneTimeResetPasswordForm.getTwoFactorRecoveryCode().isEmpty()) {
319+
if (!backupCodeManager.verify(orcid, oneTimeResetPasswordForm.getTwoFactorRecoveryCode())) {
320+
oneTimeResetPasswordForm.setInvalidTwoFactorRecoveryCode(true);
321+
return oneTimeResetPasswordForm;
322+
}
323+
}
324+
else if (oneTimeResetPasswordForm.getTwoFactorCode() != null && !oneTimeResetPasswordForm.getTwoFactorCode().isEmpty()) {
325+
if (!twoFactorAuthenticationManager.verificationCodeIsValid(oneTimeResetPasswordForm.getTwoFactorCode(), orcid)) {
326+
oneTimeResetPasswordForm.setInvalidTwoFactorCode(true);
327+
return oneTimeResetPasswordForm;
328+
}
329+
}
330+
else {
331+
oneTimeResetPasswordForm.setInvalidTwoFactorCode(true);
332+
return oneTimeResetPasswordForm;
333+
}
334+
}
335+
}
336+
337+
profileEntityManager.updatePassword(orcid, oneTimeResetPasswordForm.getNewPassword().getValue());
309338
//send the security notification email on change password
310339
if(Features.SEND_EMAIL_ON_RESET_PASSWORD.isActive()) {
311340
recordEmailSender.sendOrcidSecurityResetPasswordEmail(orcid);
@@ -316,7 +345,7 @@ public ModelAndView resetPasswordEmail(HttpServletRequest request, @PathVariable
316345
String redirectUrl = calculateRedirectUrl(request, response, false);
317346
oneTimeResetPasswordForm.setSuccessRedirectLocation(redirectUrl);
318347
// Remove credentials before return
319-
oneTimeResetPasswordForm.setPassword(null);
348+
oneTimeResetPasswordForm.setNewPassword(null);
320349
oneTimeResetPasswordForm.setRetypedPassword(null);
321350
return oneTimeResetPasswordForm;
322351
}

orcid-web/src/main/java/org/orcid/frontend/web/forms/OneTimeResetPasswordForm.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22

33
import java.util.List;
44

5-
import org.orcid.pojo.ajaxForm.ErrorsInterface;
5+
import org.orcid.pojo.AuthChallenge;
66
import org.orcid.pojo.ajaxForm.Text;
77

8-
public class OneTimeResetPasswordForm implements ErrorsInterface {
8+
public class OneTimeResetPasswordForm extends AuthChallenge {
99

10-
private Text password;
10+
private String orcid;
11+
12+
private Text newPassword;
1113

1214
private Text retypedPassword;
1315

@@ -17,15 +19,23 @@ public class OneTimeResetPasswordForm implements ErrorsInterface {
1719

1820
private List<String> errors;
1921

20-
public Text getPassword() {
21-
if (password == null) {
22-
password = new Text();
22+
public Text getNewPassword() {
23+
if (newPassword == null) {
24+
newPassword = new Text();
2325
}
24-
return password;
26+
return newPassword;
27+
}
28+
29+
public void setNewPassword(Text newPassword) {
30+
this.newPassword = newPassword;
31+
}
32+
33+
public String getOrcid() {
34+
return orcid;
2535
}
2636

27-
public void setPassword(Text password) {
28-
this.password = password;
37+
public void setOrcid(String orcid) {
38+
this.orcid = orcid;
2939
}
3040

3141
public Text getRetypedPassword() {

0 commit comments

Comments
 (0)