1 package com.atlassian.user.impl.ldap.adaptor;
2
3 import com.atlassian.user.EntityException;
4 import com.atlassian.user.Group;
5 import com.atlassian.user.impl.EntityMissingException;
6 import com.atlassian.user.impl.RepositoryException;
7 import com.atlassian.user.impl.ldap.LDAPEntity;
8 import com.atlassian.user.impl.ldap.LDAPGroupFactory;
9 import com.atlassian.user.impl.ldap.properties.LdapSearchProperties;
10 import com.atlassian.user.impl.ldap.repository.LdapContextFactory;
11 import com.atlassian.user.impl.ldap.search.LDAPPagerInfo;
12 import com.atlassian.user.impl.ldap.search.LdapFilterFactory;
13 import com.atlassian.user.impl.ldap.search.page.LDAPEntityPager;
14 import com.atlassian.user.search.page.Pager;
15 import com.atlassian.user.util.LDAPUtils;
16 import com.atlassian.util.profiling.UtilTimerStack;
17 import com.opensymphony.util.TextUtils;
18 import net.sf.ldaptemplate.support.filter.EqualsFilter;
19 import net.sf.ldaptemplate.support.filter.Filter;
20 import org.apache.log4j.Logger;
21
22 import javax.naming.Context;
23 import javax.naming.NamingEnumeration;
24 import javax.naming.NamingException;
25 import javax.naming.directory.Attributes;
26 import javax.naming.directory.DirContext;
27 import javax.naming.directory.SearchControls;
28 import javax.naming.directory.SearchResult;
29 import java.net.Inet4Address;
30 import java.net.URI;
31 import java.net.URISyntaxException;
32 import java.net.UnknownHostException;
33 import java.text.MessageFormat;
34
35 public abstract class AbstractLDAPGroupAdaptor implements LDAPGroupAdaptor
36 {
37 protected final Logger log = Logger.getLogger(this.getClass());
38
39 protected final LdapSearchProperties searchProperties;
40 private final LdapFilterFactory filterFactory;
41
42 protected final LdapContextFactory repository;
43 protected final LDAPGroupFactory groupFactory;
44
45 protected AbstractLDAPGroupAdaptor(LdapContextFactory repo, LdapSearchProperties searchProperties,
46 LDAPGroupFactory groupFactory, LdapFilterFactory filterFactory)
47 {
48 this.repository = repo;
49 this.searchProperties = searchProperties;
50 this.filterFactory = filterFactory;
51 this.groupFactory = groupFactory;
52 }
53
54 public Group getGroup(String name) throws EntityException
55 {
56 DirContext ctx = null;
57 NamingEnumeration<SearchResult> enume;
58 Group group = null;
59
60 String filter = constructGroupSearchFilter(name).encode();
61 String baseDn = searchProperties.getBaseGroupNamespace();
62
63 try
64 {
65 ctx = repository.getLDAPContext();
66 if (log.isDebugEnabled())
67 log.debug("AbstractLDAPGroupAdapter.getGroup:" + filter);
68
69 enume = ctx.search(baseDn, filter,
70 LDAPUtils.createSearchControls(new String[]{searchProperties.getGroupnameAttribute()}, searchProperties.isGroupSearchScopeAllDepths(),
71 searchProperties.getTimeLimitMillis()));
72 if (enume == null) return null;
73
74 while (enume.hasMoreElements())
75 {
76 SearchResult result = enume.nextElement();
77 Attributes attrs = result.getAttributes();
78 group = groupFactory.getGroup(attrs, result.getName());
79 }
80 }
81 catch (NamingException e)
82 {
83 String msg = "Exception when retrieving LDAP group {0} (base DN: {1}, filter: {2})";
84 throw new RepositoryException(MessageFormat.format(msg, name, baseDn, filter), e);
85 }
86 finally
87 {
88 try
89 {
90 if (ctx != null) ctx.close();
91 }
92 catch (NamingException e)
93 {
94 log.warn("Failed to close LDAP connection after search for group: " + name, e);
95 }
96 }
97
98 return group;
99 }
100
101 public Pager<Group> getGroups() throws EntityException
102 {
103 LDAPPagerInfo ldapPagerInfo = getGroupEntries();
104
105 return new LDAPEntityPager<Group>(searchProperties, repository, groupFactory, ldapPagerInfo);
106 }
107
108 public LDAPPagerInfo getGroupEntries() throws EntityException
109 {
110 return getGroupEntries("*");
111 }
112
113 public LDAPPagerInfo getGroupEntries(String groupName) throws EntityException
114 {
115 return getGroupEntries(groupName, null, null);
116 }
117
118 public LDAPPagerInfo getGroupEntries(String[] attributesToReturn, Filter additionalSearchFilter)
119 throws EntityException
120 {
121 return getGroupEntries("*", attributesToReturn, additionalSearchFilter);
122 }
123
124
125
126
127
128
129 public LDAPPagerInfo getGroupEntries(String groupName, String[] attributesToReturn, Filter additionalSearchFilter)
130 throws RepositoryException
131 {
132 Filter searchFilter = constructGroupSearchFilter(groupName, additionalSearchFilter);
133 return search(searchFilter, attributesToReturn);
134 }
135
136
137
138
139
140 public LDAPPagerInfo search(Filter searchFilter) throws RepositoryException
141 {
142 return search(searchFilter, null);
143 }
144
145
146
147
148
149
150
151 public LDAPPagerInfo search(Filter filter, String[] attributesToReturn) throws RepositoryException
152 {
153 if (UtilTimerStack.isActive())
154 UtilTimerStack.push(this.getClass().getName() + "_search(" + filter + ")");
155
156 try
157 {
158 if (attributesToReturn == null)
159 attributesToReturn = new String[]{searchProperties.getGroupnameAttribute()};
160
161 Filter groupSearchFilter = filterFactory.getGroupSearchFilter();
162 if (filter != null)
163 groupSearchFilter = LDAPUtils.makeAndFilter(groupSearchFilter, filter);
164
165 SearchControls ctls = LDAPUtils.createSearchControls(
166 attributesToReturn, searchProperties.isGroupSearchScopeAllDepths(),
167 searchProperties.getTimeLimitMillis());
168 NamingEnumeration<SearchResult> groupSearchEnume = null;
169
170 if (UtilTimerStack.isActive())
171 UtilTimerStack.push(this.getClass().getName() + "_search_JNDI_RAW_(" + groupSearchFilter + ")");
172 DirContext ctx = null;
173 try
174 {
175 ctx = repository.getLDAPContext();
176
177 log.debug("Searching for groups using base name space:" + searchProperties.getBaseGroupNamespace() +
178 " and encoded filter " + groupSearchFilter.encode());
179
180 groupSearchEnume = ctx.search(searchProperties.getBaseGroupNamespace(), groupSearchFilter.encode(), ctls);
181
182 if (groupSearchEnume.hasMore())
183 log.debug("found at least one group");
184 else
185 log.debug("no groups found");
186 }
187 catch (NamingException e)
188 {
189 String host = "<unknown>";
190 String ipAddress = "<unknown>";
191 try
192 {
193 String urlString = (String) repository.getJNDIEnv().get(Context.PROVIDER_URL);
194 URI uri = new URI(urlString);
195 host = uri.getHost();
196 ipAddress = Inet4Address.getByName(host).getHostAddress();
197 }
198 catch (URISyntaxException use)
199 {
200 log.debug("Error while retrieving LDAP server info", use);
201 }
202 catch (UnknownHostException uhe)
203 {
204 log.debug("Error while retrieving LDAP server info", uhe);
205 }
206 log.error("Error searching for groups from LDAP server " + host + "[" + ipAddress + "]");
207 throw new RepositoryException(e);
208 }
209 finally
210 {
211 if (UtilTimerStack.isActive())
212 UtilTimerStack.pop(this.getClass().getName() + "_search_JNDI_RAW_(" + groupSearchFilter + ")");
213
214 try
215 {
216 if (ctx != null) ctx.close();
217 }
218 catch (NamingException e)
219 {
220 log.warn("Exception trying to close LDAP context, possible resource leak", e);
221 }
222 }
223
224 return new LDAPPagerInfo(groupSearchEnume, groupSearchFilter,
225 searchProperties.getBaseGroupNamespace(), searchProperties.isGroupSearchScopeAllDepths(),
226 attributesToReturn, searchProperties.getTimeLimitMillis());
227 }
228 finally
229 {
230 if (UtilTimerStack.isActive())
231 UtilTimerStack.pop(this.getClass().getName() + "_search(" + filter + ")");
232 }
233 }
234
235 protected Filter constructGroupSearchFilter(String name)
236 {
237 return constructGroupSearchFilter(name, null);
238 }
239
240 protected Filter constructGroupSearchFilter(String name, Filter patternToAnd)
241 {
242 Filter searchFilter = null;
243
244 if (TextUtils.stringSet(name) && !"*".equals(name))
245 searchFilter = new EqualsFilter(searchProperties.getGroupnameAttribute(), name);
246
247 searchFilter = addGroupSearchFilter(searchFilter);
248
249 if (patternToAnd != null)
250 return LDAPUtils.makeAndFilter(searchFilter, patternToAnd);
251
252 return searchFilter;
253 }
254
255 private Filter addGroupSearchFilter(Filter searchFilter)
256 {
257 return LDAPUtils.makeAndFilter(searchFilter, filterFactory.getGroupSearchFilter());
258 }
259
260
261
262
263
264
265
266
267
268
269
270
271 protected String getFirstPhraseFromDN(String dn)
272 {
273 String[] rdns = dn.split(",");
274 String[] firstPhrase = rdns[0].split("=");
275
276 return firstPhrase[1];
277 }
278
279
280 public String getGroupDN(String groupName) throws EntityException
281 {
282 LDAPPagerInfo ldapPagerInfo = getGroupEntries(groupName, new String[]{"dn"}, null);
283
284 if (!ldapPagerInfo.getNamingEnumeration().hasMoreElements())
285 throw new EntityMissingException("Could not get DN for group [" + groupName + "]");
286
287 SearchResult result = ldapPagerInfo.getNamingEnumeration().nextElement();
288
289 String groupDN = result.getName();
290
291 if (groupDN.indexOf(searchProperties.getBaseGroupNamespace()) == -1)
292 groupDN = groupDN + "," + searchProperties.getBaseGroupNamespace();
293
294 return groupDN;
295 }
296
297 public String getGroupDN(Group group) throws EntityException
298 {
299 if (group instanceof LDAPEntity)
300 {
301 LDAPEntity entity = (LDAPEntity) group;
302 return entity.getDistinguishedName();
303 }
304 else
305 throw new IllegalArgumentException("Group is not an LDAPEntity");
306 }
307
308 public LDAPGroupFactory getGroupFactory()
309 {
310 return groupFactory;
311 }
312 }