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 javax.naming.InvalidNameException;
24 import javax.naming.NameNotFoundException;
25 import javax.naming.NamingException;
26
27 import org.apache.directory.server.core.entry.ClonedServerEntry;
28 import org.apache.directory.server.core.entry.ServerAttribute;
29 import org.apache.directory.server.ldap.LdapSession;
30 import org.apache.directory.shared.ldap.codec.util.LdapURLEncodingException;
31 import org.apache.directory.shared.ldap.constants.SchemaConstants;
32 import org.apache.directory.shared.ldap.entry.EntryAttribute;
33 import org.apache.directory.shared.ldap.entry.Value;
34 import org.apache.directory.shared.ldap.exception.LdapException;
35 import org.apache.directory.shared.ldap.message.AddRequest;
36 import org.apache.directory.shared.ldap.message.BindRequest;
37 import org.apache.directory.shared.ldap.message.CompareRequest;
38 import org.apache.directory.shared.ldap.message.DeleteRequest;
39 import org.apache.directory.shared.ldap.message.LdapResult;
40 import org.apache.directory.shared.ldap.message.ManageDsaITControl;
41 import org.apache.directory.shared.ldap.message.ModifyDnRequest;
42 import org.apache.directory.shared.ldap.message.ModifyRequest;
43 import org.apache.directory.shared.ldap.message.Referral;
44 import org.apache.directory.shared.ldap.message.ReferralImpl;
45 import org.apache.directory.shared.ldap.message.ResultCodeEnum;
46 import org.apache.directory.shared.ldap.message.ResultResponseRequest;
47 import org.apache.directory.shared.ldap.message.SearchRequest;
48 import org.apache.directory.shared.ldap.name.LdapDN;
49 import org.apache.directory.shared.ldap.util.ExceptionUtils;
50 import org.apache.directory.shared.ldap.util.LdapURL;
51
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55
56
57
58
59
60
61
62
63
64
65
66 public abstract class ReferralAwareRequestHandler<T extends ResultResponseRequest> extends LdapRequestHandler<T>
67 {
68 private static final Logger LOG = LoggerFactory.getLogger( ReferralAwareRequestHandler.class );
69
70
71 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
72
73
74
75
76
77 @Override
78 public final void handle( LdapSession session, T req ) throws Exception
79 {
80 LOG.debug( "Handling single reply request: {}", req );
81
82 LdapDN reqTargetDn = null;
83
84 switch ( req.getType() )
85 {
86 case ADD_REQUEST:
87 reqTargetDn = ( ( AddRequest ) req ).getEntryDn();
88 break;
89 case BIND_REQUEST:
90
91 reqTargetDn = ( ( BindRequest ) req ).getName();
92 break;
93 case COMPARE_REQUEST:
94 reqTargetDn = ( ( CompareRequest ) req ).getName();
95 break;
96 case DEL_REQUEST:
97 reqTargetDn = ( ( DeleteRequest ) req ).getName();
98 break;
99 case EXTENDED_REQ:
100 throw new IllegalStateException(
101 "Although ExtendedRequests are SingleReplyRequests they're not handled" +
102 " using this base class. They have no target entry unlike the rest of" +
103 " the SingleReplyRequests" );
104 case MOD_DN_REQUEST:
105
106
107
108
109
110
111 if ( req.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
112 {
113 LOG.debug( "ManageDsaITControl detected." );
114 handleIgnoringReferrals( session, ( ( ModifyDnRequest ) req ).getName(), null, req );
115 }
116 else
117 {
118 LOG.debug( "ManageDsaITControl NOT detected." );
119
120 if ( ( ( ModifyDnRequest ) req ).getNewSuperior() == null )
121 {
122 handleWithReferrals( session, ( ( ModifyDnRequest ) req ).getName(), req );
123 }
124 else
125 {
126
127 handleModifyDnWithReferrals( session, req );
128 }
129 }
130 return;
131 case MODIFY_REQUEST:
132 reqTargetDn = ( ( ModifyRequest ) req ).getName();
133 break;
134 case SEARCH_REQUEST:
135 reqTargetDn = ( ( SearchRequest ) req ).getBase();
136 break;
137 default:
138 throw new IllegalStateException(
139 "Unidentified single reply request/response type: " + req );
140 }
141
142 if ( req.getControls().containsKey( ManageDsaITControl.CONTROL_OID ) )
143 {
144 LOG.debug( "ManageDsaITControl detected." );
145 handleIgnoringReferrals( session, reqTargetDn, null, req );
146 }
147 else
148 {
149 LOG.debug( "ManageDsaITControl NOT detected." );
150 handleWithReferrals( session, reqTargetDn, req );
151 }
152 }
153
154
155 public static final boolean isEntryReferral( ClonedServerEntry entry ) throws Exception
156 {
157 return entry.getOriginalEntry().contains( SchemaConstants.OBJECT_CLASS_AT, SchemaConstants.REFERRAL_OC );
158 }
159
160
161
162
163
164
165
166
167
168
169
170
171
172 public static final ClonedServerEntry getFarthestReferralAncestor( LdapSession session, LdapDN target )
173 throws Exception
174 {
175 ClonedServerEntry entry;
176 ClonedServerEntry farthestReferralAncestor = null;
177 LdapDN dn = ( LdapDN ) target.clone();
178
179 try
180 {
181 dn.remove( dn.size() - 1 );
182 }
183 catch ( InvalidNameException e2 )
184 {
185
186 }
187
188 while ( ! dn.isEmpty() )
189 {
190 LOG.debug( "Walking ancestors of {} to find referrals.", dn );
191
192 try
193 {
194 entry = session.getCoreSession().lookup( dn );
195
196 if ( isEntryReferral( entry ) )
197 {
198 farthestReferralAncestor = entry;
199 }
200
201 dn.remove( dn.size() - 1 );
202 }
203 catch ( NameNotFoundException e )
204 {
205 LOG.debug( "Entry for {} not found.", dn );
206
207
208 try
209 {
210 dn.remove( dn.size() - 1 );
211 }
212 catch ( InvalidNameException e1 )
213 {
214
215 }
216 }
217 }
218
219 return farthestReferralAncestor;
220 }
221
222
223 private void handleModifyDnWithReferrals( LdapSession session, T modifyDnRequest )
224 {
225 ModifyDnRequest req = ( ModifyDnRequest ) modifyDnRequest;
226 LdapResult result = req.getResultResponse().getLdapResult();
227 ClonedServerEntry entry = null;
228 ClonedServerEntry superiorEntry = null;
229
230
231
232
233
234
235
236
237
238
239 try
240 {
241 entry = session.getCoreSession().lookup( req.getName() );
242 LOG.debug( "Entry for {} was found: ", req.getName(), entry );
243 }
244 catch ( NameNotFoundException e )
245 {
246
247 LOG.debug( "Entry for {} not found.", req.getName() );
248 }
249 catch ( Exception e )
250 {
251
252 handleException( session, modifyDnRequest, e );
253 return;
254 }
255
256 try
257 {
258 superiorEntry = session.getCoreSession().lookup( req.getNewSuperior() );
259 LOG.debug( "New superior entry for {} was found: ", req.getName(), entry );
260 }
261 catch ( NameNotFoundException e )
262 {
263
264 LOG.debug( "New superior entry for {} not found.", req.getName() );
265 }
266 catch ( Exception e )
267 {
268
269 handleException( session, modifyDnRequest, e );
270 return;
271 }
272
273
274
275
276
277 if ( entry != null )
278 {
279 try
280 {
281 if ( isEntryReferral( entry ) )
282 {
283 LOG.debug( "Entry is a referral: {}", entry );
284 handleReferralEntry( session, req.getName(), modifyDnRequest, entry );
285 return;
286 }
287 else
288 {
289 if ( superiorEntry != null && isEntryReferral( superiorEntry ) )
290 {
291 result.setErrorMessage( "Superior entry is a referral." );
292 result.setMatchedDn( req.getNewSuperior() );
293 result.setResultCode( ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
294 session.getIoSession().write( req.getResultResponse() );
295 return;
296 }
297 else if ( superiorEntry == null )
298 {
299 ClonedServerEntry referralAncestor = getFarthestReferralAncestor( session,
300 req.getNewSuperior() );
301
302 if ( referralAncestor != null )
303 {
304 result.setErrorMessage( "Superior entry does has referral ancestor." );
305 result.setResultCode( ResultCodeEnum.AFFECTS_MULTIPLE_DSAS );
306 session.getIoSession().write( req.getResultResponse() );
307 return;
308 }
309 else
310 {
311 result.setErrorMessage( "Superior entry does not exist." );
312 result.setResultCode( ResultCodeEnum.NO_SUCH_OBJECT );
313 session.getIoSession().write( req.getResultResponse() );
314 return;
315 }
316 }
317
318 LOG.debug( "Entry is NOT a referral: {}", entry );
319 handleIgnoringReferrals( session, req.getName(), entry, modifyDnRequest );
320 return;
321 }
322 }
323 catch ( Exception e )
324 {
325 handleException( session, modifyDnRequest, e );
326 }
327 }
328
329
330
331
332
333
334
335
336
337 if ( entry == null )
338 {
339 ClonedServerEntry referralAncestor = null;
340
341 try
342 {
343 referralAncestor = getFarthestReferralAncestor( session, req.getName() );
344 }
345 catch ( Exception e )
346 {
347 handleException( session, modifyDnRequest, e );
348 return;
349 }
350
351 if ( referralAncestor == null && ! ( req instanceof AddRequest ) )
352 {
353 result.setErrorMessage( "Entry not found." );
354 result.setResultCode( ResultCodeEnum.NO_SUCH_OBJECT );
355 session.getIoSession().write( req.getResultResponse() );
356 return;
357 }
358 else if ( ( req instanceof AddRequest ) && referralAncestor == null )
359 {
360 handleIgnoringReferrals( session, req.getName(), entry, modifyDnRequest );
361 return;
362 }
363
364
365 try
366 {
367 Referral referral = getReferralOnAncestor( session, req.getName(), modifyDnRequest, referralAncestor );
368 result.setResultCode( ResultCodeEnum.REFERRAL );
369 result.setReferral( referral );
370 session.getIoSession().write( req.getResultResponse() );
371 }
372 catch ( Exception e )
373 {
374 handleException( session, modifyDnRequest, e );
375 }
376 }
377 }
378
379
380
381
382
383 private void handleWithReferrals( LdapSession session, LdapDN reqTargetDn, T req )
384 {
385 LdapResult result = req.getResultResponse().getLdapResult();
386 ClonedServerEntry entry = null;
387
388
389
390
391
392
393
394
395
396
397 if ( ! ( req instanceof AddRequest ) )
398 {
399 try
400 {
401 entry = session.getCoreSession().lookup( reqTargetDn );
402 LOG.debug( "Entry for {} was found: ", reqTargetDn, entry );
403 }
404 catch ( NameNotFoundException e )
405 {
406
407 LOG.debug( "Entry for {} not found.", reqTargetDn );
408 }
409 catch ( Exception e )
410 {
411
412 handleException( session, req, e );
413 return;
414 }
415 }
416
417
418
419
420
421 if ( entry != null )
422 {
423 try
424 {
425 if ( isEntryReferral( entry ) )
426 {
427 LOG.debug( "Entry is a referral: {}", entry );
428
429 if ( req instanceof SearchRequest )
430 {
431 handleReferralEntryForSearch( session, ( SearchRequest ) req, entry );
432 }
433 else
434 {
435 handleReferralEntry( session, reqTargetDn, req, entry );
436 }
437 return;
438 }
439 else
440 {
441 LOG.debug( "Entry is NOT a referral: {}", entry );
442 handleIgnoringReferrals( session, reqTargetDn, entry, req );
443 return;
444 }
445 }
446 catch ( Exception e )
447 {
448 handleException( session, req, e );
449 }
450 }
451
452
453
454
455
456
457
458
459
460 if ( entry == null )
461 {
462 ClonedServerEntry referralAncestor = null;
463
464 try
465 {
466 referralAncestor = getFarthestReferralAncestor( session, reqTargetDn );
467 }
468 catch ( Exception e )
469 {
470 handleException( session, req, e );
471 return;
472 }
473
474 if ( referralAncestor == null && ! ( req instanceof AddRequest ) )
475 {
476 result.setErrorMessage( "Entry not found." );
477 result.setResultCode( ResultCodeEnum.NO_SUCH_OBJECT );
478 session.getIoSession().write( req.getResultResponse() );
479 return;
480 }
481 else if ( ( req instanceof AddRequest ) && referralAncestor == null )
482 {
483 handleIgnoringReferrals( session, reqTargetDn, entry, req );
484 return;
485 }
486
487
488 try
489 {
490 Referral referral = null;
491
492 if ( req instanceof SearchRequest )
493 {
494 referral = getReferralOnAncestorForSearch( session, ( SearchRequest ) req, referralAncestor );
495 }
496 else
497 {
498 referral = getReferralOnAncestor( session, reqTargetDn, req, referralAncestor );
499 }
500
501 result.setResultCode( ResultCodeEnum.REFERRAL );
502 result.setReferral( referral );
503 session.getIoSession().write( req.getResultResponse() );
504 }
505 catch ( Exception e )
506 {
507 handleException( session, req, e );
508 }
509 }
510 }
511
512
513
514
515
516
517
518
519
520
521
522
523 private void handleReferralEntry( LdapSession session, LdapDN reqTargetDn, T req, ClonedServerEntry entry )
524 {
525 LdapResult result = req.getResultResponse().getLdapResult();
526 ReferralImpl refs = new ReferralImpl();
527 result.setReferral( refs );
528 result.setResultCode( ResultCodeEnum.REFERRAL );
529 result.setErrorMessage( "Encountered referral attempting to handle request." );
530 result.setMatchedDn( reqTargetDn );
531
532 EntryAttribute refAttr = entry.getOriginalEntry().get( SchemaConstants.REF_AT );
533 for ( Value<?> refval : refAttr )
534 {
535 refs.addLdapUrl( ( String ) refval.get() );
536 }
537
538 session.getIoSession().write( req.getResultResponse() );
539 }
540
541
542
543
544
545
546
547
548
549
550
551
552 private void handleReferralEntryForSearch( LdapSession session, SearchRequest req, ClonedServerEntry entry )
553 throws Exception
554 {
555 LdapResult result = req.getResultResponse().getLdapResult();
556 ReferralImpl referral = new ReferralImpl();
557 result.setReferral( referral );
558 result.setResultCode( ResultCodeEnum.REFERRAL );
559 result.setErrorMessage( "Encountered referral attempting to handle request." );
560 result.setMatchedDn( req.getBase() );
561
562 EntryAttribute refAttr = entry.getOriginalEntry().get( SchemaConstants.REF_AT );
563 for ( Value<?> refval : refAttr )
564 {
565 String refstr = ( String ) refval.get();
566
567
568 if ( ! refstr.startsWith( "ldap" ) )
569 {
570 referral.addLdapUrl( refstr );
571 continue;
572 }
573
574
575 LdapURL ldapUrl = new LdapURL();
576 try
577 {
578 ldapUrl.parse( refstr.toCharArray() );
579 }
580 catch ( LdapURLEncodingException e )
581 {
582 LOG.error( "Bad URL ({}) for ref in {}. Reference will be ignored.", refstr, entry );
583 continue;
584 }
585
586 ldapUrl.setForceScopeRendering( true );
587 ldapUrl.setAttributes( req.getAttributes() );
588 ldapUrl.setScope( req.getScope().getJndiScope() );
589 referral.addLdapUrl( ldapUrl.toString() );
590 }
591
592 session.getIoSession().write( req.getResultResponse() );
593 }
594
595
596
597
598
599
600
601
602
603
604 public Referral getReferralOnAncestor( LdapSession session, LdapDN reqTargetDn, T req,
605 ClonedServerEntry referralAncestor ) throws Exception
606 {
607 LOG.debug( "Inside getReferralOnAncestor()" );
608
609 ServerAttribute refAttr = ( ServerAttribute ) referralAncestor.getOriginalEntry()
610 .get( SchemaConstants.REF_AT );
611 Referral referral = new ReferralImpl();
612
613 for ( Value<?> value : refAttr )
614 {
615 String ref = ( String ) value.get();
616
617 LOG.debug( "Calculating LdapURL for referrence value {}", ref );
618
619
620 if ( ! ref.startsWith( "ldap" ) )
621 {
622 referral.addLdapUrl( ref );
623 continue;
624 }
625
626
627 LdapURL ldapUrl = new LdapURL();
628 try
629 {
630 ldapUrl.parse( ref.toCharArray() );
631 }
632 catch ( LdapURLEncodingException e )
633 {
634 LOG.error( "Bad URL ({}) for ref in {}. Reference will be ignored.", ref, referralAncestor );
635 }
636
637 LdapDN urlDn = new LdapDN( ldapUrl.getDn().getUpName() );
638 urlDn.normalize( session.getCoreSession().getDirectoryService().getRegistries()
639 .getAttributeTypeRegistry().getNormalizerMapping() );
640
641 if ( urlDn.getNormName().equals( referralAncestor.getDn().getNormName() ) )
642 {
643
644 StringBuilder buf = new StringBuilder();
645 buf.append( ldapUrl.getScheme() );
646 buf.append( ldapUrl.getHost() );
647
648 if ( ldapUrl.getPort() > 0 )
649 {
650 buf.append( ":" );
651 buf.append( ldapUrl.getPort() );
652 }
653
654 referral.addLdapUrl( buf.toString() );
655 continue;
656 }
657
658
659
660
661
662
663 int diff = reqTargetDn.size() - referralAncestor.getDn().size();
664 LdapDN extra = new LdapDN();
665
666
667
668 LdapDN reqUnnormalizedDn = new LdapDN( reqTargetDn.getUpName() );
669 for ( int jj = 0; jj < diff; jj++ )
670 {
671 extra.add( reqUnnormalizedDn.get( referralAncestor.getDn().size() + jj ) );
672 }
673
674 urlDn.addAll( extra );
675
676 StringBuilder buf = new StringBuilder();
677 buf.append( ldapUrl.getScheme() );
678 buf.append( ldapUrl.getHost() );
679
680 if ( ldapUrl.getPort() > 0 )
681 {
682 buf.append( ":" );
683 buf.append( ldapUrl.getPort() );
684 }
685
686 buf.append( "/" );
687 buf.append( LdapURL.urlEncode( urlDn.getUpName(), false ) );
688 referral.addLdapUrl( buf.toString() );
689 }
690
691 return referral;
692 }
693
694
695
696
697
698
699
700
701
702
703 public Referral getReferralOnAncestorForSearch( LdapSession session, SearchRequest req,
704 ClonedServerEntry referralAncestor ) throws Exception
705 {
706 LOG.debug( "Inside getReferralOnAncestor()" );
707
708 ServerAttribute refAttr = ( ServerAttribute ) referralAncestor.getOriginalEntry()
709 .get( SchemaConstants.REF_AT );
710 Referral referral = new ReferralImpl();
711
712 for ( Value<?> value : refAttr )
713 {
714 String ref = ( String ) value.get();
715
716 LOG.debug( "Calculating LdapURL for referrence value {}", ref );
717
718
719 if ( ! ref.startsWith( "ldap" ) )
720 {
721 referral.addLdapUrl( ref );
722 continue;
723 }
724
725
726 LdapURL ldapUrl = new LdapURL();
727 try
728 {
729 ldapUrl.parse( ref.toCharArray() );
730 }
731 catch ( LdapURLEncodingException e )
732 {
733 LOG.error( "Bad URL ({}) for ref in {}. Reference will be ignored.", ref, referralAncestor );
734 }
735
736
737 LdapDN urlDn = new LdapDN( ldapUrl.getDn().getUpName() );
738 urlDn.normalize( session.getCoreSession().getDirectoryService().getRegistries()
739 .getAttributeTypeRegistry().getNormalizerMapping() );
740
741 if ( urlDn.getNormName().equals( req.getBase().getNormName() ) )
742 {
743 ldapUrl.setForceScopeRendering( true );
744 ldapUrl.setAttributes( req.getAttributes() );
745 ldapUrl.setScope( req.getScope().getJndiScope() );
746 referral.addLdapUrl( ldapUrl.toString() );
747 continue;
748 }
749
750
751
752
753
754
755 int diff = req.getBase().size() - referralAncestor.getDn().size();
756 LdapDN extra = new LdapDN();
757
758
759
760 LdapDN reqUnnormalizedDn = new LdapDN( req.getBase().getUpName() );
761 for ( int jj = 0; jj < diff; jj++ )
762 {
763 extra.add( reqUnnormalizedDn.get( referralAncestor.getDn().size() + jj ) );
764 }
765
766 ldapUrl.getDn().addAll( extra );
767 ldapUrl.setForceScopeRendering( true );
768 ldapUrl.setAttributes( req.getAttributes() );
769 ldapUrl.setScope( req.getScope().getJndiScope() );
770 referral.addLdapUrl( ldapUrl.toString() );
771 }
772
773 return referral;
774 }
775
776
777
778
779
780 public void handleException( LdapSession session, T req, Exception e )
781 {
782 LdapResult result = req.getResultResponse().getLdapResult();
783
784
785
786
787 ResultCodeEnum code;
788 if ( e instanceof LdapException )
789 {
790 code = ( ( LdapException ) e ).getResultCode();
791 }
792 else
793 {
794 code = ResultCodeEnum.getBestEstimate( e, req.getType() );
795 }
796 result.setResultCode( code );
797
798
799
800
801
802
803 String msg = code.toString() + ": failed for " + req + ": " + e.getMessage();
804 LOG.error( msg, e );
805 if ( IS_DEBUG )
806 {
807 msg += ":\n" + ExceptionUtils.getStackTrace( e );
808 }
809 result.setErrorMessage( msg );
810
811 if ( e instanceof NamingException )
812 {
813 NamingException ne = ( NamingException ) e;
814
815
816 boolean setMatchedDn =
817 code == ResultCodeEnum.NO_SUCH_OBJECT ||
818 code == ResultCodeEnum.ALIAS_PROBLEM ||
819 code == ResultCodeEnum.INVALID_DN_SYNTAX ||
820 code == ResultCodeEnum.ALIAS_DEREFERENCING_PROBLEM;
821
822 if ( ( ne.getResolvedName() != null ) && setMatchedDn )
823 {
824 result.setMatchedDn( ( LdapDN ) ne.getResolvedName() );
825 }
826 }
827
828 session.getIoSession().write( req.getResultResponse() );
829 }
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848 public abstract void handleIgnoringReferrals( LdapSession session, LdapDN reqTargetDn,
849 ClonedServerEntry entry, T req );
850 }