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 2016/07/08 17:20:34 UTC

shiro git commit: SHIRO-278: Renamed JndiLdapRealm to DefaultLdapRealm

Repository: shiro
Updated Branches:
  refs/heads/1.3.x d577ca293 -> dbfacf1f5


SHIRO-278: Renamed JndiLdapRealm to DefaultLdapRealm

JndiLdapRealm still exists for backwards compatibility (until 2.0), as a child class of DefaultLdapRealm.


Project: http://git-wip-us.apache.org/repos/asf/shiro/repo
Commit: http://git-wip-us.apache.org/repos/asf/shiro/commit/dbfacf1f
Tree: http://git-wip-us.apache.org/repos/asf/shiro/tree/dbfacf1f
Diff: http://git-wip-us.apache.org/repos/asf/shiro/diff/dbfacf1f

Branch: refs/heads/1.3.x
Commit: dbfacf1f5a1f480cd1e3c68a5350f10870719652
Parents: d577ca2
Author: Brian Demers <bd...@apache.org>
Authored: Fri Jul 8 11:37:06 2016 -0400
Committer: Brian Demers <bd...@apache.org>
Committed: Fri Jul 8 13:19:38 2016 -0400

----------------------------------------------------------------------
 .../shiro/realm/ldap/DefaultLdapRealm.java      | 430 +++++++++++++++++
 .../realm/ldap/JndiLdapContextFactory.java      |   6 +-
 .../apache/shiro/realm/ldap/JndiLdapRealm.java  | 460 ++-----------------
 .../shiro/realm/ldap/LdapContextFactory.java    |   2 +-
 .../shiro/realm/ldap/DefaultLdapRealmTest.java  | 179 ++++++++
 .../shiro/realm/ldap/JndiLdapRealmTest.java     | 134 +-----
 6 files changed, 650 insertions(+), 561 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/shiro/blob/dbfacf1f/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapRealm.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapRealm.java b/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapRealm.java
