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.metadata;
19  
20  import java.net.MalformedURLException;
21  import java.net.URL;
22  import java.security.cert.X509Certificate;
23  
24  import javax.net.ssl.X509TrustManager;
25  import javax.xml.namespace.QName;
26  
27  import org.apache.commons.httpclient.HttpClient;
28  import org.apache.commons.httpclient.UsernamePasswordCredentials;
29  import org.apache.commons.httpclient.auth.AuthScope;
30  import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider;
31  import org.opensaml.ws.soap.client.http.HttpClientBuilder;
32  import org.opensaml.ws.soap.client.http.TLSProtocolSocketFactory;
33  import org.opensaml.xml.util.DatatypeHelper;
34  import org.opensaml.xml.util.XMLHelper;
35  import org.slf4j.Logger;
36  import org.slf4j.LoggerFactory;
37  import org.springframework.beans.factory.BeanCreationException;
38  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
39  import org.springframework.beans.factory.xml.ParserContext;
40  import org.w3c.dom.Element;
41  
42  import edu.internet2.middleware.shibboleth.common.config.SpringConfigurationUtils;
43  
44  /**
45   * Spring bean definition parser for Shibboleth file backed url metadata provider definition.
46   */
47  public class HTTPMetadataProviderBeanDefinitionParser extends AbstractReloadingMetadataProviderBeanDefinitionParser {
48  
49      /** Schema type name. */
50      public static final QName TYPE_NAME = new QName(MetadataNamespaceHandler.NAMESPACE, "HTTPMetadataProvider");
51  
52      /** Class logger. */
53      private Logger log = LoggerFactory.getLogger(HTTPMetadataProviderBeanDefinitionParser.class);
54  
55      /** {@inheritDoc} */
56      protected Class getBeanClass(Element element) {
57          return HTTPMetadataProvider.class;
58      }
59  
60      /** {@inheritDoc} */
61      protected void doParse(Element config, ParserContext parserContext, BeanDefinitionBuilder builder) {
62          String providerId = getProviderId(config);
63  
64          super.doParse(config, parserContext, builder);
65  
66          String metadataURL = DatatypeHelper.safeTrimOrNullString(config.getAttributeNS(null, "metadataURL"));
67          URL metadataURI = null;
68          try {
69              metadataURI = new URL(metadataURL);
70          } catch (MalformedURLException e) {
71              throw new BeanCreationException("metadataURL attribute for metadata provider " + providerId
72                      + " must be present and must contain a valid URL");
73          }
74  
75          HttpClient httpClient = buildHttpClient(config, providerId, metadataURI);
76          builder.addConstructorArgValue(httpClient);
77  
78          log.debug("Metadata provider '{}' metadata URL: {}", providerId, metadataURL);
79          builder.addConstructorArgValue(metadataURL);
80      }
81  
82      /**
83       * Builds the HTTP client used to fetch metadata.
84       * 
85       * @param config the metadata provider configuration element
86       * @param providerId the ID of the metadata provider
87       * @param metadataURL the URL from which metadata will be fetched
88       * 
89       * @return the constructed HTTP client
90       */
91      protected HttpClient buildHttpClient(Element config, String providerId, URL metadataURL) {
92          HttpClientBuilder builder = new HttpClientBuilder();
93  
94          int requestTimeout = 5000;
95          if (config.hasAttributeNS(null, "requestTimeout")) {
96              requestTimeout = (int) SpringConfigurationUtils.parseDurationToMillis(
97                      "'requestTimeout' on metadata provider " + providerId,
98                      config.getAttributeNS(null, "requestTimeout"), 0);
99          }
100         log.debug("Metadata provider '{}' HTTP request timeout: {}ms", providerId, requestTimeout);
101         builder.setConnectionTimeout(requestTimeout);
102 
103         if (metadataURL.getProtocol().equalsIgnoreCase("https")) {
104             boolean disregardSslCertificate = false;
105             if (config.hasAttributeNS(null, "disregardSslCertificate")) {
106                 disregardSslCertificate = XMLHelper.getAttributeValueAsBoolean(config.getAttributeNodeNS(null,
107                         "disregardSslCertificate"));
108             }
109 
110             log.debug("Metadata provider '{}' disregards server SSL certificate: {}", providerId,
111                     disregardSslCertificate);
112             if (disregardSslCertificate) {
113                 builder.setHttpsProtocolSocketFactory(new TLSProtocolSocketFactory(null, buildNoTrustTrustManager()));
114             }
115         }
116 
117         setHttpProxySettings(builder, config, providerId);
118 
119         HttpClient httpClient = builder.buildClient();
120         setHttpBasicAuthSettings(httpClient, config, providerId, metadataURL);
121 
122         return httpClient;
123     }
124 
125     /**
126      * Builds a {@link X509TrustManager} which bypasses all X.509 validation steps.
127      * 
128      * @return the trustless trust manager
129      */
130     protected X509TrustManager buildNoTrustTrustManager() {
131         X509TrustManager noTrustManager = new X509TrustManager() {
132 
133             /** {@inheritDoc} */
134             public void checkClientTrusted(X509Certificate[] certs, String auth) {
135             }
136 
137             /** {@inheritDoc} */
138             public void checkServerTrusted(X509Certificate[] certs, String auth) {
139             }
140 
141             /** {@inheritDoc} */
142             public X509Certificate[] getAcceptedIssuers() {
143                 return new X509Certificate[] {};
144             }
145         };
146 
147         return noTrustManager;
148     }
149 
150     /**
151      * Sets the HTTP proxy properties, if any, for the HTTP client used to fetch metadata.
152      * 
153      * @param builder the HTTP client builder
154      * @param config the metadata provider configuration
155      * @param providerId the ID of the metadata provider
156      */
157     protected void setHttpProxySettings(HttpClientBuilder builder, Element config, String providerId) {
158         String proxyHost = DatatypeHelper.safeTrimOrNullString(config.getAttributeNS(null, "proxyHost"));
159         if (proxyHost == null) {
160             return;
161         }
162         log.debug("Metadata provider '{}' HTTP proxy host: {}", providerId, proxyHost);
163         builder.setProxyHost(proxyHost);
164 
165         if (config.hasAttributeNS(null, "proxyPort")) {
166             int proxyPort = Integer.parseInt(config.getAttributeNS(null, "proxyPort"));
167             log.debug("Metadata provider '{}' HTTP proxy port: ", providerId, proxyPort);
168             builder.setProxyPort(proxyPort);
169         }
170 
171         String proxyUser = DatatypeHelper.safeTrimOrNullString(config.getAttributeNS(null, "proxyUser"));
172         if (proxyUser != null) {
173             log.debug("Metadata provider '{}' HTTP proxy username: ", providerId, proxyUser);
174             builder.setProxyUsername(proxyUser);
175             log.debug("Metadata provider '{}' HTTP proxy password not shown", providerId);
176             builder.setProxyPassword(DatatypeHelper.safeTrimOrNullString(config.getAttributeNS(null, "proxyPassword")));
177         }
178     }
179 
180     /**
181      * Sets the basic authentication properties, if any, for the HTTP client used to fetch metadata.
182      * 
183      * @param httpClient the HTTP client
184      * @param config the metadata provider configuration
185      * @param providerId the ID of the metadata provider
186      * @param metadataURL the URL from which metadata will be fetched
187      */
188     protected void setHttpBasicAuthSettings(HttpClient httpClient, Element config, String providerId, URL metadataURL) {
189         String authUser = DatatypeHelper.safeTrimOrNullString(config.getAttributeNS(null, "basicAuthUser"));
190         if (authUser == null) {
191             return;
192         }
193         log.debug("Metadata provider '{}' HTTP Basic Auth username: {}", providerId, authUser);
194 
195         String authPassword = DatatypeHelper.safeTrimOrNullString(config.getAttributeNS(null, "basicAuthPassword"));
196         log.debug("Metadata provider '{}' HTTP Basic Auth password not show", providerId);
197 
198         UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(authUser, authPassword);
199         AuthScope authScope = new AuthScope(metadataURL.getHost(), metadataURL.getPort());
200         httpClient.getState().setCredentials(authScope, credentials);
201     }
202 }