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(getPluginId(), maximumCachedElements, false, false, ttlInSeconds,
144                         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                 SSLContext ctx = SSLContext.getInstance("TLS");
206                 ctx.init(sslKeyManagers, sslTrustManagers, null);
207                 ldapConfig.setSslSocketFactory(ctx.getSocketFactory());
208             } catch (GeneralSecurityException e) {
209                 logger.error("Error initializing key managers", e);
210             } catch (IOException e) {
211                 logger.error("Error initializing key managers", e);
212             }
213         }
214 
215         SSLContext ctx = null;
216         if (sslTrustManagers != null || sslKeyManagers != null) {
217             ctx = SSLContext.getInstance("TLS");
218             ctx.init(sslKeyManagers, sslTrustManagers, null);
219         }
220         return ctx;
221     }
222 
223     /**
224      * Gets the authentication type used when connecting to the directory.
225      * 
226      * @return authentication type used when connecting to the directory
227      */
228     public AUTHENTICATION_TYPE getAuthenticationType() {
229         return AUTHENTICATION_TYPE.getAuthenticationTypeByName(ldapConfig.getAuthtype());
230     }
231 
232     /**
233      * Gets the base search DN.
234      * 
235      * @return the base search DN
236      */
237     public String getBaseDN() {
238         return this.ldapConfig.getBaseDn();
239     }
240 
241     /**
242      * Gets the time to live, in milliseconds, for cache elements.
243      * 
244      * @return time to live, in milliseconds, for cache elements
245      */
246     public long getCacheElementTimeToLive() {
247         return cacheElementTtl;
248     }
249 
250     /**
251      * Gets the manager for the results cache.
252      * 
253      * @return manager for the results cache
254      */
255     public CacheManager getCacheManager() {
256         return cacheManager;
257     }
258 
259     /**
260      * Gets the client authentication material used when connecting to the LDAP via SSL or TLS.
261      * 
262      * @return client authentication material used when connecting to the LDAP via SSL or TLS
263      */
264     public X509Credential getConnectionCredential() {
265         return connectionCredential;
266     }
267 
268     /**
269      * Gets the LDAP query filter template.
270      * 
271      * @return LDAP query filter template
272      */
273     public String getFilterTemplate() {
274         return filterTemplate;
275     }
276 
277     /**
278      * Gets the LDAP connection provider specific properties.
279      * 
280      * @return LDAP connection provider specific properties
281      */
282     public Map<String, String> getLdapProperties() {
283         return ldapProperties;
284     }
285 
286     /**
287      * Gets the LDAP server's URL.
288      * 
289      * @return LDAP server's URL
290      */
291     public String getLdapUrl() {
292         return ldapConfig.getLdapUrl();
293     }
294 
295     /**
296      * Gets the LDAP connection strategy.
297      * 
298      * @return connection strategy
299      */
300     public ConnectionStrategy getConnectionStrategy() {
301         return connStrategy;
302     }
303 
304     /**
305      * Gets the maximum number of elements that will be cached.
306      * 
307      * @return maximum number of elements that will be cached
308      */
309     public int getMaximumCachedElements() {
310         return maximumCachedElements;
311     }
312 
313     /**
314      * Gets the maximum number of results to return from a query.
315      * 
316      * @return maximum number of results to return from a query
317      */
318     public int getMaxResultSize() {
319         return (int) ldapConfig.getCountLimit();
320     }
321 
322     /** {@inheritDoc} */
323     public Class<?> getObjectType() {
324         return LdapDataConnector.class;
325     }
326 
327     /**
328      * Gets the ldap pool strategy.
329      * 
330      * @return ldap pool strategy
331      */
332     public LdapPoolStrategy getPoolStrategy() {
333         return ldapPoolStrategy;
334     }
335 
336     /**
337      * Gets the pool validator.
338      * 
339      * @return pool validator
340      */
341     public LdapValidator getPoolValidator() {
342         return ldapValidator;
343     }
344 
345     /**
346      * Gets the principal DN used to bind to the directory.
347      * 
348      * @return principal DN used to bind to the directory
349      */
350     public String getPrincipal() {
351         return ldapConfig.getBindDn();
352     }
353 
354     /**
355      * Gets the credential of the principal DN used to bind to the directory.
356      * 
357      * @return credential of the principal DN used to bind to the directory
358      */
359     public String getPrincipalCredential() {
360         return (String) ldapConfig.getBindCredential();
361     }
362 
363     /**
364      * Gets the attributes to return from a query.
365      * 
366      * @return attributes to return from a query
367      */
368     public List<String> getReturnAttributes() {
369         return returnAttributes;
370     }
371 
372     /**
373      * Gets the search scope of a query.
374      * 
375      * @return search scope of a query
376      */
377     public SearchScope getSearchScope() {
378         return ldapConfig.getSearchScope();
379     }
380 
381     /**
382      * Gets the maximum amount of time, in milliseconds, to wait for a search to complete.
383      * 
384      * @return maximum amount of time, in milliseconds, to wait for a search to complete
385      */
386     public int getSearchTimeLimit() {
387         return ldapConfig.getTimeLimit();
388     }
389 
390     /**
391      * Gets the template engine used to construct query filters.
392      * 
393      * @return template engine used to construct query filters
394      */
395     public TemplateEngine getTemplateEngine() {
396         return templateEngine;
397     }
398 
399     /**
400      * Gets the trust material used when connecting to the LDAP via SSL or TLS.
401      * 
402      * @return trust material used when connecting to the LDAP via SSL or TLS
403      */
404     public X509Credential getTrustCredential() {
405         return trustCredential;
406     }
407 
408     /**
409      * Gets whether to use StartTLS when connecting to the LDAP.
410      * 
411      * @return whether to use StartTLS when connecting to the LDAP
412      */
413     public boolean getUseStartTLS() {
414         return ldapConfig.isTlsEnabled();
415     }
416 
417     /**
418      * Gets whether LDAP attribute names used as Shibboleth attribute IDs will be lowercased.
419      * 
420      * @return whether LDAP attribute names used as Shibboleth attribute IDs will be lowercased
421      */
422     public boolean isLowercaseAttributeNames() {
423         return lowercaseAttributeNames;
424     }
425 
426     /**
427      * Gets whether to merge multiple results into a single result.
428      * 
429      * @return whether to merge multiple results into a single result
430      */
431     public boolean isMergeResults() {
432         return mergeResults;
433     }
434 
435     /**
436      * Gets whether a query that returns no results is an error condition.
437      * 
438      * @return whether a query that returns no results is an error condition
439      */
440     public boolean isNoResultsIsError() {
441         return noResultsIsError;
442     }
443 
444     /**
445      * Sets the authentication type used when connecting to the directory.
446      * 
447      * @param type authentication type used when connecting to the directory
448      */
449     public void setAuthenticationType(AUTHENTICATION_TYPE type) {
450         ldapConfig.setAuthtype(type.getAuthTypeName());
451     }
452     
453     /**
454      * Sets the base search DN.
455      * 
456      * @param dn the base search DN
457      */
458     public void setBaseDN(String dn) {
459         String trimmedDN = DatatypeHelper.safeTrimOrNullString(dn);
460         if(trimmedDN != null){
461             ldapConfig.setBaseDn(trimmedDN);
462         }else{
463             ldapConfig.setBaseDn("");
464         }
465     }
466     
467     /**
468      * Sets the time to live, in milliseconds, for cache elements.
469      * 
470      * @param ttl time to live, in milliseconds, for cache elements
471      */
472     public void setCacheElementTimeToLive(long ttl) {
473         cacheElementTtl = ttl;
474     }
475 
476     /**
477      * Sets the manager for the results cache.
478      * 
479      * @param manager manager for the results cache
480      */
481     public void setCacheManager(CacheManager manager) {
482         cacheManager = manager;
483     }
484 
485     /**
486      * Sets the client authentication material used when connecting to the LDAP via SSL or TLS.
487      * 
488      * @param credential client authentication material used when connecting to the LDAP via SSL or TLS
489      */
490     public void setConnectionCredential(X509Credential credential) {
491         connectionCredential = credential;
492     }
493 
494     /**
495      * Sets the LDAP query filter template.
496      * 
497      * @param template LDAP query filter template
498      */
499     public void setFilterTemplate(String template) {
500         filterTemplate = DatatypeHelper.safeTrimOrNullString(template);
501     }
502 
503     /**
504      * Sets the LDAP connection provider specific properties.
505      * 
506      * @param properties LDAP connection provider specific properties
507      */
508     public void setLdapProperties(Map<String, String> properties) {
509         ldapProperties = properties;
510     }
511 
512     /**
513      * Sets the LDAP server's URL.
514      * 
515      * @param url LDAP server's URL
516      */
517     public void setLdapUrl(String url) {
518         ldapConfig.setLdapUrl(DatatypeHelper.safeTrimOrNullString(url));
519     }
520 
521     /**
522      * Sets the LDAP connection strategy.
523      * 
524      * @param strategy connection strategy
525      */
526     public void setConnectionStrategy(ConnectionStrategy strategy) {
527         connStrategy = strategy;
528     }
529 
530     /**
531      * Sets whether LDAP attribute names used as Shibboleth attribute IDs will be lowercased.
532      * 
533      * @param lowercase whether LDAP attribute names used as Shibboleth attribute IDs will be lowercased
534      */
535     public void setLowercaseAttributeNames(boolean lowercase) {
536         lowercaseAttributeNames = lowercase;
537     }
538 
539     /**
540      * Sets the maximum number of elements that will be cached.
541      * 
542      * @param max maximum number of elements that will be cached
543      */
544     public void setMaximumCachedElements(int max) {
545         maximumCachedElements = max;
546     }
547 
548     /**
549      * Sets the maximum number of results to return from a query.
550      * 
551      * @param max maximum number of results to return from a query
552      */
553     public void setMaxResultSize(int max) {
554         ldapConfig.setCountLimit(max);
555     }
556 
557     /**
558      * Sets whether to merge multiple results into a single result.
559      * 
560      * @param merge whether to merge multiple results into a single result
561      */
562     public void setMergeResults(boolean merge) {
563         mergeResults = merge;
564     }
565 
566     /**
567      * Sets whether a query that returns no results is an error condition.
568      * 
569      * @param isError whether a query that returns no results is an error condition
570      */
571     public void setNoResultsIsError(boolean isError) {
572         noResultsIsError = isError;
573     }
574 
575     /**
576      * Sets the ldap pool strategy.
577      * 
578      * @param strategy to use for pooling
579      */
580     public void setPoolStrategy(LdapPoolStrategy strategy) {
581         ldapPoolStrategy = strategy;
582     }
583 
584     /**
585      * Sets the validator used to validate pool connections.
586      * 
587      * @param validator validator used to validate pool connections
588      */
589     public void setPoolValidator(LdapValidator validator) {
590         ldapValidator = validator;
591     }
592 
593     /**
594      * Sets the principal DN used to bind to the directory.
595      * 
596      * @param principalName principal DN used to bind to the directory
597      */
598     public void setPrincipal(String principalName) {
599         ldapConfig.setBindDn(DatatypeHelper.safeTrimOrNullString(principalName));
600     }
601 
602     /**
603      * Sets the credential of the principal DN used to bind to the directory.
604      * 
605      * @param credential credential of the principal DN used to bind to the directory
606      */
607     public void setPrincipalCredential(String credential) {
608         ldapConfig.setBindCredential(DatatypeHelper.safeTrimOrNullString(credential));
609     }
610 
611     /**
612      * Sets the attributes to return from a query.
613      * 
614      * @param attributes attributes to return from a query
615      */
616     public void setReturnAttributes(List<String> attributes) {
617         returnAttributes = attributes;
618     }
619 
620     /**
621      * Sets the search scope of a query.
622      * 
623      * @param scope search scope of a query
624      */
625     public void setSearchScope(SearchScope scope) {
626         ldapConfig.setSearchScope(scope);
627     }
628 
629     /**
630      * Sets the maximum amount of time, in milliseconds, to wait for a search to complete.
631      * 
632      * @param timeLimit maximum amount of time, in milliseconds, to wait for a search to complete
633      */
634     public void setSearchTimeLimit(int timeLimit) {
635         ldapConfig.setTimeLimit(timeLimit);
636     }
637 
638     /**
639      * Sets the template engine used to construct query filters.
640      * 
641      * @param engine template engine used to construct query filters
642      */
643     public void setTemplateEngine(TemplateEngine engine) {
644         templateEngine = engine;
645     }
646 
647     /**
648      * Sets the trust material used when connecting to the LDAP via SSL or TLS.
649      * 
650      * @param credential trust material used when connecting to the LDAP via SSL or TLS
651      */
652     public void setTrustCredential(X509Credential credential) {
653         trustCredential = credential;
654     }
655 
656     /**
657      * Sets whether to use StartTLS when connecting to the LDAP.
658      * 
659      * @param startTLS whether to use StartTLS when connecting to the LDAP
660      */
661     public void setUseStartTLS(boolean startTLS) {
662         ldapConfig.setTls(startTLS);
663     }
664 }