View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.transport.apache;
20  
21  import java.util.Locale;
22  import java.util.Map;
23  import java.util.Queue;
24  
25  import org.apache.http.Header;
26  import org.apache.http.HttpHost;
27  import org.apache.http.HttpRequest;
28  import org.apache.http.HttpRequestInterceptor;
29  import org.apache.http.HttpResponse;
30  import org.apache.http.auth.AuthOption;
31  import org.apache.http.auth.AuthScope;
32  import org.apache.http.auth.AuthState;
33  import org.apache.http.auth.MalformedChallengeException;
34  import org.apache.http.auth.NTCredentials;
35  import org.apache.http.auth.UsernamePasswordCredentials;
36  import org.apache.http.client.CredentialsProvider;
37  import org.apache.http.client.config.AuthSchemes;
38  import org.apache.http.client.protocol.HttpClientContext;
39  import org.apache.http.conn.routing.RouteInfo;
40  import org.apache.http.impl.client.BasicCredentialsProvider;
41  import org.apache.http.impl.client.ProxyAuthenticationStrategy;
42  import org.apache.http.protocol.BasicHttpContext;
43  import org.apache.http.protocol.HttpContext;
44  
45  /**
46   * Adds system proxy credentials only while selecting authentication for the current route's proxy.
47   *
48   * @since 2.0.24
49   */
50  final class SystemProxyAuthenticationStrategy extends ProxyAuthenticationStrategy implements HttpRequestInterceptor {
51      private static final String SYSTEM_PROXY = SystemProxyAuthenticationStrategy.class.getName() + ".proxy";
52  
53      @Override
54      public void process(HttpRequest request, HttpContext context) {
55          HttpHost authenticatedProxy = (HttpHost) context.getAttribute(SYSTEM_PROXY);
56          RouteInfo route = HttpClientContext.adapt(context).getHttpRoute();
57          if (authenticatedProxy != null && route != null && !authenticatedProxy.equals(route.getProxyHost())) {
58              // HttpClient retains Basic proxy authentication across redirects, even when the proxy changes.
59              AuthState state = HttpClientContext.adapt(context).getProxyAuthState();
60              if (state != null) {
61                  state.reset();
62              }
63              request.removeHeaders("Proxy-Authorization");
64              context.removeAttribute(SYSTEM_PROXY);
65          }
66      }
67  
68      @Override
69      public Queue<AuthOption> select(
70              Map<String, Header> challenges, HttpHost authhost, HttpResponse response, HttpContext context)
71              throws MalformedChallengeException {
72          Queue<AuthOption> options = super.select(challenges, authhost, response, context);
73          if (!options.isEmpty()) {
74              return options;
75          }
76          RouteInfo route = HttpClientContext.adapt(context).getHttpRoute();
77          if (route == null || !authhost.equals(route.getProxyHost())) {
78              return options;
79          }
80          String routeProtocol = route.getTargetHost().getSchemeName().toLowerCase(Locale.ENGLISH);
81          CredentialsProvider credentials = credentials(authhost, routeProtocol);
82          String fallbackProtocol = "https".equalsIgnoreCase(routeProtocol) ? "http" : "https";
83          if (credentials == null) {
84              credentials = credentials(authhost, fallbackProtocol);
85          }
86          if (credentials == null) {
87              return options;
88          }
89          // A child context keeps proxy secrets out of server authentication, including after redirects.
90          HttpClientContext proxyContext = HttpClientContext.adapt(new BasicHttpContext(context));
91          proxyContext.setCredentialsProvider(credentials);
92          options = super.select(challenges, authhost, response, proxyContext);
93          if (!options.isEmpty()) {
94              context.setAttribute(SYSTEM_PROXY, authhost);
95          }
96          return options;
97      }
98  
99      private static CredentialsProvider credentials(HttpHost proxy, String protocol) {
100         String prefix = protocol + ".proxy";
101         if (!proxy.getHostName().equalsIgnoreCase(System.getProperty(prefix + "Host"))) {
102             return null;
103         }
104         try {
105             String configuredPort = System.getProperty(prefix + "Port");
106             int port = configuredPort == null ? defaultPort(protocol) : Integer.parseInt(configuredPort);
107             if (proxy.getPort() != port) {
108                 return null;
109             }
110         } catch (NumberFormatException e) {
111             return null;
112         }
113         String username = System.getProperty(prefix + "User");
114         if (username == null) {
115             return null;
116         }
117         String password = System.getProperty(prefix + "Password", "");
118         BasicCredentialsProvider credentials = new BasicCredentialsProvider();
119         credentials.setCredentials(new AuthScope(proxy), new UsernamePasswordCredentials(username, password));
120         // HttpClient defines only the protocol-independent http.auth.ntlm.domain system property.
121         credentials.setCredentials(
122                 new AuthScope(proxy, AuthScope.ANY_REALM, AuthSchemes.NTLM),
123                 new NTCredentials(username, password, null, System.getProperty("http.auth.ntlm.domain")));
124         return credentials;
125     }
126 
127     private static int defaultPort(String protocol) {
128         return "https".equalsIgnoreCase(protocol) ? 443 : 80;
129     }
130 }