1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.bremersee.ldaptive;
18
19 import java.time.Duration;
20 import java.util.ArrayList;
21 import java.util.List;
22 import java.util.Objects;
23 import java.util.function.Predicate;
24 import java.util.function.Supplier;
25 import lombok.Data;
26 import lombok.EqualsAndHashCode;
27 import lombok.Getter;
28 import lombok.Setter;
29 import lombok.ToString;
30 import org.ldaptive.ActivePassiveConnectionStrategy;
31 import org.ldaptive.BindConnectionInitializer;
32 import org.ldaptive.ConnectionConfig;
33 import org.ldaptive.ConnectionFactory;
34 import org.ldaptive.ConnectionInitializer;
35 import org.ldaptive.ConnectionValidator;
36 import org.ldaptive.DefaultConnectionFactory;
37 import org.ldaptive.DnsSrvConnectionStrategy;
38 import org.ldaptive.PooledConnectionFactory;
39 import org.ldaptive.RandomConnectionStrategy;
40 import org.ldaptive.RetryMetadata;
41 import org.ldaptive.ReturnAttributes;
42 import org.ldaptive.RoundRobinConnectionStrategy;
43 import org.ldaptive.SearchConnectionValidator;
44 import org.ldaptive.SearchRequest;
45 import org.ldaptive.SearchScope;
46 import org.ldaptive.ad.extended.FastBindConnectionInitializer;
47 import org.ldaptive.pool.IdlePruneStrategy;
48 import org.ldaptive.ssl.AllowAnyHostnameVerifier;
49 import org.ldaptive.ssl.CertificateHostnameVerifier;
50 import org.ldaptive.ssl.DefaultHostnameVerifier;
51 import org.ldaptive.ssl.SslConfig;
52 import org.ldaptive.ssl.X509CredentialConfig;
53
54
55
56
57
58
59 @Getter
60 @Setter
61 @ToString(exclude = {"bindCredentials"})
62 @EqualsAndHashCode(exclude = {"bindCredentials"})
63 public class LdaptiveProperties {
64
65
66
67
68 private boolean immutable;
69
70
71
72
73
74 private String ldapUrl;
75
76
77
78
79 private Duration connectTimeout = Duration.ofMinutes(1);
80
81
82
83
84 private Duration startTlsTimeout = Duration.ofMinutes(1);
85
86
87
88
89 private Duration responseTimeout = Duration.ofMinutes(1);
90
91
92
93
94
95 private Duration reconnectTimeout = Duration.ofMinutes(2);
96
97
98
99
100 private boolean autoReconnect = true;
101
102
103
104
105 private ReconnectStrategy reconnectStrategy = ReconnectStrategy.ONE_RECONNECT_ATTEMPT;
106
107
108
109
110 private boolean autoReplay = true;
111
112
113
114
115 private SslProperties sslConfig = new SslProperties();
116
117
118
119
120 private boolean useStartTls;
121
122
123
124
125 private String bindDn;
126
127
128
129
130 private String bindCredentials;
131
132
133
134
135 private boolean fastBind = false;
136
137
138
139
140 private ConnectionStrategy connectionStrategy = ConnectionStrategy.ACTIVE_PASSIVE;
141
142
143
144
145 private ConnectionValidatorProperties connectionValidator = new ConnectionValidatorProperties();
146
147
148
149
150 private boolean pooled = false;
151
152
153
154
155 private ConnectionPoolProperties connectionPool = new ConnectionPoolProperties();
156
157
158
159
160 public LdaptiveProperties() {
161 super();
162 }
163
164
165
166
167
168
169 public ConnectionConfig createConnectionConfig() {
170 ConnectionInitializer[] connectionInitializers;
171 if (hasText(getBindDn()) && hasText(getBindCredentials())) {
172 connectionInitializers = new ConnectionInitializer[]{
173 BindConnectionInitializer.builder()
174 .dn(getBindDn())
175 .credential(getBindCredentials())
176 .build()
177 };
178 } else if (isFastBind()) {
179 connectionInitializers = new ConnectionInitializer[]{
180 new FastBindConnectionInitializer()
181 };
182 } else {
183 connectionInitializers = new ConnectionInitializer[]{};
184 }
185 ConnectionConfig connectionConfig = ConnectionConfig.builder()
186 .connectTimeout(getConnectTimeout())
187 .startTLSTimeout(getStartTlsTimeout())
188 .responseTimeout(getResponseTimeout())
189 .reconnectTimeout(getReconnectTimeout())
190 .autoReconnect(isAutoReconnect())
191 .autoReconnectCondition(getReconnectStrategy().get())
192 .autoReplay(isAutoReplay())
193 .sslConfig(getSslConfig().createSslConfig())
194 .useStartTLS(isUseStartTls())
195 .connectionInitializers(connectionInitializers)
196 .connectionStrategy(getConnectionStrategy().get())
197 .connectionValidator(getConnectionValidator().createConnectionValidator())
198 .url(getLdapUrl())
199 .build();
200 if (isImmutable()) {
201 connectionConfig.freeze();
202 }
203 return connectionConfig;
204 }
205
206
207
208
209
210
211 public ConnectionFactory createConnectionFactory() {
212 if (isPooled()) {
213 ConnectionPoolProperties properties = getConnectionPool();
214 PooledConnectionFactory factory = PooledConnectionFactory.builder()
215 .config(createConnectionConfig())
216 .blockWaitTime(properties.getBlockWaitTime())
217 .connectOnCreate(properties.isConnectOnCreate())
218 .failFastInitialize(properties.isFailFastInitialize())
219 .max(properties.getMaxPoolSize())
220 .min(properties.getMinPoolSize())
221 .pruneStrategy(
222 new IdlePruneStrategy(properties.getPrunePeriod(), properties.getIdleTime()))
223 .validateOnCheckIn(properties.isValidateOnCheckIn())
224 .validateOnCheckOut(properties.isValidateOnCheckOut())
225 .validatePeriodically(properties.isValidatePeriodically())
226 .validator(properties.getValidator().createConnectionValidator())
227 .build();
228 factory.initialize();
229 return factory;
230 }
231 return new DefaultConnectionFactory(createConnectionConfig());
232 }
233
234
235
236
237 public enum ReconnectStrategy implements Supplier<Predicate<RetryMetadata>> {
238
239
240
241
242 ONE_RECONNECT_ATTEMPT(ConnectionConfig.ONE_RECONNECT_ATTEMPT),
243
244
245
246
247 INFINITE_RECONNECT_ATTEMPTS(ConnectionConfig.INFINITE_RECONNECT_ATTEMPTS),
248
249
250
251
252 INFINITE_RECONNECT_ATTEMPTS_WITH_BACKOFF(
253 ConnectionConfig.INFINITE_RECONNECT_ATTEMPTS_WITH_BACKOFF);
254
255 private final Predicate<RetryMetadata> condition;
256
257 ReconnectStrategy(Predicate<RetryMetadata> condition) {
258 this.condition = condition;
259 }
260
261 @Override
262 public Predicate<RetryMetadata> get() {
263 return condition;
264 }
265 }
266
267
268
269
270 @Data
271 public static class SslProperties {
272
273
274
275
276 private String trustCertificates;
277
278
279
280
281 private String authenticationCertificate;
282
283
284
285
286 private String authenticationKey;
287
288
289
290
291 private HostnameVerifier hostnameVerifier = HostnameVerifier.DEFAULT;
292
293
294
295
296 public SslProperties() {
297 super();
298 }
299
300
301
302
303
304
305 public SslConfig createSslConfig() {
306 if (hasText(getTrustCertificates())
307 || hasText(getAuthenticationCertificate())
308 || hasText(getAuthenticationKey())) {
309
310 X509CredentialConfig x509 = new X509CredentialConfig();
311 if (hasText(getAuthenticationCertificate())) {
312 x509.setAuthenticationCertificate(getAuthenticationCertificate());
313 }
314 if (hasText(getAuthenticationKey())) {
315 x509.setAuthenticationKey(getAuthenticationKey());
316 }
317 if (hasText(getTrustCertificates())) {
318 x509.setTrustCertificates(getTrustCertificates());
319 }
320 SslConfig sc = new SslConfig();
321 sc.setCredentialConfig(x509);
322 sc.setHostnameVerifier(getHostnameVerifier().get());
323 return sc;
324 }
325 return null;
326 }
327
328
329
330
331 public enum HostnameVerifier implements Supplier<CertificateHostnameVerifier> {
332
333
334
335
336 DEFAULT(new DefaultHostnameVerifier()),
337
338
339
340
341 ALLOW_ANY(new AllowAnyHostnameVerifier());
342
343 private final CertificateHostnameVerifier verifier;
344
345 HostnameVerifier(CertificateHostnameVerifier verifier) {
346 this.verifier = verifier;
347 }
348
349 @Override
350 public CertificateHostnameVerifier get() {
351 return verifier;
352 }
353 }
354 }
355
356
357
358
359 public enum ConnectionStrategy implements Supplier<org.ldaptive.ConnectionStrategy> {
360
361
362
363
364
365 ACTIVE_PASSIVE(new ActivePassiveConnectionStrategy()),
366
367
368
369
370 RANDOM(new RandomConnectionStrategy()),
371
372
373
374
375
376 ROUND_ROBIN(new RoundRobinConnectionStrategy()),
377
378
379
380
381
382
383 DNS(new DnsSrvConnectionStrategy());
384
385 private final org.ldaptive.ConnectionStrategy strategy;
386
387 ConnectionStrategy(org.ldaptive.ConnectionStrategy strategy) {
388 this.strategy = strategy;
389 }
390
391 @Override
392 public org.ldaptive.ConnectionStrategy get() {
393 return strategy;
394 }
395 }
396
397
398
399
400 @Data
401 public static class ConnectionValidatorProperties {
402
403
404
405
406 private Duration validatePeriod = Duration.ofMinutes(30);
407
408
409
410
411 private Duration validateTimeout = Duration.ofSeconds(5);
412
413
414
415
416 private SearchRequestProperties searchRequest = new SearchRequestProperties();
417
418
419
420
421 public ConnectionValidatorProperties() {
422 super();
423 }
424
425
426
427
428
429
430 public ConnectionValidator createConnectionValidator() {
431 if (hasText(getSearchRequest().getBaseDn())) {
432 return new SearchConnectionValidator(
433 validatePeriod,
434 validateTimeout,
435 getSearchRequest().createSearchRequest());
436 }
437 return null;
438 }
439
440
441
442
443 @Data
444 public static class SearchRequestProperties {
445
446
447
448
449 private String baseDn;
450
451
452
453
454 private SearchFilterProperties searchFilter = new SearchFilterProperties();
455
456
457
458
459 private Integer sizeLimit;
460
461
462
463
464 private SearchScope searchScope;
465
466
467
468
469 private List<String> returnAttributes = new ArrayList<>();
470
471
472
473
474 public SearchRequestProperties() {
475 super();
476 }
477
478
479
480
481
482
483 public String[] returnAttributesAsArray() {
484 if (returnAttributes.isEmpty()) {
485 return ReturnAttributes.NONE.value();
486 }
487 return returnAttributes.toArray(new String[0]);
488 }
489
490
491
492
493
494
495 public SearchRequest createSearchRequest() {
496 String nonNullBaseDn = Objects.requireNonNullElse(getBaseDn(), "");
497 SearchRequest searchRequest;
498 if (Objects.nonNull(getSearchFilter()) && hasText(getSearchFilter().getFilter())) {
499 searchRequest = new SearchRequest(nonNullBaseDn);
500 searchRequest.setFilter(getSearchFilter().getFilter());
501 searchRequest.setReturnAttributes(returnAttributesAsArray());
502 if (getSearchScope() != null) {
503 searchRequest.setSearchScope(getSearchScope());
504 }
505 if (getSizeLimit() != null) {
506 searchRequest.setSizeLimit(getSizeLimit());
507 }
508 } else {
509 searchRequest = SearchRequest
510 .objectScopeSearchRequest(nonNullBaseDn, returnAttributesAsArray());
511 }
512 return searchRequest;
513 }
514
515
516
517
518 @Data
519 public static class SearchFilterProperties {
520
521
522
523
524 private String filter;
525
526
527
528
529 public SearchFilterProperties() {
530 super();
531 }
532 }
533 }
534 }
535
536
537
538
539 @Data
540 public static class ConnectionPoolProperties {
541
542
543
544
545 private Duration blockWaitTime = Duration.ofMinutes(1);
546
547
548
549
550 private int minPoolSize = 3;
551
552
553
554
555 private int maxPoolSize = 10;
556
557
558
559
560 private boolean connectOnCreate = true;
561
562
563
564
565 private boolean failFastInitialize = true;
566
567
568
569
570 private boolean validateOnCheckIn = false;
571
572
573
574
575 private boolean validateOnCheckOut = false;
576
577
578
579
580 private boolean validatePeriodically = false;
581
582
583
584
585 private ConnectionValidatorProperties validator = new ConnectionValidatorProperties();
586
587
588
589
590 private Duration prunePeriod = Duration.ofMinutes(5);
591
592
593
594
595 private Duration idleTime = Duration.ofMinutes(10);
596
597
598
599
600 public ConnectionPoolProperties() {
601 super();
602 }
603
604 }
605
606
607
608
609
610
611
612 protected static boolean hasText(String value) {
613 return Objects.nonNull(value) && !value.isBlank();
614 }
615 }