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 1628650) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java (revision ) @@ -119,8 +119,12 @@ checkSuccess(success, "UserId/Password mismatch."); if (isPasswordExpired(user)) { + // change the password if the credentials object has the UserConstants.CREDENTIALS_ATTRIBUTE_NEWPASSWORD + // attribute set + if (!changePassword(user, creds)) { - throw new CredentialExpiredException("User password has expired"); - } + throw new CredentialExpiredException("User password has expired"); + } + } } else if (credentials instanceof ImpersonationCredentials) { ImpersonationCredentials ipCreds = (ImpersonationCredentials) credentials; AuthInfo info = ipCreds.getImpersonatorInfo(); @@ -141,6 +145,21 @@ if (!success) { throw new FailedLoginException(msg); } + } + + private static boolean changePassword(User user, SimpleCredentials credentials) throws RepositoryException { + Object newPasswordObject = credentials.getAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD); + if (null != newPasswordObject) { + if (newPasswordObject instanceof String) { + user.changePassword((String) newPasswordObject); + log.debug("User " + user.getID() + ": changed user password"); + return true; + } else { + log.warn("Aborted password change for user [" + user.getID() + "]: provided new password is of" + + " incompatible type " + newPasswordObject.getClass().getName()); + } + } + return false; } private boolean equalUserId(ImpersonationCredentials creds) { \ No newline at end of file Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryTest.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/PasswordExpiryTest.java (revision 1628650) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryTest.java (revision ) @@ -129,6 +129,22 @@ } @Test + public void testAuthenticatePasswordExpiredThenChanged() throws Exception { + Authentication a = new UserAuthentication(getUserConfiguration(), root, userId); + // set password last modified to beginning of epoch + root.getTree(getTestUser().getPath()).getChild(UserConstants.REP_PWD).setProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED, 0); + root.commit(); + try { + SimpleCredentials creds = new SimpleCredentials(userId, userId.toCharArray()); + creds.setAttribute(UserConstants.CREDENTIALS_ATTRIBUTE_NEWPASSWORD, userId); + a.authenticate(creds); + // success + } catch (CredentialExpiredException e) { + fail("Credentials should not be expired"); + } + } + + @Test public void testAuthenticateBeforePasswordExpired() throws Exception { Authentication a = new UserAuthentication(getUserConfiguration(), root, userId); // set password last modified to beginning of epoch Index: oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserConstants.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserConstants.java (revision 1628650) +++ oak-core/src/main/java/org/apache/jackrabbit/oak/spi/security/user/UserConstants.java (revision ) @@ -222,4 +222,10 @@ * Default value for {@link #PARAM_PASSWORD_INITIAL_CHANGE} */ boolean DEFAULT_PASSWORD_INITIAL_CHANGE = false; + + /** + * Name of the {@link javax.jcr.SimpleCredentials} attribute containing the new password. + * This may be used change the password via the credentials object. + */ + String CREDENTIALS_ATTRIBUTE_NEWPASSWORD = "newPassword"; }