Index: oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java (revision 1873068) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java (date 1579801651000) @@ -137,6 +137,8 @@ } } catch (RepositoryException e) { throw new LoginException(e.getMessage()); + } finally { + removeNewPwAttribute(credentials); } return success; } @@ -177,6 +179,17 @@ return false; } + /** + * Make sure the new-password attribute is no longer present on the credentials after the login phase + * + * @param credentials + */ + private static void removeNewPwAttribute(Credentials credentials) { + if (credentials instanceof SimpleCredentials) { + ((SimpleCredentials) credentials).removeAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD); + } + } + private boolean impersonate(AuthInfo info, User user) { try { if (user.getID().equals(info.getUserID())) { Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryAndForceInitialChangeTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryAndForceInitialChangeTest.java (revision 1873068) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryAndForceInitialChangeTest.java (date 1579801380000) @@ -32,7 +32,9 @@ import org.junit.Before; import org.junit.Test; +import static org.apache.jackrabbit.oak.spi.security.user.UserConstants.CREDENTIALS_ATTRIBUTE_NEWPASSWORD; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -101,4 +103,19 @@ // during user creation pw last modified is set, thus it shouldn't expire a.authenticate(new SimpleCredentials(userId, userId.toCharArray())); } + + @Test + public void testAuthenticateWithNewPasswordAttribute() throws Exception { + Authentication a = new UserAuthentication(getUserConfiguration(), root, userId); + SimpleCredentials sc = new SimpleCredentials(userId, userId.toCharArray()); + sc.setAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD, "SureChangedMyPassword!"); + try { + // the user should need to change the password on first login + // if new-pw attribute is present it will be used to reset password + assertTrue(a.authenticate(sc)); + } finally { + // upon authentication the CREDENTIALS_ATTRIBUTE_NEWPASSWORD must be removed + assertNull(sc.getAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD)); + } + } } Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/ResetExpiredPasswordTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/ResetExpiredPasswordTest.java (revision 1873068) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/ResetExpiredPasswordTest.java (date 1579801651000) @@ -34,6 +34,8 @@ import org.junit.Test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -43,6 +45,7 @@ public class ResetExpiredPasswordTest extends AbstractSecurityTest implements UserConstants { private String userId; + private SimpleCredentials creds; @Before public void before() throws Exception { @@ -63,13 +66,18 @@ } private void authenticate(String expiredPw, Object newPw) throws LoginException { - SimpleCredentials creds = new SimpleCredentials(userId, expiredPw.toCharArray()); + creds = new SimpleCredentials(userId, expiredPw.toCharArray()); creds.setAttribute(UserConstants.CREDENTIALS_ATTRIBUTE_NEWPASSWORD, newPw); Authentication a = new UserAuthentication(getUserConfiguration(), root, userId); a.authenticate(creds); } + private static void assertCredentials(SimpleCredentials sc) { + assertNotNull(sc); + assertNull(sc.getAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD)); + } + @Test public void testPasswordChangePersisted() throws Exception { authenticate(userId, "newPw"); @@ -78,11 +86,13 @@ Root rootBasedOnSeparateSession = login(getAdminCredentials()).getLatestRoot(); Tree userTree = rootBasedOnSeparateSession.getTree(getTestUser().getPath()); assertTrue(PasswordUtil.isSame(userTree.getProperty(UserConstants.REP_PASSWORD).getValue(Type.STRING), "newPw")); + assertCredentials(creds); } @Test public void testAuthenticatePasswordExpiredThenChanged() throws Exception { authenticate(userId, userId); + assertCredentials(creds); } @Test @@ -96,6 +106,7 @@ Tree userTree = root.getTree(getTestUser().getPath()); assertTrue(PasswordUtil.isSame(userTree.getProperty(UserConstants.REP_PASSWORD).getValue(Type.STRING), userId)); assertEquals(0, userTree.getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED).getValue(Type.LONG).longValue()); + assertCredentials(creds); } } @@ -110,6 +121,7 @@ Tree userTree = root.getTree(getTestUser().getPath()); assertTrue(PasswordUtil.isSame(userTree.getProperty(UserConstants.REP_PASSWORD).getValue(Type.STRING), userId)); assertEquals(0, userTree.getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED).getValue(Type.LONG).longValue()); + assertCredentials(creds); } } }