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 1861580) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryAndForceInitialChangeTest.java (date 1560871130000) @@ -17,6 +17,7 @@ package org.apache.jackrabbit.oak.security.user; import java.util.UUID; +import javax.jcr.RepositoryException; import javax.jcr.SimpleCredentials; import javax.security.auth.login.CredentialExpiredException; @@ -24,14 +25,17 @@ import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.oak.AbstractSecurityTest; import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Tree; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; import org.apache.jackrabbit.oak.spi.security.authentication.Authentication; import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; import org.apache.jackrabbit.oak.spi.security.user.UserConstants; +import org.jetbrains.annotations.NotNull; import org.junit.Before; import org.junit.Test; +import static org.apache.jackrabbit.oak.spi.security.user.UserConstants.REP_PWD; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -57,6 +61,11 @@ return ConfigurationParameters.of(ImmutableMap.of(UserConfiguration.NAME, parameters)); } + @NotNull + private Tree getUserTree(@NotNull User user) throws RepositoryException { + return root.getTree(user.getPath()); + } + @Test public void testCreateUser() throws Exception { String newUserId = "newuser" + UUID.randomUUID(); @@ -65,7 +74,7 @@ user = getUserManager(root).createUser(newUserId, newUserId); root.commit(); - assertFalse(root.getTree(user.getPath()).hasChild(UserConstants.REP_PWD)); + assertFalse(getUserTree(user).hasChild(UserConstants.REP_PWD)); assertFalse(user.hasProperty(UserConstants.REP_PWD + "/" + UserConstants.REP_PASSWORD_LAST_MODIFIED)); } finally { if (user != null) { @@ -93,7 +102,7 @@ User user = getTestUser(); user.changePassword(userId); root.commit(); - PropertyState p = root.getTree(user.getPath()).getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED); + PropertyState p = getUserTree(user).getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED); long newModTime = p.getValue(Type.LONG); assertTrue(newModTime > 0); @@ -101,4 +110,31 @@ // during user creation pw last modified is set, thus it shouldn't expire a.authenticate(new SimpleCredentials(userId, userId.toCharArray())); } + + /** + * rep:passwordLastModified must NOT be created otherwise the user might never be forced to change pw upon first login. + */ + @Test + public void testSetPasswordImportExistingUser() throws Exception { + UserManagerImpl userManager = (UserManagerImpl) getUserManager(root); + Tree userTree = getUserTree(getTestUser()); + assertFalse(userTree.hasChild(REP_PWD)); + + userManager.setPassword(userTree, getTestUser().getID(), "pwd", true); + assertFalse(userTree.hasChild(REP_PWD)); + } + + /** + * rep:passwordLastModified must NOT be created in accordance to UserManager.createUser + */ + @Test + public void testSetPasswordImportNewUser() throws Exception { + UserManagerImpl userManager = (UserManagerImpl) getUserManager(root); + User u = userManager.createUser("uNew", null); + Tree userTree = getUserTree(u); + assertFalse(userTree.hasChild(REP_PWD)); + + userManager.setPassword(userTree, "uNew", "pwd", true); + assertFalse(userTree.hasChild(REP_PWD)); + } } 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 1861580) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryTest.java (date 1560870950000) @@ -16,28 +16,34 @@ */ package org.apache.jackrabbit.oak.security.user; -import java.util.UUID; -import javax.jcr.SimpleCredentials; -import javax.security.auth.login.CredentialExpiredException; -import javax.security.auth.login.LoginException; - import org.apache.jackrabbit.api.security.user.User; +import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.jackrabbit.oak.AbstractSecurityTest; import org.apache.jackrabbit.oak.api.PropertyState; import org.apache.jackrabbit.oak.api.Tree; import org.apache.jackrabbit.oak.api.Type; +import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager; +import org.apache.jackrabbit.oak.plugins.tree.TreeUtil; import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants; -import org.apache.jackrabbit.oak.plugins.nodetype.ReadOnlyNodeTypeManager; import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; import org.apache.jackrabbit.oak.spi.security.authentication.Authentication; import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; import org.apache.jackrabbit.oak.spi.security.user.UserConstants; -import org.apache.jackrabbit.oak.plugins.tree.TreeUtil; +import org.jetbrains.annotations.NotNull; import org.junit.Before; import org.junit.Test; +import javax.jcr.RepositoryException; +import javax.jcr.SimpleCredentials; +import javax.security.auth.login.CredentialExpiredException; +import javax.security.auth.login.LoginException; +import java.util.UUID; + +import static org.apache.jackrabbit.oak.spi.security.user.UserConstants.REP_PASSWORD_LAST_MODIFIED; +import static org.apache.jackrabbit.oak.spi.security.user.UserConstants.REP_PWD; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -61,6 +67,11 @@ return ConfigurationParameters.of(UserConfiguration.NAME, userConfig); } + @NotNull + private Tree getUserTree(@NotNull User user) throws RepositoryException { + return root.getTree(user.getPath()); + } + @Test public void testCreateUser() throws Exception { String newUserId = "newuser" + UUID.randomUUID(); @@ -70,7 +81,7 @@ user = getUserManager(root).createUser(newUserId, newUserId); root.commit(); - Tree pwdTree = root.getTree(user.getPath()).getChild(UserConstants.REP_PWD); + Tree pwdTree = getUserTree(user).getChild(UserConstants.REP_PWD); assertTrue(pwdTree.exists()); assertTrue(TreeUtil.isNodeType(pwdTree, UserConstants.NT_REP_PASSWORD, root.getTree(NodeTypeConstants.NODE_TYPES_PATH))); @@ -95,13 +106,13 @@ @Test public void testChangePassword() throws Exception { User user = getTestUser(); - PropertyState p1 = root.getTree(user.getPath()).getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED); + PropertyState p1 = getUserTree(user).getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED); long oldModTime = p1.getValue(Type.LONG, 0); assertTrue(oldModTime > 0); waitForSystemTimeIncrement(oldModTime); user.changePassword(userId); root.commit(); - PropertyState p2 = root.getTree(user.getPath()).getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED); + PropertyState p2 = getUserTree(user).getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED); long newModTime = p2.getValue(Type.LONG, 0); assertTrue(newModTime > oldModTime); } @@ -117,7 +128,7 @@ public void testAuthenticatePasswordExpired() 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); + getUserTree(getTestUser()).getChild(UserConstants.REP_PWD).setProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED, 0); root.commit(); try { a.authenticate(new SimpleCredentials(userId, userId.toCharArray())); @@ -131,7 +142,7 @@ public void testAuthenticateBeforePasswordExpired() 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); + getUserTree(getTestUser()).getChild(UserConstants.REP_PWD).setProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED, 0); root.commit(); try { a.authenticate(new SimpleCredentials(userId, "wrong".toCharArray())); @@ -146,7 +157,7 @@ public void testAuthenticatePasswordExpiredChangePassword() 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); + getUserTree(getTestUser()).getChild(UserConstants.REP_PWD).setProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED, 0); root.commit(); // changing the password should reset the pw last mod and the pw no longer be expired @@ -160,4 +171,40 @@ User adminUser = getUserManager(root).getAuthorizable(getUserConfiguration().getParameters().getConfigValue(UserConstants.PARAM_ADMIN_ID, UserConstants.DEFAULT_ADMIN_ID), User.class); assertFalse(root.getTree(adminUser.getPath()).getChild(UserConstants.REP_PWD).exists()); } + + /** + * import existing user: rep:passwordLastModified must not be updated (unless this property is explicitly included in the xml import) + */ + @Test + public void testSetPasswordImportExistingUser() throws Exception { + UserManager userManager = getUserManager(root); + if (userManager instanceof UserManagerImpl) { + Tree userTree = getUserTree(getTestUser()); + assertTrue(userTree.hasChild(REP_PWD)); + PropertyState lastMod = userTree.getChild(REP_PWD).getProperty(REP_PASSWORD_LAST_MODIFIED); + + waitForSystemTimeIncrement(lastMod.getValue(Type.LONG)); + + ((UserManagerImpl) userManager).setPassword(userTree, getTestUser().getID(), "pwd", true); + assertTrue(userTree.hasChild(REP_PWD)); + assertEquals(lastMod.getValue(Type.LONG), userTree.getChild(REP_PWD).getProperty(REP_PASSWORD_LAST_MODIFIED).getValue(Type.LONG)); + } + } + + /** + * import new user: rep:passwordLastModified must be created as it would when calling UserManager.createUser + */ + @Test + public void testSetPasswordImportNewUser() throws Exception { + UserManager userManager = getUserManager(root); + if (userManager instanceof UserManagerImpl) { + User u = userManager.createUser("uNew", null); + Tree userTree = getUserTree(u); + assertFalse(userTree.hasChild(REP_PWD)); + + ((UserManagerImpl) userManager).setPassword(userTree, "uNew", "pwd", true); + assertTrue(userTree.hasChild(REP_PWD)); + assertTrue(userTree.getChild(REP_PWD).hasProperty(REP_PASSWORD_LAST_MODIFIED)); + } + } } Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordForceInitialPasswordChangeTest.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/PasswordForceInitialPasswordChangeTest.java (revision 1861580) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordForceInitialPasswordChangeTest.java (date 1560871130000) @@ -19,18 +19,22 @@ import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.oak.AbstractSecurityTest; import org.apache.jackrabbit.oak.api.PropertyState; +import org.apache.jackrabbit.oak.api.Tree; import org.apache.jackrabbit.oak.api.Type; import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; import org.apache.jackrabbit.oak.spi.security.authentication.Authentication; import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; import org.apache.jackrabbit.oak.spi.security.user.UserConstants; +import org.jetbrains.annotations.NotNull; import org.junit.Before; import org.junit.Test; +import javax.jcr.RepositoryException; import javax.jcr.SimpleCredentials; import javax.security.auth.login.CredentialExpiredException; import java.util.UUID; +import static org.apache.jackrabbit.oak.spi.security.user.UserConstants.REP_PWD; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -57,6 +61,11 @@ return ConfigurationParameters.of(UserConfiguration.NAME, userConfig); } + @NotNull + private Tree getUserTree(@NotNull User user) throws RepositoryException { + return root.getTree(user.getPath()); + } + @Test public void testCreateUser() throws Exception { String newUserId = "newuser" + UUID.randomUUID(); @@ -66,8 +75,8 @@ user = getUserManager(root).createUser(newUserId, newUserId); root.commit(); - assertFalse(root.getTree(user.getPath()).hasChild(UserConstants.REP_PWD)); - assertFalse(user.hasProperty(UserConstants.REP_PWD + "/" + UserConstants.REP_PASSWORD_LAST_MODIFIED)); + assertFalse(getUserTree(user).hasChild(REP_PWD)); + assertFalse(user.hasProperty(REP_PWD + "/" + UserConstants.REP_PASSWORD_LAST_MODIFIED)); } finally { if (user != null) { user.remove(); @@ -90,11 +99,11 @@ @Test public void testChangePassword() throws Exception { User user = getTestUser(); - PropertyState p1 = root.getTree(user.getPath()).getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED); + PropertyState p1 = getUserTree(user).getChild(REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED); assertNull(p1); user.changePassword(userId); root.commit(); - PropertyState p2 = root.getTree(user.getPath()).getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED); + PropertyState p2 = getUserTree(user).getChild(REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED); assertNotNull(p2); assertTrue(p2.getValue(Type.LONG) > 0); @@ -102,4 +111,31 @@ Authentication a = new UserAuthentication(getUserConfiguration(), root, userId); a.authenticate(new SimpleCredentials(userId, userId.toCharArray())); } + + /** + * rep:passwordLastModified must NOT be created otherwise the user might never be forced to change pw upon first login. + */ + @Test + public void testSetPasswordImportExistingUser() throws Exception { + UserManagerImpl userManager = (UserManagerImpl) getUserManager(root); + Tree userTree = getUserTree(getTestUser()); + assertFalse(userTree.hasChild(REP_PWD)); + + userManager.setPassword(userTree, getTestUser().getID(), "pwd", true); + assertFalse(userTree.hasChild(REP_PWD)); + } + + /** + * rep:passwordLastModified must NOT be created in accordance to UserManager.createUser + */ + @Test + public void testSetPasswordImportNewUser() throws Exception { + UserManagerImpl userManager = (UserManagerImpl) getUserManager(root); + User u = userManager.createUser("uNew", null); + Tree userTree = getUserTree(u); + assertFalse(userTree.hasChild(REP_PWD)); + + userManager.setPassword(userTree, "uNew", "pwd", true); + assertFalse(userTree.hasChild(REP_PWD)); + } } Index: oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserManagerImplTest.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/UserManagerImplTest.java (revision 1861580) +++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/UserManagerImplTest.java (date 1560845890000) @@ -184,14 +184,14 @@ Tree userTree = root.getTree(user.getPath()); for (String pw : pwds) { - userMgr.setPassword(userTree, testUserId, pw, true); + userMgr.setPassword(userTree, testUserId, pw, false); String pwHash = userTree.getProperty(UserConstants.REP_PASSWORD).getValue(Type.STRING); assertNotNull(pwHash); assertTrue(PasswordUtil.isSame(pwHash, pw)); } for (String pw : pwds) { - userMgr.setPassword(userTree, testUserId, pw, false); + userMgr.setPassword(userTree, testUserId, pw, true); String pwHash = userTree.getProperty(UserConstants.REP_PASSWORD).getValue(Type.STRING); assertNotNull(pwHash); if (!pw.startsWith("{")) { Index: oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportInitialPwChangeTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportInitialPwChangeTest.java (date 1560866781000) +++ oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/security/user/UserImportInitialPwChangeTest.java (date 1560866781000) @@ -0,0 +1,200 @@ +/* + * 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.jackrabbit.oak.jcr.security.user; + +import jdk.nashorn.internal.ir.annotations.Ignore; +import org.apache.jackrabbit.JcrConstants; +import org.apache.jackrabbit.api.security.user.Authorizable; +import org.apache.jackrabbit.api.security.user.User; +import org.apache.jackrabbit.oak.commons.PathUtils; +import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters; +import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration; +import org.apache.jackrabbit.oak.spi.security.user.UserConstants; +import org.apache.jackrabbit.oak.spi.xml.ImportBehavior; +import org.apache.jackrabbit.spi.Name; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.junit.Test; +import org.xml.sax.ContentHandler; +import org.xml.sax.helpers.AttributesImpl; + +import javax.jcr.ImportUUIDBehavior; +import javax.jcr.LoginException; +import javax.jcr.Node; +import javax.jcr.Property; +import javax.jcr.PropertyIterator; +import javax.jcr.PropertyType; +import javax.jcr.Session; +import javax.jcr.SimpleCredentials; +import javax.jcr.Value; +import javax.security.auth.login.CredentialExpiredException; +import java.util.HashMap; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Testing user import with default {@link org.apache.jackrabbit.oak.spi.xml.ImportBehavior} + * and initial-pw-change option enabled + */ +public class UserImportInitialPwChangeTest extends AbstractImportTest { + + @Override + protected String getTargetPath() { + return USERPATH; + } + + @Override + protected String getImportBehavior() { + return ImportBehavior.NAME_BESTEFFORT; + } + + @Nullable + protected ConfigurationParameters getConfigurationParameters() { + HashMap userParams = new HashMap() {{ + put(UserConstants.PARAM_PASSWORD_INITIAL_CHANGE, true); + }}; + return ConfigurationParameters.of(UserConfiguration.NAME, ConfigurationParameters.of(userParams)); + } + + @Test + public void testImportUserWithoutPwdNode() throws Exception { + // import user + String xml = "\n" + + "" + + " rep:User" + + " e358efa4-89f5-3062-b10d-d7316b65649e" + + " t" + + " {sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375" + + " t"+ + ""; + + doImport(USERPATH, xml); + + Authorizable authorizable = getUserManager().getAuthorizable("t"); + Node userNode = getImportSession().getNode(authorizable.getPath()); + assertFalse(userNode.hasNode(UserConstants.REP_PWD)); + getImportSession().save(); + + try { + getImportSession().getRepository().login(new SimpleCredentials("t", "t".toCharArray())).logout(); + fail("must be prompted for initial pw change!"); + } catch (LoginException e) { + assertTrue(e.getCause() instanceof CredentialExpiredException); + } + } + + @Test + public void testImportExistingUserWithoutPwdNode() throws Exception { + User user = getUserManager().createUser("t", "t"); + getImportSession().save(); + String userPath = user.getPath(); + String uuid = getImportSession().getProperty(PathUtils.concat(userPath, JcrConstants.JCR_UUID)).getString(); + + // import user + String xml = "\n" + + "" + + " rep:User" + + " "+uuid+"" + + " t" + + " {sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375" + + " t"+ + " " + + " "+ JcrConstants.NT_UNSTRUCTURED +"" + + " " + + ""; + + doImport(PathUtils.getParentPath(userPath), xml, ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING); + + Authorizable authorizable = getUserManager().getAuthorizable("t"); + Node userNode = getImportSession().getNode(authorizable.getPath()); + assertFalse(userNode.hasNode(UserConstants.REP_PWD)); + getImportSession().save(); + + try { + getImportSession().getRepository().login(new SimpleCredentials("t", "t".toCharArray())).logout(); + fail("must be prompted for initial pw change!"); + } catch (LoginException e) { + assertTrue(e.getCause() instanceof CredentialExpiredException); + } + } + + @Test + public void testImportUserWithPwdNodeMissingLastModified() throws Exception { + // import user + String xml = "\n" + + "" + + " rep:User" + + " e358efa4-89f5-3062-b10d-d7316b65649e" + + " t" + + " {sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375" + + " t"+ + " " + + " "+ UserConstants.NT_REP_PASSWORD +"" + + " " + + ""; + + doImport(USERPATH, xml); + + Authorizable authorizable = getUserManager().getAuthorizable("t"); + Node userNode = getImportSession().getNode(authorizable.getPath()); + assertTrue(userNode.hasNode(UserConstants.REP_PWD)); + Node pwdNode = userNode.getNode(UserConstants.REP_PWD); + assertFalse(pwdNode.hasProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED)); + getImportSession().save(); + + try { + getImportSession().getRepository().login(new SimpleCredentials("t", "t".toCharArray())).logout(); + fail("must be prompted for initial pw change!"); + } catch (LoginException e) { + assertTrue(e.getCause() instanceof CredentialExpiredException); + } + } + + @Test + public void testImportUserWithPwdNodeWithLastModified() throws Exception { + // import user + long now = System.currentTimeMillis(); + String xml = "\n" + + "" + + " rep:User" + + " e358efa4-89f5-3062-b10d-d7316b65649e" + + " t" + + " {sha1}8efd86fb78a56a5145ed7739dcb00c78581c5375" + + " t"+ + " " + + " "+ UserConstants.NT_REP_PASSWORD +"" + + " "+now+"" + + " " + + ""; + + doImport(USERPATH, xml); + + Authorizable authorizable = getUserManager().getAuthorizable("t"); + Node userNode = getImportSession().getNode(authorizable.getPath()); + assertTrue(userNode.hasNode(UserConstants.REP_PWD)); + Node pwdNode = userNode.getNode(UserConstants.REP_PWD); + assertTrue(pwdNode.hasProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED)); + assertEquals(now, pwdNode.getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED).getLong()); + getImportSession().save(); + + // login must succeed + getImportSession().getRepository().login(new SimpleCredentials("t", "t".toCharArray())).logout(); + } +}