1   package com.atlassian.user.impl.ldap.search;
2   
3   /**
4    * Handles all searches for LDAP. The managers simply send this component instructions.
5    */
6   
7   import com.atlassian.user.EntityException;
8   import com.atlassian.user.User;
9   import com.atlassian.user.impl.RepositoryException;
10  import com.atlassian.user.impl.ldap.LDAPEntity;
11  import com.atlassian.user.impl.ldap.properties.LdapSearchProperties;
12  import com.atlassian.user.impl.ldap.repository.LdapContextFactory;
13  import com.atlassian.user.util.LDAPUtils;
14  import com.atlassian.util.profiling.UtilTimerStack;
15  import net.sf.ldaptemplate.support.filter.EqualsFilter;
16  import net.sf.ldaptemplate.support.filter.Filter;
17  import org.apache.log4j.Logger;
18  
19  import javax.naming.NamingEnumeration;
20  import javax.naming.NamingException;
21  import javax.naming.directory.DirContext;
22  import javax.naming.directory.SearchControls;
23  import javax.naming.directory.SearchResult;
24  
25  public class DefaultLDAPUserAdaptor implements LDAPUserAdaptor
26  {
27      protected final Logger log = Logger.getLogger(this.getClass());
28  
29      private final LdapContextFactory repository;
30      private final LdapSearchProperties searchProperties;
31      private final LdapFilterFactory filterFactory;
32  
33      public DefaultLDAPUserAdaptor(LdapContextFactory repository, LdapSearchProperties searchProperties,
34          LdapFilterFactory filterFactory)
35      {
36          this.filterFactory = filterFactory;
37          this.searchProperties = searchProperties;
38          this.repository = repository;
39      }
40  
41      private String[] getDefaultAttributes()
42      {
43          String[] defaultAttributesToReturn = new String[4];
44          defaultAttributesToReturn[0] = searchProperties.getUsernameAttribute();
45          defaultAttributesToReturn[1] = searchProperties.getFirstnameAttribute();
46          defaultAttributesToReturn[2] = searchProperties.getSurnameAttribute();
47          defaultAttributesToReturn[3] = searchProperties.getEmailAttribute();
48  
49          return defaultAttributesToReturn;
50      }
51  
52      public LDAPPagerInfo search(Filter searchFilter) throws RepositoryException
53      {
54          return search(searchFilter, getDefaultAttributes());
55      }
56  
57      public LDAPPagerInfo search(Filter userFilter, String[] attributesToReturnFromSearch) throws RepositoryException
58      {
59          DirContext ctx = null;
60  
61          SearchControls ctls = LDAPUtils.createSearchControls(attributesToReturnFromSearch,
62              searchProperties.isUserSearchScopeAllDepths(),
63              searchProperties.getTimeLimitMillis());
64  
65          NamingEnumeration userSearchEnume;
66          Filter filter = LDAPUtils.makeAndFilter(filterFactory.getUserSearchFilter(), userFilter);
67  
68          try
69          {
70              ctx = repository.getLDAPContext();
71  
72              if (UtilTimerStack.isActive())
73                  UtilTimerStack.push(this.getClass().getName() + "_search_JNDI_RAW_" + filter);
74  
75              if (log.isDebugEnabled())
76                  log.debug("DefaultLDAPUserAdapter.search:" + filter.encode());
77  
78              userSearchEnume = ctx.search(searchProperties.getBaseUserNamespace(), filter.encode(), ctls);
79  
80              return new LDAPPagerInfo(userSearchEnume, filter,
81                  searchProperties.getBaseUserNamespace(),
82                  searchProperties.isUserSearchScopeAllDepths(),
83                  attributesToReturnFromSearch, searchProperties.getTimeLimitMillis());
84          }
85          catch (NamingException e)
86          {
87              throw new RepositoryException(e);
88          }
89          finally
90          {
91              try
92              {
93                  if (ctx != null) ctx.close();
94              }
95              catch (NamingException e)
96              {
97                  log.warn("Exception closing context", e);
98              }
99  
100             if (UtilTimerStack.isActive())
101                 UtilTimerStack.pop(this.getClass().getName() + "_search_JNDI_RAW_" + filter);
102         }
103 
104     }
105 
106     public LDAPPagerInfo getUserAttributes(String username, String[] specifiedAttributes) throws RepositoryException
107     {
108         Filter searchFilter = new EqualsFilter(searchProperties.getUsernameAttribute(), username);
109         return search(LDAPUtils.makeAndFilter(filterFactory.getUserSearchFilter(), searchFilter), specifiedAttributes);
110     }
111 
112     public String getUserDN(User user) throws EntityException
113     {
114         if (user instanceof LDAPEntity)
115         {
116             LDAPEntity entity = (LDAPEntity) user;
117             return entity.getDistinguishedName();
118         }
119 
120         return getUserDN(user.getName());
121     }
122 
123     public String getUserDN(String username) throws EntityException
124     {
125         if (UtilTimerStack.isActive())
126             UtilTimerStack.push(this.getClass().getName() + "_getUserDN(" + username + ")");
127 
128         LDAPPagerInfo ldapPagerInfo = getUserAttributes(username, getDefaultAttributes());
129 
130         if (ldapPagerInfo.getNamingEnumeration().hasMoreElements())
131         {
132             SearchResult result = (SearchResult) ldapPagerInfo.getNamingEnumeration().nextElement();
133 
134             String userDN = result.getName();
135             // strip any quotes which JNDI has put around the name
136             if (userDN.startsWith("\"") && userDN.endsWith("\""))
137             {
138                 userDN = userDN.substring(1, userDN.length() - 1);
139             }
140             //we need a DN. The result only returns the full DN if the result was in the base getGroupEntries context
141             if (userDN.indexOf(searchProperties.getBaseUserNamespace()) == -1)
142                 userDN = userDN + "," + searchProperties.getBaseUserNamespace();
143 
144             try
145             {
146                 ldapPagerInfo.getNamingEnumeration().close();
147             }
148             catch (NamingException e)
149             {
150                 throw new EntityException(e);
151             }
152 
153             if (UtilTimerStack.isActive())
154                 UtilTimerStack.pop(this.getClass().getName() + "_getUserDN(" + username + ")");
155 
156             return userDN;
157         }
158 
159         throw new RepositoryException("Could not locate a DN for user [" + username + "]");
160     }
161 
162     /**
163      * Builds a LDAP getGroupEntries filter matching RFC-2254 by ANDing the getGroupEntries term.
164      *
165      * @param firstTerm - a <code>StringBuffer</code> which will become the final getGroupEntries filter
166      * @param addedFilter - a String holding a getGroupEntries Term to be ANDed.
167      * @return <code>String</code> representing the completed firstTerm
168      */
169     public StringBuffer addSearchTermToFilter(StringBuffer firstTerm, String addedFilter)
170     {
171         if (firstTerm != null)
172             if (addedFilter.indexOf("(") == 0 && addedFilter.lastIndexOf(")") == addedFilter.length() - 1)
173                 firstTerm = new StringBuffer("(&" + firstTerm + addedFilter + ")");
174             else
175                 firstTerm = new StringBuffer("(&" + firstTerm + "(" + addedFilter + "))");
176         else
177             firstTerm = new StringBuffer(addedFilter);
178 
179         return firstTerm;
180     }
181 }