You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shiro.apache.org by bd...@apache.org on 2014/02/28 04:53:44 UTC
svn commit: r1572813 - in /shiro/trunk/core/src:
main/java/org/apache/shiro/realm/ldap/ test/java/org/apache/shiro/realm/ldap/
Author: bdemers
Date: Fri Feb 28 03:53:44 2014
New Revision: 1572813
URL: http://svn.apache.org/r1572813
Log:
Updating JndiLdapContextFactory to conform to spec rfc4513 section-5.1.2
Added:
shiro/trunk/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapContextFactoryTest.java (with props)
Modified:
shiro/trunk/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapContextFactory.java
shiro/trunk/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java
shiro/trunk/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapContextFactoryTest.java
Modified: shiro/trunk/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapContextFactory.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapContextFactory.java?rev=1572813&r1=1572812&r2=1572813&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapContextFactory.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapContextFactory.java Fri Feb 28 03:53:44 2014
@@ -20,11 +20,13 @@ package org.apache.shiro.realm.ldap;
import java.util.Hashtable;
import java.util.Map;
+import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
+import org.apache.shiro.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -55,6 +57,7 @@ public class DefaultLdapContextFactory i
* to enable LDAP connection pooling.
*/
protected static final String SUN_CONNECTION_POOLING_PROPERTY = "com.sun.jndi.ldap.connect.pool";
+ private static final String SIMPLE_AUTHENTICATION_MECHANISM_NAME = "simple";
/*--------------------------------------------
| I N S T A N C E V A R I A B L E S |
@@ -62,7 +65,7 @@ public class DefaultLdapContextFactory i
private static final Logger log = LoggerFactory.getLogger(DefaultLdapContextFactory.class);
- protected String authentication = "simple";
+ protected String authentication = SIMPLE_AUTHENTICATION_MECHANISM_NAME;
protected String principalSuffix = null;
@@ -254,6 +257,61 @@ public class DefaultLdapContextFactory i
"with pooling [" + (usePooling ? "enabled" : "disabled") + "]");
}
+ // validate the config before creating the context
+ validateAuthenticationInfo(env);
+
+ return createLdapContext(env);
+ }
+
+ /**
+ * Creates and returns a new {@link javax.naming.ldap.InitialLdapContext} instance. This method exists primarily
+ * to support testing where a mock LdapContext can be returned instead of actually creating a connection, but
+ * subclasses are free to provide a different implementation if necessary.
+ *
+ * @param env the JNDI environment settings used to create the LDAP connection
+ * @return an LdapConnection
+ * @throws NamingException if a problem occurs creating the connection
+ */
+ protected LdapContext createLdapContext(Hashtable env) throws NamingException {
return new InitialLdapContext(env, null);
}
-}
+
+
+ /**
+ * Validates the configuration in the JNDI <code>environment</code> settings and throws an exception if a problem
+ * exists.
+ * <p/>
+ * This implementation will throw a {@link AuthenticationException} if the authentication mechanism is set to
+ * 'simple', the principal is non-empty, and the credentials are empty (as per
+ * <a href="http://tools.ietf.org/html/rfc4513#section-5.1.2">rfc4513 section-5.1.2</a>).
+ *
+ * @param environment the JNDI environment settings to be validated
+ * @throws AuthenticationException if a configuration problem is detected
+ */
+ private void validateAuthenticationInfo(Hashtable<String, Object> environment)
+ throws AuthenticationException
+ {
+ // validate when using Simple auth both principal and credentials are set
+ if(SIMPLE_AUTHENTICATION_MECHANISM_NAME.equals(environment.get(Context.SECURITY_AUTHENTICATION))) {
+
+ // only validate credentials if we have a non-empty principal
+ if( environment.get(Context.SECURITY_PRINCIPAL) != null &&
+ StringUtils.hasText( String.valueOf( environment.get(Context.SECURITY_PRINCIPAL) ))) {
+
+ Object credentials = environment.get(Context.SECURITY_CREDENTIALS);
+
+ // from the FAQ, we need to check for empty credentials:
+ // http://docs.oracle.com/javase/tutorial/jndi/ldap/faq.html
+ if( credentials == null ||
+ (credentials instanceof byte[] && ((byte[])credentials).length <= 0) || // empty byte[]
+ (credentials instanceof char[] && ((char[])credentials).length <= 0) || // empty char[]
+ (String.class.isInstance(credentials) && !StringUtils.hasText(String.valueOf(credentials)))) {
+
+ throw new javax.naming.AuthenticationException("LDAP Simple authentication requires both a "
+ + "principal and credentials.");
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
Modified: shiro/trunk/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java?rev=1572813&r1=1572812&r2=1572813&view=diff
==============================================================================
--- shiro/trunk/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java (original)
+++ shiro/trunk/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java Fri Feb 28 03:53:44 2014
@@ -22,6 +22,7 @@ import org.apache.shiro.util.StringUtils
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.ldap.InitialLdapContext;
@@ -488,6 +489,9 @@ public class JndiLdapContextFactory impl
new Object[]{url, principal, (pooling ? "enabled" : "disabled")});
}
+ // validate the config before creating the context
+ validateAuthenticationInfo(env);
+
return createLdapContext(env);
}
@@ -504,4 +508,42 @@ public class JndiLdapContextFactory impl
return new InitialLdapContext(env, null);
}
+
+ /**
+ * Validates the configuration in the JNDI <code>environment</code> settings and throws an exception if a problem
+ * exists.
+ * <p/>
+ * This implementation will throw a {@link AuthenticationException} if the authentication mechanism is set to
+ * 'simple', the principal is non-empty, and the credentials are empty (as per
+ * <a href="http://tools.ietf.org/html/rfc4513#section-5.1.2">rfc4513 section-5.1.2</a>).
+ *
+ * @param environment the JNDI environment settings to be validated
+ * @throws AuthenticationException if a configuration problem is detected
+ */
+ protected void validateAuthenticationInfo(Hashtable<String, Object> environment)
+ throws AuthenticationException
+ {
+ // validate when using Simple auth both principal and credentials are set
+ if(SIMPLE_AUTHENTICATION_MECHANISM_NAME.equals(environment.get(Context.SECURITY_AUTHENTICATION))) {
+
+ // only validate credentials if we have a non-empty principal
+ if( environment.get(Context.SECURITY_PRINCIPAL) != null &&
+ StringUtils.hasText( String.valueOf( environment.get(Context.SECURITY_PRINCIPAL) ))) {
+
+ Object credentials = environment.get(Context.SECURITY_CREDENTIALS);
+
+ // from the FAQ, we need to check for empty credentials:
+ // http://docs.oracle.com/javase/tutorial/jndi/ldap/faq.html
+ if( credentials == null ||
+ (credentials instanceof byte[] && ((byte[])credentials).length <= 0) || // empty byte[]
+ (credentials instanceof char[] && ((char[])credentials).length <= 0) || // empty char[]
+ (String.class.isInstance(credentials) && !StringUtils.hasText(String.valueOf(credentials)))) {
+
+ throw new javax.naming.AuthenticationException("LDAP Simple authentication requires both a "
+ + "principal and credentials.");
+ }
+ }
+ }
+ }
+
}
Added: shiro/trunk/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapContextFactoryTest.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapContextFactoryTest.java?rev=1572813&view=auto
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapContextFactoryTest.java (added)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapContextFactoryTest.java Fri Feb 28 03:53:44 2014
@@ -0,0 +1,106 @@
+/*
+ * 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.shiro.realm.ldap;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.naming.AuthenticationException;
+import javax.naming.Context;
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.UUID;
+
+import static junit.framework.Assert.*;
+import static org.easymock.EasyMock.createNiceMock;
+
+/**
+ * Tests for the {@link org.apache.shiro.realm.ldap.DefaultLdapContextFactory} class.
+ *
+ * @since 1.2
+ */
+public class DefaultLdapContextFactoryTest
+{
+
+ private DefaultLdapContextFactory factory;
+
+ @Before
+ public void setUp() {
+ factory = new DefaultLdapContextFactory() {
+ //Fake a JNDI DefaultLdapContextFactory for the tests:
+ @Override
+ protected LdapContext createLdapContext(Hashtable env) throws NamingException {
+ return createNiceMock(LdapContext.class);
+ }
+ };
+ }
+
+ /**
+ * This is the only test that does not fake the JNDI environment. It is provided for 100% test coverage.
+ *
+ * @throws javax.naming.NamingException thrown because the host is always broken.
+ */
+ @Test(expected = NamingException.class)
+ public void testGetLdapContext() throws NamingException {
+ factory = new DefaultLdapContextFactory();
+ //garbage URL to test that the context is being created, but fails:
+ String brokenHost = UUID.randomUUID().toString();
+ factory.setUrl("ldap://" + brokenHost + ":389");
+ factory.getLdapContext((Object) "foo", "bar");
+ }
+
+ @Test(expected = IllegalStateException.class)
+ public void testGetLdapContextWithoutUrl() throws NamingException {
+ factory.getLdapContext((Object) "foo", "bar");
+ }
+
+
+
+
+
+ @Test(expected = AuthenticationException.class)
+ public void testEmptyStringCredentials() throws NamingException {
+ factory.setUrl("ldap://localhost:389");
+ factory.getLdapContext((Object)"jcoder", "");
+ }
+
+ @Test(expected = AuthenticationException.class)
+ public void testEmptyCharArrayCredentials() throws NamingException {
+ factory.setUrl("ldap://localhost:389");
+ factory.getLdapContext((Object)"jcoder", new char[0]);
+ }
+
+ @Test(expected = AuthenticationException.class)
+ public void testEmptyByteArrayCredentials() throws NamingException {
+ factory.setUrl("ldap://localhost:389");
+ factory.getLdapContext((Object)"jcoder", new byte[0]);
+ }
+
+ @Test(expected = AuthenticationException.class)
+ public void testEmptyNullCredentials() throws NamingException {
+ factory.setUrl("ldap://localhost:389");
+ factory.getLdapContext((Object)"jcoder", null);
+ }
+
+
+
+}
Propchange: shiro/trunk/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapContextFactoryTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: shiro/trunk/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapContextFactoryTest.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Modified: shiro/trunk/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapContextFactoryTest.java
URL: http://svn.apache.org/viewvc/shiro/trunk/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapContextFactoryTest.java?rev=1572813&r1=1572812&r2=1572813&view=diff
==============================================================================
--- shiro/trunk/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapContextFactoryTest.java (original)
+++ shiro/trunk/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapContextFactoryTest.java Fri Feb 28 03:53:44 2014
@@ -21,6 +21,7 @@ package org.apache.shiro.realm.ldap;
import org.junit.Before;
import org.junit.Test;
+import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.naming.ldap.LdapContext;
@@ -183,5 +184,30 @@ public class JndiLdapContextFactoryTest
factory.getSystemLdapContext();
}
+ @Test(expected = AuthenticationException.class)
+ public void testEmptyStringCredentials() throws NamingException {
+ factory.setUrl("ldap://localhost:389");
+ factory.getLdapContext((Object)"jcoder", "");
+ }
+
+ @Test(expected = AuthenticationException.class)
+ public void testEmptyCharArrayCredentials() throws NamingException {
+ factory.setUrl("ldap://localhost:389");
+ factory.getLdapContext((Object)"jcoder", new char[0]);
+ }
+
+ @Test(expected = AuthenticationException.class)
+ public void testEmptyByteArrayCredentials() throws NamingException {
+ factory.setUrl("ldap://localhost:389");
+ factory.getLdapContext((Object)"jcoder", new byte[0]);
+ }
+
+ @Test(expected = AuthenticationException.class)
+ public void testEmptyNullCredentials() throws NamingException {
+ factory.setUrl("ldap://localhost:389");
+ factory.getLdapContext((Object)"jcoder", null);
+ }
+
+
}