You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2015/11/12 10:24:10 UTC

svn commit: r1713987 - in /tomcat/trunk: java/org/apache/catalina/realm/JNDIRealm.java test/org/apache/catalina/realm/TestJNDIRealm.java

Author: markt
Date: Thu Nov 12 09:24:10 2015
New Revision: 1713987

URL: http://svn.apache.org/viewvc?rev=1713987&view=rev
Log:
Add support for DIGEST authentication to the JNDIRealm
Based on a patch by Alexis Hassler
This closes #24

Added:
    tomcat/trunk/test/org/apache/catalina/realm/TestJNDIRealm.java   (with props)
Modified:
    tomcat/trunk/java/org/apache/catalina/realm/JNDIRealm.java

Modified: tomcat/trunk/java/org/apache/catalina/realm/JNDIRealm.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/realm/JNDIRealm.java?rev=1713987&r1=1713986&r2=1713987&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/realm/JNDIRealm.java (original)
+++ tomcat/trunk/java/org/apache/catalina/realm/JNDIRealm.java Thu Nov 12 09:24:10 2015
@@ -2178,8 +2178,23 @@ public class JNDIRealm extends RealmBase
      */
     @Override
     protected String getPassword(String username) {
+        String userPassword = getUserPassword();
+        if (userPassword == null || userPassword.isEmpty()) {
+            return null;
+        }
 
-        return (null);
+        try {
+            User user = getUser(open(), username, null);
+             if (user == null) {
+                // User should be found...
+                return null;
+            } else {
+                // ... and have a password
+                return user.getPassword();
+            }
+        } catch (NamingException e) {
+            return null;
+        }
 
     }
 

