View Javadoc

1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.http.impl.auth;
29  
30  import java.io.IOException;
31  import java.util.Locale;
32  import java.util.Map;
33  import java.util.Queue;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.http.Header;
38  import org.apache.http.HttpException;
39  import org.apache.http.HttpHost;
40  import org.apache.http.HttpRequest;
41  import org.apache.http.HttpResponse;
42  import org.apache.http.auth.AuthOption;
43  import org.apache.http.auth.AuthProtocolState;
44  import org.apache.http.auth.AuthScheme;
45  import org.apache.http.auth.AuthState;
46  import org.apache.http.auth.AuthenticationException;
47  import org.apache.http.auth.ContextAwareAuthScheme;
48  import org.apache.http.auth.Credentials;
49  import org.apache.http.auth.MalformedChallengeException;
50  import org.apache.http.client.AuthenticationStrategy;
51  import org.apache.http.protocol.HttpContext;
52  import org.apache.http.util.Asserts;
53  
54  /**
55   * @since 4.3
56   */
57  public class HttpAuthenticator {
58  
59      private final Log log;
60  
61      public HttpAuthenticator(final Log log) {
62          super();
63          this.log = log != null ? log : LogFactory.getLog(getClass());
64      }
65  
66      public HttpAuthenticator() {
67          this(null);
68      }
69  
70      public boolean isAuthenticationRequested(
71              final HttpHost host,
72              final HttpResponse response,
73              final AuthenticationStrategy authStrategy,
74              final AuthState authState,
75              final HttpContext context) {
76          if (authStrategy.isAuthenticationRequested(host, response, context)) {
77              this.log.debug("Authentication required");
78              if (authState.getState() == AuthProtocolState.SUCCESS) {
79                  authStrategy.authFailed(host, authState.getAuthScheme(), context);
80              }
81              return true;
82          } else {
83              switch (authState.getState()) {
84              case CHALLENGED:
85              case HANDSHAKE:
86                  this.log.debug("Authentication succeeded");
87                  authState.setState(AuthProtocolState.SUCCESS);
88                  authStrategy.authSucceeded(host, authState.getAuthScheme(), context);
89                  break;
90              case SUCCESS:
91                  break;
92              default:
93                  authState.setState(AuthProtocolState.UNCHALLENGED);
94              }
95              return false;
96          }
97      }
98  
99      public boolean handleAuthChallenge(
100             final HttpHost host,
101             final HttpResponse response,
102             final AuthenticationStrategy authStrategy,
103             final AuthState authState,
104             final HttpContext context) {
105         try {
106             if (this.log.isDebugEnabled()) {
107                 this.log.debug(host.toHostString() + " requested authentication");
108             }
109             final Map<String, Header> challenges = authStrategy.getChallenges(host, response, context);
110             if (challenges.isEmpty()) {
111                 this.log.debug("Response contains no authentication challenges");
112                 return false;
113             }
114 
115             final AuthScheme authScheme = authState.getAuthScheme();
116             switch (authState.getState()) {
117             case FAILURE:
118                 return false;
119             case SUCCESS:
120                 authState.reset();
121                 break;
122             case CHALLENGED:
123             case HANDSHAKE:
124                 if (authScheme == null) {
125                     this.log.debug("Auth scheme is null");
126                     authStrategy.authFailed(host, null, context);
127                     authState.reset();
128                     authState.setState(AuthProtocolState.FAILURE);
129                     return false;
130                 }
131             case UNCHALLENGED:
132                 if (authScheme != null) {
133                     final String id = authScheme.getSchemeName();
134                     final Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH));
135                     if (challenge != null) {
136                         this.log.debug("Authorization challenge processed");
137                         authScheme.processChallenge(challenge);
138                         if (authScheme.isComplete()) {
139                             this.log.debug("Authentication failed");
140                             authStrategy.authFailed(host, authState.getAuthScheme(), context);
141                             authState.reset();
142                             authState.setState(AuthProtocolState.FAILURE);
143                             return false;
144                         } else {
145                             authState.setState(AuthProtocolState.HANDSHAKE);
146                             return true;
147                         }
148                     } else {
149                         authState.reset();
150                         // Retry authentication with a different scheme
151                     }
152                 }
153             }
154             final Queue<AuthOption> authOptions = authStrategy.select(challenges, host, response, context);
155             if (authOptions != null && !authOptions.isEmpty()) {
156                 if (this.log.isDebugEnabled()) {
157                     this.log.debug("Selected authentication options: " + authOptions);
158                 }
159                 authState.setState(AuthProtocolState.CHALLENGED);
160                 authState.update(authOptions);
161                 return true;
162             } else {
163                 return false;
164             }
165         } catch (final MalformedChallengeException ex) {
166             if (this.log.isWarnEnabled()) {
167                 this.log.warn("Malformed challenge: " +  ex.getMessage());
168             }
169             authState.reset();
170             return false;
171         }
172     }
173 
174     public void generateAuthResponse(
175             final HttpRequest request,
176             final AuthState authState,
177             final HttpContext context) throws HttpException, IOException {
178         AuthScheme authScheme = authState.getAuthScheme();
179         Credentials creds = authState.getCredentials();
180         switch (authState.getState()) {
181         case FAILURE:
182             return;
183         case SUCCESS:
184             ensureAuthScheme(authScheme);
185             if (authScheme.isConnectionBased()) {
186                 return;
187             }
188             break;
189         case CHALLENGED:
190             final Queue<AuthOption> authOptions = authState.getAuthOptions();
191             if (authOptions != null) {
192                 while (!authOptions.isEmpty()) {
193                     final AuthOption authOption = authOptions.remove();
194                     authScheme = authOption.getAuthScheme();
195                     creds = authOption.getCredentials();
196                     authState.update(authScheme, creds);
197                     if (this.log.isDebugEnabled()) {
198                         this.log.debug("Generating response to an authentication challenge using "
199                                 + authScheme.getSchemeName() + " scheme");
200                     }
201                     try {
202                         final Header header = doAuth(authScheme, creds, request, context);
203                         request.addHeader(header);
204                         break;
205                     } catch (final AuthenticationException ex) {
206                         if (this.log.isWarnEnabled()) {
207                             this.log.warn(authScheme + " authentication error: " + ex.getMessage());
208                         }
209                     }
210                 }
211                 return;
212             } else {
213                 ensureAuthScheme(authScheme);
214             }
215         }
216         if (authScheme != null) {
217             try {
218                 final Header header = doAuth(authScheme, creds, request, context);
219                 request.addHeader(header);
220             } catch (final AuthenticationException ex) {
221                 if (this.log.isErrorEnabled()) {
222                     this.log.error(authScheme + " authentication error: " + ex.getMessage());
223                 }
224             }
225         }
226     }
227 
228     private void ensureAuthScheme(final AuthScheme authScheme) {
229         Asserts.notNull(authScheme, "Auth scheme");
230     }
231 
232     @SuppressWarnings("deprecation")
233     private Header doAuth(
234             final AuthScheme authScheme,
235             final Credentials creds,
236             final HttpRequest request,
237             final HttpContext context) throws AuthenticationException {
238         if (authScheme instanceof ContextAwareAuthScheme) {
239             return ((ContextAwareAuthScheme) authScheme).authenticate(creds, request, context);
240         } else {
241             return authScheme.authenticate(creds, request);
242         }
243     }
244 
245 }