1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.server.ldap.handlers;
21
22
23 import java.util.concurrent.TimeUnit;
24
25 import org.apache.directory.server.core.DirectoryService;
26 import org.apache.directory.server.core.entry.ClonedServerEntry;
27 import org.apache.directory.server.core.entry.ServerStringValue;
28 import org.apache.directory.server.core.event.EventType;
29 import org.apache.directory.server.core.event.NotificationCriteria;
30 import org.apache.directory.server.core.filtering.EntryFilteringCursor;
31 import org.apache.directory.server.core.partition.PartitionNexus;
32 import org.apache.directory.server.ldap.LdapSession;
33 import org.apache.directory.shared.ldap.codec.util.LdapURLEncodingException;
34 import org.apache.directory.shared.ldap.constants.SchemaConstants;
35 import org.apache.directory.shared.ldap.entry.EntryAttribute;
36 import org.apache.directory.shared.ldap.entry.Value;
37 import org.apache.directory.shared.ldap.exception.OperationAbandonedException;
38 import org.apache.directory.shared.ldap.filter.EqualityNode;
39 import org.apache.directory.shared.ldap.filter.OrNode;
40 import org.apache.directory.shared.ldap.filter.PresenceNode;
41 import org.apache.directory.shared.ldap.message.LdapResult;
42 import org.apache.directory.shared.ldap.message.ManageDsaITControl;
43 import org.apache.directory.shared.ldap.message.PersistentSearchControl;
44 import org.apache.directory.shared.ldap.message.ReferralImpl;
45 import org.apache.directory.shared.ldap.message.Response;
46 import org.apache.directory.shared.ldap.message.ResultCodeEnum;
47 import org.apache.directory.shared.ldap.filter.SearchScope;
48 import org.apache.directory.shared.ldap.message.SearchRequest;
49 import org.apache.directory.shared.ldap.message.SearchResponseDone;
50 import org.apache.directory.shared.ldap.message.SearchResponseEntry;
51 import org.apache.directory.shared.ldap.message.SearchResponseEntryImpl;
52 import org.apache.directory.shared.ldap.message.SearchResponseReference;
53 import org.apache.directory.shared.ldap.message.SearchResponseReferenceImpl;
54 import org.apache.directory.shared.ldap.name.LdapDN;
55 import org.apache.directory.shared.ldap.schema.AttributeType;
56 import org.apache.directory.shared.ldap.util.LdapURL;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 import static org.apache.directory.server.ldap.LdapService.NO_SIZE_LIMIT;
61 import static org.apache.directory.server.ldap.LdapService.NO_TIME_LIMIT;
62
63 import javax.naming.NamingException;
64
65
66
67
68
69
70
71
72 public class SearchHandler extends ReferralAwareRequestHandler<SearchRequest>
73 {
74 private static final Logger LOG = LoggerFactory.getLogger( SearchHandler.class );
75
76
77 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
78
79
80 private AttributeType objectClassAttributeType;
81
82
83
84
85
86
87
88
89
90
91 private EqualityNode<String> newIsReferralEqualityNode( LdapSession session ) throws Exception
92 {
93 if ( objectClassAttributeType == null )
94 {
95 objectClassAttributeType = session.getCoreSession().getDirectoryService().getRegistries()
96 .getAttributeTypeRegistry().lookup( SchemaConstants.OBJECT_CLASS_AT );
97 }
98
99 EqualityNode<String> ocIsReferral = new EqualityNode<String>( SchemaConstants.OBJECT_CLASS_AT,
100 new ServerStringValue( objectClassAttributeType, SchemaConstants.REFERRAL_OC ) );
101
102 return ocIsReferral;
103 }
104
105
106
107
108
109
110
111
112
113
114
115
116 private void handlePersistentSearch( LdapSession session, SearchRequest req,
117 PersistentSearchControl psearchControl ) throws Exception
118 {
119
120
121
122
123 if ( ! psearchControl.isChangesOnly() )
124 {
125 SearchResponseDone done = doSimpleSearch( session, req );
126
127
128 if ( done.getLdapResult().getResultCode() != ResultCodeEnum.SUCCESS )
129 {
130 session.getIoSession().write( done );
131 return;
132 }
133 }
134
135 if ( req.isAbandoned() )
136 {
137 return;
138 }
139
140
141 PersistentSearchListener handler = new PersistentSearchListener( session, req );
142
143
144
145
146 NotificationCriteria criteria = new NotificationCriteria();
147 criteria.setAliasDerefMode( req.getDerefAliases() );
148 criteria.setBase( req.getBase() );
149 criteria.setFilter( req.getFilter() );
150 criteria.setScope( req.getScope() );
151 criteria.setEventMask( EventType.getEventTypes( psearchControl.getChangeTypes() ) );
152 getLdapServer().getDirectoryService().getEventService().addListener( handler, criteria );
153 req.addAbandonListener( new SearchAbandonListener( ldapService, handler ) );
154 return;
155 }
156
157
158
159
160
161
162
163
164
165 private void handleRootDseSearch( LdapSession session, SearchRequest req ) throws Exception
166 {
167 EntryFilteringCursor cursor = null;
168
169 try
170 {
171 cursor = session.getCoreSession().search( req );
172
173
174 cursor.beforeFirst();
175 boolean hasRootDSE = false;
176
177 while ( cursor.next() )
178 {
179 if ( hasRootDSE )
180 {
181
182 LOG.error( "Got back more than one entry for search on RootDSE which means " +
183 "Cursor is not functioning properly!" );
184 }
185 else
186 {
187 hasRootDSE = true;
188 ClonedServerEntry entry = cursor.get();
189 session.getIoSession().write( generateResponse( session, req, entry ) );
190 }
191 }
192
193
194 session.getIoSession().write( req.getResultResponse() );
195 }
196 finally
197 {
198
199 if ( cursor != null )
200 {
201 try
202 {
203 cursor.close();
204 }
205 catch ( NamingException e )
206 {
207 LOG.error( "failed on list.close()", e );
208 }
209 }
210 }
211 }
212
213
214
215
216
217
218
219
220
221
222
223
224
225 private void setTimeLimitsOnCursor( SearchRequest req, LdapSession session, final EntryFilteringCursor cursor )
226 {
227
228 if ( session.getCoreSession().isAnAdministrator() && req.getTimeLimit() == NO_TIME_LIMIT )
229 {
230 return;
231 }
232
233
234
235
236
237
238 if ( ldapService.getMaxTimeLimit() == NO_TIME_LIMIT && req.getTimeLimit() == NO_TIME_LIMIT )
239 {
240 return;
241 }
242
243
244
245
246
247
248 if ( req.getTimeLimit() == 0 )
249 {
250 cursor.setClosureMonitor( new SearchTimeLimitingMonitor( ldapService.getMaxTimeLimit(), TimeUnit.SECONDS ) );
251 return;
252 }
253
254
255
256
257
258
259 if ( ldapService.getMaxTimeLimit() >= req.getTimeLimit() )
260 {
261 cursor.setClosureMonitor( new SearchTimeLimitingMonitor( req.getTimeLimit(), TimeUnit.SECONDS ) );
262 return;
263 }
264
265
266
267
268
269
270 cursor.setClosureMonitor( new SearchTimeLimitingMonitor( ldapService.getMaxTimeLimit(), TimeUnit.SECONDS ) );
271 }
272
273
274 private int getSearchSizeLimits( SearchRequest req, LdapSession session )
275 {
276 LOG.debug( "req size limit = {}, configured size limit = {}", req.getSizeLimit(),
277 ldapService.getMaxSizeLimit() );
278
279
280 if ( session.getCoreSession().isAnAdministrator() && req.getSizeLimit() == NO_SIZE_LIMIT )
281 {
282 return NO_SIZE_LIMIT;
283 }
284
285
286 if ( session.getCoreSession().isAnAdministrator() )
287 {
288 return req.getSizeLimit();
289 }
290
291
292
293
294
295
296 if ( ldapService.getMaxSizeLimit() == NO_SIZE_LIMIT && req.getSizeLimit() == NO_SIZE_LIMIT )
297 {
298 return NO_SIZE_LIMIT;
299 }
300
301
302
303
304
305
306 if ( req.getSizeLimit() == 0 )
307 {
308 return ldapService.getMaxSizeLimit();
309 }
310
311 if ( ldapService.getMaxSizeLimit() == NO_SIZE_LIMIT )
312 {
313 return req.getSizeLimit();
314 }
315
316
317
318
319
320
321 if ( ldapService.getMaxSizeLimit() >= req.getSizeLimit() )
322 {
323 return req.getSizeLimit();
324 }
325
326
327
328
329
330
331 return ldapService.getMaxSizeLimit();
332 }
333
334
335
336
337
338
339
340
341
342
343
344
345
346 private SearchResponseDone doSimpleSearch( LdapSession session, SearchRequest req )
347 throws Exception
348 {
349
350
351
352
353 EntryFilteringCursor cursor = null;
354
355 try
356 {
357 LdapResult ldapResult = req.getResultResponse().getLdapResult();
358 cursor = session.getCoreSession().search( req );
359 req.addAbandonListener( new SearchAbandonListener( ldapService, cursor ) );
360 setTimeLimitsOnCursor( req, session, cursor );
361 final int sizeLimit = getSearchSizeLimits( req, session );
362 LOG.debug( "using {} for size limit", sizeLimit );
363
364
365 cursor.beforeFirst();
366
367 if ( sizeLimit == NO_SIZE_LIMIT )
368 {
369 while ( cursor.next() )
370 {
371 if ( session.getIoSession().isClosing() )
372 {
373 break;
374 }
375 ClonedServerEntry entry = cursor.get();
376 session.getIoSession().write( generateResponse( session, req, entry ) );
377 }
378 }
379 else
380 {
381 int count = 0;
382 while ( cursor.next() )
383 {
384 if ( session.getIoSession().isClosing() )
385 {
386 break;
387 }
388 if ( count < sizeLimit )
389 {
390 ClonedServerEntry entry = cursor.get();
391 session.getIoSession().write( generateResponse( session, req, entry ) );
392 count++;
393 }
394 else
395 {
396
397 ldapResult.setResultCode( ResultCodeEnum.SIZE_LIMIT_EXCEEDED );
398 return ( SearchResponseDone ) req.getResultResponse();
399 }
400 }
401 }
402
403
404 ldapResult.setResultCode( ResultCodeEnum.SUCCESS );
405 return ( SearchResponseDone ) req.getResultResponse();
406 }
407 finally
408 {
409 if ( cursor != null )
410 {
411 try
412 {
413 cursor.close();
414 }
415 catch ( NamingException e )
416 {
417 LOG.error( "failed on list.close()", e );
418 }
419 }
420 }
421 }
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436 private Response generateResponse( LdapSession session, SearchRequest req, ClonedServerEntry entry ) throws Exception
437 {
438 EntryAttribute ref = entry.getOriginalEntry().get( SchemaConstants.REF_AT );
439 boolean hasManageDsaItControl = req.getControls().containsKey( ManageDsaITControl.CONTROL_OID );
440
441 if ( ref != null && ! hasManageDsaItControl )
442 {
443 SearchResponseReference respRef;
444 respRef = new SearchResponseReferenceImpl( req.getMessageId() );
445 respRef.setReferral( new ReferralImpl() );
446
447 for ( Value<?> val : ref )
448 {
449 String url = ( String ) val.get();
450
451 if ( ! url.startsWith( "ldap" ) )
452 {
453 respRef.getReferral().addLdapUrl( url );
454 }
455
456 LdapURL ldapUrl = new LdapURL();
457 ldapUrl.setForceScopeRendering( true );
458 try
459 {
460 ldapUrl.parse( url.toCharArray() );
461 }
462 catch ( LdapURLEncodingException e )
463 {
464 LOG.error( "Bad URL ({}) for ref in {}. Reference will be ignored.", url, entry );
465 }
466
467 switch( req.getScope() )
468 {
469 case SUBTREE:
470 ldapUrl.setScope( SearchScope.SUBTREE.getJndiScope() );
471 break;
472 case ONELEVEL:
473 ldapUrl.setScope( SearchScope.OBJECT.getJndiScope() );
474 break;
475 default:
476 throw new IllegalStateException( "Unexpected base scope." );
477 }
478
479 respRef.getReferral().addLdapUrl( ldapUrl.toString() );
480 }
481
482 return respRef;
483 }
484 else
485 {
486 SearchResponseEntry respEntry;
487 respEntry = new SearchResponseEntryImpl( req.getMessageId() );
488 respEntry.setEntry( entry );
489 respEntry.setObjectName( entry.getDn() );
490
491 return respEntry;
492 }
493 }
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515 public void modifyFilter( LdapSession session, SearchRequest req ) throws Exception
516 {
517 if ( req.hasControl( ManageDsaITControl.CONTROL_OID ) )
518 {
519 return;
520 }
521
522
523
524
525
526
527 if ( isSubSchemaSubEntrySearch( session, req ) )
528 {
529 return;
530 }
531
532
533
534
535
536
537
538
539
540 if ( req.getFilter() instanceof PresenceNode )
541 {
542 PresenceNode presenceNode = ( PresenceNode ) req.getFilter();
543
544 AttributeType at = session.getCoreSession().getDirectoryService()
545 .getRegistries().getAttributeTypeRegistry().lookup( presenceNode.getAttribute() );
546 if ( at.getOid().equals( SchemaConstants.OBJECT_CLASS_AT_OID ) )
547 {
548 return;
549 }
550 }
551
552
553 req.setFilter( new OrNode( req.getFilter(), newIsReferralEqualityNode( session ) ) );
554 }
555
556
557
558
559
560
561
562
563
564
565
566
567 public void handleIgnoringReferrals( LdapSession session, LdapDN reqTargetDn,
568 ClonedServerEntry entry, SearchRequest req )
569 {
570 if ( IS_DEBUG )
571 {
572 LOG.debug( "Message received: {}", req.toString() );
573 }
574
575
576 session.registerOutstandingRequest( req );
577
578 try
579 {
580
581 modifyFilter( session, req );
582
583
584
585
586 if ( isRootDSESearch( req ) )
587 {
588 handleRootDseSearch( session, req );
589 return;
590 }
591
592
593
594
595
596 PersistentSearchControl psearchControl = ( PersistentSearchControl )
597 req.getControls().get( PersistentSearchControl.CONTROL_OID );
598
599 if ( psearchControl != null )
600 {
601 handlePersistentSearch( session, req, psearchControl );
602
603
604 return;
605 }
606
607
608
609
610
611 SearchResponseDone done = doSimpleSearch( session, req );
612 session.getIoSession().write( done );
613 session.unregisterOutstandingRequest( req );
614 }
615 catch ( Exception e )
616 {
617
618
619
620
621
622
623
624
625
626
627
628
629 if ( e instanceof OperationAbandonedException )
630 {
631 return;
632 }
633
634 handleException( session, req, e );
635 }
636 }
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652 private static boolean isRootDSESearch( SearchRequest req )
653 {
654 boolean isBaseIsRoot = req.getBase().isEmpty();
655 boolean isBaseScope = req.getScope() == SearchScope.OBJECT;
656 boolean isRootDSEFilter = false;
657
658 if ( req.getFilter() instanceof PresenceNode )
659 {
660 String attribute = ( ( PresenceNode ) req.getFilter() ).getAttribute();
661 isRootDSEFilter = attribute.equalsIgnoreCase( SchemaConstants.OBJECT_CLASS_AT ) ||
662 attribute.equals( SchemaConstants.OBJECT_CLASS_AT_OID );
663 }
664
665 return isBaseIsRoot && isBaseScope && isRootDSEFilter;
666 }
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692 private static boolean isSubSchemaSubEntrySearch( LdapSession session, SearchRequest req ) throws Exception
693 {
694 LdapDN base = req.getBase();
695 String baseNormForm = ( base.isNormalized() ? base.getNormName() : base.toNormName() );
696
697 DirectoryService ds = session.getCoreSession().getDirectoryService();
698 PartitionNexus nexus = ds.getPartitionNexus();
699 Value<?> subschemaSubentry = nexus.getRootDSE( null ).get( SchemaConstants.SUBSCHEMA_SUBENTRY_AT ).get();
700 LdapDN subschemaSubentryDn = new LdapDN( ( String ) ( subschemaSubentry.get() ) );
701 subschemaSubentryDn.normalize( ds.getRegistries().getAttributeTypeRegistry().getNormalizerMapping() );
702 String subschemaSubentryDnNorm = subschemaSubentryDn.getNormName();
703
704 return subschemaSubentryDnNorm.equals( baseNormForm );
705 }
706 }