diff --git a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset.properties b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset.properties index 3c4818ec56..affd3baa6f 100644 --- a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset.properties +++ b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset.properties @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. password-reset=Password reset -self.pwd.reset.success=Your password has been reset successfully! -self.pwd.reset.success.msg=An email has been sent to your address. +self.pwd.reset.success=Password reset process started +self.pwd.reset.success.msg=If an account matching the provided information exists, password reset instructions will be sent to the associated email address in a few seconds. self.pwd.reset.error=Error during password reset! self.pwd.reset.error.msg=Try again or contact an administrator. diff --git a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_fr_CA.properties b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_fr_CA.properties index 3c4818ec56..affd3baa6f 100644 --- a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_fr_CA.properties +++ b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_fr_CA.properties @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. password-reset=Password reset -self.pwd.reset.success=Your password has been reset successfully! -self.pwd.reset.success.msg=An email has been sent to your address. +self.pwd.reset.success=Password reset process started +self.pwd.reset.success.msg=If an account matching the provided information exists, password reset instructions will be sent to the associated email address in a few seconds. self.pwd.reset.error=Error during password reset! self.pwd.reset.error.msg=Try again or contact an administrator. diff --git a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_it.properties b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_it.properties index c8a9683197..52e91c1fbc 100644 --- a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_it.properties +++ b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_it.properties @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. password-reset=Reset della password -self.pwd.reset.success=La password \u00e8 stata resettata con successo -self.pwd.reset.success.msg=Una email \u00e8 stata inviata all'indirizzo configurato +self.pwd.reset.success=Processo di reset password avviato +self.pwd.reset.success.msg=Se esiste un account corrispondente alle informazioni fornite, le istruzioni per il reset della password verranno inviate all'indirizzo email associato entro pochi secondi. self.pwd.reset.error=Error during password reset! self.pwd.reset.error.msg=Try again or contact an administrator. diff --git a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_ja.properties b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_ja.properties index 791121e5d1..d294a2ab02 100644 --- a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_ja.properties +++ b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_ja.properties @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. password-reset=\u30d1\u30b9\u30ef\u30fc\u30c9\u306e\u30ea\u30bb\u30c3\u30c8 -self.pwd.reset.success=\u30d1\u30b9\u30ef\u30fc\u30c9\u3092\u30ea\u30bb\u30c3\u30c8\u3057\u307e\u3057\u305f -self.pwd.reset.success.msg=An email has been sent to your address. +self.pwd.reset.success=Password reset process started +self.pwd.reset.success.msg=If an account matching the provided information exists, password reset instructions will be sent to the associated email address in a few seconds. self.pwd.reset.error=Error during password reset! self.pwd.reset.error.msg=Try again or contact an administrator. diff --git a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_pt_BR.properties b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_pt_BR.properties index 7195575402..af504335ba 100644 --- a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_pt_BR.properties +++ b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_pt_BR.properties @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. password-reset=Resetar a senha -self.pwd.reset.success=Senha redefinida com sucesso -self.pwd.reset.success.msg=An email has been sent to your address. +self.pwd.reset.success=Password reset process started +self.pwd.reset.success.msg=If an account matching the provided information exists, password reset instructions will be sent to the associated email address in a few seconds. self.pwd.reset.error=Error during password reset! self.pwd.reset.error.msg=Try again or contact an administrator. diff --git a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_ru.properties b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_ru.properties index 2bc9d14a11..dc00337122 100644 --- a/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_ru.properties +++ b/client/idrepo/enduser/src/main/resources/org/apache/syncope/client/enduser/pages/SelfPasswordReset_ru.properties @@ -15,7 +15,7 @@ # specific language governing permissions and limitations # under the License. password-reset=\u0412\u043e\u0441\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u043f\u0430\u0440\u043e\u043b\u044f -self.pwd.reset.success=\u041f\u0430\u0440\u043e\u043b\u044c \u0443\u0441\u043f\u0435\u0448\u043d\u043e \u0441\u0431\u0440\u043e\u0448\u0435\u043d -self.pwd.reset.success.msg=An email has been sent to your address. +self.pwd.reset.success=Password reset process started +self.pwd.reset.success.msg=If an account matching the provided information exists, password reset instructions will be sent to the associated email address in a few seconds. self.pwd.reset.error=Error during password reset! self.pwd.reset.error.msg=Try again or contact an administrator. diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java index a2d6d38ae5..c53aade2ab 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java @@ -523,7 +523,8 @@ public UserSelfLogic userSelfLogic( final DelegationDAO delegationDAO, final AccessTokenDAO accessTokenDAO, final ExternalResourceDAO resourceDAO, - final RuleProvider ruleProvider) { + final RuleProvider ruleProvider, + final SecurityProperties securityProperties) { return new UserSelfLogic( realmSearchDAO, @@ -537,7 +538,8 @@ public UserSelfLogic userSelfLogic( delegationDAO, accessTokenDAO, resourceDAO, - ruleProvider); + ruleProvider, + securityProperties); } @ConditionalOnMissingBean diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserSelfLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserSelfLogic.java index 2369054201..6372e493ab 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserSelfLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserSelfLogic.java @@ -62,6 +62,7 @@ import org.apache.syncope.core.spring.policy.AccountPolicyException; import org.apache.syncope.core.spring.policy.PasswordPolicyException; import org.apache.syncope.core.spring.security.AuthContextUtils; +import org.apache.syncope.core.spring.security.SecurityProperties; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Transactional; @@ -85,6 +86,8 @@ protected static void throwMfaWasSet(final String username) { protected final RuleProvider ruleProvider; + protected final SecurityProperties securityProperties; + public UserSelfLogic( final RealmSearchDAO realmSearchDAO, final AnyTypeDAO anyTypeDAO, @@ -97,7 +100,8 @@ public UserSelfLogic( final DelegationDAO delegationDAO, final AccessTokenDAO accessTokenDAO, final ExternalResourceDAO resourceDAO, - final RuleProvider ruleProvider) { + final RuleProvider ruleProvider, + final SecurityProperties securityProperties) { super(realmSearchDAO, anyTypeDAO, @@ -111,6 +115,7 @@ public UserSelfLogic( this.accessTokenDAO = accessTokenDAO; this.resourceDAO = resourceDAO; this.ruleProvider = ruleProvider; + this.securityProperties = securityProperties; } @PreAuthorize("isAuthenticated() " @@ -274,17 +279,27 @@ public void requestPasswordReset(final String username, final String securityAns throw sce; } - String key = userDAO.findKey(username). - orElseThrow(() -> new NotFoundException("User " + username)); + Optional key = userDAO.findKey(username); + if (key.isEmpty()) { + if (!securityProperties.getPasswordReset().isHideDetails()) { + throw new NotFoundException("User " + username); + } + LOG.warn("Ignoring password reset request for unknown user"); + return; + } if (confParamOps.get( AuthContextUtils.getDomain(), StandardConfParams.PASSWORD_RESET_SECURITY_QUESTION, false, boolean.class) - && (securityAnswer == null || !provisioningManager.checkSecurityAnswer(key, securityAnswer))) { + && (securityAnswer == null || !provisioningManager.checkSecurityAnswer(key.get(), securityAnswer))) { - throw SyncopeClientException.build(ClientExceptionType.InvalidSecurityAnswer); + if (!securityProperties.getPasswordReset().isHideDetails()) { + throw SyncopeClientException.build(ClientExceptionType.InvalidSecurityAnswer); + } + LOG.warn("Ignoring password reset request with missing or invalid security answer"); + return; } - provisioningManager.requestPasswordReset(key, AuthContextUtils.getUsername(), REST_CONTEXT); + provisioningManager.requestPasswordReset(key.get(), AuthContextUtils.getUsername(), REST_CONTEXT); } @PreAuthorize("hasRole('" + IdRepoEntitlement.ANONYMOUS + "')") @@ -298,7 +313,9 @@ public void confirmPasswordReset(final String token, final String password) { } String key = userDAO.findByToken(token). - orElseThrow(() -> new NotFoundException("User with token " + token)); + orElseThrow(() -> new NotFoundException(securityProperties.getPasswordReset().isHideDetails() + ? "Invalid password reset token" + : "User with token " + token)); provisioningManager.confirmPasswordReset( key, token, password, AuthContextUtils.getUsername(), REST_CONTEXT); diff --git a/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserSelfLogicPasswordResetTest.java b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserSelfLogicPasswordResetTest.java new file mode 100644 index 0000000000..fb55e71be0 --- /dev/null +++ b/core/idrepo/logic/src/test/java/org/apache/syncope/core/logic/UserSelfLogicPasswordResetTest.java @@ -0,0 +1,156 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.logic; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.util.Optional; +import org.apache.syncope.common.keymaster.client.api.ConfParamOps; +import org.apache.syncope.common.keymaster.client.api.StandardConfParams; +import org.apache.syncope.common.lib.SyncopeClientException; +import org.apache.syncope.common.lib.types.ClientExceptionType; +import org.apache.syncope.core.persistence.api.EncryptorManager; +import org.apache.syncope.core.persistence.api.dao.AccessTokenDAO; +import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; +import org.apache.syncope.core.persistence.api.dao.DelegationDAO; +import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; +import org.apache.syncope.core.persistence.api.dao.NotFoundException; +import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; +import org.apache.syncope.core.persistence.api.dao.UserDAO; +import org.apache.syncope.core.provisioning.api.UserProvisioningManager; +import org.apache.syncope.core.provisioning.api.data.UserDataBinder; +import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils; +import org.apache.syncope.core.provisioning.api.rules.RuleProvider; +import org.apache.syncope.core.spring.security.SecurityProperties; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class UserSelfLogicPasswordResetTest { + + private ConfParamOps confParamOps; + + private UserDAO userDAO; + + private UserProvisioningManager provisioningManager; + + private SecurityProperties securityProperties; + + private UserSelfLogic logic; + + @BeforeEach + public void setUp() { + confParamOps = mock(ConfParamOps.class); + userDAO = mock(UserDAO.class); + provisioningManager = mock(UserProvisioningManager.class); + securityProperties = new SecurityProperties(); + + when(confParamOps.get(any(), eq(StandardConfParams.PASSWORD_RESET_ALLOWED), eq(false), eq(boolean.class))). + thenReturn(true); + when(confParamOps.get( + any(), eq(StandardConfParams.PASSWORD_RESET_SECURITY_QUESTION), eq(false), eq(boolean.class))). + thenReturn(true); + + logic = new UserSelfLogic( + mock(RealmSearchDAO.class), + mock(AnyTypeDAO.class), + mock(TemplateUtils.class), + userDAO, + mock(UserDataBinder.class), + provisioningManager, + mock(EncryptorManager.class), + confParamOps, + mock(DelegationDAO.class), + mock(AccessTokenDAO.class), + mock(ExternalResourceDAO.class), + mock(RuleProvider.class), + securityProperties); + } + + @Test + public void defaultPasswordResetHidesUnknownUser() { + when(userDAO.findKey("missing")).thenReturn(Optional.empty()); + + assertDoesNotThrow(() -> logic.requestPasswordReset("missing", "answer")); + verify(provisioningManager, never()).requestPasswordReset(anyString(), anyString(), anyString()); + } + + @Test + public void passwordResetDetailsCanBeExposedForCompatibility() { + securityProperties.getPasswordReset().setHideDetails(false); + when(userDAO.findKey("missing")).thenReturn(Optional.empty()); + + NotFoundException e = assertThrows( + NotFoundException.class, + () -> logic.requestPasswordReset("missing", "answer")); + assertTrue(e.getMessage().contains("missing")); + } + + @Test + public void defaultPasswordResetHidesInvalidSecurityAnswer() { + when(userDAO.findKey("rossini")).thenReturn(Optional.of("user-key")); + when(provisioningManager.checkSecurityAnswer("user-key", "wrong")).thenReturn(false); + + assertDoesNotThrow(() -> logic.requestPasswordReset("rossini", "wrong")); + verify(provisioningManager, never()).requestPasswordReset(anyString(), anyString(), anyString()); + } + + @Test + public void invalidSecurityAnswerDetailsCanBeExposedForCompatibility() { + securityProperties.getPasswordReset().setHideDetails(false); + when(userDAO.findKey("rossini")).thenReturn(Optional.of("user-key")); + when(provisioningManager.checkSecurityAnswer("user-key", "wrong")).thenReturn(false); + + SyncopeClientException e = assertThrows( + SyncopeClientException.class, + () -> logic.requestPasswordReset("rossini", "wrong")); + assertEquals(ClientExceptionType.InvalidSecurityAnswer, e.getType()); + } + + @Test + public void defaultPasswordResetDoesNotReflectInvalidToken() { + when(userDAO.findByToken("WRONG TOKEN")).thenReturn(Optional.empty()); + + NotFoundException e = assertThrows( + NotFoundException.class, + () -> logic.confirmPasswordReset("WRONG TOKEN", "password")); + assertFalse(e.getMessage().contains("WRONG TOKEN")); + } + + @Test + public void invalidTokenDetailsCanBeExposedForCompatibility() { + securityProperties.getPasswordReset().setHideDetails(false); + when(userDAO.findByToken("WRONG TOKEN")).thenReturn(Optional.empty()); + + NotFoundException e = assertThrows( + NotFoundException.class, + () -> logic.confirmPasswordReset("WRONG TOKEN", "password")); + assertTrue(e.getMessage().contains("WRONG TOKEN")); + } +} diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityProperties.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityProperties.java index fce444315f..be776ad0c3 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityProperties.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/SecurityProperties.java @@ -68,6 +68,19 @@ public void setLockSeconds(final long lockSeconds) { } } + public static class PasswordResetProperties { + + private boolean hideDetails = true; + + public boolean isHideDetails() { + return hideDetails; + } + + public void setHideDetails(final boolean hideDetails) { + this.hideDetails = hideDetails; + } + } + public static class DigesterProperties { private int saltIterations = 1; @@ -149,6 +162,8 @@ public void setUseLenientSaltSizeCheck(final boolean useLenientSaltSizeCheck) { private final AuthenticationThrottleProperties authenticationThrottle = new AuthenticationThrottleProperties(); + private final PasswordResetProperties passwordReset = new PasswordResetProperties(); + private final DigesterProperties digester = new DigesterProperties(); public String getAdminUser() { @@ -243,6 +258,10 @@ public AuthenticationThrottleProperties getAuthenticationThrottle() { return authenticationThrottle; } + public PasswordResetProperties getPasswordReset() { + return passwordReset; + } + public DigesterProperties getDigester() { return digester; } diff --git a/core/starter/src/main/resources/core.properties b/core/starter/src/main/resources/core.properties index fb4c232b99..3b036fe7a6 100644 --- a/core/starter/src/main/resources/core.properties +++ b/core/starter/src/main/resources/core.properties @@ -107,6 +107,8 @@ security.authenticationThrottle.maxAttempts=5 security.authenticationThrottle.windowSeconds=60 security.authenticationThrottle.lockSeconds=60 +security.passwordReset.hideDetails=true + # default for LDAP / RFC2307 SSHA security.digester.saltIterations=1 security.digester.saltSizeBytes=8 diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java index 6a5ba9d34e..9fd61a8002 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserSelfITCase.java @@ -19,6 +19,7 @@ package org.apache.syncope.fit.core; import static org.awaitility.Awaitility.await; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -323,6 +324,24 @@ public void delete() { ? "deleteApproval" : null, deleted.getStatus()); } + @Test + public void passwordResetHidesEnumerationDetails() { + confParamOps.set(SyncopeConstants.MASTER_DOMAIN, StandardConfParams.PASSWORD_RESET_SECURITY_QUESTION, true); + + assertDoesNotThrow(() -> ANONYMOUS_CLIENT.getService(UserSelfService.class).requestPasswordReset( + UUID.randomUUID() + "@syncope.apache.org", "Rossi")); + + UserCR user = UserITCase.getUniqueSample("pwdResetEnumeration@syncope.apache.org"); + user.setSecurityQuestion("887028ea-66fc-41e7-b397-620d7ea6dfbb"); + user.setSecurityAnswer("Rossi"); + UserTO userTO = createUser(user).getEntity(); + assertNotNull(userTO); + + assertDoesNotThrow(() -> ANONYMOUS_CLIENT.getService(UserSelfService.class).requestPasswordReset( + user.getUsername(), "WRONG")); + assertNull(Optional.ofNullable(getToken(userTO.getKey()).get("token")).map(Object::toString).orElse(null)); + } + @Test public void passwordReset() throws Exception { // 0. ensure that password request DOES require security question @@ -347,12 +366,11 @@ public void passwordReset() throws Exception { assertNotNull(read); // 3. request password reset (as anonymous) providing the expected security answer - try { - ANONYMOUS_CLIENT.getService(UserSelfService.class).requestPasswordReset(user.getUsername(), "WRONG"); - fail("This should not happen"); - } catch (SyncopeClientException e) { - assertEquals(ClientExceptionType.InvalidSecurityAnswer, e.getType()); - } + ANONYMOUS_CLIENT.getService(UserSelfService.class).requestPasswordReset(user.getUsername(), "WRONG"); + assertNull(Optional.ofNullable(getToken(read.getKey()).get("token")).map(Object::toString).orElse(null)); + ANONYMOUS_CLIENT.getService(UserSelfService.class).requestPasswordReset( + UUID.randomUUID() + "@syncope.apache.org", "Rossi"); + ANONYMOUS_CLIENT.getService(UserSelfService.class).requestPasswordReset(user.getUsername(), "Rossi"); awaitIfExtSearchEnabled(); @@ -373,7 +391,7 @@ public void passwordReset() throws Exception { fail("This should not happen"); } catch (SyncopeClientException e) { assertEquals(ClientExceptionType.NotFound, e.getType()); - assertTrue(e.getMessage().contains("WRONG TOKEN")); + assertFalse(e.getMessage().contains("WRONG TOKEN")); } ANONYMOUS_CLIENT.getService(UserSelfService.class).confirmPasswordReset(token, "newPassword123"); @@ -419,7 +437,7 @@ public void passwordResetWithoutSecurityQuestion() { fail("This should not happen"); } catch (SyncopeClientException e) { assertEquals(ClientExceptionType.NotFound, e.getType()); - assertTrue(e.getMessage().contains("WRONG TOKEN")); + assertFalse(e.getMessage().contains("WRONG TOKEN")); } ANONYMOUS_CLIENT.getService(UserSelfService.class).confirmPasswordReset(token, "newPassword123"); diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/enduser/AnonymousITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/enduser/AnonymousITCase.java index b190552831..093624cff3 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/enduser/AnonymousITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/enduser/AnonymousITCase.java @@ -125,12 +125,17 @@ public void selfPasswordReset() { pwdResetForm + ":selfPasswordResetPanelCard:contentPanel:securityQuestion", question.getContent()); - // 3. submit form and receive an error + // 3. submit form with missing answer and receive the generic success page TESTER.executeAjaxEvent(pwdResetForm + ":submit", Constants.ON_CLICK); - TESTER.assertErrorMessages("Invalid Security Answer"); + TESTER.assertRenderedPage(SelfResult.class); TESTER.cleanupFeedbackMessages(); // 3.1 set the correct answer + TESTER.startPage(Login.class); + TESTER.assertRenderedPage(Login.class); + TESTER.clickLink("self-pwd-reset"); + TESTER.assertRenderedPage(SelfPasswordReset.class); + formTester = TESTER.newFormTester(pwdResetForm); formTester.setValue("selfPasswordResetPanelCard:contentPanel:username", "selfpwdreset"); TESTER.executeAjaxEvent(