[prev in list] [next in list] [prev in thread] [next in thread] 

List:       oss-security
Subject:    [oss-security] CVE-2020-1940: Apache Jackrabbit Oak sensitive information disclosure vulnerability
From:       Angela Schreiber <anchela () adobe ! com>
Date:       2020-01-28 14:26:26
Message-ID: MWHPR02MB22389492555FC92CF8BE4E5CB40A0 () MWHPR02MB2238 ! namprd02 ! prod ! outlook ! com
[Download RAW message or body]

[Attachment #2 (multipart/alternative)]


CVE-2020-1940:
Apache Jackrabbit Oak sensitive information disclosure vulnerability

Vendor:
The Apache Software Foundation

Versions Affected:
Apache Jackrabbit Oak (org.apache.jackrabbit.oak-core) 1.2.0 to 1.22.0

Description:
The optional initial password change and password expiration features [1] a=
re prone to a
sensitive information disclosure vulnerability. The code mandates the chang=
ed password to
be passed as an additional attribute to the credentials object but does not=
 remove it upon
processing during the first phase of the authentication. In combination wit=
h additional,
independent authentication mechanisms, this may lead to the new password be=
ing disclosed.

Mitigation:
1.12.0 - 1.22.0 should be upgraded to 1.24.0
1.10.x should be upgraded to 1.10.8
For older maintained and affected branches (1.2.x, 1.4.x, 1.6.x, 1.8.x) ple=
ase find patches attached.

Credits:
The issue was reported by Andrew Khoury and Russ Wright of Adobe.

References:
[1] http://jackrabbit.apache.org/oak/docs/security/user/expiry.html


[Attachment #5 (text/html)]

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<style type="text/css" style="display:none;"> P {margin-top:0;margin-bottom:0;} </style>
</head>
<body dir="ltr">
<div style="font-family: &quot;Courier New&quot;, monospace; font-size: 10pt; color: rgb(0, 0, \
0);"> <pre>CVE-2020-1940: 
Apache Jackrabbit Oak sensitive information disclosure vulnerability

Vendor: 
The Apache Software Foundation

Versions Affected: 
Apache Jackrabbit Oak (org.apache.jackrabbit.oak-core) 1.2.0 to 1.22.0

Description:
The optional initial password change and password expiration features [1] are prone to a
sensitive information disclosure vulnerability. The code mandates the changed password to
be passed as an additional attribute to the credentials object but does not remove it upon
processing during the first phase of the authentication. In combination with additional,
independent authentication mechanisms, this may lead to the new password being disclosed.

Mitigation:
1.12.0 - 1.22.0 should be upgraded to 1.24.0
1.10.x should be upgraded to 1.10.8
For older maintained and affected branches (1.2.x, 1.4.x, 1.6.x, 1.8.x) please find patches \
attached.

Credits:
The issue was reported by Andrew Khoury and Russ Wright of Adobe.

References:
[1] <a href="http://jackrabbit.apache.org/oak/docs/security/user/expiry.html" target="_blank" \
rel="noopener noreferrer">http://jackrabbit.apache.org/oak/docs/security/user/expiry.html</a> \
</pre> </div>
</body>
</html>


["UserAuthentication_1_2.patch" (application/octet-stream)]

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);
         }
     }
 }


["UserAuthentication_1_4.patch" (application/octet-stream)]

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 \
1579798784000) @@ -137,6 +137,8 @@
             }
         } catch (RepositoryException e) {
             throw new LoginException(e.getMessage());
+        } finally {
+            removeNewPwAttribute(credentials);
         }
         return success;
     }
@@ -180,6 +182,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 \
1579798724000) @@ -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/PasswordExpiryHistoryTest.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/PasswordExpiryHistoryTest.java	(revision \
                1873068)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryHistoryTest.java	(date \
1579798724000) @@ -36,6 +36,7 @@
 import javax.security.auth.login.CredentialExpiredException;
 import java.util.List;
 
+import static org.apache.jackrabbit.oak.spi.security.user.UserConstants.CREDENTIALS_ATTRIBUTE_NEWPASSWORD;
  import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
@@ -103,6 +104,8 @@
                         "credentials should contain pw change failure reason",
                         "New password is identical to the current password.",
                         attr);
+            } finally {
+                assertNull(pwChangeCreds.getAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD));
             }
         }
     }
@@ -134,6 +137,8 @@
                         "credentials should contain pw change failure reason",
                         "New password was found in password history.",
                         attr);
+            } finally {
+                assertNull(pwChangeCreds.getAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD));
             }
         }
     }
