Author: angela
Date: Tue Oct 30 09:19:18 2018
New Revision: 1845206

URL: http://svn.apache.org/viewvc?rev=1845206&view=rev
Log:
OAK-7863 : Allow for admin password to expire

Added:
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryAdminTest.java
      - copied, changed from r1844647, 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryTest.java
Modified:
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserConfigurationImpl.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/Utils.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryTest.java
    jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/expiry.md

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java?rev=1845206&r1=1845205&r2=1845206&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserAuthentication.java
 Tue Oct 30 09:19:18 2018
@@ -79,6 +79,8 @@ class UserAuthentication implements Auth
 
     private static final Logger log = 
LoggerFactory.getLogger(UserAuthentication.class);
 
+    public static final String PARAM_PASSWORD_EXPIRY_FOR_ADMIN = 
"passwordExpiryForAdmin";
+
     private final UserConfiguration config;
     private final Root root;
     private final String loginId;
@@ -242,13 +244,13 @@ class UserAuthentication implements Auth
     }
 
     private boolean isPasswordExpired(@NotNull User user) throws 
RepositoryException {
-        // the password of the "admin" user never expires
-        if (user.isAdmin()) {
+        ConfigurationParameters params = config.getParameters();
+        // unless PARAM_PASSWORD_EXPIRY_FOR_ADMIN is enabled, the password of 
the "admin" user never expires
+        if (!Utils.canHavePasswordExpired(user, params)) {
             return false;
         }
 
         boolean expired = false;
-        ConfigurationParameters params = config.getParameters();
         int maxAge = params.getConfigValue(PARAM_PASSWORD_MAX_AGE, 
DEFAULT_PASSWORD_MAX_AGE);
         boolean forceInitialPwChange = 
params.getConfigValue(PARAM_PASSWORD_INITIAL_CHANGE, 
DEFAULT_PASSWORD_INITIAL_CHANGE);
         if (maxAge > 0) {

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserConfigurationImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserConfigurationImpl.java?rev=1845206&r1=1845205&r2=1845206&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserConfigurationImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserConfigurationImpl.java
 Tue Oct 30 09:19:18 2018
@@ -151,6 +151,12 @@ public class UserConfigurationImpl exten
         int passwordHistorySize() default 
UserConstants.PASSWORD_HISTORY_DISABLED_SIZE;
 
         @AttributeDefinition(
+                name = "Enable Password Expiry for Admin User",
+                description = "When enabled, the admin user will also be 
subject to password expiry. The default value is false for backwards 
compatibility."
+        )
+        boolean passwordExpiryForAdmin() default false;
+
+        @AttributeDefinition(
                 name = "Principal Cache Expiration",
                 description = "Optional configuration defining the number of 
milliseconds " +
                         "until the principal cache expires (NOTE: currently 
only respected for principal resolution " +

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java?rev=1845206&r1=1845205&r2=1845206&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/UserManagerImpl.java
 Tue Oct 30 09:19:18 2018
@@ -471,7 +471,7 @@ public class UserManagerImpl implements
         boolean forceInitialPwChange = forceInitialPasswordChangeEnabled();
         boolean isNewUser = userTree.getStatus() == Tree.Status.NEW;
 
-        if (!UserUtil.isAdmin(config, userId)
+        if (Utils.canHavePasswordExpired(userId, config)
                 // only expiry is enabled, set in all cases
                 && ((expiryEnabled && !forceInitialPwChange)
                 // as soon as force initial pw is enabled, we set in all cases 
except new users,

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/Utils.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/Utils.java?rev=1845206&r1=1845205&r2=1845206&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/Utils.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/security/user/Utils.java
 Tue Oct 30 09:19:18 2018
@@ -18,13 +18,16 @@ package org.apache.jackrabbit.oak.securi
 
 import javax.jcr.AccessDeniedException;
 
+import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.oak.api.Tree;
 import org.apache.jackrabbit.oak.commons.PathUtils;
 import org.apache.jackrabbit.oak.plugins.tree.TreeUtil;
+import org.apache.jackrabbit.oak.spi.security.ConfigurationParameters;
+import org.apache.jackrabbit.oak.spi.security.user.util.UserUtil;
 import org.apache.jackrabbit.util.Text;
 import org.jetbrains.annotations.NotNull;
 
-class Utils {
+final class Utils {
 
     private Utils() {}
 
@@ -76,4 +79,20 @@ class Utils {
             }
         }
     }
+
+    static boolean canHavePasswordExpired(@NotNull String userId, @NotNull 
ConfigurationParameters config) {
+        if (UserUtil.isAdmin(config, userId) && 
!config.getConfigValue(UserAuthentication.PARAM_PASSWORD_EXPIRY_FOR_ADMIN, 
false)) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    static boolean canHavePasswordExpired(@NotNull User user, @NotNull 
ConfigurationParameters config) {
+        if (user.isAdmin() && 
!config.getConfigValue(UserAuthentication.PARAM_PASSWORD_EXPIRY_FOR_ADMIN, 
false)) {
+            return false;
+        } else {
+            return true;
+        }
+    }
 }

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java?rev=1845206&r1=1845205&r2=1845206&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/AbstractSecurityTest.java
 Tue Oct 30 09:19:18 2018
@@ -16,12 +16,9 @@
  */
 package org.apache.jackrabbit.oak;
 
-import static com.google.common.collect.Lists.newArrayList;
-
 import java.util.Arrays;
 import java.util.List;
 import java.util.UUID;
-
 import javax.jcr.Credentials;
 import javax.jcr.NoSuchWorkspaceException;
 import javax.jcr.RepositoryException;
@@ -71,6 +68,8 @@ import org.jetbrains.annotations.Nullabl
 import org.junit.After;
 import org.junit.Before;
 
+import static com.google.common.collect.Lists.newArrayList;
+
 /**
  * AbstractOakTest is the base class for oak test execution.
  */
@@ -108,7 +107,7 @@ public abstract class AbstractSecurityTe
         withEditors(oak);
         contentRepository = oak.createContentRepository();
 
-        adminSession = login(getAdminCredentials());
+        adminSession = createAdminSession(contentRepository);
         root = adminSession.getLatestRoot();
 
         Configuration.setConfiguration(getConfiguration());
@@ -177,6 +176,11 @@ public abstract class AbstractSecurityTe
         return new SimpleCredentials(adminId, adminId.toCharArray());
     }
 
+    @NotNull
+    protected ContentSession createAdminSession(@NotNull ContentRepository 
repository) throws LoginException, NoSuchWorkspaceException {
+        return repository.login(getAdminCredentials(), null);
+    }
+
     protected NamePathMapper getNamePathMapper() {
         return namePathMapper;
     }

Copied: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryAdminTest.java
 (from r1844647, 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryTest.java)
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryAdminTest.java?p2=jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryAdminTest.java&p1=jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryTest.java&r1=1844647&r2=1845206&rev=1845206&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryAdminTest.java
 Tue Oct 30 09:19:18 2018
@@ -16,23 +16,30 @@
  */
 package org.apache.jackrabbit.oak.security.user;
 
-import java.util.UUID;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import javax.jcr.NoSuchWorkspaceException;
 import javax.jcr.SimpleCredentials;
+import javax.security.auth.Subject;
 import javax.security.auth.login.CredentialExpiredException;
 import javax.security.auth.login.LoginException;
 
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.oak.AbstractSecurityTest;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.ContentSession;
 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.nodetype.NodeTypeConstants;
 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.spi.security.ConfigurationParameters;
 import org.apache.jackrabbit.oak.spi.security.authentication.Authentication;
+import org.apache.jackrabbit.oak.spi.security.authentication.SystemSubject;
 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;
 
@@ -42,64 +49,67 @@ import static org.junit.Assert.assertNot
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-/**
- * @see <a href="https://issues.apache.org/jira/browse/OAK-1922";>OAK-1922</a>
- */
-public class PasswordExpiryTest extends AbstractSecurityTest {
+public class PasswordExpiryAdminTest extends AbstractSecurityTest {
 
+    private User user;
     private String userId;
 
     @Before
     public void before() throws Exception {
         super.before();
-        userId = getTestUser().getID();
+        user = 
getUserManager(root).getAuthorizable(UserConstants.DEFAULT_ADMIN_ID, 
User.class);
+        userId = user.getID();
     }
 
+
     @Override
     protected ConfigurationParameters getSecurityConfigParameters() {
-        ConfigurationParameters userConfig = 
ConfigurationParameters.of(UserConstants.PARAM_PASSWORD_MAX_AGE, 10);
+        ConfigurationParameters userConfig = ConfigurationParameters.of(
+                UserConstants.PARAM_PASSWORD_MAX_AGE, 10,
+                UserAuthentication.PARAM_PASSWORD_EXPIRY_FOR_ADMIN, true);
         return ConfigurationParameters.of(UserConfiguration.NAME, userConfig);
     }
 
-    @Test
-    public void testCreateUser() throws Exception {
-        String newUserId = "newuser" + UUID.randomUUID();
-        User user = null;
-
+    @NotNull
+    @Override
+    protected ContentSession createAdminSession(@NotNull ContentRepository 
repository) throws LoginException, NoSuchWorkspaceException {
         try {
-            user = getUserManager(root).createUser(newUserId, newUserId);
-            root.commit();
-
-            Tree pwdTree = 
root.getTree(user.getPath()).getChild(UserConstants.REP_PWD);
-            assertTrue(pwdTree.exists());
-            assertTrue(TreeUtil.isNodeType(pwdTree, 
UserConstants.NT_REP_PASSWORD, 
root.getTree(NodeTypeConstants.NODE_TYPES_PATH)));
-
-            ReadOnlyNodeTypeManager ntMgr = 
ReadOnlyNodeTypeManager.getInstance(root, getNamePathMapper());
-            assertTrue(ntMgr.getDefinition(pwdTree.getParent(), 
pwdTree).isProtected());
-
-            PropertyState property = 
pwdTree.getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED);
-            assertNotNull(property);
-            assertEquals(Type.LONG, property.getType());
-            assertTrue(property.getValue(Type.LONG, 0) > 0);
-
-            // protected properties must not be exposed by User#hasProperty
-            assertFalse(user.hasProperty(UserConstants.REP_PWD + "/" + 
UserConstants.REP_PASSWORD_LAST_MODIFIED));
-        } finally {
-            if (user != null) {
-                user.remove();
-                root.commit();
-            }
+            return Subject.doAs(SystemSubject.INSTANCE, new 
PrivilegedExceptionAction<ContentSession>() {
+                @Override
+                public ContentSession run() throws NoSuchWorkspaceException, 
LoginException {
+                    return repository.login(null, null);
+                }
+            });
+        } catch (PrivilegedActionException e) {
+            throw new RuntimeException(e);
         }
     }
 
     @Test
+    public void testUserNode() throws Exception {
+        Tree pwdTree = 
root.getTree(user.getPath()).getChild(UserConstants.REP_PWD);
+        assertTrue(pwdTree.exists());
+        assertTrue(TreeUtil.isNodeType(pwdTree, UserConstants.NT_REP_PASSWORD, 
root.getTree(NodeTypeConstants.NODE_TYPES_PATH)));
+
+        ReadOnlyNodeTypeManager ntMgr = 
ReadOnlyNodeTypeManager.getInstance(root, getNamePathMapper());
+        assertTrue(ntMgr.getDefinition(pwdTree.getParent(), 
pwdTree).isProtected());
+
+        PropertyState property = 
pwdTree.getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED);
+        assertNotNull(property);
+        assertEquals(Type.LONG, property.getType());
+        assertTrue(property.getValue(Type.LONG, 0) > 0);
+
+        // protected properties must not be exposed by User#hasProperty
+        assertFalse(user.hasProperty(UserConstants.REP_PWD + "/" + 
UserConstants.REP_PASSWORD_LAST_MODIFIED));
+    }
+
+    @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);
         long oldModTime = p1.getValue(Type.LONG, 0);
         assertTrue(oldModTime > 0);
         waitForSystemTimeIncrement(oldModTime);
-        user.changePassword(userId);
+        user.changePassword(user.getID());
         root.commit();
         PropertyState p2 = 
root.getTree(user.getPath()).getChild(UserConstants.REP_PWD).getProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED);
         long newModTime = p2.getValue(Type.LONG, 0);
@@ -117,7 +127,7 @@ public class PasswordExpiryTest extends
     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);
+        
root.getTree(user.getPath()).getChild(UserConstants.REP_PWD).setProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED,
 0);
         root.commit();
         try {
             a.authenticate(new SimpleCredentials(userId, 
userId.toCharArray()));
@@ -131,7 +141,7 @@ public class PasswordExpiryTest extends
     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);
+        
root.getTree(user.getPath()).getChild(UserConstants.REP_PWD).setProperty(UserConstants.REP_PASSWORD_LAST_MODIFIED,
 0);
         root.commit();
         try {
             a.authenticate(new SimpleCredentials(userId, 
"wrong".toCharArray()));
@@ -146,11 +156,11 @@ public class PasswordExpiryTest extends
     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);
+        
root.getTree(user.getPath()).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
-        getTestUser().changePassword(userId);
+        user.changePassword(userId);
         root.commit();
         assertTrue(a.authenticate(new SimpleCredentials(userId, 
userId.toCharArray())));
     }

Modified: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryTest.java?rev=1845206&r1=1845205&r2=1845206&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryTest.java
 Tue Oct 30 09:19:18 2018
@@ -154,4 +154,10 @@ public class PasswordExpiryTest extends
         root.commit();
         assertTrue(a.authenticate(new SimpleCredentials(userId, 
userId.toCharArray())));
     }
+
+    @Test
+    public void testByDefaultAdminHasNoPwNode() throws Exception {
+        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());
+    }
 }

