View Javadoc

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