@@ -160,6 +165,8 @@
             } catch (CredentialExpiredException c) {
                 // success, pw found in history
                 assertNull(pwChangeCreds.getAttribute(PasswordHistoryException.class.getSimpleName()));
 +            } finally {
+                assertNull(pwChangeCreds.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 \
1579798784000) @@ -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);
         }
     }
 }


["UserAuthentication_1_6.patch" (application/octet-stream)]

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 \
1579796714000) @@ -137,6 +137,8 @@
             }
         } catch (RepositoryException e) {
             throw new LoginException(e.getMessage());
+        } finally {
+            removeNewPwAttribute(credentials);
         }
         return success;
     }
@@ -180,6 +182,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 \
1579796558000) @@ -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/PasswordExpiryHistoryTest.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/PasswordExpiryHistoryTest.java	(revision \
                1873068)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryHistoryTest.java	(date \
1579796558000) @@ -36,6 +36,7 @@
 import javax.security.auth.login.CredentialExpiredException;
 import java.util.List;
 
+import static org.apache.jackrabbit.oak.spi.security.user.UserConstants.CREDENTIALS_ATTRIBUTE_NEWPASSWORD;
  import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
@@ -103,6 +104,8 @@
                         "credentials should contain pw change failure reason",
                         "New password is identical to the current password.",
                         attr);
+            } finally {
+                assertNull(pwChangeCreds.getAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD));
             }
         }
     }
@@ -134,6 +137,8 @@
                         "credentials should contain pw change failure reason",
                         "New password was found in password history.",
                         attr);
+            } finally {
+                assertNull(pwChangeCreds.getAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD));
             }
         }
     }
@@ -160,6 +165,8 @@
             } catch (CredentialExpiredException c) {
                 // success, pw found in history
                 assertNull(pwChangeCreds.getAttribute(PasswordHistoryException.class.getSimpleName()));
 +            } finally {
+                assertNull(pwChangeCreds.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 \
1579796714000) @@ -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);
         }
     }
 }


["UserAuthentication_1-8.patch" (application/octet-stream)]

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 \
1579795776000) @@ -142,6 +142,8 @@
             principal = user.getPrincipal();
         } catch (RepositoryException e) {
             throw new LoginException(e.getMessage());
+        } finally {
+            removeNewPwAttribute(credentials);
         }
         return success;
     }
@@ -204,6 +206,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(@NotNull 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 \
1579795828000) @@ -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/PasswordExpiryHistoryTest.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/PasswordExpiryHistoryTest.java	(revision \
                1873068)
+++ oak-core/src/test/java/org/apache/jackrabbit/oak/security/user/PasswordExpiryHistoryTest.java	(date \
1579795828000) @@ -36,6 +36,7 @@
 import javax.security.auth.login.CredentialExpiredException;
 import java.util.List;
 
+import static org.apache.jackrabbit.oak.spi.security.user.UserConstants.CREDENTIALS_ATTRIBUTE_NEWPASSWORD;
  import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
@@ -103,6 +104,8 @@
                         "credentials should contain pw change failure reason",
                         "New password is identical to the current password.",
                         attr);
+            } finally {
+                assertNull(pwChangeCreds.getAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD));
             }
         }
     }
@@ -134,6 +137,8 @@
                         "credentials should contain pw change failure reason",
                         "New password was found in password history.",
                         attr);
+            } finally {
+                assertNull(pwChangeCreds.getAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD));
             }
         }
     }
@@ -160,6 +165,8 @@
             } catch (CredentialExpiredException c) {
                 // success, pw found in history
                 assertNull(pwChangeCreds.getAttribute(PasswordHistoryException.class.getSimpleName()));
 +            } finally {
+                assertNull(pwChangeCreds.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 \
1579795828000) @@ -30,10 +30,13 @@
 import org.apache.jackrabbit.oak.spi.security.user.UserConfiguration;
 import org.apache.jackrabbit.oak.spi.security.user.UserConstants;
 import org.apache.jackrabbit.oak.spi.security.user.util.PasswordUtil;
+import org.jetbrains.annotations.Nullable;
 import org.junit.Before;
 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 +46,7 @@
 public class ResetExpiredPasswordTest extends AbstractSecurityTest implements UserConstants {
 
     private String userId;
+    private SimpleCredentials creds;
 
     @Before
     public void before() throws Exception {
@@ -63,13 +67,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(@Nullable SimpleCredentials sc) {
+        assertNotNull(sc);
+        assertNull(sc.getAttribute(CREDENTIALS_ATTRIBUTE_NEWPASSWORD));
+    }
+
     @Test
     public void testPasswordChangePersisted() throws Exception {
         authenticate(userId, "newPw");
@@ -78,11 +87,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 +107,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 +122,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);
         }
     }
 }



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic