You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@shiro.apache.org by Charles Syperski <cs...@dupage88.net> on 2012/10/01 20:31:20 UTC

Problem with (JndiLdap)Realms

I think I just hit a pretty big hurdle with Shiro and I am looking for 
some assistance or recommendations.  Here is a bit of background.  I 
extended the JndiLdapRealm to support sub tree searching as well as 
assigning roles if you matched a custom ldap filter.  The code is at the 
bottom of this email:

Basically what I wanted to do was have two realms authenticating against 
the same ldap tree, one for students and one for staff. Below is my 
configuration:

#this realm automatically assigns users to role net.dupage88.role.student
studentRealm = net.dupage88.usercentral.shiro.CWSJndiLdapRealm
studentRealm.searchBase = o=dist88
studentRealm.searchFilter = 
(&(cn={0})(objectClass=inetOrgPerson)(groupMembership=cn=everyonestaff,ou=groups,o=dist88))
studentRealm.assignedRoles = net.dupage88.role.staff
studentRealm.contextFactory.url = ldap://10.2.50.35:389
studentRealm.contextFactory.systemUsername = cn=user,o=dist88
studentRealm.contextFactory.systemPassword = xxxxxxx

# This real automatically assings users to role: net.dupage88.role.staff
staffRealm = net.dupage88.usercentral.shiro.CWSJndiLdapRealm
staffRealm.searchBase = o=dist88
staffRealm.searchFilter = 
(&(cn={0})(objectClass=inetOrgPerson)(groupMembership=cn=everyonestudent,ou=groups,o=dist88))
staffRealm.assignedRoles = net.dupage88.role.student
staffRealm.contextFactory.url = ldap://10.2.50.35:389
staffRealm.contextFactory.systemUsername = cn=user,o=dist88
staffRealm.contextFactory.systemPassword = xxxxxxxx

The problem I am finding is that authentication works great for me but 
the authorization isn't not working.  The reason I wanted to do this is 
so that I could have my student's in role net.dupage88.role.student 
without having to manually assign them any right and the same for my 
staff members all based on an LDAP filter.

Here is my problem, I assumed that queryForAuthorizationInfo was only 
called for principals that were authenticated against a given realm.  
But it seems that even if a users was authenticated again another realm, 
that this method is still called on all realms.  So in my example, if a 
student is authenticated against the studentRealm AuthorizationInfo is 
still pulled from the staffRealm via 
staffRealm->queryForAuthorizationInfo.  Is this correct?

So is it possible to only call queryForAuthorizationInfo on the realm 
where the user was authenticated against?
My problem is that I can't guarantee that my principals (read usernames) 
are going to be unique across all of my realms.  So even if I requery my 
tree with the users principal information I can not to sure that they 
match or are the same user based on the cn or uid because they might be 
from another realm but have the same username.

In summary it seems like Shiro assumes the usernames are unique across 
realms, is this correct?  I was hoping to build a unified authentication 
system where all our district users (students, staff, parents) would go 
through the same system, but parents don't live in ldap, they are in a 
table, so they may use the same username as a staff member like 'asmith' 
and thus in my current setup they would get the staff role : (  Is there 
anyway to work around this?

I feel like Shiro was close to being the panacea that I was looking for 
but now I am wondering if I shot myself in the foot.

Thanks for any help you can offer!

===============================================

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;
     private String assignedRoles;

     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);        // uses simple -> change to subclass to 
support 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!" );
     }

     private Set<String> parseRoles()
     {
         Set<String> roles = new HashSet<>();
         if ( StringUtils.hasLength(assignedRoles))
         {
             Pattern pattern = Pattern.compile("[a-zA-Z0-9\\.\\-_]*");
             String[] parts = assignedRoles.split(",");
             for( String part : parts )
             {
                 if ( ! StringUtils.hasLength(part))
                     continue;

                 if ( pattern.matcher(part.trim()).matches() )
                 {
                     roles.add(part.trim());
                 }
             }
         }
         return roles;
     }

     @Override
     protected AuthorizationInfo 
queryForAuthorizationInfo(PrincipalCollection principals, 
LdapContextFactory ldapContextFactory) throws NamingException
     {
         if (principals == null)
         {
             return null;
         }

         Set<String> roleNames = new HashSet<String>();
         String username = (String) getAvailablePrincipal(principals);
         if ( StringUtils.hasLength(username))
         {
             Set<String> roles = parseRoles();
             SimpleAuthorizationInfo info = new 
SimpleAuthorizationInfo(roleNames);
             info.setRoles(roles);
             return info;
         }
         return null;
     }

     /**
      * @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;
     }

     /**
      * @return the assignedPermissions
      */
     public String getAssignedRoles()
     {
         return assignedRoles;
     }

     /**
      * @param assignedPermissions the assignedPermissions to set
      */
     public void setAssignedRoles(String assignedRoles)
     {
         this.assignedRoles = assignedRoles;
     }
}

Re: Problem with (JndiLdap)Realms

Posted by Charles Syperski <cs...@dupage88.net>.
Thanks so much, that couldn't have been easier.  You are awesome!


On 10/01/2012 01:49 PM, Jared Bunting wrote:
> Here is the implementation of "getAvailablePrincipal":
>
>      protected Object getAvailablePrincipal(PrincipalCollection
> principals) {
>          Object primary = null;
>          if (!CollectionUtils.isEmpty(principals)) {
>              Collection thisPrincipals = principals.fromRealm(getName());
>              if (!CollectionUtils.isEmpty(thisPrincipals)) {
>                  primary = thisPrincipals.iterator().next();
>              } else {
>                  //no principals attributed to this particular realm.
> Fall back to the 'master' primary:
>                  primary = principals.getPrimaryPrincipal();
>              }
>          }
>
>          return primary;
>      }
>
>
> Seems to me that, rather than using this method, you use one just like
> it that skips the "else" clause.  Then, if the principal didn't come
> from this realm, you don't add any roles.
>
> Hope that helps,
> Jared
>
> On Mon 01 Oct 2012 01:31:20 PM CDT, Charles Syperski wrote:
>> I think I just hit a pretty big hurdle with Shiro and I am looking for
>> some assistance or recommendations.  Here is a bit of background.  I
>> extended the JndiLdapRealm to support sub tree searching as well as
>> assigning roles if you matched a custom ldap filter.  The code is at
>> the bottom of this email:
>>
>> Basically what I wanted to do was have two realms authenticating
>> against the same ldap tree, one for students and one for staff. Below
>> is my configuration:
>>
>> #this realm automatically assigns users to role net.dupage88.role.student
>> studentRealm = net.dupage88.usercentral.shiro.CWSJndiLdapRealm
>> studentRealm.searchBase = o=dist88
>> studentRealm.searchFilter =
>> (&(cn={0})(objectClass=inetOrgPerson)(groupMembership=cn=everyonestaff,ou=groups,o=dist88))
>>
>> studentRealm.assignedRoles = net.dupage88.role.staff
>> studentRealm.contextFactory.url = ldap://10.2.50.35:389
>> studentRealm.contextFactory.systemUsername = cn=user,o=dist88
>> studentRealm.contextFactory.systemPassword = xxxxxxx
>>
>> # This real automatically assings users to role: net.dupage88.role.staff
>> staffRealm = net.dupage88.usercentral.shiro.CWSJndiLdapRealm
>> staffRealm.searchBase = o=dist88
>> staffRealm.searchFilter =
>> (&(cn={0})(objectClass=inetOrgPerson)(groupMembership=cn=everyonestudent,ou=groups,o=dist88))
>>
>> staffRealm.assignedRoles = net.dupage88.role.student
>> staffRealm.contextFactory.url = ldap://10.2.50.35:389
>> staffRealm.contextFactory.systemUsername = cn=user,o=dist88
>> staffRealm.contextFactory.systemPassword = xxxxxxxx
>>
>> The problem I am finding is that authentication works great for me but
>> the authorization isn't not working.  The reason I wanted to do this
>> is so that I could have my student's in role net.dupage88.role.student
>> without having to manually assign them any right and the same for my
>> staff members all based on an LDAP filter.
>>
>> Here is my problem, I assumed that queryForAuthorizationInfo was only
>> called for principals that were authenticated against a given realm.
>> But it seems that even if a users was authenticated again another
>> realm, that this method is still called on all realms.  So in my
>> example, if a student is authenticated against the studentRealm
>> AuthorizationInfo is still pulled from the staffRealm via
>> staffRealm->queryForAuthorizationInfo.  Is this correct?
>>
>> So is it possible to only call queryForAuthorizationInfo on the realm
>> where the user was authenticated against?
>> My problem is that I can't guarantee that my principals (read
>> usernames) are going to be unique across all of my realms.  So even if
>> I requery my tree with the users principal information I can not to
>> sure that they match or are the same user based on the cn or uid
>> because they might be from another realm but have the same username.
>>
>> In summary it seems like Shiro assumes the usernames are unique across
>> realms, is this correct?  I was hoping to build a unified
>> authentication system where all our district users (students, staff,
>> parents) would go through the same system, but parents don't live in
>> ldap, they are in a table, so they may use the same username as a
>> staff member like 'asmith' and thus in my current setup they would get
>> the staff role : (  Is there anyway to work around this?
>>
>> I feel like Shiro was close to being the panacea that I was looking
>> for but now I am wondering if I shot myself in the foot.
>>
>> Thanks for any help you can offer!
>>
>> ===============================================
>>
>> 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;
>>      private String assignedRoles;
>>
>>      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);        // uses simple -> change to subclass to
>> support 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!" );
>>      }
>>
>>      private Set<String> parseRoles()
>>      {
>>          Set<String> roles = new HashSet<>();
>>          if ( StringUtils.hasLength(assignedRoles))
>>          {
>>              Pattern pattern = Pattern.compile("[a-zA-Z0-9\\.\\-_]*");
>>              String[] parts = assignedRoles.split(",");
>>              for( String part : parts )
>>              {
>>                  if ( ! StringUtils.hasLength(part))
>>                      continue;
>>
>>                  if ( pattern.matcher(part.trim()).matches() )
>>                  {
>>                      roles.add(part.trim());
>>                  }
>>              }
>>          }
>>          return roles;
>>      }
>>
>>      @Override
>>      protected AuthorizationInfo
>> queryForAuthorizationInfo(PrincipalCollection principals,
>> LdapContextFactory ldapContextFactory) throws NamingException
>>      {
>>          if (principals == null)
>>          {
>>              return null;
>>          }
>>
>>          Set<String> roleNames = new HashSet<String>();
>>          String username = (String) getAvailablePrincipal(principals);
>>          if ( StringUtils.hasLength(username))
>>          {
>>              Set<String> roles = parseRoles();
>>              SimpleAuthorizationInfo info = new
>> SimpleAuthorizationInfo(roleNames);
>>              info.setRoles(roles);
>>              return info;
>>          }
>>          return null;
>>      }
>>
>>      /**
>>       * @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;
>>      }
>>
>>      /**
>>       * @return the assignedPermissions
>>       */
>>      public String getAssignedRoles()
>>      {
>>          return assignedRoles;
>>      }
>>
>>      /**
>>       * @param assignedPermissions the assignedPermissions to set
>>       */
>>      public void setAssignedRoles(String assignedRoles)
>>      {
>>          this.assignedRoles = assignedRoles;
>>      }
>> }
>


Re: Problem with (JndiLdap)Realms

Posted by Jared Bunting <ja...@peachjean.com>.
Here is the implementation of "getAvailablePrincipal":

    protected Object getAvailablePrincipal(PrincipalCollection 
principals) {
        Object primary = null;
        if (!CollectionUtils.isEmpty(principals)) {
            Collection thisPrincipals = principals.fromRealm(getName());
            if (!CollectionUtils.isEmpty(thisPrincipals)) {
                primary = thisPrincipals.iterator().next();
            } else {
                //no principals attributed to this particular realm.  
Fall back to the 'master' primary:
                primary = principals.getPrimaryPrincipal();
            }
        }

        return primary;
    }


Seems to me that, rather than using this method, you use one just like 
it that skips the "else" clause.  Then, if the principal didn't come 
from this realm, you don't add any roles.

Hope that helps,
Jared

On Mon 01 Oct 2012 01:31:20 PM CDT, Charles Syperski wrote:
> I think I just hit a pretty big hurdle with Shiro and I am looking for
> some assistance or recommendations.  Here is a bit of background.  I
> extended the JndiLdapRealm to support sub tree searching as well as
> assigning roles if you matched a custom ldap filter.  The code is at
> the bottom of this email:
>
> Basically what I wanted to do was have two realms authenticating
> against the same ldap tree, one for students and one for staff. Below
> is my configuration:
>
> #this realm automatically assigns users to role net.dupage88.role.student
> studentRealm = net.dupage88.usercentral.shiro.CWSJndiLdapRealm
> studentRealm.searchBase = o=dist88
> studentRealm.searchFilter =
> (&(cn={0})(objectClass=inetOrgPerson)(groupMembership=cn=everyonestaff,ou=groups,o=dist88))
>
> studentRealm.assignedRoles = net.dupage88.role.staff
> studentRealm.contextFactory.url = ldap://10.2.50.35:389
> studentRealm.contextFactory.systemUsername = cn=user,o=dist88
> studentRealm.contextFactory.systemPassword = xxxxxxx
>
> # This real automatically assings users to role: net.dupage88.role.staff
> staffRealm = net.dupage88.usercentral.shiro.CWSJndiLdapRealm
> staffRealm.searchBase = o=dist88
> staffRealm.searchFilter =
> (&(cn={0})(objectClass=inetOrgPerson)(groupMembership=cn=everyonestudent,ou=groups,o=dist88))
>
> staffRealm.assignedRoles = net.dupage88.role.student
> staffRealm.contextFactory.url = ldap://10.2.50.35:389
> staffRealm.contextFactory.systemUsername = cn=user,o=dist88
> staffRealm.contextFactory.systemPassword = xxxxxxxx
>
> The problem I am finding is that authentication works great for me but
> the authorization isn't not working.  The reason I wanted to do this
> is so that I could have my student's in role net.dupage88.role.student
> without having to manually assign them any right and the same for my
> staff members all based on an LDAP filter.
>
> Here is my problem, I assumed that queryForAuthorizationInfo was only
> called for principals that were authenticated against a given realm.
> But it seems that even if a users was authenticated again another
> realm, that this method is still called on all realms.  So in my
> example, if a student is authenticated against the studentRealm
> AuthorizationInfo is still pulled from the staffRealm via
> staffRealm->queryForAuthorizationInfo.  Is this correct?
>
> So is it possible to only call queryForAuthorizationInfo on the realm
> where the user was authenticated against?
> My problem is that I can't guarantee that my principals (read
> usernames) are going to be unique across all of my realms.  So even if
> I requery my tree with the users principal information I can not to
> sure that they match or are the same user based on the cn or uid
> because they might be from another realm but have the same username.
>
> In summary it seems like Shiro assumes the usernames are unique across
> realms, is this correct?  I was hoping to build a unified
> authentication system where all our district users (students, staff,
> parents) would go through the same system, but parents don't live in
> ldap, they are in a table, so they may use the same username as a
> staff member like 'asmith' and thus in my current setup they would get
> the staff role : (  Is there anyway to work around this?
>
> I feel like Shiro was close to being the panacea that I was looking
> for but now I am wondering if I shot myself in the foot.
>
> Thanks for any help you can offer!
>
> ===============================================
>
> 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;
>     private String assignedRoles;
>
>     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);        // uses simple -> change to subclass to
> support 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!" );
>     }
>
>     private Set<String> parseRoles()
>     {
>         Set<String> roles = new HashSet<>();
>         if ( StringUtils.hasLength(assignedRoles))
>         {
>             Pattern pattern = Pattern.compile("[a-zA-Z0-9\\.\\-_]*");
>             String[] parts = assignedRoles.split(",");
>             for( String part : parts )
>             {
>                 if ( ! StringUtils.hasLength(part))
>                     continue;
>
>                 if ( pattern.matcher(part.trim()).matches() )
>                 {
>                     roles.add(part.trim());
>                 }
>             }
>         }
>         return roles;
>     }
>
>     @Override
>     protected AuthorizationInfo
> queryForAuthorizationInfo(PrincipalCollection principals,
> LdapContextFactory ldapContextFactory) throws NamingException
>     {
>         if (principals == null)
>         {
>             return null;
>         }
>
>         Set<String> roleNames = new HashSet<String>();
>         String username = (String) getAvailablePrincipal(principals);
>         if ( StringUtils.hasLength(username))
>         {
>             Set<String> roles = parseRoles();
>             SimpleAuthorizationInfo info = new
> SimpleAuthorizationInfo(roleNames);
>             info.setRoles(roles);
>             return info;
>         }
>         return null;
>     }
>
>     /**
>      * @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;
>     }
>
>     /**
>      * @return the assignedPermissions
>      */
>     public String getAssignedRoles()
>     {
>         return assignedRoles;
>     }
>
>     /**
>      * @param assignedPermissions the assignedPermissions to set
>      */
>     public void setAssignedRoles(String assignedRoles)
>     {
>         this.assignedRoles = assignedRoles;
>     }
> }