diff --git a/src/org/labkey/test/BaseWebDriverTest.java b/src/org/labkey/test/BaseWebDriverTest.java index 88fac31b26..1e7f745b9e 100644 --- a/src/org/labkey/test/BaseWebDriverTest.java +++ b/src/org/labkey/test/BaseWebDriverTest.java @@ -2477,12 +2477,6 @@ public void waitForComplete() } } - public void ensureSignedOut() - { - if(isElementPresent(Locator.id("userMenuPopupLink"))) - signOut(); - } - protected void reloadStudyFromZip(File folderArchive) { reloadStudyFromZip(folderArchive, true, 2); diff --git a/src/org/labkey/test/LabKeySiteWrapper.java b/src/org/labkey/test/LabKeySiteWrapper.java index 7868d81788..03f757301c 100644 --- a/src/org/labkey/test/LabKeySiteWrapper.java +++ b/src/org/labkey/test/LabKeySiteWrapper.java @@ -416,8 +416,20 @@ public void attemptSignIn(String email, String password) public void signInShouldFail(String email, String password, String... expectedMessages) { attemptSignIn(email, password); - String errorText = waitForElement(Locator.id("errors").withText()).getText(); - assertElementPresent(Locator.tagWithName("form", "login")); + + WebElement element = waitForElement(Locator.tagWithClass("div", "auth-header")); + final String errorText; + + // Could be on the "Sign In" page or the "Change Password" page + if (element.getText().equals("Sign In")) + { + errorText = getText(Locator.id("errors")); + } + else + { + assertEquals("Change Password", element.getText()); + errorText = getText(Locator.tagWithClass("div", "auth-form-body").childTag("p")); + } List missingErrors = getMissingTexts(new TextSearcher(errorText), expectedMessages); assertTrue(String.format("Wrong errors.\nExpected: ['%s']\nActual: '%s'", String.join("',\n'", expectedMessages), errorText), missingErrors.isEmpty()); diff --git a/src/org/labkey/test/tests/SimpleModuleTest.java b/src/org/labkey/test/tests/SimpleModuleTest.java index 8864bc29bd..a5a92ee7fb 100644 --- a/src/org/labkey/test/tests/SimpleModuleTest.java +++ b/src/org/labkey/test/tests/SimpleModuleTest.java @@ -1807,7 +1807,6 @@ private void doTestCustomLogin() lookAndFeelSettingsPage.setAltLoginPage("simpletest-testCustomLogin"); lookAndFeelSettingsPage.save(); signOut(); - ensureSignedOut(); beginAt(WebTestHelper.buildURL("login", "login")); waitForAnyElement("Should be on login or Home portal", Locator.id("email"), SiteNavBar.Locators.userMenu); @@ -1821,7 +1820,6 @@ private void doTestCustomLogin() lookAndFeelSettingsPage.setAltLoginPage("simpletest-testCustomLoginWithReturnUrl"); lookAndFeelSettingsPage.save(); signOut(); - ensureSignedOut(); beginAt(WebTestHelper.buildURL("login", "login")); waitForAnyElement("Should be on login or Home portal", Locator.id("email"), SiteNavBar.Locators.userMenu); @@ -1835,7 +1833,6 @@ private void doTestCustomLogin() lookAndFeelSettingsPage.setAltLoginPage(""); lookAndFeelSettingsPage.save(); signOut(); - ensureSignedOut(); signIn(); log("restore original login page"); diff --git a/src/org/labkey/test/tests/core/login/PasswordTest.java b/src/org/labkey/test/tests/core/login/PasswordTest.java index 82db3cf13a..5fea5c0fda 100644 --- a/src/org/labkey/test/tests/core/login/PasswordTest.java +++ b/src/org/labkey/test/tests/core/login/PasswordTest.java @@ -23,9 +23,11 @@ import org.labkey.remoteapi.CommandException; import org.labkey.remoteapi.Connection; import org.labkey.remoteapi.SimplePostCommand; +import org.labkey.remoteapi.security.EnsureLoginCommand; import org.labkey.test.BaseWebDriverTest; import org.labkey.test.Locator; import org.labkey.test.TestTimeoutException; +import org.labkey.test.WebTestHelper; import org.labkey.test.categories.BVT; import org.labkey.test.components.core.login.SetPasswordForm; import org.labkey.test.pages.core.login.DatabaseAuthConfigureDialog; @@ -46,6 +48,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.labkey.test.components.core.login.SetPasswordForm.SHORT_PASSWORD; import static org.labkey.test.components.core.login.SetPasswordForm.VERY_STRONG_PASSWORD; import static org.labkey.test.components.core.login.SetPasswordForm.VERY_WEAK_PASSWORD; @@ -206,9 +209,6 @@ public void testPasswordReset() password = adminPasswordResetTest(username, password+"adminReset"); String resetUrl = userForgotPasswordWorkflowTest(username, password); - - ensureSignedOut(); - beginAt(resetUrl); attemptSetInvalidPassword("fooba", "fooba", "Your password must be at least eight characters and cannot contain spaces."); @@ -253,13 +253,105 @@ public void testPasswordParameter() assertTrue("Expected email/password in URL to be rejected.", rejectedProperly); } + @Test + public void testChooseNewPasswordMessages() throws IOException + { + // Hold an admin connection open, allowing us to reset the config regardless of what happens during the test + Connection adminConnection = WebTestHelper.getRemoteApiConnection(); + DbLoginProperties savedProperties = DbLoginUtils.getDbLoginConfig(adminConnection); + + try + { + DbLoginUtils.setDbLoginConfig(adminConnection, + PasswordStrength.Good, + PasswordExpiration.Never + ); + + // Set a weak password + signOut(); + String resetUrl = userInitiatePasswordReset(USER); + beginAt(resetUrl); + new SetPasswordForm(getDriver()) + .setNewPassword(WEAK_PASSWORD) + .clickSubmit(); + + // Test bogus password first + signOut(); + signInShouldFail("bogus", "The email address and password you entered did not match any accounts on file."); + signIn(USER, WEAK_PASSWORD); + signOut(); + + // Change the configuration and test password that no longer meets complexity requirements + DbLoginUtils.setDbLoginConfig(adminConnection, + PasswordStrength.Strong, + PasswordExpiration.Never + ); + signInShouldFail(WEAK_PASSWORD, "Your password does not meet the complexity requirements; please choose a new password."); + String strongPassword = VERY_STRONG_PASSWORD + "!"; + changeInvalidPassword(WEAK_PASSWORD, strongPassword); + + // Change the configuration and test expired password + DbLoginUtils.setDbLoginConfig(adminConnection, + PasswordStrength.Strong, + PasswordExpiration.FiveSeconds + ); + // Wait six seconds for expiration + sleep(6000); + signInShouldFail(strongPassword, "Your password has expired; please choose a new password."); + changeInvalidPassword(strongPassword, VERY_STRONG_PASSWORD + "@"); + } + finally + { + DbLoginUtils.setDbLoginConfig(adminConnection, savedProperties); + } + } + + // Attempt to sign in via UI and API, expecting both to fail with the specified message + private void signInShouldFail(String password, String expectedMessage) throws IOException + { + signInShouldFail(USER, password, expectedMessage); + Connection userConnection = new Connection(WebTestHelper.getBaseURL(), USER, password); + EnsureLoginCommand ensureLoginCommand = new EnsureLoginCommand(); + try + { + ensureLoginCommand.execute(userConnection, "/"); + fail("Expected execute() to throw an exception."); + } + catch (CommandException e) + { + assertEquals(HttpStatus.SC_UNAUTHORIZED, e.getStatusCode()); + assertEquals(expectedMessage, e.getMessage()); + } + } + + @LogMethod + private void resetPassword(String password) + { + signOut(); + String resetUrl = userInitiatePasswordReset(USER); + beginAt(resetUrl); + new SetPasswordForm(getDriver()) + .setNewPassword(password) + .clickSubmit(); + } + + @LogMethod + private void changeInvalidPassword(String oldPassword, String newPassword) + { + new SetPasswordForm(getDriver()) + .setOldPassword(oldPassword) + .setNewPassword(newPassword) + .clickSubmit(); + signOut(); + } + @LogMethod protected void attemptSetInvalidPassword(String password1, String password2, String error) { new SetPasswordForm(getDriver()) - .setPassword1(password1) - .setPassword2(password2) - .clickSubmitExpectingError(error); + .setPassword1(password1) + .setPassword2(password2) + .clickSubmitExpectingError(error); } /** @@ -274,13 +366,10 @@ protected void attemptSetInvalidPassword(String password1, String password2, Str @LogMethod private String userForgotPasswordWorkflowTest(String username, String password) { - ensureSignedOut(); - String resetUrl = userInitiatePasswordReset(username); - signOut(); - //attempt sign in with old password- should succeed + //attempt sign in with old password - should succeed signIn(username, password); signOut(); @@ -290,14 +379,14 @@ private String userForgotPasswordWorkflowTest(String username, String password) @LogMethod public String userInitiatePasswordReset(String username) { + signOut(); goToHome(); - ensureSignedOut(); - clickAndWait(Locator.linkWithText("Sign In")); clickAndWait(Locator.linkContainingText("Forgot password")); setFormElement(Locator.id("email"), username); clickButtonContainingText("Reset", 0); + // Need to sign in as admin to see the email signIn(); return getPasswordResetUrl(_userId); } diff --git a/src/org/labkey/test/util/core/login/DbLoginUtils.java b/src/org/labkey/test/util/core/login/DbLoginUtils.java index b778a611e9..20a7042301 100644 --- a/src/org/labkey/test/util/core/login/DbLoginUtils.java +++ b/src/org/labkey/test/util/core/login/DbLoginUtils.java @@ -36,8 +36,9 @@ public static DbLoginProperties getDbLoginConfig(Connection connection) { CommandResponse response = postCommand.execute(connection, "/"); dbLoginProperties = new DbLoginProperties( - PasswordStrength.valueOf(response.getProperty("currentSettings.strength")), - PasswordExpiration.valueOf(response.getProperty("currentSettings.expiration"))); + PasswordStrength.valueOf(response.getProperty("currentSettings.strength")), + PasswordExpiration.valueOf(response.getProperty("currentSettings.expiration")) + ); } catch (IOException | CommandException e) { @@ -47,6 +48,12 @@ public static DbLoginProperties getDbLoginConfig(Connection connection) return dbLoginProperties; } + @LogMethod + public static void setDbLoginConfig(Connection connection, DbLoginProperties dbLoginProperties) + { + setDbLoginConfig(connection, dbLoginProperties.strength(), dbLoginProperties.expiration()); + } + @LogMethod public static void setDbLoginConfig(Connection connection, PasswordStrength strength, PasswordExpiration expiration) { @@ -74,7 +81,7 @@ public static void resetDbLoginConfig(Connection connection) { if (initialLoginProperties != null) { - setDbLoginConfig(connection, initialLoginProperties.strength, initialLoginProperties.expiration); + setDbLoginConfig(connection, initialLoginProperties); initialLoginProperties = null; } }