1 package com.atlassian.user.impl.ldap.search.query;
2
3
4
5
6
7
8
9
10 import com.atlassian.user.EntityException;
11 import com.atlassian.user.User;
12 import com.atlassian.user.Group;
13 import com.atlassian.user.repository.RepositoryIdentifier;
14 import com.atlassian.user.impl.RepositoryException;
15 import com.atlassian.user.impl.ldap.DefaultLDAPGroupFactory;
16 import com.atlassian.user.impl.ldap.LDAPGroupFactory;
17 import com.atlassian.user.impl.ldap.LDAPUserFactory;
18 import com.atlassian.user.impl.ldap.LiteralFilter;
19 import com.atlassian.user.impl.ldap.adaptor.LDAPGroupAdaptor;
20 import com.atlassian.user.impl.ldap.properties.LdapSearchProperties;
21 import com.atlassian.user.impl.ldap.properties.LdapMembershipProperties;
22 import com.atlassian.user.impl.ldap.repository.LdapContextFactory;
23 import com.atlassian.user.impl.ldap.search.DefaultLDAPUserAdaptor;
24 import com.atlassian.user.impl.ldap.search.LDAPPagerInfo;
25 import com.atlassian.user.impl.ldap.search.LDAPUserAdaptor;
26 import com.atlassian.user.impl.ldap.search.LdapFilterFactory;
27 import com.atlassian.user.impl.ldap.search.page.LDAPEntityPager;
28 import com.atlassian.user.search.DefaultSearchResult;
29 import com.atlassian.user.search.SearchResult;
30 import com.atlassian.user.search.page.Pager;
31 import com.atlassian.user.search.query.*;
32 import com.atlassian.util.profiling.UtilTimerStack;
33 import net.sf.ldaptemplate.support.LdapEncoder;
34
35 import javax.naming.NamingEnumeration;
36 import javax.naming.NamingException;
37 import javax.naming.directory.Attribute;
38 import javax.naming.directory.Attributes;
39 import java.util.Iterator;
40
41 public class LDAPEntityQueryParser implements EntityQueryParser
42 {
43 public static final String OPEN_PARAN = "(";
44 public static final String CLOSE_PARAN = ")";
45 public static final String EQ = "=";
46 public static final String AND = "&";
47 public static final String OR = "|";
48 public static final String WILDCARD = TermQuery.WILDCARD;
49
50 private final LdapContextFactory repository;
51 private final LDAPUserAdaptor userAdaptor;
52 private final LDAPGroupAdaptor groupAdaptor;
53 private final LDAPGroupFactory groupFactory;
54 private final LDAPUserFactory userFactory;
55
56 private final RepositoryIdentifier repositoryIdentifier;
57 private final LdapSearchProperties searchProperties;
58 private final LdapMembershipProperties membershipProperties;
59
60 public LDAPEntityQueryParser(LdapContextFactory repository, LDAPGroupAdaptor groupAdaptor,
61 RepositoryIdentifier repositoryIdentifier, LDAPUserFactory userFactory,
62 LdapSearchProperties searchProperties, LdapMembershipProperties membershipProperties,
63 LdapFilterFactory filterFactory)
64 {
65 this.repositoryIdentifier = repositoryIdentifier;
66 this.repository = repository;
67 this.groupAdaptor = groupAdaptor;
68 this.userFactory = userFactory;
69 this.userAdaptor = new DefaultLDAPUserAdaptor(this.repository, searchProperties, filterFactory);
70 this.groupFactory = new DefaultLDAPGroupFactory(searchProperties, membershipProperties);
71 this.searchProperties = searchProperties;
72 this.membershipProperties = membershipProperties;
73 }
74
75 public com.atlassian.user.search.SearchResult findUsers(Query query) throws EntityException
76 {
77 if (UtilTimerStack.isActive())
78 UtilTimerStack.push(this.getClass().getName() + "_findUsers");
79
80 String parsedQuery = null;
81 Pager iter;
82 parsedQuery = directQuery(query, parsedQuery);
83
84 LDAPPagerInfo info = userAdaptor.search(new LiteralFilter(parsedQuery));
85 iter = new LDAPEntityPager<User>(searchProperties, repository, userFactory, info);
86 DefaultSearchResult searchResult = new DefaultSearchResult(iter, repositoryIdentifier.getKey());
87
88 if (UtilTimerStack.isActive())
89 UtilTimerStack.pop(this.getClass().getName() + "_findUsers");
90
91 return searchResult;
92 }
93
94 public com.atlassian.user.search.SearchResult findGroups(Query query) throws EntityException
95 {
96 String parsedQuery = directQuery(query, null);
97 LDAPPagerInfo info = groupAdaptor.search(new LiteralFilter(parsedQuery));
98 Pager pager = new LDAPEntityPager<Group>(searchProperties, repository, groupFactory, info);
99 return new DefaultSearchResult(pager, repositoryIdentifier.getKey());
100 }
101
102 public SearchResult findUsers(Query query, QueryContext context) throws EntityException
103 {
104 if (!context.contains(repositoryIdentifier))
105 return null;
106
107 return findUsers(query);
108 }
109
110 public SearchResult findGroups(Query query, QueryContext context) throws EntityException
111 {
112 if (!context.contains(repositoryIdentifier))
113 return null;
114
115 return findGroups(query);
116 }
117
118 private String directQuery(Query query, String defaultQuery) throws EntityException
119 {
120 if (query instanceof TermQuery)
121 {
122 StringBuffer parsedQueryStringBuffer = parseQuery((TermQuery) query);
123 return parsedQueryStringBuffer.toString();
124 }
125 else if (query instanceof BooleanQuery)
126 {
127 return parseQuery((BooleanQuery) query).toString();
128 }
129
130 return defaultQuery;
131 }
132
133 public StringBuffer parseQuery(BooleanQuery query) throws EntityException
134 {
135 StringBuffer parsedClause = new StringBuffer();
136 parsedClause.append(OPEN_PARAN);
137
138 if (query.isAND())
139 parsedClause.append(AND);
140 else
141 parsedClause.append(OR);
142
143 Iterator queryIter = query.getQueries().iterator();
144
145 while (queryIter.hasNext())
146 {
147 Query foundQuery = (Query) queryIter.next();
148 if (foundQuery instanceof BooleanQuery)
149 parsedClause.append(parseQuery((BooleanQuery) foundQuery));
150 else
151 parsedClause.append(parseQuery((TermQuery) foundQuery));
152 }
153
154 parsedClause.append(CLOSE_PARAN);
155
156 return parsedClause;
157 }
158
159 public StringBuffer parseQuery(TermQuery q) throws EntityException
160 {
161 StringBuffer parsedQuery = null;
162
163 if (q instanceof UserNameTermQuery)
164 parsedQuery = parseTermQuery(q, searchProperties.getUsernameAttribute());
165 else if (q instanceof GroupNameTermQuery)
166 parsedQuery = parseTermQuery(q, searchProperties.getGroupnameAttribute());
167 else if (q instanceof EmailTermQuery)
168 parsedQuery = parseTermQuery(q, searchProperties.getEmailAttribute());
169 else if (q instanceof FullNameTermQuery)
170 parsedQuery = parseFullNameTermQuery(q);
171 else if (q instanceof UsersInGroupTwoTermQuery)
172 parsedQuery = parseMemberNamesInGroupTermQuery();
173 else if (q instanceof GroupsOfUserTwoTermQuery)
174 parsedQuery = parseGroupsOfUserTwoTermQuery((GroupsOfUserTwoTermQuery) q);
175
176 return parsedQuery;
177 }
178
179 private StringBuffer parseFullNameTermQuery(TermQuery q)
180 {
181 StringBuffer query = new StringBuffer();
182
183 query.insert(0, OR);
184 query.insert(0, "(");
185 query.append(parseTermQuery(q, searchProperties.getFirstnameAttribute()));
186 query.append(parseTermQuery(q, searchProperties.getSurnameAttribute()));
187 query.append(")");
188
189 return query;
190 }
191
192
193
194
195
196 private StringBuffer parseMemberNamesInGroupTermQuery() throws EntityException
197 {
198 if (!membershipProperties.isMembershipAttributeOnGroup())
199 throw new UnsupportedOperationException();
200
201 LDAPPagerInfo pagerInfo = groupAdaptor.getGroupEntries(null);
202 NamingEnumeration enume = pagerInfo.getNamingEnumeration();
203 return parseMembershipAttributesForGroupNames(enume);
204 }
205
206
207
208
209 private StringBuffer parseGroupsOfUserTwoTermQuery(GroupsOfUserTwoTermQuery q) throws EntityException
210 {
211 String userPattern = null;
212
213 UserNameTermQuery uQuery = q.getUserNameTermQuery();
214 if (uQuery.isMatchingSubstring())
215 {
216 if (uQuery.getMatchingRule().equals(TermQuery.SUBSTRING_STARTS_WITH))
217 userPattern = uQuery.getTerm() + WILDCARD;
218 else if (uQuery.getMatchingRule().equals(TermQuery.SUBSTRING_ENDS_WITH))
219 userPattern = WILDCARD + uQuery.getTerm();
220 else if (uQuery.getMatchingRule().equals(TermQuery.SUBSTRING_CONTAINS))
221 userPattern = WILDCARD + uQuery.getTerm() + WILDCARD;
222 }
223 else
224 userPattern = uQuery.getTerm();
225
226
227 LDAPPagerInfo pagerInfo = groupAdaptor.getGroupEntriesViaMembership(userPattern);
228
229 StringBuffer groupQuery;
230
231 if (membershipProperties.isMembershipAttributeOnGroup())
232 groupQuery = buildQueryForUsersInStaticGroups(pagerInfo.getNamingEnumeration());
233 else
234 groupQuery = buildQueryForUsersInDynamicGroups(pagerInfo.getNamingEnumeration());
235
236 return groupQuery;
237 }
238
239 private StringBuffer buildQueryForUsersInDynamicGroups(NamingEnumeration enume) throws EntityException
240 {
241 StringBuffer parsedQuery = null;
242
243 while (enume.hasMoreElements())
244 {
245 javax.naming.directory.SearchResult result = (javax.naming.directory.SearchResult) enume.nextElement();
246 Attributes attrs = result.getAttributes();
247
248 Attribute attr = attrs.get(membershipProperties.getMembershipAttribute());
249
250 if (attr == null)
251 return null;
252
253 try
254 {
255 NamingEnumeration membershipEnume = attr.getAll();
256 while (membershipEnume.hasMoreElements())
257 {
258 boolean wrapping = false;
259
260 if (parsedQuery == null)
261 {
262 parsedQuery = new StringBuffer();
263 }
264 else
265 {
266 parsedQuery.insert(0, OR);
267 parsedQuery.insert(0, OPEN_PARAN);
268 wrapping = true;
269 }
270
271 parsedQuery.append(OPEN_PARAN);
272 parsedQuery.append(((String) membershipEnume.nextElement()).split(",")[0]);
273 parsedQuery.append(CLOSE_PARAN);
274
275 if (wrapping)
276 parsedQuery.append(CLOSE_PARAN);
277 }
278 }
279 catch (Exception e)
280 {
281 throw new RepositoryException(e);
282 }
283 }
284
285 return parsedQuery;
286 }
287
288 private StringBuffer buildQueryForUsersInStaticGroups(NamingEnumeration enume) throws EntityException
289 {
290 StringBuffer parsedQuery = null;
291
292 while (enume.hasMoreElements())
293 {
294 javax.naming.directory.SearchResult result = (javax.naming.directory.SearchResult) enume.nextElement();
295 Attributes attrs = result.getAttributes();
296
297 Attribute attr = attrs.get(searchProperties.getGroupnameAttribute());
298
299 if (attr == null)
300 return null;
301
302 try
303 {
304 NamingEnumeration membershipEnume = attr.getAll();
305 while (membershipEnume.hasMoreElements())
306 {
307 boolean wrapping = false;
308
309 if (parsedQuery == null)
310 {
311 parsedQuery = new StringBuffer();
312 }
313 else
314 {
315 parsedQuery.insert(0, OR);
316 parsedQuery.insert(0, OPEN_PARAN);
317 wrapping = true;
318 }
319
320 parsedQuery.append(OPEN_PARAN);
321 parsedQuery.append(searchProperties.getGroupnameAttribute());
322 parsedQuery.append(EQ);
323 parsedQuery.append(((String) membershipEnume.nextElement()).split(",")[0]);
324 parsedQuery.append(CLOSE_PARAN);
325
326 if (wrapping)
327 parsedQuery.append(CLOSE_PARAN);
328 }
329 }
330 catch (Exception e)
331 {
332 throw new RepositoryException(e);
333 }
334 }
335
336 return parsedQuery;
337 }
338
339 private StringBuffer parseMembershipAttributesForGroupNames(NamingEnumeration enume)
340 {
341 StringBuffer parsedQuery = null;
342
343 while (enume.hasMoreElements())
344 {
345 javax.naming.directory.SearchResult result = (javax.naming.directory.SearchResult) enume.nextElement();
346 Attributes attrs = result.getAttributes();
347
348 Attribute attr;
349
350 if (membershipProperties.isMembershipAttributeOnGroup())
351 attr = attrs.get(membershipProperties.getMembershipAttribute());
352 else
353 attr = attrs.get(searchProperties.getGroupnameAttribute());
354
355 try
356 {
357 if (parsedQuery == null)
358 parsedQuery = parseMembershipAttributeForGroupNames(attr);
359 else
360 {
361 parsedQuery.insert(0, "(|");
362 parsedQuery = parsedQuery.append(parseMembershipAttributeForGroupNames(attr));
363 parsedQuery.append(")");
364 }
365 }
366 catch (Exception e)
367 {
368 e.printStackTrace();
369 }
370 }
371 return parsedQuery;
372 }
373
374 private StringBuffer parseMembershipAttributeForGroupNames(Attribute attr) throws NamingException
375 {
376 if (attr == null)
377 return null;
378
379 StringBuffer parsedQuery = null;
380 NamingEnumeration membershipEnume = attr.getAll();
381 while (membershipEnume.hasMoreElements())
382 {
383 boolean wrapping = false;
384
385 if (parsedQuery == null)
386 {
387 parsedQuery = new StringBuffer();
388 }
389 else
390 {
391 parsedQuery.insert(0, OR);
392 parsedQuery.insert(0, OPEN_PARAN);
393 wrapping = true;
394 }
395
396 if (membershipProperties.isMembershipAttributeOnGroup())
397 {
398 parsedQuery.append(OPEN_PARAN);
399 parsedQuery.append(((String) membershipEnume.nextElement()).split(",")[0]);
400 parsedQuery.append(CLOSE_PARAN);
401 }
402 else
403 {
404 parsedQuery.append(OPEN_PARAN);
405 parsedQuery.append(searchProperties.getGroupnameAttribute());
406 parsedQuery.append(EQ);
407 parsedQuery.append(((String) membershipEnume.nextElement()).split(",")[0]);
408 parsedQuery.append(CLOSE_PARAN);
409
410 }
411
412 if (wrapping)
413 parsedQuery.append(CLOSE_PARAN);
414 }
415
416 return parsedQuery;
417 }
418
419
420
421
422
423
424
425
426 private StringBuffer parseTermQuery(TermQuery q, String attributeType)
427 {
428 StringBuffer parsedQuery;
429 parsedQuery = new StringBuffer();
430 parsedQuery.append(OPEN_PARAN);
431 parsedQuery.append(attributeType);
432 parsedQuery.append(EQ);
433
434 if (q.isMatchingSubstring())
435 {
436 if ((q.getMatchingRule().equals(TermQuery.SUBSTRING_ENDS_WITH)) ||
437 (q.getMatchingRule().equals(TermQuery.SUBSTRING_CONTAINS)))
438 {
439 parsedQuery.append(WILDCARD);
440 }
441 }
442
443 parsedQuery.append(LdapEncoder.filterEncode(q.getTerm()));
444
445 if (q.isMatchingSubstring())
446 {
447 if ((q.getMatchingRule().equals(TermQuery.SUBSTRING_STARTS_WITH)) ||
448 (q.getMatchingRule().equals(TermQuery.SUBSTRING_CONTAINS)))
449 {
450 parsedQuery.append(WILDCARD);
451 }
452 }
453
454 parsedQuery.append(CLOSE_PARAN);
455 return parsedQuery;
456 }
457 }