1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.shibboleth.common.config.attribute.resolver.dataConnector;
18
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22
23 import javax.xml.namespace.QName;
24
25 import org.opensaml.xml.util.DatatypeHelper;
26 import org.opensaml.xml.util.XMLHelper;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29 import org.springframework.beans.factory.config.RuntimeBeanReference;
30 import org.springframework.beans.factory.support.BeanDefinitionBuilder;
31 import org.springframework.beans.factory.xml.ParserContext;
32 import org.w3c.dom.Element;
33
34 import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector.LdapPoolEmptyStrategy;
35 import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector.LdapPoolVTStrategy;
36 import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector.LdapDataConnector.AUTHENTICATION_TYPE;
37 import edu.internet2.middleware.shibboleth.common.config.SpringConfigurationUtils;
38 import edu.vt.middleware.ldap.SearchFilter;
39 import edu.vt.middleware.ldap.LdapConfig.SearchScope;
40 import edu.vt.middleware.ldap.handler.ConnectionHandler.ConnectionStrategy;
41 import edu.vt.middleware.ldap.pool.CompareLdapValidator;
42 import edu.vt.middleware.ldap.pool.LdapPoolConfig;
43 import edu.vt.middleware.ldap.pool.LdapValidator;
44
45
46 public class LdapDataConnectorBeanDefinitionParser extends BaseDataConnectorBeanDefinitionParser {
47
48
49 public static final QName TYPE_NAME = new QName(DataConnectorNamespaceHandler.NAMESPACE, "LDAPDirectory");
50
51
52 private final Logger log = LoggerFactory.getLogger(LdapDataConnectorBeanDefinitionParser.class);
53
54
55 protected Class<?> getBeanClass(Element element) {
56 return LdapDataConnectorFactoryBean.class;
57 }
58
59
60 protected void doParse(String pluginId, Element pluginConfig, Map<QName, List<Element>> pluginConfigChildren,
61 BeanDefinitionBuilder pluginBuilder, ParserContext parserContext) {
62 super.doParse(pluginId, pluginConfig, pluginConfigChildren, pluginBuilder, parserContext);
63
64 processBasicConnectionConfig(pluginId, pluginConfig, pluginConfigChildren, pluginBuilder, parserContext);
65 processSecurityConfig(pluginId, pluginConfig, pluginConfigChildren, pluginBuilder, parserContext);
66 processResultHandlingConfig(pluginId, pluginConfig, pluginConfigChildren, pluginBuilder, parserContext);
67
68 Map<String, String> ldapProperties = processLDAPProperties(pluginConfigChildren.get(new QName(
69 DataConnectorNamespaceHandler.NAMESPACE, "LDAPProperty")));
70 if (ldapProperties != null) {
71 log.debug("Data connector {} LDAP properties: {}", pluginId, ldapProperties);
72 pluginBuilder.addPropertyValue("ldapProperties", ldapProperties);
73 }
74
75 processPoolingConfig(pluginId, pluginConfig, pluginConfigChildren, pluginBuilder, parserContext);
76
77 processCacheConfig(pluginId, pluginConfig, pluginBuilder);
78 }
79
80
81
82
83
84
85
86
87
88
89 protected void processBasicConnectionConfig(String pluginId, Element pluginConfig,
90 Map<QName, List<Element>> pluginConfigChildren, BeanDefinitionBuilder pluginBuilder,
91 ParserContext parserContext) {
92
93 String ldapURL = pluginConfig.getAttributeNS(null, "ldapURL");
94 log.debug("Data connector {} LDAP URL: {}", pluginId, ldapURL);
95 pluginBuilder.addPropertyValue("ldapUrl", ldapURL);
96
97 ConnectionStrategy connStrategy = ConnectionStrategy.ACTIVE_PASSIVE;
98 if (pluginConfig.hasAttributeNS(null, "connectionStrategy")) {
99 connStrategy = ConnectionStrategy.valueOf(pluginConfig.getAttributeNS(null, "connectionStrategy"));
100 }
101 log.debug("Data connector {} connection strategy: {}", pluginId, connStrategy);
102 pluginBuilder.addPropertyValue("connectionStrategy", connStrategy);
103
104 if (pluginConfig.hasAttributeNS(null, "baseDN")) {
105 String baseDN = pluginConfig.getAttributeNS(null, "baseDN");
106 log.debug("Data connector {} base DN: {}", pluginId, baseDN);
107 pluginBuilder.addPropertyValue("baseDN", baseDN);
108 }
109
110 AUTHENTICATION_TYPE authnType = AUTHENTICATION_TYPE.SIMPLE;
111 if (pluginConfig.hasAttributeNS(null, "authenticationType")) {
112 authnType = AUTHENTICATION_TYPE.valueOf(pluginConfig.getAttributeNS(null, "authenticationType"));
113 }
114 log.debug("Data connector {} authentication type: {}", pluginId, authnType);
115 pluginBuilder.addPropertyValue("authenticationType", authnType);
116
117 String principal = pluginConfig.getAttributeNS(null, "principal");
118 log.debug("Data connector {} principal: {}", pluginId, principal);
119 pluginBuilder.addPropertyValue("principal", principal);
120
121 String credential = pluginConfig.getAttributeNS(null, "principalCredential");
122 pluginBuilder.addPropertyValue("principalCredential", credential);
123
124 String templateEngineRef = pluginConfig.getAttributeNS(null, "templateEngine");
125 pluginBuilder.addPropertyReference("templateEngine", templateEngineRef);
126
127 String filterTemplate = pluginConfigChildren.get(
128 new QName(DataConnectorNamespaceHandler.NAMESPACE, "FilterTemplate")).get(0).getTextContent();
129 filterTemplate = DatatypeHelper.safeTrimOrNullString(filterTemplate);
130 log.debug("Data connector {} LDAP filter template: {}", pluginId, filterTemplate);
131 pluginBuilder.addPropertyValue("filterTemplate", filterTemplate);
132
133 SearchScope searchScope = SearchScope.SUBTREE;
134 if (pluginConfig.hasAttributeNS(null, "searchScope")) {
135 searchScope = SearchScope.valueOf(pluginConfig.getAttributeNS(null, "searchScope"));
136 }
137 log.debug("Data connector {} search scope: {}", pluginId, searchScope);
138 pluginBuilder.addPropertyValue("searchScope", searchScope);
139
140 QName returnAttributesName = new QName(DataConnectorNamespaceHandler.NAMESPACE, "ReturnAttributes");
141 if (pluginConfigChildren.containsKey(returnAttributesName)) {
142 List<String> returnAttributes = XMLHelper.getElementContentAsList(pluginConfigChildren.get(
143 returnAttributesName).get(0));
144 log.debug("Data connector {} return attributes: {}", pluginId, returnAttributes);
145 pluginBuilder.addPropertyValue("returnAttributes", returnAttributes);
146 }
147 }
148
149
150
151
152
153
154
155
156
157
158 protected void processSecurityConfig(String pluginId, Element pluginConfig,
159 Map<QName, List<Element>> pluginConfigChildren, BeanDefinitionBuilder pluginBuilder,
160 ParserContext parserContext) {
161 RuntimeBeanReference trustCredential = processCredential(pluginConfigChildren.get(new QName(
162 DataConnectorNamespaceHandler.NAMESPACE, "StartTLSTrustCredential")), parserContext);
163 if (trustCredential != null) {
164 log.debug("Data connector {} using provided SSL/TLS trust material", pluginId);
165 pluginBuilder.addPropertyValue("trustCredential", trustCredential);
166 }
167
168 RuntimeBeanReference connectionCredential = processCredential(pluginConfigChildren.get(new QName(
169 DataConnectorNamespaceHandler.NAMESPACE, "StartTLSAuthenticationCredential")), parserContext);
170 if (connectionCredential != null) {
171 log.debug("Data connector {} using provided SSL/TLS client authentication material", pluginId);
172 pluginBuilder.addPropertyValue("connectionCredential", connectionCredential);
173 }
174
175 boolean useStartTLS = false;
176 if (pluginConfig.hasAttributeNS(null, "useStartTLS")) {
177 useStartTLS = XMLHelper.getAttributeValueAsBoolean(pluginConfig.getAttributeNodeNS(null, "useStartTLS"));
178 }
179 log.debug("Data connector {} use startTLS: {}", pluginId, useStartTLS);
180 pluginBuilder.addPropertyValue("useStartTLS", useStartTLS);
181 }
182
183
184
185
186
187
188
189
190
191
192 protected void processResultHandlingConfig(String pluginId, Element pluginConfig,
193 Map<QName, List<Element>> pluginConfigChildren, BeanDefinitionBuilder pluginBuilder,
194 ParserContext parserContext) {
195 int searchTimeLimit = 3000;
196 if (pluginConfig.hasAttributeNS(null, "searchTimeLimit")) {
197 searchTimeLimit = (int) SpringConfigurationUtils.parseDurationToMillis(
198 "'searchTimeLimit' on data connector " + pluginId, pluginConfig.getAttributeNS(null,
199 "searchTimeLimit"), 0);
200 }
201 log.debug("Data connector {} search timeout: {}ms", pluginId, searchTimeLimit);
202 pluginBuilder.addPropertyValue("searchTimeLimit", searchTimeLimit);
203
204 int maxResultSize = 1;
205 if (pluginConfig.hasAttributeNS(null, "maxResultSize")) {
206 maxResultSize = Integer.parseInt(pluginConfig.getAttributeNS(null, "maxResultSize"));
207 }
208 log.debug("Data connector {} max search result size: {}", pluginId, maxResultSize);
209 pluginBuilder.addPropertyValue("maxResultSize", maxResultSize);
210
211 boolean mergeResults = false;
212 if (pluginConfig.hasAttributeNS(null, "mergeResults")) {
213 mergeResults = XMLHelper.getAttributeValueAsBoolean(pluginConfig.getAttributeNodeNS(null, "mergeResults"));
214 }
215 log.debug("Data connector {} merge results: {}", pluginId, mergeResults);
216 pluginBuilder.addPropertyValue("mergeResults", mergeResults);
217
218 boolean noResultsIsError = false;
219 if (pluginConfig.hasAttributeNS(null, "noResultIsError")) {
220 noResultsIsError = XMLHelper.getAttributeValueAsBoolean(pluginConfig.getAttributeNodeNS(null,
221 "noResultIsError"));
222 }
223 log.debug("Data connector {} no results is error: {}", pluginId, noResultsIsError);
224 pluginBuilder.addPropertyValue("noResultsIsError", noResultsIsError);
225
226 boolean lowercaseAttributeNames = false;
227 if (pluginConfig.hasAttributeNS(null, "lowercaseAttributeNames")) {
228 lowercaseAttributeNames = XMLHelper.getAttributeValueAsBoolean(pluginConfig.getAttributeNodeNS(null,
229 "lowercaseAttributeNames"));
230 }
231 log.debug("Data connector {} will lower case attribute IDs: {}", pluginId, lowercaseAttributeNames);
232 pluginBuilder.addPropertyValue("lowercaseAttributeNames", lowercaseAttributeNames);
233 }
234
235
236
237
238
239
240
241
242
243
244 protected void processPoolingConfig(String pluginId, Element pluginConfig,
245 Map<QName, List<Element>> pluginConfigChildren, BeanDefinitionBuilder pluginBuilder,
246 ParserContext parserContext) {
247
248 List<Element> poolConfigElems = pluginConfigChildren.get(new QName(DataConnectorNamespaceHandler.NAMESPACE,
249 "ConnectionPool"));
250 if (poolConfigElems == null || poolConfigElems.size() == 0) {
251 log.debug("Data connector {} is pooling connections: {}", pluginId, false);
252 pluginBuilder.addPropertyValue("poolStrategy", new LdapPoolEmptyStrategy());
253 return;
254 }
255
256 Element poolConfigElem = poolConfigElems.get(0);
257
258 LdapPoolConfig ldapPoolConfig = new LdapPoolConfig();
259 LdapPoolVTStrategy ldapPoolStrategy = new LdapPoolVTStrategy();
260 ldapPoolStrategy.setLdapPoolConfig(ldapPoolConfig);
261 log.debug("Data connector {} is pooling connections: {}", pluginId, true);
262 pluginBuilder.addPropertyValue("poolStrategy", ldapPoolStrategy);
263
264 int poolMinSize = 0;
265 if (pluginConfig.hasAttributeNS(null, "poolInitialSize")) {
266 poolMinSize = Integer.parseInt(pluginConfig.getAttributeNS(null, "poolInitialSize"));
267 log
268 .warn("Data connector {} using deprecated attribute poolInitialSize on <DataConnector> use minPoolSize on child <PoolConfig> instead");
269 } else if (poolConfigElem != null && poolConfigElem.hasAttributeNS(null, "minPoolSize")) {
270 poolMinSize = Integer.parseInt(poolConfigElem.getAttributeNS(null, "minPoolSize"));
271 }
272 log.debug("Data connector {} pool minimum connections: {}", pluginId, poolMinSize);
273 ldapPoolConfig.setMinPoolSize(poolMinSize);
274
275 int poolMaxSize = 3;
276 if (pluginConfig.hasAttributeNS(null, "poolMaxIdleSize")) {
277 poolMaxSize = Integer.parseInt(pluginConfig.getAttributeNS(null, "poolMaxIdleSize"));
278 log
279 .warn("Data connector {} using deprecated attribute poolMaxIdleSize on <DataConnector> use maxPoolSize on child <PoolConfig> instead");
280 } else if (poolConfigElem != null && poolConfigElem.hasAttributeNS(null, "maxPoolSize")) {
281 poolMaxSize = Integer.parseInt(poolConfigElem.getAttributeNS(null, "maxPoolSize"));
282 }
283 log.debug("Data connector {} pool maximum connections: {}", pluginId, poolMaxSize);
284 ldapPoolConfig.setMaxPoolSize(poolMaxSize);
285
286 boolean blockWhenEmpty = true;
287 if (poolConfigElem != null && poolConfigElem.hasAttributeNS(null, "blockWhenEmpty")) {
288 blockWhenEmpty = XMLHelper.getAttributeValueAsBoolean(poolConfigElem.getAttributeNodeNS(null,
289 "blockWhenEmpty"));
290 }
291 log.debug("Data connector {} pool block when empty: {}", pluginId, blockWhenEmpty);
292 ldapPoolStrategy.setBlockWhenEmpty(blockWhenEmpty);
293
294 boolean poolValidatePeriodically = false;
295 if (poolConfigElem != null && poolConfigElem.hasAttributeNS(null, "validatePeriodically")) {
296 poolValidatePeriodically = XMLHelper.getAttributeValueAsBoolean(poolConfigElem.getAttributeNodeNS(null,
297 "validatePeriodically"));
298 }
299 log.debug("Data connector {} pool validate periodically: {}", pluginId, poolValidatePeriodically);
300 ldapPoolConfig.setValidatePeriodically(poolValidatePeriodically);
301
302 int poolValidateTimerPeriod = 1800000;
303 if (poolConfigElem != null && poolConfigElem.hasAttributeNS(null, "validateTimerPeriod")) {
304 poolValidateTimerPeriod = (int) SpringConfigurationUtils.parseDurationToMillis("validateTimerPeriod",
305 poolConfigElem.getAttributeNS(null, "validateTimerPeriod"), 0);
306 }
307 log.debug("Data connector {} pool validate timer period: {}ms", pluginId, poolValidateTimerPeriod);
308 ldapPoolConfig.setValidateTimerPeriod(poolValidateTimerPeriod);
309
310 String poolValidateDn = "";
311 if (poolConfigElem != null && poolConfigElem.hasAttributeNS(null, "validateDN")) {
312 poolValidateDn = poolConfigElem.getAttributeNS(null, "validateDN");
313 }
314 String poolValidateFilter = "(objectClass=*)";
315 if (poolConfigElem != null && poolConfigElem.hasAttributeNS(null, "validateFilter")) {
316 poolValidateFilter = poolConfigElem.getAttributeNS(null, "validateFilter");
317 }
318 LdapValidator poolValidator = new CompareLdapValidator(poolValidateDn, new SearchFilter(poolValidateFilter));
319 log.debug("Data connector {} pool validation filter: {}", pluginId, poolValidateFilter);
320 pluginBuilder.addPropertyValue("poolValidator", poolValidator);
321
322 int poolExpirationTime = 600000;
323 if (poolConfigElem != null && poolConfigElem.hasAttributeNS(null, "expirationTime")) {
324 poolExpirationTime = (int) SpringConfigurationUtils.parseDurationToMillis("expirationTime", poolConfigElem
325 .getAttributeNS(null, "expirationTime"), 0);
326 }
327 log.debug("Data connector {} pool expiration time: {}ms", pluginId, poolExpirationTime);
328 ldapPoolConfig.setExpirationTime(poolExpirationTime);
329 }
330
331
332
333
334
335
336
337
338 protected void processCacheConfig(String pluginId, Element pluginConfig, BeanDefinitionBuilder pluginBuilder) {
339 boolean cacheResults = false;
340 String cacheManagerId = "shibboleth.CacheManager";
341 long cacheElementTtl = 4 * 60 * 60 * 1000;
342 int maximumCachedElements = 500;
343
344 List<Element> cacheConfigs = XMLHelper.getChildElementsByTagNameNS(pluginConfig,
345 DataConnectorNamespaceHandler.NAMESPACE, "ResultCache");
346 if (cacheConfigs != null && !cacheConfigs.isEmpty()) {
347 Element cacheConfig = cacheConfigs.get(0);
348
349 cacheResults = true;
350
351 if (cacheConfig.hasAttributeNS(null, "cacheManagerRef")) {
352 cacheManagerId = DatatypeHelper.safeTrim(cacheConfig.getAttributeNS(null, "cacheManagerRef"));
353 }
354
355 if (cacheConfig.hasAttributeNS(null, "elementTimeToLive")) {
356 cacheElementTtl = SpringConfigurationUtils.parseDurationToMillis("elementTimeToLive on data connector "
357 + pluginId, cacheConfig.getAttributeNS(null, "elementTimeToLive"), 0);
358 }
359
360 if (cacheConfig.hasAttributeNS(null, "maximumCachedElements")) {
361 maximumCachedElements = Integer.parseInt(DatatypeHelper.safeTrim(cacheConfig.getAttributeNS(null,
362 "maximumCachedElements")));
363 }
364 }
365
366 if (pluginConfig.hasAttributeNS(null, "cacheResults")) {
367 log.warn("Data connection {}: use of 'cacheResults' attribute is deprecated. Use <ResultCache> instead.",
368 pluginId);
369 cacheResults = XMLHelper.getAttributeValueAsBoolean(pluginConfig.getAttributeNodeNS(null, "cacheResults"));
370 }
371
372 if (cacheResults) {
373 log.debug("Data connector {} is caching results: {}", pluginId, cacheResults);
374
375 pluginBuilder.addPropertyReference("cacheManager", cacheManagerId);
376
377 log.debug("Data connector {} cache element time to live: {}ms", pluginId, cacheElementTtl);
378 pluginBuilder.addPropertyValue("cacheElementTimeToLive", cacheElementTtl);
379
380 log.debug("Data connector {} maximum number of caches elements: {}", pluginId, maximumCachedElements);
381 pluginBuilder.addPropertyValue("maximumCachedElements", maximumCachedElements);
382 }
383
384 }
385
386
387
388
389
390
391
392
393 protected Map<String, String> processLDAPProperties(List<Element> propertyElems) {
394 if (propertyElems == null || propertyElems.size() == 0) {
395 return null;
396 }
397
398 HashMap<String, String> properties = new HashMap<String, String>(5);
399
400 String propName;
401 String propValue;
402 for (Element propertyElem : propertyElems) {
403 propName = DatatypeHelper.safeTrimOrNullString(propertyElem.getAttributeNS(null, "name"));
404 propValue = DatatypeHelper.safeTrimOrNullString(propertyElem.getAttributeNS(null, "value"));
405 properties.put(propName, propValue);
406 }
407
408 return properties;
409 }
410
411
412
413
414
415
416
417
418
419 protected RuntimeBeanReference processCredential(List<Element> credentials, ParserContext parserContext) {
420 if (credentials == null) {
421 return null;
422 }
423
424 Element credentialElem = credentials.get(0);
425 return SpringConfigurationUtils.parseCustomElement(credentialElem, parserContext);
426 }
427 }