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>(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>(repository, groupFactory, info);
99 return new DefaultSearchResult(pager, repositoryIdentifier.getKey());
100 }
101
102 public SearchResult findUsers(Query query, QueryContext context) throws EntityException
103 {
104 SearchResult result = null;
105
106 if (context.getRepositoryKeys().contains(repositoryIdentifier.getKey()) ||
107 context.getRepositoryKeys().contains(QueryContext.ALL_REPOSITORIES))
108 {
109 result = findUsers(query);
110 }
111
112 if (result == null)
113 return null;
114
115 return result;
116 }
117
118 public SearchResult findGroups(Query query, QueryContext context) throws EntityException
119 {
120 SearchResult result = null;
121
122 if (context.getRepositoryKeys().contains(repositoryIdentifier.getKey()) ||
123 context.getRepositoryKeys().contains(QueryContext.ALL_REPOSITORIES))
124 {
125 result = findGroups(query);
126 }
127
128 if (result == null)
129 return null;
130
131 return result;
132 }
133
134 private String directQuery(Query query, String defaultQuery) throws EntityException
135 {
136 if (query instanceof TermQuery)
137 {
138 StringBuffer parsedQueryStringBuffer = parseQuery((TermQuery) query);
139 return parsedQueryStringBuffer.toString();
140 }
141 else if (query instanceof BooleanQuery)
142 {
143 return parseQuery((BooleanQuery) query).toString();
144 }
145
146 return defaultQuery;
147 }
148
149 public StringBuffer parseQuery(BooleanQuery query) throws EntityException
150 {
151 StringBuffer parsedClause = new StringBuffer();
152 parsedClause.append(OPEN_PARAN);
153
154 if (query.isAND())
155 parsedClause.append(AND);
156 else
157 parsedClause.append(OR);
158
159 Iterator queryIter = query.getQueries().iterator();
160
161 while (queryIter.hasNext())
162 {
163 Query foundQuery = (Query) queryIter.next();
164 if (foundQuery instanceof BooleanQuery)
165 parsedClause.append(parseQuery((BooleanQuery) foundQuery));
166 else
167 parsedClause.append(parseQuery((TermQuery) foundQuery));
168 }
169
170 parsedClause.append(CLOSE_PARAN);
171
172 return parsedClause;
173 }
174
175 public StringBuffer parseQuery(TermQuery q) throws EntityException
176 {
177 StringBuffer parsedQuery = null;
178
179 if (q instanceof UserNameTermQuery)
180 parsedQuery = parseTermQuery(q, searchProperties.getUsernameAttribute());
181 else if (q instanceof GroupNameTermQuery)
182 parsedQuery = parseTermQuery(q, searchProperties.getGroupnameAttribute());
183 else if (q instanceof EmailTermQuery)
184 parsedQuery = parseTermQuery(q, searchProperties.getEmailAttribute());
185 else if (q instanceof FullNameTermQuery)
186 parsedQuery = parseFullNameTermQuery(q);
187 else if (q instanceof UsersInGroupTwoTermQuery)
188 parsedQuery = parseMemberNamesInGroupTermQuery();
189 else if (q instanceof GroupsOfUserTwoTermQuery)
190 parsedQuery = parseGroupsOfUserTwoTermQuery((GroupsOfUserTwoTermQuery) q);
191
192 return parsedQuery;
193 }
194
195 private StringBuffer parseFullNameTermQuery(TermQuery q)
196 {
197 StringBuffer query = new StringBuffer();
198
199 query.insert(0, OR);
200 query.insert(0, "(");
201 query.append(parseTermQuery(q, searchProperties.getFirstnameAttribute()));
202 query.append(parseTermQuery(q, searchProperties.getSurnameAttribute()));
203 query.append(")");
204
205 return query;
206 }
207
208
209
210
211
212 private StringBuffer parseMemberNamesInGroupTermQuery() throws EntityException
213 {
214 if (!membershipProperties.isMembershipAttributeOnGroup())
215 throw new UnsupportedOperationException();
216
217 LDAPPagerInfo pagerInfo = groupAdaptor.getGroupEntries(null);
218 NamingEnumeration enume = pagerInfo.getNamingEnumeration();
219 return parseMembershipAttributesForGroupNames(enume);
220 }
221
222
223
224
225 private StringBuffer parseGroupsOfUserTwoTermQuery(GroupsOfUserTwoTermQuery q) throws EntityException
226 {
227 String userPattern = null;
228
229 UserNameTermQuery uQuery = q.getUserNameTermQuery();
230 if (uQuery.isMatchingSubstring())
231 {
232 if (uQuery.getMatchingRule().equals(TermQuery.SUBSTRING_STARTS_WITH))
233 userPattern = uQuery.getTerm() + WILDCARD;
234 else if (uQuery.getMatchingRule().equals(TermQuery.SUBSTRING_ENDS_WITH))
235 userPattern = WILDCARD + uQuery.getTerm();
236 else if (uQuery.getMatchingRule().equals(TermQuery.SUBSTRING_CONTAINS))
237 userPattern = WILDCARD + uQuery.getTerm() + WILDCARD;
238 }
239 else
240 userPattern = uQuery.getTerm();
241
242
243 LDAPPagerInfo pagerInfo = groupAdaptor.getGroupEntriesViaMembership(userPattern);
244
245 StringBuffer groupQuery;
246
247 if (membershipProperties.isMembershipAttributeOnGroup())
248 groupQuery = buildQueryForUsersInStaticGroups(pagerInfo.getNamingEnumeration());
249 else
250 groupQuery = buildQueryForUsersInDynamicGroups(pagerInfo.getNamingEnumeration());
251
252 return groupQuery;
253 }
254
255 private StringBuffer buildQueryForUsersInDynamicGroups(NamingEnumeration enume) throws EntityException
256 {
257 StringBuffer parsedQuery = null;
258
259 while (enume.hasMoreElements())
260 {
261 javax.naming.directory.SearchResult result = (javax.naming.directory.SearchResult) enume.nextElement();
262 Attributes attrs = result.getAttributes();
263
264 Attribute attr = attrs.get(membershipProperties.getMembershipAttribute());
265
266 if (attr == null)
267 return null;
268
269 try
270 {
271 NamingEnumeration membershipEnume = attr.getAll();
272 while (membershipEnume.hasMoreElements())
273 {
274 boolean wrapping = false;
275
276 if (parsedQuery == null)
277 {
278 parsedQuery = new StringBuffer();
279 }
280 else
281 {
282 parsedQuery.insert(0, OR);
283 parsedQuery.insert(0, OPEN_PARAN);
284 wrapping = true;
285 }
286
287 parsedQuery.append(OPEN_PARAN);
288 parsedQuery.append(((String) membershipEnume.nextElement()).split(",")[0]);
289 parsedQuery.append(CLOSE_PARAN);
290
291 if (wrapping)
292 parsedQuery.append(CLOSE_PARAN);
293 }
294 }
295 catch (Exception e)
296 {
297 throw new RepositoryException(e);
298 }
299 }
300
301 return parsedQuery;
302 }
303
304 private StringBuffer buildQueryForUsersInStaticGroups(NamingEnumeration enume) throws EntityException
305 {
306 StringBuffer parsedQuery = null;
307
308 while (enume.hasMoreElements())
309 {
310 javax.naming.directory.SearchResult result = (javax.naming.directory.SearchResult) enume.nextElement();
311 Attributes attrs = result.getAttributes();
312
313 Attribute attr = attrs.get(searchProperties.getGroupnameAttribute());
314
315 if (attr == null)
316 return null;
317
318 try
319 {
320 NamingEnumeration membershipEnume = attr.getAll();
321 while (membershipEnume.hasMoreElements())
322 {
323 boolean wrapping = false;
324
325 if (parsedQuery == null)
326 {
327 parsedQuery = new StringBuffer();
328 }
329 else
330 {
331 parsedQuery.insert(0, OR);
332 parsedQuery.insert(0, OPEN_PARAN);
333 wrapping = true;
334 }
335
336 parsedQuery.append(OPEN_PARAN);
337 parsedQuery.append(searchProperties.getGroupnameAttribute());
338 parsedQuery.append(EQ);
339 parsedQuery.append(((String) membershipEnume.nextElement()).split(",")[0]);
340 parsedQuery.append(CLOSE_PARAN);
341
342 if (wrapping)
343 parsedQuery.append(CLOSE_PARAN);
344 }
345 }
346 catch (Exception e)
347 {
348 throw new RepositoryException(e);
349 }
350 }
351
352 return parsedQuery;
353 }
354
355 private StringBuffer parseMembershipAttributesForGroupNames(NamingEnumeration enume)
356 {
357 StringBuffer parsedQuery = null;
358
359 while (enume.hasMoreElements())
360 {
361 javax.naming.directory.SearchResult result = (javax.naming.directory.SearchResult) enume.nextElement();
362 Attributes attrs = result.getAttributes();
363
364 Attribute attr;
365
366 if (membershipProperties.isMembershipAttributeOnGroup())
367 attr = attrs.get(membershipProperties.getMembershipAttribute());
368 else
369 attr = attrs.get(searchProperties.getGroupnameAttribute());
370
371 try
372 {
373 if (parsedQuery == null)
374 parsedQuery = parseMembershipAttributeForGroupNames(attr);
375 else
376 {
377 parsedQuery.insert(0, "(|");
378 parsedQuery = parsedQuery.append(parseMembershipAttributeForGroupNames(attr));
379 parsedQuery.append(")");
380 }
381 }
382 catch (Exception e)
383 {
384 e.printStackTrace();
385 }
386 }
387 return parsedQuery;
388 }
389
390 private StringBuffer parseMembershipAttributeForGroupNames(Attribute attr) throws NamingException
391 {
392 if (attr == null)
393 return null;
394
395 StringBuffer parsedQuery = null;
396 NamingEnumeration membershipEnume = attr.getAll();
397 while (membershipEnume.hasMoreElements())
398 {
399 boolean wrapping = false;
400
401 if (parsedQuery == null)
402 {
403 parsedQuery = new StringBuffer();
404 }
405 else
406 {
407 parsedQuery.insert(0, OR);
408 parsedQuery.insert(0, OPEN_PARAN);
409 wrapping = true;
410 }
411
412 if (membershipProperties.isMembershipAttributeOnGroup())
413 {
414 parsedQuery.append(OPEN_PARAN);
415 parsedQuery.append(((String) membershipEnume.nextElement()).split(",")[0]);
416 parsedQuery.append(CLOSE_PARAN);
417 }
418 else
419 {
420 parsedQuery.append(OPEN_PARAN);
421 parsedQuery.append(searchProperties.getGroupnameAttribute());
422 parsedQuery.append(EQ);
423 parsedQuery.append(((String) membershipEnume.nextElement()).split(",")[0]);
424 parsedQuery.append(CLOSE_PARAN);
425
426 }
427
428 if (wrapping)
429 parsedQuery.append(CLOSE_PARAN);
430 }
431
432 return parsedQuery;
433 }
434
435
436
437
438
439
440
441
442 private StringBuffer parseTermQuery(TermQuery q, String attributeType)
443 {
444 StringBuffer parsedQuery;
445 parsedQuery = new StringBuffer();
446 parsedQuery.append(OPEN_PARAN);
447 parsedQuery.append(attributeType);
448 parsedQuery.append(EQ);
449
450 if (q.isMatchingSubstring())
451 {
452 if ((q.getMatchingRule().equals(TermQuery.SUBSTRING_ENDS_WITH)) ||
453 (q.getMatchingRule().equals(TermQuery.SUBSTRING_CONTAINS)))
454 {
455 parsedQuery.append(WILDCARD);
456 }
457 }
458
459 parsedQuery.append(LdapEncoder.filterEncode(q.getTerm()));
460
461 if (q.isMatchingSubstring())
462 {
463 if ((q.getMatchingRule().equals(TermQuery.SUBSTRING_STARTS_WITH)) ||
464 (q.getMatchingRule().equals(TermQuery.SUBSTRING_CONTAINS)))
465 {
466 parsedQuery.append(WILDCARD);
467 }
468 }
469
470 parsedQuery.append(CLOSE_PARAN);
471 return parsedQuery;
472 }
473 }