Modified: jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/expiry.md
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/expiry.md?rev=1845206&r1=1845205&r2=1845206&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/expiry.md 
(original)
+++ jackrabbit/oak/trunk/oak-doc/src/site/markdown/security/user/expiry.md Tue 
Oct 30 09:19:18 2018
@@ -47,10 +47,11 @@ OSGi configuration. By default both feat
 
 The following configuration options are supported:
 
-| Parameter                       | Type    | Default  | Description           
 |
-|-------------------------------------      |-------- 
-|------------------------|
-| `PARAM_PASSWORD_MAX_AGE`        | int     | 0        | Number of days until 
the password expires. |
-| `PARAM_PASSWORD_INITIAL_CHANGE` | boolean | false    | boolean flag to 
enable initial pw change.  |
+| Parameter                         | Type    | Default  | Description         
                       |
+|-----------------------------------|---------|----------|--------------------------------------------|
+| `PARAM_PASSWORD_MAX_AGE`          | int     | 0        | Number of days 
until the password expires. |
+| `PARAM_PASSWORD_INITIAL_CHANGE`   | boolean | false    | boolean flag to 
enable initial pw change.  |
+| `PARAM_PASSWORD_EXPIRY_FOR_ADMIN` | boolean | false    | flag to enable pw 
expiry for admin user.   |
 
 Note:
 


Reply via email to