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 ch.boye.httpclientandroidlib.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 ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; 36 /* LogFactory removed by HttpClient for Android script. */ 37 import ch.boye.httpclientandroidlib.Header; 38 import ch.boye.httpclientandroidlib.HttpException; 39 import ch.boye.httpclientandroidlib.HttpHost; 40 import ch.boye.httpclientandroidlib.HttpRequest; 41 import ch.boye.httpclientandroidlib.HttpResponse; 42 import ch.boye.httpclientandroidlib.auth.AuthOption; 43 import ch.boye.httpclientandroidlib.auth.AuthProtocolState; 44 import ch.boye.httpclientandroidlib.auth.AuthScheme; 45 import ch.boye.httpclientandroidlib.auth.AuthState; 46 import ch.boye.httpclientandroidlib.auth.AuthenticationException; 47 import ch.boye.httpclientandroidlib.auth.ContextAwareAuthScheme; 48 import ch.boye.httpclientandroidlib.auth.Credentials; 49 import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; 50 import ch.boye.httpclientandroidlib.client.AuthenticationStrategy; 51 import ch.boye.httpclientandroidlib.protocol.HttpContext; 52 import ch.boye.httpclientandroidlib.util.Asserts; 53 54 /** 55 * @since 4.3 56 */ 57 public class HttpAuthenticator { 58 59 public HttpClientAndroidLog log; 60 HttpAuthenticator(final HttpClientAndroidLog log)61 public HttpAuthenticator(final HttpClientAndroidLog log) { 62 super(); 63 this.log = log != null ? log : new HttpClientAndroidLog(getClass()); 64 } 65 HttpAuthenticator()66 public HttpAuthenticator() { 67 this(null); 68 } 69 isAuthenticationRequested( final HttpHost host, final HttpResponse response, final AuthenticationStrategy authStrategy, final AuthState authState, final HttpContext context)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 handleAuthChallenge( final HttpHost host, final HttpResponse response, final AuthenticationStrategy authStrategy, final AuthState authState, final HttpContext context)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 generateAuthResponse( final HttpRequest request, final AuthState authState, final HttpContext context)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 ensureAuthScheme(final AuthScheme authScheme)228 private void ensureAuthScheme(final AuthScheme authScheme) { 229 Asserts.notNull(authScheme, "Auth scheme"); 230 } 231 232 @SuppressWarnings("deprecation") doAuth( final AuthScheme authScheme, final Credentials creds, final HttpRequest request, final HttpContext context)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 } 246