[jira] [Commented] (SHIRO-347) Improved JndiLdapRealm Concept

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[jira] [Commented] (SHIRO-347) Improved JndiLdapRealm Concept

JIRA jira@apache.org

    [ https://issues.apache.org/jira/browse/SHIRO-347?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15967264#comment-15967264 ]

Jorge Machado commented on SHIRO-347:
-------------------------------------

+1 on this. Having exact the same issue here.

> Improved JndiLdapRealm Concept
> ------------------------------
>
>                 Key: SHIRO-347
>                 URL: https://issues.apache.org/jira/browse/SHIRO-347
>             Project: Shiro
>          Issue Type: New Feature
>          Components: Realms
>    Affects Versions: 1.2.0
>         Environment: No dependency
>            Reporter: Charles Syperski
>            Priority: Trivial
>              Labels: JndiLdapRealm, ldap, realm
>             Fix For: 2.0.0
>
>   Original Estimate: 2h
>  Remaining Estimate: 2h
>
> I am new to this framework, but I have a concept for extending the JndiLdapRealm to make it more usable for larger LDAP trees.  I have found the JndiLdapRealm to be VERY limiting, especially since it only
> allows a single OU due to the use of 'userDnTemplate'.  I have extended the JndiLdapRealm class to allow for sub-tree searches with a base OU as well as customized search filters.  This implementation probably isn't production ready, and doesn't follow the coding standards of Shiro, it is more of a proof of concept.  Since I don't see a way to attach files/diffs, the source and configuration for Shiro.ini is below:
> == Start Source ==
> package net.dupage88.usercentral.shiro;
> import java.util.ArrayList;
> import java.util.List;
> import javax.naming.NamingEnumeration;
> import javax.naming.NamingException;
> import javax.naming.directory.SearchControls;
> import javax.naming.directory.SearchResult;
> import javax.naming.ldap.LdapContext;
> import org.apache.shiro.authc.AuthenticationInfo;
> import org.apache.shiro.authc.AuthenticationToken;
> import org.apache.shiro.realm.ldap.JndiLdapRealm;
> import org.apache.shiro.realm.ldap.LdapContextFactory;
> import org.apache.shiro.realm.ldap.LdapUtils;
> import org.apache.shiro.util.StringUtils;
> import org.slf4j.Logger;
> import org.slf4j.LoggerFactory;
> /**
>  *
>  * @author csyperski
>  */
> public class CWSJndiLdapRealm extends JndiLdapRealm
> {  
>     private static final Logger log = LoggerFactory.getLogger(CWSJndiLdapRealm.class);
>    
>     public  static final String DEFAULT_SEARCH_FILTER = "(uid={0})";
>     private String searchBase;
>     private String searchFilter;
>    
>     public CWSJndiLdapRealm()
>     {
>         super();
>         searchFilter = DEFAULT_SEARCH_FILTER;
>         searchBase = null;
>     }
>    
>     @Override
>     public void setUserDnTemplate(String template) throws IllegalArgumentException
>     {
>         throw new RuntimeException("This method is not implemented, please use setSeachFilter and setBaseDn");
>     }
>    
>     @Override
>     public String getUserDnTemplate()
>     {
>         throw new RuntimeException("This method is not implemented, please use getSeachFilter and getBaseDn");
>     }
>    
>     protected String getSearchFilter(String principal) throws IllegalArgumentException, IllegalStateException
>     {
>         if (!StringUtils.hasText(principal))
>         {
>             throw new IllegalArgumentException("User principal cannot be null or empty.");
>         }
>        
>         if ( ! searchFilter.contains("{0}") )
>         {
>             log.warn("You didn't include {0} in your searchFilter, I assume you know what you are doing!");
>         }
>         return searchFilter.replace("{0}", principal);
>     }
>    
>     @Override
>     protected AuthenticationInfo queryForAuthenticationInfo(AuthenticationToken token,
>                                                             LdapContextFactory ldapContextFactory)
>             throws NamingException {
>        
>         Object principal = token.getPrincipal();
>         Object credentials = token.getCredentials();
>         if ( searchBase == null || searchBase.trim().length() == 0 )
>         {
>             log.error("searchBase must be defined");
>             return null;
>         }
>        
>         if ( searchFilter == null || searchFilter.trim().length() == 0 )
>         {
>             log.error("searchFilter must be defined");
>             return null;
>         }
>        
>         if ( ! (principal instanceof String) )
>         {
>             log.error("principal must be a string");
>             return null;
>         }
>         String filter = getSearchFilter((String)principal);
>         log.debug("Using base: {}", searchBase);
>         log.debug("Using filter: {}", filter);
>        
>         LdapContext ctx = null;
>        
>         SearchControls searchControls = new SearchControls();  
>         searchControls.setReturningAttributes(new String[] { "dn" });
>         searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);  
>        
>         /* We don't know what the search filter will look like
>          * So if we get multiple results, we should try them all.
>          */
>         List<String> possibleDns = new ArrayList<String>();
>        
>         /* Lets search for the user first */
>         try
>         {
>             log.debug("Searching for user '{}' through LDAP", principal);
>             ctx = ldapContextFactory.getSystemLdapContext();
>             NamingEnumeration response = ctx.search(searchBase, filter, searchControls);
>             while (response.hasMoreElements())  
>             {  
>                 possibleDns.add(((SearchResult)response.next()).getNameInNamespace());
>             }  
>         }
>         finally
>         {
>             LdapUtils.closeContext(ctx);
>         }
>        
>         /* Now lets authenticate */
>         if ( ! possibleDns.isEmpty() )
>         {
>             for( String dn : possibleDns )
>             {
>                 ctx = null;
>                 try
>                 {
>                     log.debug("Attempting dn '{}' through LDAP", dn);
>                     ctx = ldapContextFactory.getLdapContext(dn, credentials);
>                     log.debug("Authenticated: {}!", dn);
>                     return createAuthenticationInfo(token, principal, credentials, ctx);        // currently uses simple -> change to a subclass thats supports holding the dn?
>                 }
>                 catch ( NamingException e )
>                 {
>                     log.debug("Failed to authenticate for: {}", dn);
>                 }
>                 finally
>                 {
>                     LdapUtils.closeContext(ctx);
>                 }
>             }
>         }
>         throw new NamingException( "User: '" + (String)principal +  "' not authenticated!" );
>     }
>     /**
>      * @return the searchBase
>      */
>     public String getSearchBase()
>     {
>         return searchBase;
>     }
>     /**
>      * @param searchBase the searchBase to set
>      */
>     public void setSearchBase(String searchBase)
>     {
>         this.searchBase = searchBase;
>     }
>     /**
>      * @return the searchFilter
>      */
>     public String getSearchFilter()
>     {
>         return searchFilter;
>     }
>     /**
>      * @param searchFilter the searchFilter to set
>      */
>     public void setSearchFilter(String searchFilter)
>     {
>         this.searchFilter = searchFilter;
>     }
> }
> == End Source ==
> == Configuration Details ==
> Shiro.ini
> ldapRealm = [yourpackagename].CWSJndiLdapRealm
> # Removed the need for this below
> # userDnTemplate limited the scope to a single OU (I believe)
> # so it is replaced by searchBase and searchFilter
> #ldapRealm.userDnTemplate = cn={0},ou=test,o=test
> # This is the root of the LDAP search
> ldapRealm.searchBase = o=test # NEW - The search root
> # Search filter allows for more complex queries like (&(cn={0})(objectClass=inetOrgPerson))
> # or (&(uid={0})(objectClass=inetOrgPerson)(memberOf=somegroup))
> ldapRealm.searchFilter = (cn={0})
> ldapRealm.contextFactory.url = ldap://X.X.X.X:389
> ldapRealm.contextFactory.authenticationMechanism = DIGEST-MD5
> ldapRealm.contextFactory.systemUsername = cn=someuser,o=test
> ldapRealm.contextFactory.systemPassword = somepassword



--
This message was sent by Atlassian JIRA
(v6.3.15#6346)