new file mode 100644
index 0000000..dec43a9
--- /dev/null
+++ b/core/src/main/java/org/apache/shiro/realm/ldap/DefaultLdapRealm.java
@@ -0,0 +1,430 @@
+/*
+ * 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.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationInfo;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.SimpleAuthenticationInfo;
+import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
+import org.apache.shiro.authz.AuthorizationException;
+import org.apache.shiro.authz.AuthorizationInfo;
+import org.apache.shiro.ldap.UnsupportedAuthenticationMechanismException;
+import org.apache.shiro.realm.AuthorizingRealm;
+import org.apache.shiro.subject.PrincipalCollection;
+import org.apache.shiro.util.StringUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.naming.AuthenticationNotSupportedException;
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+
+/**
+ * An LDAP {@link org.apache.shiro.realm.Realm Realm} implementation utilizing Sun's/Oracle's
+ * <a href="http://download-llnw.oracle.com/javase/tutorial/jndi/ldap/jndi.html">JNDI API as an LDAP API</a>.  This is
+ * Shiro's default implementation for supporting LDAP, as using the JNDI API has been a common approach for Java LDAP
+ * support for many years.
+ * <p/>
+ * This realm implementation and its backing {@link JndiLdapContextFactory} should cover 99% of all Shiro-related LDAP
+ * authentication and authorization needs.  However, if it does not suit your needs, you might want to look into
+ * creating your own realm using an alternative, perhaps more robust, LDAP communication API, such as the
+ * <a href="http://directory.apache.org/api/">Apache LDAP API</a>.
+ * <h2>Authentication</h2>
+ * During an authentication attempt, if the submitted {@code AuthenticationToken}'s
+ * {@link org.apache.shiro.authc.AuthenticationToken#getPrincipal() principal} is a simple username, but the
+ * LDAP directory expects a complete User Distinguished Name (User DN) to establish a connection, the
+ * {@link #setUserDnTemplate(String) userDnTemplate} property must be configured.  If not configured,
+ * the property will pass the simple username directly as the User DN, which is often incorrect in most LDAP
+ * environments (maybe Microsoft ActiveDirectory being the exception).
+ * <h2>Authorization</h2>
+ * By default, authorization is effectively disabled due to the default
+ * {@link #doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} implementation returning {@code null}.
+ * If you wish to perform authorization based on an LDAP schema, you must subclass this one
+ * and override that method to reflect your organization's data model.
+ * <h2>Configuration</h2>
+ * This class primarily provides the {@link #setUserDnTemplate(String) userDnTemplate} property to allow you to specify
+ * the your LDAP server's User DN format.  Most other configuration is performed via the nested
+ * {@link LdapContextFactory contextFactory} property.
+ * <p/>
+ * For example, defining this realm in Shiro .ini:
+ * <pre>
+ * [main]
+ * ldapRealm = org.apache.shiro.realm.ldap.DefaultLdapRealm
+ * ldapRealm.userDnTemplate = uid={0},ou=users,dc=mycompany,dc=com
+ * ldapRealm.contextFactory.url = ldap://ldapHost:389
+ * ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
+ * ldapRealm.contextFactory.environment[some.obscure.jndi.key] = some value
+ * ...
+ * </pre>
+ * The default {@link #setContextFactory contextFactory} instance is a {@link JndiLdapContextFactory}.  See that
+ * class's JavaDoc for more information on configuring the LDAP connection as well as specifying JNDI environment
+ * properties as necessary.
+ *
+ * @see JndiLdapContextFactory
+ *
+ * @since 1.3
+ */
+public class DefaultLdapRealm extends AuthorizingRealm {
+
+    private static final Logger log = LoggerFactory.getLogger(DefaultLdapRealm.class);
+
+    //The zero index currently means nothing, but could be utilized in the future for other substitution techniques.
+    private static final String USERDN_SUBSTITUTION_TOKEN = "{0}";
+
+    private String userDnPrefix;
+    private String userDnSuffix;
+
+    /*--------------------------------------------
+    |    I N S T A N C E   V A R I A B L E S    |
+    ============================================*/
+    /**
+     * The LdapContextFactory instance used to acquire {@link javax.naming.ldap.LdapContext LdapContext}'s at runtime
+     * to acquire connections to the LDAP directory to perform authentication attempts and authorizatino queries.
+     */
+    private LdapContextFactory contextFactory;
+
+    /*--------------------------------------------
+    |         C O N S T R U C T O R S           |
+    ============================================*/
+
+    /**
+     * Default no-argument constructor that defaults the internal {@link LdapContextFactory} instance to a
+     * {@link JndiLdapContextFactory}.
+     */
+    public DefaultLdapRealm() {
+        //Credentials Matching is not necessary - the LDAP directory will do it automatically:
+        setCredentialsMatcher(new AllowAllCredentialsMatcher());
+        //Any Object principal and Object credentials may be passed to the LDAP provider, so accept any token:
+        setAuthenticationTokenClass(AuthenticationToken.class);
+        this.contextFactory = new JndiLdapContextFactory();
+    }
+
+    /*--------------------------------------------
+    |  A C C E S S O R S / M O D I F I E R S    |
+    ============================================*/
+
+    /**
+     * Returns the User DN prefix to use when building a runtime User DN value or {@code null} if no
+     * {@link #getUserDnTemplate() userDnTemplate} has been configured.  If configured, this value is the text that
+     * occurs before the {@link #USERDN_SUBSTITUTION_TOKEN} in the {@link #getUserDnTemplate() userDnTemplate} value.
+     *
+     * @return the the User DN prefix to use when building a runtime User DN value or {@code null} if no
+     *         {@link #getUserDnTemplate() userDnTemplate} has been configured.
+     */
+    protected String getUserDnPrefix() {
+        return userDnPrefix;
+    }
+
+    /**
+     * Returns the User DN suffix to use when building a runtime User DN value.  or {@code null} if no
+     * {@link #getUserDnTemplate() userDnTemplate} has been configured.  If configured, this value is the text that
+     * occurs after the {@link #USERDN_SUBSTITUTION_TOKEN} in the {@link #getUserDnTemplate() userDnTemplate} value.
+     *
+     * @return the User DN suffix to use when building a runtime User DN value or {@code null} if no
+     *         {@link #getUserDnTemplate() userDnTemplate} has been configured.
+     */
+    protected String getUserDnSuffix() {
+        return userDnSuffix;
+    }
+
+    /*--------------------------------------------
+    |               M E T H O D S               |
+    ============================================*/
+
+    /**
+     * Sets the User Distinguished Name (DN) template to use when creating User DNs at runtime.  A User DN is an LDAP
+     * fully-qualified unique user identifier which is required to establish a connection with the LDAP
+     * directory to authenticate users and query for authorization information.
+     * <h2>Usage</h2>
+     * User DN formats are unique to the LDAP directory's schema, and each environment differs - you will need to
+     * specify the format corresponding to your directory.  You do this by specifying the full User DN as normal, but
+     * but you use a <b>{@code {0}}</b> placeholder token in the string representing the location where the
+     * user's submitted principal (usually a username or uid) will be substituted at runtime.
+     * <p/>
+     * For example,  if your directory
+     * uses an LDAP {@code uid} attribute to represent usernames, the User DN for the {@code jsmith} user may look like
+     * this:
+     * <p/>
+     * <pre>uid=jsmith,ou=users,dc=mycompany,dc=com</pre>
+     * <p/>
+     * in which case you would set this property with the following template value:
+     * <p/>
+     * <pre>uid=<b>{0}</b>,ou=users,dc=mycompany,dc=com</pre>
+     * <p/>
+     * If no template is configured, the raw {@code AuthenticationToken}
+     * {@link AuthenticationToken#getPrincipal() principal} will be used as the LDAP principal.  This is likely
+     * incorrect as most LDAP directories expect a fully-qualified User DN as opposed to the raw uid or username.  So,
+     * ensure you set this property to match your environment!
+     *
+     * @param template the User Distinguished Name template to use for runtime substitution
+     * @throws IllegalArgumentException if the template is null, empty, or does not contain the
+     *                                  {@code {0}} substitution token.
+     * @see LdapContextFactory#getLdapContext(Object,Object)
+     */
+    public void setUserDnTemplate(String template) throws IllegalArgumentException {
+        if (!StringUtils.hasText(template)) {
+            String msg = "User DN template cannot be null or empty.";
+            throw new IllegalArgumentException(msg);
+        }
+        int index = template.indexOf(USERDN_SUBSTITUTION_TOKEN);
+        if (index < 0) {
+            String msg = "User DN template must contain the '" +
+                    USERDN_SUBSTITUTION_TOKEN + "' replacement token to understand where to " +
+                    "insert the runtime authentication principal.";
+            throw new IllegalArgumentException(msg);
+        }
+        String prefix = template.substring(0, index);
+        String suffix = template.substring(prefix.length() + USERDN_SUBSTITUTION_TOKEN.length());
+        if (log.isDebugEnabled()) {
+            log.debug("Determined user DN prefix [{}] and suffix [{}]", prefix, suffix);
+        }
+        this.userDnPrefix = prefix;
+        this.userDnSuffix = suffix;
+    }
+
+    /**
+     * Returns the User Distinguished Name (DN) template to use when creating User DNs at runtime - see the
+     * {@link #setUserDnTemplate(String) setUserDnTemplate} JavaDoc for a full explanation.
+     *
+     * @return the User Distinguished Name (DN) template to use when creating User DNs at runtime.
+     */
+    public String getUserDnTemplate() {
+        return getUserDn(USERDN_SUBSTITUTION_TOKEN);
+    }
+
+    /**
+     * Returns the LDAP User Distinguished Name (DN) to use when acquiring an
+     * {@link javax.naming.ldap.LdapContext LdapContext} from the {@link LdapContextFactory}.
+     * <p/>
+     * If the the {@link #getUserDnTemplate() userDnTemplate} property has been set, this implementation will construct
+     * the User DN by substituting the specified {@code principal} into the configured template.  If the
+     * {@link #getUserDnTemplate() userDnTemplate} has not been set, the method argument will be returned directly
+     * (indicating that the submitted authentication token principal <em>is</em> the User DN).
+     *
+     * @param principal the principal to substitute into the configured {@link #getUserDnTemplate() userDnTemplate}.
+     * @return the constructed User DN to use at runtime when acquiring an {@link javax.naming.ldap.LdapContext}.
+     * @throws IllegalArgumentException if the method argument is null or empty
+     * @throws IllegalStateException    if the {@link #getUserDnTemplate userDnTemplate} has not been set.
+     * @see LdapContextFactory#getLdapContext(Object, Object)
+     */
+    protected String getUserDn(String principal) throws IllegalArgumentException, IllegalStateException {
+        if (!StringUtils.hasText(principal)) {
+            throw new IllegalArgumentException("User principal cannot be null or empty for User DN construction.");
+        }
+        String prefix = getUserDnPrefix();
+        String suffix = getUserDnSuffix();
+        if (prefix == null && suffix == null) {
+            log.debug("userDnTemplate property has not been configured, indicating the submitted " +
+                    "AuthenticationToken's principal is the same as the User DN.  Returning the method argument " +
+                    "as is.");
+            return principal;
+        }
+
+        int prefixLength = prefix != null ? prefix.length() : 0;
+        int suffixLength = suffix != null ? suffix.length() : 0;
+        StringBuilder sb = new StringBuilder(prefixLength + principal.length() + suffixLength);
+        if (prefixLength > 0) {
+            sb.append(prefix);
+        }
+        sb.append(principal);
+        if (suffixLength > 0) {
+            sb.append(suffix);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Sets the LdapContextFactory instance used to acquire connections to the LDAP directory during authentication
+     * attempts and authorization queries.  Unless specified otherwise, the default is a {@link JndiLdapContextFactory}
+     * instance.
+     *
+     * @param contextFactory the LdapContextFactory instance used to acquire connections to the LDAP directory during
+     *                       authentication attempts and authorization queries
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    public void setContextFactory(LdapContextFactory contextFactory) {
+        this.contextFactory = contextFactory;
+    }
+
+    /**
+     * Returns the LdapContextFactory instance used to acquire connections to the LDAP directory during authentication
+     * attempts and authorization queries.  Unless specified otherwise, the default is a {@link JndiLdapContextFactory}
+     * instance.
+     *
+     * @return the LdapContextFactory instance used to acquire connections to the LDAP directory during
+     *         authentication attempts and authorization queries
+     */
+    public LdapContextFactory getContextFactory() {
+        return this.contextFactory;
+    }
+
+    /*--------------------------------------------
+    |               M E T H O D S                |
+    ============================================*/
+
+    /**
+     * Delegates to {@link #queryForAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken, LdapContextFactory)},
+     * wrapping any {@link NamingException}s in a Shiro {@link AuthenticationException} to satisfy the parent method
+     * signature.
+     *
+     * @param token the authentication token containing the user's principal and credentials.
+     * @return the {@link AuthenticationInfo} acquired after a successful authentication attempt
+     * @throws AuthenticationException if the authentication attempt fails or if a
+     *                                 {@link NamingException} occurs.
+     */
+    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
+        AuthenticationInfo info;
+        try {
+            info = queryForAuthenticationInfo(token, getContextFactory());
+        } catch (AuthenticationNotSupportedException e) {
+            String msg = "Unsupported configured authentication mechanism";
+            throw new UnsupportedAuthenticationMechanismException(msg, e);
+        } catch (javax.naming.AuthenticationException e) {
+            throw new AuthenticationException("LDAP authentication failed.", e);
+        } catch (NamingException e) {
+            String msg = "LDAP naming error while attempting to authenticate user.";
+            throw new AuthenticationException(msg, e);
+        }
+
+        return info;
+    }
+
+
+    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
+        AuthorizationInfo info;
+        try {
+            info = queryForAuthorizationInfo(principals, getContextFactory());
+        } catch (NamingException e) {
+            String msg = "LDAP naming error while attempting to retrieve authorization for user [" + principals + "].";
+            throw new AuthorizationException(msg, e);
+        }
+
+        return info;
+    }
+
+    /**
+     * Returns the principal to use when creating the LDAP connection for an authentication attempt.
+     * <p/>
+     * This implementation uses a heuristic: it checks to see if the specified token's
+     * {@link AuthenticationToken#getPrincipal() principal} is a {@code String}, and if so,
+     * {@link #getUserDn(String) converts it} from what is
+     * assumed to be a raw uid or username {@code String} into a User DN {@code String}.  Almost all LDAP directories
+     * expect the authentication connection to present a User DN and not an unqualified username or uid.
+     * <p/>
+     * If the token's {@code principal} is not a String, it is assumed to already be in the format supported by the
+     * underlying {@link LdapContextFactory} implementation and the raw principal is returned directly.
+     *
+     * @param token the {@link AuthenticationToken} submitted during the authentication process
+     * @return the User DN or raw principal to use to acquire the LdapContext.
+     * @see LdapContextFactory#getLdapContext(Object, Object)
+     */
+    protected Object getLdapPrincipal(AuthenticationToken token) {
+        Object principal = token.getPrincipal();
+        if (principal instanceof String) {
+            String sPrincipal = (String) principal;
+            return getUserDn(sPrincipal);
+        }
+        return principal;
+    }
+
+    /**
+     * This implementation opens an LDAP connection using the token's
+     * {@link #getLdapPrincipal(org.apache.shiro.authc.AuthenticationToken) discovered principal} and provided
+     * {@link AuthenticationToken#getCredentials() credentials}.  If the connection opens successfully, the
+     * authentication attempt is immediately considered successful and a new
+     * {@link AuthenticationInfo} instance is
+     * {@link #createAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken, Object, Object, javax.naming.ldap.LdapContext) created}
+     * and returned.  If the connection cannot be opened, either because LDAP authentication failed or some other
+     * JNDI problem, an {@link NamingException} will be thrown.
+     *
+     * @param token              the submitted authentication token that triggered the authentication attempt.
+     * @param ldapContextFactory factory used to retrieve LDAP connections.
+     * @return an {@link AuthenticationInfo} instance representing the authenticated user's information.
+     * @throws NamingException if any LDAP errors occur.
+     */
+    protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token,
+                                                            LdapContextFactory ldapContextFactory)
+            throws NamingException {
+
+        Object principal = token.getPrincipal();
+        Object credentials = token.getCredentials();
+
+        log.debug("Authenticating user '{}' through LDAP", principal);
+
+        principal = getLdapPrincipal(token);
+
+        LdapContext ctx = null;
+        try {
+            ctx = ldapContextFactory.getLdapContext(principal, credentials);
+            //context was opened successfully, which means their credentials were valid.  Return the AuthenticationInfo:
+            return createAuthenticationInfo(token, principal, credentials, ctx);
+        } finally {
+            LdapUtils.closeContext(ctx);
+        }
+    }
+
+    /**
+     * Returns the {@link AuthenticationInfo} resulting from a Subject's successful LDAP authentication attempt.
+     * <p/>
+     * This implementation ignores the {@code ldapPrincipal}, {@code ldapCredentials}, and the opened
+     * {@code ldapContext} arguments and merely returns an {@code AuthenticationInfo} instance mirroring the
+     * submitted token's principal and credentials.  This is acceptable because this method is only ever invoked after
+     * a successful authentication attempt, which means the provided principal and credentials were correct, and can
+     * be used directly to populate the (now verified) {@code AuthenticationInfo}.
+     * <p/>
+     * Subclasses however are free to override this method for more advanced construction logic.
+     *
+     * @param token           the submitted {@code AuthenticationToken} that resulted in a successful authentication
+     * @param ldapPrincipal   the LDAP principal used when creating the LDAP connection.  Unlike the token's
+     *                        {@link AuthenticationToken#getPrincipal() principal}, this value is usually a constructed
+     *                        User DN and not a simple username or uid.  The exact value is depending on the
+     *                        configured
+     *                        <a href="http://download-llnw.oracle.com/javase/tutorial/jndi/ldap/auth_mechs.html">
+     *                        LDAP authentication mechanism</a> in use.
+     * @param ldapCredentials the LDAP credentials used when creating the LDAP connection.
+     * @param ldapContext     the LdapContext created that resulted in a successful authentication.  It can be used
+     *                        further by subclasses for more complex operations.  It does not need to be closed -
+     *                        it will be closed automatically after this method returns.
+     * @return the {@link AuthenticationInfo} resulting from a Subject's successful LDAP authentication attempt.
+     * @throws NamingException if there was any problem using the {@code LdapContext}
+     */
+    @SuppressWarnings({"UnusedDeclaration"})
+    protected AuthenticationInfo createAuthenticationInfo(AuthenticationToken token, Object ldapPrincipal,
+                                                          Object ldapCredentials, LdapContext ldapContext)
+            throws NamingException {
+        return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName());
+    }
+
+
+    /**
+     * Method that should be implemented by subclasses to build an
+     * {@link AuthorizationInfo} object by querying the LDAP context for the
+     * specified principal.</p>
+     *
+     * @param principals          the principals of the Subject whose AuthenticationInfo should be queried from the LDAP server.
+     * @param ldapContextFactory factory used to retrieve LDAP connections.
+     * @return an {@link AuthorizationInfo} instance containing information retrieved from the LDAP server.
+     * @throws NamingException if any LDAP errors occur during the search.
+     */
+    protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals,
+                                                          LdapContextFactory ldapContextFactory) throws NamingException {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/shiro/blob/dbfacf1f/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java b/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java
index d298f53..ca9ff25 100644
--- a/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java
+++ b/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapContextFactory.java
@@ -49,14 +49,14 @@ import java.util.Map;
  * For example, consider the following two identical configurations:
  * <pre>
  * [main]
- * ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
+ * ldapRealm = org.apache.shiro.realm.ldap.DefaultLdapRealm
  * ldapRealm.contextFactory.url = ldap://localhost:389
  * ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
  * </pre>
  * and
  * <pre>
  * [main]
- * ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
+ * ldapRealm = org.apache.shiro.realm.ldap.DefaultLdapRealm
  * ldapRealm.contextFactory.environment[java.naming.provider.url] = ldap://localhost:389
  * ldapRealm.contextFactory.environment[java.naming.security.authentication] = DIGEST-MD5
  * </pre>
@@ -68,7 +68,7 @@ import java.util.Map;
  * For example:
  * <pre>
  * [main]
- * ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
+ * ldapRealm = org.apache.shiro.realm.ldap.DefaultLdapRealm
  * ldapRealm.contextFactory.url = ldap://localhost:389
  * ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
  * ldapRealm.contextFactory.environment[some.other.obscure.jndi.key] = some value

http://git-wip-us.apache.org/repos/asf/shiro/blob/dbfacf1f/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapRealm.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapRealm.java b/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapRealm.java
index a642b2c..1c7fd54 100644
--- a/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapRealm.java
+++ b/core/src/main/java/org/apache/shiro/realm/ldap/JndiLdapRealm.java
@@ -1,430 +1,30 @@
-/*
- * 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.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.authc.AuthenticationInfo;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.SimpleAuthenticationInfo;
-import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
-import org.apache.shiro.authz.AuthorizationException;
-import org.apache.shiro.authz.AuthorizationInfo;
-import org.apache.shiro.ldap.UnsupportedAuthenticationMechanismException;
-import org.apache.shiro.realm.AuthorizingRealm;
-import org.apache.shiro.subject.PrincipalCollection;
-import org.apache.shiro.util.StringUtils;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import javax.naming.AuthenticationNotSupportedException;
-import javax.naming.NamingException;
-import javax.naming.ldap.LdapContext;
-
-/**
- * An LDAP {@link org.apache.shiro.realm.Realm Realm} implementation utilizing Sun's/Oracle's
- * <a href="http://download-llnw.oracle.com/javase/tutorial/jndi/ldap/jndi.html">JNDI API as an LDAP API</a>.  This is
- * Shiro's default implementation for supporting LDAP, as using the JNDI API has been a common approach for Java LDAP
- * support for many years.
- * <p/>
- * This realm implementation and its backing {@link JndiLdapContextFactory} should cover 99% of all Shiro-related LDAP
- * authentication and authorization needs.  However, if it does not suit your needs, you might want to look into
- * creating your own realm using an alternative, perhaps more robust, LDAP communication API, such as the
- * <a href="http://directory.apache.org/api/">Apache LDAP API</a>.
- * <h2>Authentication</h2>
- * During an authentication attempt, if the submitted {@code AuthenticationToken}'s
- * {@link org.apache.shiro.authc.AuthenticationToken#getPrincipal() principal} is a simple username, but the
- * LDAP directory expects a complete User Distinguished Name (User DN) to establish a connection, the
- * {@link #setUserDnTemplate(String) userDnTemplate} property must be configured.  If not configured,
- * the property will pass the simple username directly as the User DN, which is often incorrect in most LDAP
- * environments (maybe Microsoft ActiveDirectory being the exception).
- * <h2>Authorization</h2>
- * By default, authorization is effectively disabled due to the default
- * {@link #doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)} implementation returning {@code null}.
- * If you wish to perform authorization based on an LDAP schema, you must subclass this one
- * and override that method to reflect your organization's data model.
- * <h2>Configuration</h2>
- * This class primarily provides the {@link #setUserDnTemplate(String) userDnTemplate} property to allow you to specify
- * the your LDAP server's User DN format.  Most other configuration is performed via the nested
- * {@link LdapContextFactory contextFactory} property.
- * <p/>
- * For example, defining this realm in Shiro .ini:
- * <pre>
- * [main]
- * ldapRealm = org.apache.shiro.realm.ldap.JndiLdapRealm
- * ldapRealm.userDnTemplate = uid={0},ou=users,dc=mycompany,dc=com
- * ldapRealm.contextFactory.url = ldap://ldapHost:389
- * ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
- * ldapRealm.contextFactory.environment[some.obscure.jndi.key] = some value
- * ...
- * </pre>
- * The default {@link #setContextFactory contextFactory} instance is a {@link JndiLdapContextFactory}.  See that
- * class's JavaDoc for more information on configuring the LDAP connection as well as specifying JNDI environment
- * properties as necessary.
- *
- * @see JndiLdapContextFactory
- *
- * @since 1.1
- */
-public class JndiLdapRealm extends AuthorizingRealm {
-
-    private static final Logger log = LoggerFactory.getLogger(JndiLdapRealm.class);
-
-    //The zero index currently means nothing, but could be utilized in the future for other substitution techniques.
-    private static final String USERDN_SUBSTITUTION_TOKEN = "{0}";
-
-    private String userDnPrefix;
-    private String userDnSuffix;
-
-    /*--------------------------------------------
-    |    I N S T A N C E   V A R I A B L E S    |
-    ============================================*/
-    /**
-     * The LdapContextFactory instance used to acquire {@link javax.naming.ldap.LdapContext LdapContext}'s at runtime
-     * to acquire connections to the LDAP directory to perform authentication attempts and authorizatino queries.
-     */
-    private LdapContextFactory contextFactory;
-
-    /*--------------------------------------------
-    |         C O N S T R U C T O R S           |
-    ============================================*/
-
-    /**
-     * Default no-argument constructor that defaults the internal {@link LdapContextFactory} instance to a
-     * {@link JndiLdapContextFactory}.
-     */
-    public JndiLdapRealm() {
-        //Credentials Matching is not necessary - the LDAP directory will do it automatically:
-        setCredentialsMatcher(new AllowAllCredentialsMatcher());
-        //Any Object principal and Object credentials may be passed to the LDAP provider, so accept any token:
-        setAuthenticationTokenClass(AuthenticationToken.class);
-        this.contextFactory = new JndiLdapContextFactory();
-    }
-
-    /*--------------------------------------------
-    |  A C C E S S O R S / M O D I F I E R S    |
-    ============================================*/
-
-    /**
-     * Returns the User DN prefix to use when building a runtime User DN value or {@code null} if no
-     * {@link #getUserDnTemplate() userDnTemplate} has been configured.  If configured, this value is the text that
-     * occurs before the {@link #USERDN_SUBSTITUTION_TOKEN} in the {@link #getUserDnTemplate() userDnTemplate} value.
-     *
-     * @return the the User DN prefix to use when building a runtime User DN value or {@code null} if no
-     *         {@link #getUserDnTemplate() userDnTemplate} has been configured.
-     */
-    protected String getUserDnPrefix() {
-        return userDnPrefix;
-    }
-
-    /**
-     * Returns the User DN suffix to use when building a runtime User DN value.  or {@code null} if no
-     * {@link #getUserDnTemplate() userDnTemplate} has been configured.  If configured, this value is the text that
-     * occurs after the {@link #USERDN_SUBSTITUTION_TOKEN} in the {@link #getUserDnTemplate() userDnTemplate} value.
-     *
-     * @return the User DN suffix to use when building a runtime User DN value or {@code null} if no
-     *         {@link #getUserDnTemplate() userDnTemplate} has been configured.
-     */
-    protected String getUserDnSuffix() {
-        return userDnSuffix;
-    }
-
-    /*--------------------------------------------
-    |               M E T H O D S               |
-    ============================================*/
-
-    /**
-     * Sets the User Distinguished Name (DN) template to use when creating User DNs at runtime.  A User DN is an LDAP
-     * fully-qualified unique user identifier which is required to establish a connection with the LDAP
-     * directory to authenticate users and query for authorization information.
-     * <h2>Usage</h2>
-     * User DN formats are unique to the LDAP directory's schema, and each environment differs - you will need to
-     * specify the format corresponding to your directory.  You do this by specifying the full User DN as normal, but
-     * but you use a <b>{@code {0}}</b> placeholder token in the string representing the location where the
-     * user's submitted principal (usually a username or uid) will be substituted at runtime.
-     * <p/>
-     * For example,  if your directory
-     * uses an LDAP {@code uid} attribute to represent usernames, the User DN for the {@code jsmith} user may look like
-     * this:
-     * <p/>
-     * <pre>uid=jsmith,ou=users,dc=mycompany,dc=com</pre>
-     * <p/>
-     * in which case you would set this property with the following template value:
-     * <p/>
-     * <pre>uid=<b>{0}</b>,ou=users,dc=mycompany,dc=com</pre>
-     * <p/>
-     * If no template is configured, the raw {@code AuthenticationToken}
-     * {@link AuthenticationToken#getPrincipal() principal} will be used as the LDAP principal.  This is likely
-     * incorrect as most LDAP directories expect a fully-qualified User DN as opposed to the raw uid or username.  So,
-     * ensure you set this property to match your environment!
-     *
-     * @param template the User Distinguished Name template to use for runtime substitution
-     * @throws IllegalArgumentException if the template is null, empty, or does not contain the
-     *                                  {@code {0}} substitution token.
-     * @see LdapContextFactory#getLdapContext(Object,Object)
-     */
-    public void setUserDnTemplate(String template) throws IllegalArgumentException {
-        if (!StringUtils.hasText(template)) {
-            String msg = "User DN template cannot be null or empty.";
-            throw new IllegalArgumentException(msg);
-        }
-        int index = template.indexOf(USERDN_SUBSTITUTION_TOKEN);
-        if (index < 0) {
-            String msg = "User DN template must contain the '" +
-                    USERDN_SUBSTITUTION_TOKEN + "' replacement token to understand where to " +
-                    "insert the runtime authentication principal.";
-            throw new IllegalArgumentException(msg);
-        }
-        String prefix = template.substring(0, index);
-        String suffix = template.substring(prefix.length() + USERDN_SUBSTITUTION_TOKEN.length());
-        if (log.isDebugEnabled()) {
-            log.debug("Determined user DN prefix [{}] and suffix [{}]", prefix, suffix);
-        }
-        this.userDnPrefix = prefix;
-        this.userDnSuffix = suffix;
-    }
-
-    /**
-     * Returns the User Distinguished Name (DN) template to use when creating User DNs at runtime - see the
-     * {@link #setUserDnTemplate(String) setUserDnTemplate} JavaDoc for a full explanation.
-     *
-     * @return the User Distinguished Name (DN) template to use when creating User DNs at runtime.
-     */
-    public String getUserDnTemplate() {
-        return getUserDn(USERDN_SUBSTITUTION_TOKEN);
-    }
-
-    /**
-     * Returns the LDAP User Distinguished Name (DN) to use when acquiring an
-     * {@link javax.naming.ldap.LdapContext LdapContext} from the {@link LdapContextFactory}.
-     * <p/>
-     * If the the {@link #getUserDnTemplate() userDnTemplate} property has been set, this implementation will construct
-     * the User DN by substituting the specified {@code principal} into the configured template.  If the
-     * {@link #getUserDnTemplate() userDnTemplate} has not been set, the method argument will be returned directly
-     * (indicating that the submitted authentication token principal <em>is</em> the User DN).
-     *
-     * @param principal the principal to substitute into the configured {@link #getUserDnTemplate() userDnTemplate}.
-     * @return the constructed User DN to use at runtime when acquiring an {@link javax.naming.ldap.LdapContext}.
-     * @throws IllegalArgumentException if the method argument is null or empty
-     * @throws IllegalStateException    if the {@link #getUserDnTemplate userDnTemplate} has not been set.
-     * @see LdapContextFactory#getLdapContext(Object, Object)
-     */
-    protected String getUserDn(String principal) throws IllegalArgumentException, IllegalStateException {
-        if (!StringUtils.hasText(principal)) {
-            throw new IllegalArgumentException("User principal cannot be null or empty for User DN construction.");
-        }
-        String prefix = getUserDnPrefix();
-        String suffix = getUserDnSuffix();
-        if (prefix == null && suffix == null) {
-            log.debug("userDnTemplate property has not been configured, indicating the submitted " +
-                    "AuthenticationToken's principal is the same as the User DN.  Returning the method argument " +
-                    "as is.");
-            return principal;
-        }
-
-        int prefixLength = prefix != null ? prefix.length() : 0;
-        int suffixLength = suffix != null ? suffix.length() : 0;
-        StringBuilder sb = new StringBuilder(prefixLength + principal.length() + suffixLength);
-        if (prefixLength > 0) {
-            sb.append(prefix);
-        }
-        sb.append(principal);
-        if (suffixLength > 0) {
-            sb.append(suffix);
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Sets the LdapContextFactory instance used to acquire connections to the LDAP directory during authentication
-     * attempts and authorization queries.  Unless specified otherwise, the default is a {@link JndiLdapContextFactory}
-     * instance.
-     *
-     * @param contextFactory the LdapContextFactory instance used to acquire connections to the LDAP directory during
-     *                       authentication attempts and authorization queries
-     */
-    @SuppressWarnings({"UnusedDeclaration"})
-    public void setContextFactory(LdapContextFactory contextFactory) {
-        this.contextFactory = contextFactory;
-    }
-
-    /**
-     * Returns the LdapContextFactory instance used to acquire connections to the LDAP directory during authentication
-     * attempts and authorization queries.  Unless specified otherwise, the default is a {@link JndiLdapContextFactory}
-     * instance.
-     *
-     * @return the LdapContextFactory instance used to acquire connections to the LDAP directory during
-     *         authentication attempts and authorization queries
-     */
-    public LdapContextFactory getContextFactory() {
-        return this.contextFactory;
-    }
-
-    /*--------------------------------------------
-    |               M E T H O D S                |
-    ============================================*/
-
-    /**
-     * Delegates to {@link #queryForAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken, LdapContextFactory)},
-     * wrapping any {@link NamingException}s in a Shiro {@link AuthenticationException} to satisfy the parent method
-     * signature.
-     *
-     * @param token the authentication token containing the user's principal and credentials.
-     * @return the {@link AuthenticationInfo} acquired after a successful authentication attempt
-     * @throws AuthenticationException if the authentication attempt fails or if a
-     *                                 {@link NamingException} occurs.
-     */
-    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
-        AuthenticationInfo info;
-        try {
-            info = queryForAuthenticationInfo(token, getContextFactory());
-        } catch (AuthenticationNotSupportedException e) {
-            String msg = "Unsupported configured authentication mechanism";
-            throw new UnsupportedAuthenticationMechanismException(msg, e);
-        } catch (javax.naming.AuthenticationException e) {
-            throw new AuthenticationException("LDAP authentication failed.", e);
-        } catch (NamingException e) {
-            String msg = "LDAP naming error while attempting to authenticate user.";
-            throw new AuthenticationException(msg, e);
-        }
-
-        return info;
-    }
-
-
-    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
-        AuthorizationInfo info;
-        try {
-            info = queryForAuthorizationInfo(principals, getContextFactory());
-        } catch (NamingException e) {
-            String msg = "LDAP naming error while attempting to retrieve authorization for user [" + principals + "].";
-            throw new AuthorizationException(msg, e);
-        }
-
-        return info;
-    }
-
-    /**
-     * Returns the principal to use when creating the LDAP connection for an authentication attempt.
-     * <p/>
-     * This implementation uses a heuristic: it checks to see if the specified token's
-     * {@link AuthenticationToken#getPrincipal() principal} is a {@code String}, and if so,
-     * {@link #getUserDn(String) converts it} from what is
-     * assumed to be a raw uid or username {@code String} into a User DN {@code String}.  Almost all LDAP directories
-     * expect the authentication connection to present a User DN and not an unqualified username or uid.
-     * <p/>
-     * If the token's {@code principal} is not a String, it is assumed to already be in the format supported by the
-     * underlying {@link LdapContextFactory} implementation and the raw principal is returned directly.
-     *
-     * @param token the {@link AuthenticationToken} submitted during the authentication process
-     * @return the User DN or raw principal to use to acquire the LdapContext.
-     * @see LdapContextFactory#getLdapContext(Object, Object)
-     */
-    protected Object getLdapPrincipal(AuthenticationToken token) {
-        Object principal = token.getPrincipal();
-        if (principal instanceof String) {
-            String sPrincipal = (String) principal;
-            return getUserDn(sPrincipal);
-        }
-        return principal;
-    }
-
-    /**
-     * This implementation opens an LDAP connection using the token's
-     * {@link #getLdapPrincipal(org.apache.shiro.authc.AuthenticationToken) discovered principal} and provided
-     * {@link AuthenticationToken#getCredentials() credentials}.  If the connection opens successfully, the
-     * authentication attempt is immediately considered successful and a new
-     * {@link AuthenticationInfo} instance is
-     * {@link #createAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken, Object, Object, javax.naming.ldap.LdapContext) created}
-     * and returned.  If the connection cannot be opened, either because LDAP authentication failed or some other
-     * JNDI problem, an {@link NamingException} will be thrown.
-     *
-     * @param token              the submitted authentication token that triggered the authentication attempt.
-     * @param ldapContextFactory factory used to retrieve LDAP connections.
-     * @return an {@link AuthenticationInfo} instance representing the authenticated user's information.
-     * @throws NamingException if any LDAP errors occur.
-     */
-    protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token,
-                                                            LdapContextFactory ldapContextFactory)
-            throws NamingException {
-
-        Object principal = token.getPrincipal();
-        Object credentials = token.getCredentials();
-
-        log.debug("Authenticating user '{}' through LDAP", principal);
-
-        principal = getLdapPrincipal(token);
-
-        LdapContext ctx = null;
-        try {
-            ctx = ldapContextFactory.getLdapContext(principal, credentials);
-            //context was opened successfully, which means their credentials were valid.  Return the AuthenticationInfo:
-            return createAuthenticationInfo(token, principal, credentials, ctx);
-        } finally {
-            LdapUtils.closeContext(ctx);
-        }
-    }
-
-    /**
-     * Returns the {@link AuthenticationInfo} resulting from a Subject's successful LDAP authentication attempt.
-     * <p/>
-     * This implementation ignores the {@code ldapPrincipal}, {@code ldapCredentials}, and the opened
-     * {@code ldapContext} arguments and merely returns an {@code AuthenticationInfo} instance mirroring the
-     * submitted token's principal and credentials.  This is acceptable because this method is only ever invoked after
-     * a successful authentication attempt, which means the provided principal and credentials were correct, and can
-     * be used directly to populate the (now verified) {@code AuthenticationInfo}.
-     * <p/>
-     * Subclasses however are free to override this method for more advanced construction logic.
-     *
-     * @param token           the submitted {@code AuthenticationToken} that resulted in a successful authentication
-     * @param ldapPrincipal   the LDAP principal used when creating the LDAP connection.  Unlike the token's
-     *                        {@link AuthenticationToken#getPrincipal() principal}, this value is usually a constructed
-     *                        User DN and not a simple username or uid.  The exact value is depending on the
-     *                        configured
-     *                        <a href="http://download-llnw.oracle.com/javase/tutorial/jndi/ldap/auth_mechs.html">
-     *                        LDAP authentication mechanism</a> in use.
-     * @param ldapCredentials the LDAP credentials used when creating the LDAP connection.
-     * @param ldapContext     the LdapContext created that resulted in a successful authentication.  It can be used
-     *                        further by subclasses for more complex operations.  It does not need to be closed -
-     *                        it will be closed automatically after this method returns.
-     * @return the {@link AuthenticationInfo} resulting from a Subject's successful LDAP authentication attempt.
-     * @throws NamingException if there was any problem using the {@code LdapContext}
-     */
-    @SuppressWarnings({"UnusedDeclaration"})
-    protected AuthenticationInfo createAuthenticationInfo(AuthenticationToken token, Object ldapPrincipal,
-                                                          Object ldapCredentials, LdapContext ldapContext)
-            throws NamingException {
-        return new SimpleAuthenticationInfo(token.getPrincipal(), token.getCredentials(), getName());
-    }
-
-
-    /**
-     * Method that should be implemented by subclasses to build an
-     * {@link AuthorizationInfo} object by querying the LDAP context for the
-     * specified principal.</p>
-     *
-     * @param principals          the principals of the Subject whose AuthenticationInfo should be queried from the LDAP server.
-     * @param ldapContextFactory factory used to retrieve LDAP connections.
-     * @return an {@link AuthorizationInfo} instance containing information retrieved from the LDAP server.
-     * @throws NamingException if any LDAP errors occur during the search.
-     */
-    protected AuthorizationInfo queryForAuthorizationInfo(PrincipalCollection principals,
-                                                          LdapContextFactory ldapContextFactory) throws NamingException {
-        return null;
-    }
-}
+/*
+ * 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;
+
+/**
+ * This class has been replaced with DefaultLdapRealm.
+ * @see DefaultLdapRealm
+ * @deprecated Renamed to {@link DefaultLdapRealm}, this class will be removed prior to 2.0
+ * @since 1.1
+ *
+ */
+public class JndiLdapRealm extends DefaultLdapRealm {
+
+}