Added: tomcat/trunk/test/org/apache/catalina/realm/TestJNDIRealm.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/realm/TestJNDIRealm.java?rev=1713987&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/realm/TestJNDIRealm.java (added)
+++ tomcat/trunk/test/org/apache/catalina/realm/TestJNDIRealm.java Thu Nov 12 09:24:10 2015
@@ -0,0 +1,153 @@
+package org.apache.catalina.realm;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.core.TesterContext;
+import org.apache.naming.NameParserImpl;
+import org.apache.tomcat.util.security.MD5Encoder;
+import org.easymock.EasyMock;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import javax.naming.NamingEnumeration;
+import javax.naming.NamingException;
+import javax.naming.directory.*;
+import java.lang.reflect.Field;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.Principal;
+
+import static org.easymock.EasyMock.*;
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.assertThat;
+
+public class TestJNDIRealm {
+
+    private static final String ALGORITHM = "MD5";
+
+    private static final String USER = "test-user";
+    private static final String PASSWORD = "test-password";
+    private static final String REALM = "test-realm";
+
+    private static final String NONCE = "test-nonce";
+    private static final String HA2 = "test-md5a2";
+    public static final String USER_PASSWORD_ATTR = "test-pwd";
+
+    private static MessageDigest md5Helper;
+
+    @BeforeClass
+    public static void setupClass() throws Exception {
+        md5Helper = MessageDigest.getInstance(ALGORITHM);
+    }
+
+    @Test
+    public void testAuthenticateWithoutUserPassword() throws Exception {
+        // GIVEN
+        JNDIRealm realm = buildRealm(PASSWORD);
+
+        // WHEN
+        String expectedResponse =
+                MD5Encoder.encode(md5Helper.digest((ha1() + ":" + NONCE + ":" + HA2).getBytes()));
+        Principal principal =
+                realm.authenticate(USER, expectedResponse, NONCE, null, null, null, REALM, HA2);
+
+        // THEN
+        assertThat(principal, is(nullValue()));
+    }
+
+    @Test
+    public void testAuthenticateWithUserPassword() throws Exception {
+        // GIVEN
+        JNDIRealm realm = buildRealm(PASSWORD);
+        realm.setUserPassword(USER_PASSWORD_ATTR);
+
+        // WHEN
+        String expectedResponse =
+                MD5Encoder.encode(md5Helper.digest((ha1() + ":" + NONCE + ":" + HA2).getBytes()));
+        Principal principal =
+                realm.authenticate(USER, expectedResponse, NONCE, null, null, null, REALM, HA2);
+
+        // THEN
+        assertThat(principal, is(instanceOf(GenericPrincipal.class)));
+        assertThat( ((GenericPrincipal)principal).getPassword(), equalTo(PASSWORD));
+    }
+
+    @Test
+    public void testAuthenticateWithUserPasswordAndCredentialHandler() throws Exception {
+        // GIVEN
+        JNDIRealm realm = buildRealm(ha1());
+        realm.setCredentialHandler(buildCredentialHandler());
+        realm.setUserPassword(USER_PASSWORD_ATTR);
+
+        // WHEN
+        String expectedResponse =
+                MD5Encoder.encode(md5Helper.digest((ha1() + ":" + NONCE + ":" + HA2).getBytes()));
+        Principal principal =
+                realm.authenticate(USER, expectedResponse, NONCE, null, null, null, REALM, HA2);
+
+        // THEN
+        assertThat(principal, is(instanceOf(GenericPrincipal.class)));
+        assertThat( ((GenericPrincipal)principal).getPassword(), equalTo(ha1()));
+    }
+
+
+    private JNDIRealm buildRealm(String password) throws javax.naming.NamingException,
+            NoSuchFieldException, IllegalAccessException, LifecycleException {
+        Context context = new TesterContext();
+        JNDIRealm realm = new JNDIRealm();
+        realm.setContainer(context);
+        realm.setUserSearch("");
+
+        Field field = JNDIRealm.class.getDeclaredField("context");
+        field.setAccessible(true);
+        field.set(realm, mockDirContext(mockSearchResults(password)));
+
+        realm.start();
+
+        return realm;
+    }
+
+    private MessageDigestCredentialHandler buildCredentialHandler()
+            throws NoSuchAlgorithmException {
+        MessageDigestCredentialHandler credentialHandler = new MessageDigestCredentialHandler();
+        credentialHandler.setAlgorithm(ALGORITHM);
+        return credentialHandler;
+    }
+
+    private NamingEnumeration<SearchResult> mockSearchResults(String password)
+            throws NamingException {
+        @SuppressWarnings("unchecked")
+        NamingEnumeration<SearchResult> searchResults = createNiceMock(NamingEnumeration.class);
+        expect(Boolean.valueOf(searchResults.hasMore()))
+                .andReturn(Boolean.TRUE)
+                .andReturn(Boolean.FALSE)
+                .andReturn(Boolean.TRUE)
+                .andReturn(Boolean.FALSE);
+        expect(searchResults.next())
+                .andReturn(new SearchResult("ANY RESULT", "",
+                        new BasicAttributes(USER_PASSWORD_ATTR, password)))
+                .times(2);
+        EasyMock.replay(searchResults);
+        return searchResults;
+    }
+
+    private DirContext mockDirContext(NamingEnumeration<SearchResult> namingEnumeration)
+            throws NamingException {
+        DirContext dirContext = createNiceMock(InitialDirContext.class);
+        expect(dirContext.search(anyString(), anyString(), anyObject(SearchControls.class)))
+                .andReturn(namingEnumeration)
+                .times(2);
+        expect(dirContext.getNameParser(""))
+                .andReturn(new NameParserImpl()).times(2);
+        expect(dirContext.getNameInNamespace())
+                .andReturn("ANY NAME")
+                .times(2);
+        EasyMock.replay(dirContext);
+        return dirContext;
+    }
+
+    private String ha1() {
+        String a1 = USER + ":" + REALM + ":" + PASSWORD;
+        return MD5Encoder.encode(md5Helper.digest(a1.getBytes()));
+    }
+}

Propchange: tomcat/trunk/test/org/apache/catalina/realm/TestJNDIRealm.java
------------------------------------------------------------------------------
    svn:eol-style = native



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org