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