View Javadoc

1   /*
2    * Copyright 2007 University Corporation for Advanced Internet Development, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package edu.internet2.middleware.shibboleth.common.config.attribute.resolver.dataConnector;
18  
19  import java.io.IOException;
20  import java.security.GeneralSecurityException;
21  import java.security.KeyStore;
22  import java.security.cert.X509Certificate;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Map;
26  
27  import javax.net.ssl.KeyManager;
28  import javax.net.ssl.KeyManagerFactory;
29  import javax.net.ssl.SSLContext;
30  import javax.net.ssl.TrustManager;
31  import javax.net.ssl.TrustManagerFactory;
32  
33  import net.sf.ehcache.Cache;
34  import net.sf.ehcache.CacheManager;
35  
36  import org.opensaml.xml.security.x509.X509Credential;
37  import org.opensaml.xml.util.DatatypeHelper;
38  
39  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector.LdapDataConnector;
40  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector.LdapPoolStrategy;
41  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector.TemplateEngine;
42  import edu.internet2.middleware.shibboleth.common.attribute.resolver.provider.dataConnector.LdapDataConnector.AUTHENTICATION_TYPE;
43  import edu.vt.middleware.ldap.LdapConfig;
44  import edu.vt.middleware.ldap.LdapConfig.SearchScope;
45  import edu.vt.middleware.ldap.handler.BinarySearchResultHandler;
46  import edu.vt.middleware.ldap.handler.CaseChangeSearchResultHandler;
47  import edu.vt.middleware.ldap.handler.CaseChangeSearchResultHandler.CaseChange;
48  import edu.vt.middleware.ldap.handler.ConnectionHandler.ConnectionStrategy;
49  import edu.vt.middleware.ldap.handler.EntryDnSearchResultHandler;
50  import edu.vt.middleware.ldap.handler.FqdnSearchResultHandler;
51  import edu.vt.middleware.ldap.handler.MergeSearchResultHandler;
52  import edu.vt.middleware.ldap.handler.SearchResultHandler;
53  import edu.vt.middleware.ldap.pool.DefaultLdapFactory;
54  import edu.vt.middleware.ldap.pool.LdapValidator;
55  
56  /**
57   * Spring factory for creating {@link LdapDataConnector} beans.
58   */
59  public class LdapDataConnectorFactoryBean extends BaseDataConnectorFactoryBean {
60  
61      /** Ldap pool strategy. */
62      private LdapPoolStrategy ldapPoolStrategy;
63  
64      /** Ldap configuration. */
65      private LdapConfig ldapConfig = new LdapConfig();
66  
67      /** Ldap connection strategy. */
68      private ConnectionStrategy connStrategy;
69  
70      /** Ldap connection provider specific properties. */
71      private Map<String, String> ldapProperties;
72  
73      /** Connection validator that performs compares. */
74      private LdapValidator ldapValidator;
75  
76      /** Template engine used to construct filter queries. */
77      private TemplateEngine templateEngine;
78  
79      /** LDAP query filter template. */
80      private String filterTemplate;
81  
82      /** Name of the LDAP attributes to return. */
83      private List<String> returnAttributes;
84  
85      /** Trust material used when connecting to the LDAP over SSL/TLS. */
86      private X509Credential trustCredential;
87  
88      /** Client authentication material used when connecting to the LDAP over SSL/TLS. */
89      private X509Credential connectionCredential;
90  
91      /** Whether to merge multiple results into a single set of attributes. */
92      private boolean mergeResults;
93  
94      /** Whether a search returning no results should be considered an error. */
95      private boolean noResultsIsError;
96      
97      /** Whether LDAP attribute names used as Shibboleth attribute IDs will be lowercased. */
98      private boolean lowercaseAttributeNames;
99  
100     /** Whether results should be cached. */
101     private CacheManager cacheManager;
102 
103     /** Maximum number of queries to keep in the cache. */
104     private int maximumCachedElements;
105 
106     /** Length of time, in milliseconds, elements are cached. */
107     private long cacheElementTtl;
108 
109     /** {@inheritDoc} */
110     protected Object createInstance() throws Exception {
111         List<SearchResultHandler> resultHandlers = new ArrayList<SearchResultHandler>();
112         resultHandlers.add(new FqdnSearchResultHandler());
113         resultHandlers.add(new EntryDnSearchResultHandler());
114         if (mergeResults) {
115             resultHandlers.add(new MergeSearchResultHandler());
116         }
117         if (lowercaseAttributeNames) {
118             final CaseChangeSearchResultHandler srh = new CaseChangeSearchResultHandler();
119             srh.setAttributeNameCaseChange(CaseChange.LOWER);
120             resultHandlers.add(srh);
121         }
122         resultHandlers.add(new BinarySearchResultHandler());
123         ldapConfig.setSearchResultHandlers(resultHandlers.toArray(new SearchResultHandler[resultHandlers.size()]));
124         ldapConfig.getConnectionHandler().setConnectionStrategy(connStrategy);
125 
126         // set extra properties on the ldap config
127         if (ldapProperties != null) {
128             for (Map.Entry<String, String> entry : ldapProperties.entrySet()) {
129                 ldapConfig.setEnvironmentProperties(entry.getKey(), entry.getValue());
130             }
131         }
132 
133         SSLContext ctx = createSSLContext();
134         if (ctx != null) {
135             ldapConfig.setSslSocketFactory(ctx.getSocketFactory());
136         }
137 
138         Cache resultsCache = null;
139         if (cacheManager != null) {
140             resultsCache = cacheManager.getCache(getPluginId());
141             if (resultsCache == null) {
142                 long ttlInSeconds = cacheElementTtl / 1000;
143                 resultsCache = new Cache(
144                     getPluginId(), maximumCachedElements, false, false, ttlInSeconds, ttlInSeconds);
145                 cacheManager.addCache(resultsCache);
146             }
147         }
148 
149         // initialize the pool
150         DefaultLdapFactory ldapFactory = new DefaultLdapFactory(ldapConfig);
151         if (ldapValidator != null) {
152             ldapFactory.setLdapValidator(ldapValidator);
153         }
154         ldapPoolStrategy.setLdapFactory(ldapFactory);
155         ldapPoolStrategy.initialize();
156         LdapDataConnector connector = new LdapDataConnector(ldapPoolStrategy, resultsCache);
157         populateDataConnector(connector);
158         connector.setNoResultsIsError(noResultsIsError);
159         if (returnAttributes != null) {
160             connector.setReturnAttributes(returnAttributes.toArray(new String[returnAttributes.size()]));
161         }
162         connector.registerTemplate(templateEngine, filterTemplate);
163 
164         return connector;
165     }
166 
167     /**
168      * Creates an SSLContext if either trust or key material was set.
169      * 
170      * @return the created SSL context or null if no trust or key material was provided
171      * 
172      * @throws Exception thrown if the SSLContext can not be created and initialized
173      */
174     protected SSLContext createSSLContext() throws Exception {
175         // setup trust and key managers
176         TrustManager[] sslTrustManagers = null;
177         if (trustCredential != null) {
178             try {
179                 TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
180                 KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
181                 keystore.load(null, null);
182                 for (X509Certificate c : trustCredential.getEntityCertificateChain()) {
183                     keystore.setCertificateEntry("ldap_tls_trust_" + c.getSerialNumber(), c);
184                 }
185                 tmf.init(keystore);
186                 sslTrustManagers = tmf.getTrustManagers();
187             } catch (GeneralSecurityException e) {
188                 logger.error("Error initializing trust managers", e);
189             } catch (IOException e) {
190                 logger.error("Error initializing trust managers", e);
191             }
192         }
193 
194         KeyManager[] sslKeyManagers = null;
195         if (connectionCredential != null) {
196             try {
197                 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
198                 KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
199                 keystore.load(null, null);
200                 keystore.setKeyEntry("ldap_tls_client_auth", connectionCredential.getPrivateKey(), "changeit"
201                         .toCharArray(), connectionCredential.getEntityCertificateChain()
202                         .toArray(new X509Certificate[0]));
203                 kmf.init(keystore, "changeit".toCharArray());
204                 sslKeyManagers = kmf.getKeyManagers();
205             } catch (GeneralSecurityException e) {
206                 logger.error("Error initializing key managers", e);
207             } catch (IOException e) {
208                 logger.error("Error initializing key managers", e);
209             }
210         }
211 
212         SSLContext ctx = null;
213         if (sslTrustManagers != null || sslKeyManagers != null) {
214             ctx = SSLContext.getInstance("TLS");
215             ctx.init(sslKeyManagers, sslTrustManagers, null);
216         }
217         return ctx;
218     }
219 
220     /**
221      * Gets the authentication type used when connecting to the directory.
222      * 
223      * @return authentication type used when connecting to the directory
224      */
225     public AUTHENTICATION_TYPE getAuthenticationType() {
226         return AUTHENTICATION_TYPE.getAuthenticationTypeByName(ldapConfig.getAuthtype());
227     }
228 
229     /**
230      * Gets the base search DN.
231      * 
232      * @return the base search DN
233      */
234     public String getBaseDN() {
235         return this.ldapConfig.getBaseDn();
236     }
237 
238     /**
239      * Gets the time to live, in milliseconds, for cache elements.
240      * 
241      * @return time to live, in milliseconds, for cache elements
242      */
243     public long getCacheElementTimeToLive() {
244         return cacheElementTtl;
245     }
246 
247     /**
248      * Gets the manager for the results cache.
249      * 
250      * @return manager for the results cache
251      */
252     public CacheManager getCacheManager() {
253         return cacheManager;
254     }
255 
256     /**
257      * Gets the client authentication material used when connecting to the LDAP via SSL or TLS.
258      * 
259      * @return client authentication material used when connecting to the LDAP via SSL or TLS
260      */
261     public X509Credential getConnectionCredential() {
262         return connectionCredential;
263     }
264 
265     /**
266      * Gets the LDAP query filter template.
267      * 
268      * @return LDAP query filter template
269      */
270     public String getFilterTemplate() {
271         return filterTemplate;
272     }
273 
274     /**
275      * Gets the LDAP connection provider specific properties.
276      * 
277      * @return LDAP connection provider specific properties
278      */
279     public Map<String, String> getLdapProperties() {
280         return ldapProperties;
281     }
282 
283     /**
284      * Gets the LDAP server's URL.
285      * 
286      * @return LDAP server's URL
287      */
288     public String getLdapUrl() {
289         return ldapConfig.getLdapUrl();
290     }
291 
292     /**
293      * Gets the LDAP connection strategy.
294      * 
295      * @return connection strategy
296      */
297     public ConnectionStrategy getConnectionStrategy() {
298         return connStrategy;
299     }
300 
301     /**
302      * Gets the maximum number of elements that will be cached.
303      * 
304      * @return maximum number of elements that will be cached
305      */
306     public int getMaximumCachedElements() {
307         return maximumCachedElements;
308     }
309 
310     /**
311      * Gets the maximum number of results to return from a query.
312      * 
313      * @return maximum number of results to return from a query
314      */
315     public int getMaxResultSize() {
316         return (int) ldapConfig.getCountLimit();
317     }
318 
319     /** {@inheritDoc} */
320     public Class<?> getObjectType() {
321         return LdapDataConnector.class;
322     }
323 
324     /**
325      * Gets the ldap pool strategy.
326      * 
327      * @return ldap pool strategy
328      */
329     public LdapPoolStrategy getPoolStrategy() {
330         return ldapPoolStrategy;
331     }
332 
333     /**
334      * Gets the pool validator.
335      * 
336      * @return pool validator
337      */
338     public LdapValidator getPoolValidator() {
339         return ldapValidator;
340     }
341 
342     /**
343      * Gets the principal DN used to bind to the directory.
344      * 
345      * @return principal DN used to bind to the directory
346      */
347     public String getPrincipal() {
348         return ldapConfig.getBindDn();
349     }
350 
351     /**
352      * Gets the credential of the principal DN used to bind to the directory.
353      * 
354      * @return credential of the principal DN used to bind to the directory
355      */
356     public String getPrincipalCredential() {
357         return (String) ldapConfig.getBindCredential();
358     }
359 
360     /**
361      * Gets the attributes to return from a query.
362      * 
363      * @return attributes to return from a query
364      */
365     public List<String> getReturnAttributes() {
366         return returnAttributes;
367     }
368 
369     /**
370      * Gets the search scope of a query.
371      * 
372      * @return search scope of a query
373      */
374     public SearchScope getSearchScope() {
375         return ldapConfig.getSearchScope();
376     }
377 
378     /**
379      * Gets the maximum amount of time, in milliseconds, to wait for a search to complete.
380      * 
381      * @return maximum amount of time, in milliseconds, to wait for a search to complete
382      */
383     public int getSearchTimeLimit() {
384         return ldapConfig.getTimeLimit();
385     }
386 
387     /**
388      * Gets the template engine used to construct query filters.
389      * 
390      * @return template engine used to construct query filters
391      */
392     public TemplateEngine getTemplateEngine() {
393         return templateEngine;
394     }
395 
396     /**
397      * Gets the trust material used when connecting to the LDAP via SSL or TLS.
398      * 
399      * @return trust material used when connecting to the LDAP via SSL or TLS
400      */
401     public X509Credential getTrustCredential() {
402         return trustCredential;
403     }
404 
405     /**
406      * Gets whether to use StartTLS when connecting to the LDAP.
407      * 
408      * @return whether to use StartTLS when connecting to the LDAP
409      */
410     public boolean getUseStartTLS() {
411         return ldapConfig.isTlsEnabled();
412     }
413 
414     /**
415      * Gets whether LDAP attribute names used as Shibboleth attribute IDs will be lowercased.
416      * 
417      * @return whether LDAP attribute names used as Shibboleth attribute IDs will be lowercased
418      */
419     public boolean isLowercaseAttributeNames() {
420         return lowercaseAttributeNames;
421     }
422 
423     /**
424      * Gets whether to merge multiple results into a single result.
425      * 
426      * @return whether to merge multiple results into a single result
427      */
428     public boolean isMergeResults() {
429         return mergeResults;
430     }
431 
432     /**
433      * Gets whether a query that returns no results is an error condition.
434      * 
435      * @return whether a query that returns no results is an error condition
436      */
437     public boolean isNoResultsIsError() {
438         return noResultsIsError;
439     }
440 
441     /**
442      * Sets the authentication type used when connecting to the directory.
443      * 
444      * @param type authentication type used when connecting to the directory
445      */
446     public void setAuthenticationType(AUTHENTICATION_TYPE type) {
447         ldapConfig.setAuthtype(type.getAuthTypeName());
448     }
449     
450     /**
451      * Sets the base search DN.
452      * 
453      * @param dn the base search DN
454      */
455     public void setBaseDN(String dn) {
456         String trimmedDN = DatatypeHelper.safeTrimOrNullString(dn);
457         if(trimmedDN != null){
458             ldapConfig.setBaseDn(trimmedDN);
459         }else{
460             ldapConfig.setBaseDn("");
461         }
462     }
463     
464     /**
465      * Sets the time to live, in milliseconds, for cache elements.
466      * 
467      * @param ttl time to live, in milliseconds, for cache elements
468      */
469     public void setCacheElementTimeToLive(long ttl) {
470         cacheElementTtl = ttl;
471     }
472 
473     /**
474      * Sets the manager for the results cache.
475      * 
476      * @param manager manager for the results cache
477      */
478     public void setCacheManager(CacheManager manager) {
479         cacheManager = manager;
480     }
481 
482     /**
483      * Sets the client authentication material used when connecting to the LDAP via SSL or TLS.
484      * 
485      * @param credential client authentication material used when connecting to the LDAP via SSL or TLS
486      */
487     public void setConnectionCredential(X509Credential credential) {
488         connectionCredential = credential;
489     }
490 
491     /**
492      * Sets the LDAP query filter template.
493      * 
494      * @param template LDAP query filter template
495      */
496     public void setFilterTemplate(String template) {
497         filterTemplate = DatatypeHelper.safeTrimOrNullString(template);
498     }
499 
500     /**
501      * Sets the LDAP connection provider specific properties.
502      * 
503      * @param properties LDAP connection provider specific properties
504      */
505     public void setLdapProperties(Map<String, String> properties) {
506         ldapProperties = properties;
507     }
508 
509     /**
510      * Sets the LDAP server's URL.
511      * 
512      * @param url LDAP server's URL
513      */
514     public void setLdapUrl(String url) {
515         ldapConfig.setLdapUrl(DatatypeHelper.safeTrimOrNullString(url));
516     }
517 
518     /**
519      * Sets the LDAP connection strategy.
520      * 
521      * @param strategy connection strategy
522      */
523     public void setConnectionStrategy(ConnectionStrategy strategy) {
524         connStrategy = strategy;
525     }
526 
527     /**
528      * Sets whether LDAP attribute names used as Shibboleth attribute IDs will be lowercased.
529      * 
530      * @param lowercase whether LDAP attribute names used as Shibboleth attribute IDs will be lowercased
531      */
532     public void setLowercaseAttributeNames(boolean lowercase) {
533         lowercaseAttributeNames = lowercase;
534     }
535 
536     /**
537      * Sets the maximum number of elements that will be cached.
538      * 
539      * @param max maximum number of elements that will be cached
540      */
541     public void setMaximumCachedElements(int max) {
542         maximumCachedElements = max;
543     }
544 
545     /**
546      * Sets the maximum number of results to return from a query.
547      * 
548      * @param max maximum number of results to return from a query
549      */
550     public void setMaxResultSize(int max) {
551         ldapConfig.setCountLimit(max);
552     }
553 
554     /**
555      * Sets whether to merge multiple results into a single result.
556      * 
557      * @param merge whether to merge multiple results into a single result
558      */
559     public void setMergeResults(boolean merge) {
560         mergeResults = merge;
561     }
562 
563     /**
564      * Sets whether a query that returns no results is an error condition.
565      * 
566      * @param isError whether a query that returns no results is an error condition
567      */
568     public void setNoResultsIsError(boolean isError) {
569         noResultsIsError = isError;
570     }
571 
572     /**
573      * Sets the ldap pool strategy.
574      * 
575      * @param strategy to use for pooling
576      */
577     public void setPoolStrategy(LdapPoolStrategy strategy) {
578         ldapPoolStrategy = strategy;
579     }
580 
581     /**
582      * Sets the validator used to validate pool connections.
583      * 
584      * @param validator validator used to validate pool connections
585      */
586     public void setPoolValidator(LdapValidator validator) {
587         ldapValidator = validator;
588     }
589 
590     /**
591      * Sets the principal DN used to bind to the directory.
592      * 
593      * @param principalName principal DN used to bind to the directory
594      */
595     public void setPrincipal(String principalName) {
596         ldapConfig.setBindDn(DatatypeHelper.safeTrimOrNullString(principalName));
597     }
598 
599     /**
600      * Sets the credential of the principal DN used to bind to the directory.
601      * 
602      * @param credential credential of the principal DN used to bind to the directory
603      */
604     public void setPrincipalCredential(String credential) {
605         ldapConfig.setBindCredential(DatatypeHelper.safeTrimOrNullString(credential));
606     }
607 
608     /**
609      * Sets the attributes to return from a query.
610      * 
611      * @param attributes attributes to return from a query
612      */
613     public void setReturnAttributes(List<String> attributes) {
614         returnAttributes = attributes;
615     }
616 
617     /**
618      * Sets the search scope of a query.
619      * 
620      * @param scope search scope of a query
621      */
622     public void setSearchScope(SearchScope scope) {
623         ldapConfig.setSearchScope(scope);
624     }
625 
626     /**
627      * Sets the maximum amount of time, in milliseconds, to wait for a search to complete.
628      * 
629      * @param timeLimit maximum amount of time, in milliseconds, to wait for a search to complete
630      */
631     public void setSearchTimeLimit(int timeLimit) {
632         ldapConfig.setTimeLimit(timeLimit);
633     }
634 
635     /**
636      * Sets the template engine used to construct query filters.
637      * 
638      * @param engine template engine used to construct query filters
639      */
640     public void setTemplateEngine(TemplateEngine engine) {
641         templateEngine = engine;
642     }
643 
644     /**
645      * Sets the trust material used when connecting to the LDAP via SSL or TLS.
646      * 
647      * @param credential trust material used when connecting to the LDAP via SSL or TLS
648      */
649     public void setTrustCredential(X509Credential credential) {
650         trustCredential = credential;
651     }
652 
653     /**
654      * Sets whether to use StartTLS when connecting to the LDAP.
655      * 
656      * @param startTLS whether to use StartTLS when connecting to the LDAP
657      */
658     public void setUseStartTLS(boolean startTLS) {
659         ldapConfig.setTls(startTLS);
660     }
661 }