http://git-wip-us.apache.org/repos/asf/shiro/blob/dbfacf1f/core/src/main/java/org/apache/shiro/realm/ldap/LdapContextFactory.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/shiro/realm/ldap/LdapContextFactory.java b/core/src/main/java/org/apache/shiro/realm/ldap/LdapContextFactory.java
index 2e572fd..0cc90fe 100644
--- a/core/src/main/java/org/apache/shiro/realm/ldap/LdapContextFactory.java
+++ b/core/src/main/java/org/apache/shiro/realm/ldap/LdapContextFactory.java
@@ -22,7 +22,7 @@ import javax.naming.NamingException;
 import javax.naming.ldap.LdapContext;
 
 /**
- * Interface that encapsulates the creation of {@code LdapContext} objects that are used by {@link JndiLdapRealm}s to
+ * Interface that encapsulates the creation of {@code LdapContext} objects that are used by {@link DefaultLdapRealm}s to
  * perform authentication attempts and query for authorization data.
  *
  * @since 0.2

http://git-wip-us.apache.org/repos/asf/shiro/blob/dbfacf1f/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapRealmTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapRealmTest.java b/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapRealmTest.java
new file mode 100644
index 0000000..a94927d
--- /dev/null
+++ b/core/src/test/java/org/apache/shiro/realm/ldap/DefaultLdapRealmTest.java
@@ -0,0 +1,179 @@
+/*
+ * 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.apache.shiro.authc.AuthenticationException;
+import org.apache.shiro.authc.AuthenticationToken;
+import org.apache.shiro.authc.UsernamePasswordToken;
+import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.naming.NamingException;
+import javax.naming.ldap.LdapContext;
+
+import java.util.UUID;
+
+import static org.easymock.EasyMock.*;
+import static org.junit.Assert.*;
+
+/**
+ * Tests for the {@link DefaultLdapRealm} class.
+ *
+ * @since 1.3
+ */
+@SuppressWarnings({"ThrowableInstanceNeverThrown"})
+public class DefaultLdapRealmTest {
+
+    private DefaultLdapRealm realm;
+
+    // this method can be collapsed back into setUp once the JndiLdapRealm has been removed in 2.0
+    protected DefaultLdapRealm getNewRealmUnderTest() {
+        return new DefaultLdapRealm();
+    }
+
+    @Before
+    public void setUp() {
+        realm = getNewRealmUnderTest();
+    }
+
+    @Test
+    public void testDefaultInstance() {
+        assertTrue(realm.getCredentialsMatcher() instanceof AllowAllCredentialsMatcher);
+        assertEquals(AuthenticationToken.class, realm.getAuthenticationTokenClass());
+        assertTrue(realm.getContextFactory() instanceof JndiLdapContextFactory);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testSetUserDnTemplateNull() {
+        realm.setUserDnTemplate(null);
+    }
+    
+    @Test(expected=IllegalArgumentException.class)
+    public void testSetUserDnTemplateEmpty() {
+        realm.setUserDnTemplate("  ");
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testSetUserDnTemplateWithoutToken() {
+        realm.setUserDnTemplate("uid=,ou=users,dc=mycompany,dc=com");
+    }
+
+    @Test
+    public void testUserDnTemplate() {
+        String template = "uid={0},ou=users,dc=mycompany,dc=com";
+        realm.setUserDnTemplate(template);
+        assertEquals(template, realm.getUserDnTemplate());
+    }
+
+    @Test
+    public void testUserDnTemplateSubstitution() throws NamingException {
+        realm.setUserDnTemplate("uid={0},ou=users,dc=mycompany,dc=com");
+        LdapContextFactory factory = createMock(LdapContextFactory.class);
+        realm.setContextFactory(factory);
+
+        Object expectedPrincipal = "uid=jsmith,ou=users,dc=mycompany,dc=com";
+
+        expect(factory.getLdapContext(eq(expectedPrincipal), isA(Object.class))).andReturn(createNiceMock(LdapContext.class));
+        replay(factory);
+
+        realm.getAuthenticationInfo(new UsernamePasswordToken("jsmith", "secret") );
+        verify(factory);
+    }
+
+    @Test(expected= AuthenticationException.class)
+    public void testGetAuthenticationInfoNamingAuthenticationException() throws NamingException {
+        realm.setUserDnTemplate("uid={0},ou=users,dc=mycompany,dc=com");
+        LdapContextFactory factory = createMock(LdapContextFactory.class);
+        realm.setContextFactory(factory);
+
+        expect(factory.getLdapContext(isA(Object.class), isA(Object.class)))
+                .andThrow(new javax.naming.AuthenticationException("LDAP Authentication failed."));
+        replay(factory);
+
+        realm.getAuthenticationInfo(new UsernamePasswordToken("jsmith", "secret") );
+    }
+
+    @Test(expected= AuthenticationException.class)
+    public void testGetAuthenticationInfoNamingException() throws NamingException {
+        realm.setUserDnTemplate("uid={0},ou=users,dc=mycompany,dc=com");
+        LdapContextFactory factory = createMock(LdapContextFactory.class);
+        realm.setContextFactory(factory);
+
+        expect(factory.getLdapContext(isA(Object.class), isA(Object.class)))
+                .andThrow(new NamingException("Communication error."));
+        replay(factory);
+
+        realm.getAuthenticationInfo(new UsernamePasswordToken("jsmith", "secret") );
+    }
+
+    /**
+     * This test simulates that if a non-String principal (i.e. not a username) is passed as the LDAP principal, that
+     * it is not altered into a User DN and is passed as-is.  This will allow principals to be things like X.509
+     * certificates as well instead of only strings.
+     *
+     * @throws NamingException not thrown
+     */
+    @Test
+    public void testGetAuthenticationInfoNonSimpleToken() throws NamingException {
+        realm.setUserDnTemplate("uid={0},ou=users,dc=mycompany,dc=com");
+        LdapContextFactory factory = createMock(LdapContextFactory.class);
+        realm.setContextFactory(factory);
+
+        final UUID userId = UUID.randomUUID();
+
+        //ensure the userId is passed as-is:
+        expect(factory.getLdapContext(eq(userId), isA(Object.class))).andReturn(createNiceMock(LdapContext.class));
+        replay(factory);
+
+        realm.getAuthenticationInfo(new AuthenticationToken() {
+            public Object getPrincipal() {
+                return userId;
+            }
+
+            public Object getCredentials() {
+                return "secret";
+            }
+        });
+        verify(factory);
+    }
+
+    @Test(expected=IllegalArgumentException.class)
+    public void testGetUserDnNullArgument() {
+        realm.getUserDn(null);
+    }
+
+    @Test
+    public void testGetUserDnWithOutPrefixAndSuffix() {
+        realm = new DefaultLdapRealm() {
+            @Override
+            protected String getUserDnPrefix() {
+                return null;
+            }
+
+            @Override
+            protected String getUserDnSuffix() {
+                return null;
+            }
+        };
+        String principal = "foo";
+        String userDn = realm.getUserDn(principal);
+        assertEquals(principal, userDn);
+    }
+}

http://git-wip-us.apache.org/repos/asf/shiro/blob/dbfacf1f/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapRealmTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapRealmTest.java b/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapRealmTest.java
index 3d809a9..b8a6b94 100644
--- a/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapRealmTest.java
+++ b/core/src/test/java/org/apache/shiro/realm/ldap/JndiLdapRealmTest.java
@@ -18,145 +18,25 @@
  */
 package org.apache.shiro.realm.ldap;
 
-import org.apache.shiro.authc.AuthenticationException;
-import org.apache.shiro.authc.AuthenticationToken;
-import org.apache.shiro.authc.UsernamePasswordToken;
-import org.apache.shiro.authc.credential.AllowAllCredentialsMatcher;
-import org.junit.Before;
 import org.junit.Test;
-
-import javax.naming.NamingException;
-import javax.naming.ldap.LdapContext;
-
-import java.util.UUID;
-
-import static org.easymock.EasyMock.*;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
 
 /**
  * Tests for the {@link JndiLdapRealm} class.
  *
  * @since 1.1
+ * @deprecated Replaced by {@link DefaultLdapRealmTest}
  */
-@SuppressWarnings({"ThrowableInstanceNeverThrown"})
-public class JndiLdapRealmTest {
-
-    private JndiLdapRealm realm;
-
-    @Before
-    public void setUp() {
-        realm = new JndiLdapRealm();
-    }
-
-    @Test
-    public void testDefaultInstance() {
-        assertTrue(realm.getCredentialsMatcher() instanceof AllowAllCredentialsMatcher);
-        assertEquals(AuthenticationToken.class, realm.getAuthenticationTokenClass());
-        assertTrue(realm.getContextFactory() instanceof JndiLdapContextFactory);
-    }
-
-    @Test(expected=IllegalArgumentException.class)
-    public void testSetUserDnTemplateNull() {
-        realm.setUserDnTemplate(null);
-    }
-    
-    @Test(expected=IllegalArgumentException.class)
-    public void testSetUserDnTemplateEmpty() {
-        realm.setUserDnTemplate("  ");
-    }
-
-    @Test(expected=IllegalArgumentException.class)
-    public void testSetUserDnTemplateWithoutToken() {
-        realm.setUserDnTemplate("uid=,ou=users,dc=mycompany,dc=com");
-    }
-
-    @Test
-    public void testUserDnTemplate() {
-        String template = "uid={0},ou=users,dc=mycompany,dc=com";
-        realm.setUserDnTemplate(template);
-        assertEquals(template, realm.getUserDnTemplate());
-    }
-
-    @Test
-    public void testUserDnTemplateSubstitution() throws NamingException {
-        realm.setUserDnTemplate("uid={0},ou=users,dc=mycompany,dc=com");
-        LdapContextFactory factory = createMock(LdapContextFactory.class);
-        realm.setContextFactory(factory);
-
-        Object expectedPrincipal = "uid=jsmith,ou=users,dc=mycompany,dc=com";
-
-        expect(factory.getLdapContext(eq(expectedPrincipal), isA(Object.class))).andReturn(createNiceMock(LdapContext.class));
-        replay(factory);
-
-        realm.getAuthenticationInfo(new UsernamePasswordToken("jsmith", "secret") );
-        verify(factory);
-    }
-
-    @Test(expected= AuthenticationException.class)
-    public void testGetAuthenticationInfoNamingAuthenticationException() throws NamingException {
-        realm.setUserDnTemplate("uid={0},ou=users,dc=mycompany,dc=com");
-        LdapContextFactory factory = createMock(LdapContextFactory.class);
-        realm.setContextFactory(factory);
-
-        expect(factory.getLdapContext(isA(Object.class), isA(Object.class)))
-                .andThrow(new javax.naming.AuthenticationException("LDAP Authentication failed."));
-        replay(factory);
-
-        realm.getAuthenticationInfo(new UsernamePasswordToken("jsmith", "secret") );
-    }
-
-    @Test(expected= AuthenticationException.class)
-    public void testGetAuthenticationInfoNamingException() throws NamingException {
-        realm.setUserDnTemplate("uid={0},ou=users,dc=mycompany,dc=com");
-        LdapContextFactory factory = createMock(LdapContextFactory.class);
-        realm.setContextFactory(factory);
-
-        expect(factory.getLdapContext(isA(Object.class), isA(Object.class)))
-                .andThrow(new NamingException("Communication error."));
-        replay(factory);
-
-        realm.getAuthenticationInfo(new UsernamePasswordToken("jsmith", "secret") );
-    }
-
-    /**
-     * This test simulates that if a non-String principal (i.e. not a username) is passed as the LDAP principal, that
-     * it is not altered into a User DN and is passed as-is.  This will allow principals to be things like X.509
-     * certificates as well instead of only strings.
-     *
-     * @throws NamingException not thrown
-     */
-    @Test
-    public void testGetAuthenticationInfoNonSimpleToken() throws NamingException {
-        realm.setUserDnTemplate("uid={0},ou=users,dc=mycompany,dc=com");
-        LdapContextFactory factory = createMock(LdapContextFactory.class);
-        realm.setContextFactory(factory);
-
-        final UUID userId = UUID.randomUUID();
-
-        //ensure the userId is passed as-is:
-        expect(factory.getLdapContext(eq(userId), isA(Object.class))).andReturn(createNiceMock(LdapContext.class));
-        replay(factory);
-
-        realm.getAuthenticationInfo(new AuthenticationToken() {
-            public Object getPrincipal() {
-                return userId;
-            }
-
-            public Object getCredentials() {
-                return "secret";
-            }
-        });
-        verify(factory);
-    }
+@SuppressWarnings({"ThrowableInstanceNeverThrown", "deprecation"})
+public class JndiLdapRealmTest extends DefaultLdapRealmTest {
 
-    @Test(expected=IllegalArgumentException.class)
-    public void testGetUserDnNullArgument() {
-        realm.getUserDn(null);
+    protected DefaultLdapRealm getNewRealmUnderTest() {
+        return new JndiLdapRealm();
     }
 
     @Test
     public void testGetUserDnWithOutPrefixAndSuffix() {
-        realm = new JndiLdapRealm() {
+        JndiLdapRealm realm = new JndiLdapRealm() {
             @Override
             protected String getUserDnPrefix() {
                 return null;