1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.directory.ldap.client.template;
21
22
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.List;
26
27 import org.apache.directory.api.ldap.extras.controls.ppolicy_impl.PasswordPolicyDecorator;
28 import org.apache.directory.api.ldap.model.entry.Attribute;
29 import org.apache.directory.api.ldap.model.entry.Entry;
30 import org.apache.directory.api.ldap.model.entry.Value;
31 import org.apache.directory.api.ldap.model.exception.LdapException;
32 import org.apache.directory.api.ldap.model.message.AddRequest;
33 import org.apache.directory.api.ldap.model.message.AddResponse;
34 import org.apache.directory.api.ldap.model.message.BindRequest;
35 import org.apache.directory.api.ldap.model.message.BindRequestImpl;
36 import org.apache.directory.api.ldap.model.message.DeleteRequest;
37 import org.apache.directory.api.ldap.model.message.DeleteResponse;
38 import org.apache.directory.api.ldap.model.message.ModifyRequest;
39 import org.apache.directory.api.ldap.model.message.ModifyRequestImpl;
40 import org.apache.directory.api.ldap.model.message.ModifyResponse;
41 import org.apache.directory.api.ldap.model.message.ResultCodeEnum;
42 import org.apache.directory.api.ldap.model.message.ResultResponse;
43 import org.apache.directory.api.ldap.model.message.SearchRequest;
44 import org.apache.directory.api.ldap.model.message.SearchScope;
45 import org.apache.directory.api.ldap.model.name.Dn;
46 import org.apache.directory.ldap.client.api.EntryCursorImpl;
47 import org.apache.directory.ldap.client.api.LdapConnection;
48 import org.apache.directory.ldap.client.api.LdapConnectionPool;
49 import org.apache.directory.ldap.client.template.exception.LdapRequestUnsuccessfulException;
50 import org.apache.directory.ldap.client.template.exception.LdapRuntimeException;
51 import org.apache.directory.ldap.client.template.exception.PasswordException;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55
56
57
58
59
60
61
62
63
64 public class LdapConnectionTemplate implements LdapConnectionOperations, ModelFactory
65 {
66 private static Logger logger = LoggerFactory.getLogger( LdapConnectionTemplate.class );
67 private static final EntryMapper<Dn> dnEntryMapper = new EntryMapper<Dn>() {
68 @Override
69 public Dn map( Entry entry ) throws LdapException
70 {
71 return entry.getDn();
72 }
73 };
74
75 private LdapConnectionPool connectionPool;
76 private final PasswordPolicyDecorator passwordPolicyRequestControl;
77 private PasswordPolicyResponder passwordPolicyResponder;
78 private ModelFactory modelFactory;
79
80
81
82
83
84
85
86 public LdapConnectionTemplate( LdapConnectionPool connectionPool )
87 {
88 this.connectionPool = connectionPool;
89 this.passwordPolicyRequestControl = new PasswordPolicyDecorator(
90 connectionPool.getLdapApiService() );
91 this.passwordPolicyResponder = new PasswordPolicyResponderImpl(
92 connectionPool.getLdapApiService() );
93 this.modelFactory = new ModelFactoryImpl();
94 }
95
96
97 @Override
98 public AddResponse add( Dn dn, final Attribute... attributes )
99 {
100 return add( dn,
101 new RequestBuilder<AddRequest>()
102 {
103 @Override
104 public void buildRequest( AddRequest request ) throws LdapException
105 {
106 request.getEntry().add( attributes );
107 }
108 } );
109 }
110
111
112 @Override
113 public AddResponse add( Dn dn, RequestBuilder<AddRequest> requestBuilder )
114 {
115 AddRequest addRequest = newAddRequest( newEntry( dn ) );
116 try
117 {
118 requestBuilder.buildRequest( addRequest );
119 }
120 catch ( LdapException e )
121 {
122 throw new LdapRuntimeException( e );
123 }
124 return add( addRequest );
125 }
126
127
128 @Override
129 public AddResponse add( AddRequest addRequest )
130 {
131 LdapConnection connection = null;
132 try
133 {
134 connection = connectionPool.getConnection();
135 return connection.add( addRequest );
136 }
137 catch ( LdapException e )
138 {
139 throw new LdapRuntimeException( e );
140 }
141 finally
142 {
143 returnLdapConnection( connection );
144 }
145 }
146
147
148 @Override
149 public PasswordWarning authenticate( String baseDn, String filter, SearchScope scope, char[] password ) throws PasswordException
150 {
151 return authenticate( newSearchRequest( baseDn, filter, scope ), password );
152 }
153
154
155 @Override
156 public PasswordWarning authenticate( Dn baseDn, String filter, SearchScope scope, char[] password ) throws PasswordException
157 {
158 return authenticate( newSearchRequest( baseDn, filter, scope ), password );
159 }
160
161
162 @Override
163 public PasswordWarning authenticate( SearchRequest searchRequest, char[] password ) throws PasswordException
164 {
165 Dn userDn = searchFirst( searchRequest, dnEntryMapper );
166 if ( userDn == null ) {
167 throw new PasswordException().setResultCode( ResultCodeEnum.INVALID_CREDENTIALS );
168 }
169
170 return authenticate( userDn, password );
171 }
172
173
174 @Override
175 public PasswordWarning authenticate( Dn userDn, char[] password ) throws PasswordException
176 {
177 LdapConnection connection = null;
178 try
179 {
180 connection = connectionPool.getUnboundConnection();
181 return authenticateConnection( connection, userDn, password );
182 }
183 catch ( LdapException e )
184 {
185 throw new LdapRuntimeException( e );
186 }
187 finally
188 {
189 safeCloseLdapConnection( connection );
190 }
191 }
192
193
194 private PasswordWarning authenticateConnection( final LdapConnection connection,
195 final Dn userDn, final char[] password ) throws PasswordException
196 {
197 return passwordPolicyResponder.process(
198 new PasswordPolicyOperation()
199 {
200 @Override
201 public ResultResponse process() throws LdapException
202 {
203 MemoryClearingBuffer passwordBuffer = MemoryClearingBuffer.newInstance( password );
204 try
205 {
206 BindRequest bindRequest = new BindRequestImpl()
207 .setDn( userDn )
208 .setCredentials( passwordBuffer.getBytes() )
209 .addControl( passwordPolicyRequestControl );
210
211 return connection.bind( bindRequest );
212 }
213 finally
214 {
215 passwordBuffer.clear();
216 }
217 }
218 } );
219 }
220
221
222 @Override
223 public DeleteResponse delete( Dn dn )
224 {
225 return delete( dn, null );
226 }
227
228
229 @Override
230 public DeleteResponse delete( Dn dn, RequestBuilder<DeleteRequest> requestBuilder )
231 {
232 DeleteRequest deleteRequest = newDeleteRequest( dn );
233 if ( requestBuilder != null )
234 {
235 try
236 {
237 requestBuilder.buildRequest( deleteRequest );
238 }
239 catch ( LdapException e )
240 {
241 throw new LdapRuntimeException( e );
242 }
243 }
244 return delete( deleteRequest );
245 }
246
247
248 @Override
249 public DeleteResponse delete( DeleteRequest deleteRequest )
250 {
251 LdapConnection connection = null;
252 try
253 {
254 connection = connectionPool.getConnection();
255 return connection.delete( deleteRequest );
256 }
257 catch ( LdapException e )
258 {
259 throw new LdapRuntimeException( e );
260 }
261 finally
262 {
263 returnLdapConnection( connection );
264 }
265 }
266
267
268 @Override
269 public <T> T execute( ConnectionCallback<T> connectionCallback )
270 {
271 LdapConnection connection = null;
272 try
273 {
274 connection = connectionPool.getConnection();
275 return connectionCallback.doWithConnection( connection );
276 }
277 catch ( LdapException e )
278 {
279 throw new LdapRuntimeException( e );
280 }
281 finally
282 {
283 returnLdapConnection( connection );
284 }
285 }
286
287
288 @Override
289 public <T> T lookup( Dn dn, EntryMapper<T> entryMapper )
290 {
291 return lookup( dn, null, entryMapper );
292 }
293
294
295 @Override
296 public <T> T lookup( Dn dn, String[] attributes, EntryMapper<T> entryMapper )
297 {
298 LdapConnection connection = null;
299 try
300 {
301 connection = connectionPool.getConnection();
302 Entry entry = attributes == null
303 ? connection.lookup( dn )
304 : connection.lookup( dn, attributes );
305 return entry == null ? null : entryMapper.map( entry );
306 }
307 catch ( LdapException e )
308 {
309 throw new LdapRuntimeException( e );
310 }
311 finally
312 {
313 returnLdapConnection( connection );
314 }
315 }
316
317
318 private void modifyPassword( final LdapConnection connection, final Dn userDn,
319 final char[] newPassword ) throws PasswordException
320 {
321 passwordPolicyResponder.process(
322 new PasswordPolicyOperation()
323 {
324 @Override
325 public ResultResponse process() throws PasswordException, LdapException
326 {
327
328
329
330 MemoryClearingBuffer newPasswordBuffer = MemoryClearingBuffer.newInstance( newPassword );
331 try
332 {
333 ModifyRequest modifyRequest = new ModifyRequestImpl()
334 .setName( userDn )
335 .replace( "userPassword", newPasswordBuffer.getComputedBytes() )
336 .addControl( passwordPolicyRequestControl );
337
338 return connection.modify( modifyRequest );
339 }
340 finally
341 {
342 newPasswordBuffer.clear();
343 }
344 }
345 } );
346
347 }
348
349 @Override
350 public void modifyPassword( Dn userDn, char[] newPassword )
351 throws PasswordException
352 {
353 modifyPassword( userDn, null, newPassword, true );
354 }
355
356 @Override
357 public void modifyPassword( Dn userDn, char[] oldPassword,
358 char[] newPassword ) throws PasswordException
359 {
360 modifyPassword( userDn, oldPassword, newPassword, false );
361 }
362
363 @Override
364 public void modifyPassword( Dn userDn, char[] oldPassword,
365 char[] newPassword, boolean asAdmin ) throws PasswordException
366 {
367 LdapConnection connection = null;
368 try
369 {
370 if ( asAdmin )
371 {
372 connection = connectionPool.getConnection();
373 }
374 else
375 {
376 connection = connectionPool.getUnboundConnection();
377 authenticateConnection( connection, userDn, oldPassword );
378 }
379
380 modifyPassword( connection, userDn, newPassword );
381 }
382 catch ( LdapException e )
383 {
384 throw new LdapRuntimeException( e );
385 }
386 finally
387 {
388 if ( asAdmin )
389 {
390 returnLdapConnection( connection );
391 }
392 else
393 {
394 safeCloseLdapConnection( connection );
395 }
396 }
397 }
398
399
400 @Override
401 public ModifyResponse modify( Dn dn, RequestBuilder<ModifyRequest> requestBuilder )
402 {
403 ModifyRequest modifyRequest = newModifyRequest( dn );
404 try
405 {
406 requestBuilder.buildRequest( modifyRequest );
407 }
408 catch ( LdapException e )
409 {
410 throw new LdapRuntimeException( e );
411 }
412 return modify( modifyRequest );
413 }
414
415
416 @Override
417 public ModifyResponse modify( ModifyRequest modifyRequest )
418 {
419 LdapConnection connection = null;
420 try
421 {
422 connection = connectionPool.getConnection();
423 return connection.modify( modifyRequest );
424 }
425 catch ( LdapException e )
426 {
427 throw new LdapRuntimeException( e );
428 }
429 finally
430 {
431 returnLdapConnection( connection );
432 }
433 }
434
435
436 @Override
437 public AddRequest newAddRequest( Entry entry )
438 {
439 return modelFactory.newAddRequest( entry );
440 }
441
442
443 @Override
444 public Attribute newAttribute( String name, byte[]... values )
445 {
446 return modelFactory.newAttribute( name, values );
447 }
448
449
450 @Override
451 public Attribute newAttribute( String name, String... values )
452 {
453 return modelFactory.newAttribute( name, values );
454 }
455
456
457 @Override
458 public Attribute newAttribute( String name, Value<?>... values )
459 {
460 return modelFactory.newAttribute( name, values );
461 }
462
463
464 @Override
465 public DeleteRequest newDeleteRequest( Dn dn )
466 {
467 return modelFactory.newDeleteRequest( dn );
468 }
469
470
471 @Override
472 public Dn newDn( String dn )
473 {
474 return modelFactory.newDn( dn );
475 }
476
477
478 @Override
479 public Entry newEntry( String dn )
480 {
481 return modelFactory.newEntry( dn );
482 }
483
484
485 @Override
486 public Entry newEntry( Dn dn )
487 {
488 return modelFactory.newEntry( dn );
489 }
490
491
492 @Override
493 public ModifyRequest newModifyRequest( String dn )
494 {
495 return modelFactory.newModifyRequest( dn );
496 }
497
498
499 @Override
500 public ModifyRequest newModifyRequest( Dn dn )
501 {
502 return modelFactory.newModifyRequest( dn );
503 }
504
505
506 @Override
507 public SearchRequest newSearchRequest( String baseDn, String filter, SearchScope scope )
508 {
509 return modelFactory.newSearchRequest( baseDn, filter, scope );
510 }
511
512
513 @Override
514 public SearchRequest newSearchRequest( Dn baseDn, String filter, SearchScope scope )
515 {
516 return modelFactory.newSearchRequest( baseDn, filter, scope );
517 }
518
519
520 @Override
521 public SearchRequest newSearchRequest( String baseDn, String filter, SearchScope scope, String... attributes )
522 {
523 return modelFactory.newSearchRequest( baseDn, filter, scope, attributes );
524 }
525
526
527 @Override
528 public SearchRequest newSearchRequest( Dn baseDn, String filter, SearchScope scope, String... attributes )
529 {
530 return modelFactory.newSearchRequest( baseDn, filter, scope, attributes );
531 }
532
533
534 @Override
535 public <T extends ResultResponse> T responseOrException( T response )
536 {
537 if ( ResultCodeEnum.SUCCESS != response.getLdapResult().getResultCode() )
538 {
539 throw new LdapRequestUnsuccessfulException( response );
540 }
541 return response;
542 }
543
544
545 private void returnLdapConnection( LdapConnection connection )
546 {
547 if ( connection != null )
548 {
549 try
550 {
551 connectionPool.releaseConnection( connection );
552 }
553 catch ( LdapException e )
554 {
555 throw new LdapRuntimeException( e );
556 }
557 }
558 }
559
560
561 private void safeCloseLdapConnection( LdapConnection connection )
562 {
563 if ( connection != null )
564 {
565 try
566 {
567 connection.close();
568 }
569 catch ( IOException e )
570 {
571 logger.error( "Unable to close ldap connection, might be leaking connections:", e.getMessage() );
572 logger.debug( "Unable to close ldap connection, might be leaking connections:", e );
573 }
574 }
575 }
576
577
578 @Override
579 public <T> T searchFirst( String baseDn, String filter, SearchScope scope,
580 EntryMapper<T> entryMapper )
581 {
582 return searchFirst(
583 modelFactory.newSearchRequest( baseDn, filter, scope ),
584 entryMapper );
585 }
586
587
588 @Override
589 public <T> T searchFirst( Dn baseDn, String filter, SearchScope scope,
590 EntryMapper<T> entryMapper )
591 {
592 return searchFirst(
593 modelFactory.newSearchRequest( baseDn, filter, scope ),
594 entryMapper );
595 }
596
597
598 @Override
599 public <T> T searchFirst( String baseDn, String filter, SearchScope scope,
600 String[] attributes, EntryMapper<T> entryMapper )
601 {
602 return searchFirst(
603 modelFactory.newSearchRequest( baseDn, filter, scope, attributes ),
604 entryMapper );
605 }
606
607
608 @Override
609 public <T> T searchFirst( Dn baseDn, String filter, SearchScope scope,
610 String[] attributes, EntryMapper<T> entryMapper )
611 {
612 return searchFirst(
613 modelFactory.newSearchRequest( baseDn, filter, scope, attributes ),
614 entryMapper );
615 }
616
617
618 @Override
619 public <T> T searchFirst( SearchRequest searchRequest,
620 EntryMapper<T> entryMapper )
621 {
622
623
624 long originalSizeLimit = searchRequest.getSizeLimit();
625 try
626 {
627 searchRequest.setSizeLimit( 1 );
628 List<T> entries = search( searchRequest, entryMapper );
629 return entries.isEmpty() ? null : entries.get( 0 );
630 }
631 finally
632 {
633 searchRequest.setSizeLimit( originalSizeLimit );
634 }
635 }
636
637
638 @Override
639 public <T> List<T> search( String baseDn, String filter, SearchScope scope,
640 EntryMapper<T> entryMapper )
641 {
642 return search(
643 modelFactory.newSearchRequest( baseDn, filter, scope ),
644 entryMapper );
645 }
646
647
648 @Override
649 public <T> List<T> search( Dn baseDn, String filter, SearchScope scope,
650 EntryMapper<T> entryMapper )
651 {
652 return search(
653 modelFactory.newSearchRequest( baseDn, filter, scope ),
654 entryMapper );
655 }
656
657
658 @Override
659 public <T> List<T> search( String baseDn, String filter, SearchScope scope,
660 String[] attributes, EntryMapper<T> entryMapper )
661 {
662 return search(
663 modelFactory.newSearchRequest( baseDn, filter, scope, attributes ),
664 entryMapper );
665 }
666
667
668 @Override
669 public <T> List<T> search( Dn baseDn, String filter, SearchScope scope,
670 String[] attributes, EntryMapper<T> entryMapper )
671 {
672 return search(
673 modelFactory.newSearchRequest( baseDn, filter, scope, attributes ),
674 entryMapper );
675 }
676
677
678 @Override
679 public <T> List<T> search( SearchRequest searchRequest,
680 EntryMapper<T> entryMapper )
681 {
682 List<T> entries = new ArrayList<T>();
683
684 LdapConnection connection = null;
685 try
686 {
687 connection = connectionPool.getConnection();
688
689 for ( Entry entry : new EntryCursorImpl( connection.search( searchRequest ) ) )
690 {
691 entries.add( entryMapper.map( entry ) );
692 }
693 }
694 catch ( LdapException e )
695 {
696 throw new LdapRuntimeException( e );
697 }
698 finally
699 {
700 returnLdapConnection( connection );
701 }
702
703 return entries;
704 }
705
706
707
708
709
710
711
712 public void setModelFactory( ModelFactory modelFactory )
713 {
714 this.modelFactory = modelFactory;
715 }
716
717
718
719
720
721
722
723
724
725 public void setPasswordPolicyResponder( PasswordPolicyResponder passwordPolicyResponder )
726 {
727 this.passwordPolicyResponder = passwordPolicyResponder;
728 }
729 }