1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.http.impl.client;
29
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.HashMap;
33 import java.util.LinkedList;
34 import java.util.List;
35 import java.util.Locale;
36 import java.util.Map;
37 import java.util.Queue;
38
39 import org.apache.commons.logging.Log;
40 import org.apache.commons.logging.LogFactory;
41 import org.apache.http.FormattedHeader;
42 import org.apache.http.Header;
43 import org.apache.http.HttpHost;
44 import org.apache.http.HttpResponse;
45 import org.apache.http.annotation.Immutable;
46 import org.apache.http.auth.AuthOption;
47 import org.apache.http.auth.AuthScheme;
48 import org.apache.http.auth.AuthSchemeRegistry;
49 import org.apache.http.auth.AuthScope;
50 import org.apache.http.auth.Credentials;
51 import org.apache.http.auth.MalformedChallengeException;
52 import org.apache.http.client.AuthCache;
53 import org.apache.http.client.AuthenticationStrategy;
54 import org.apache.http.client.CredentialsProvider;
55 import org.apache.http.client.params.AuthPolicy;
56 import org.apache.http.client.protocol.ClientContext;
57 import org.apache.http.protocol.HTTP;
58 import org.apache.http.protocol.HttpContext;
59 import org.apache.http.util.CharArrayBuffer;
60
61 @Immutable
62 class AuthenticationStrategyImpl implements AuthenticationStrategy {
63
64 private final Log log = LogFactory.getLog(getClass());
65
66 private static final List<String> DEFAULT_SCHEME_PRIORITY =
67 Collections.unmodifiableList(Arrays.asList(new String[] {
68 AuthPolicy.SPNEGO,
69 AuthPolicy.KERBEROS,
70 AuthPolicy.NTLM,
71 AuthPolicy.DIGEST,
72 AuthPolicy.BASIC
73 }));
74
75 private final int challengeCode;
76 private final String headerName;
77 private final String prefParamName;
78
79 AuthenticationStrategyImpl(int challengeCode, final String headerName, final String prefParamName) {
80 super();
81 this.challengeCode = challengeCode;
82 this.headerName = headerName;
83 this.prefParamName = prefParamName;
84 }
85
86 public boolean isAuthenticationRequested(
87 final HttpHost authhost,
88 final HttpResponse response,
89 final HttpContext context) {
90 if (response == null) {
91 throw new IllegalArgumentException("HTTP response may not be null");
92 }
93 int status = response.getStatusLine().getStatusCode();
94 return status == this.challengeCode;
95 }
96
97 public Map<String, Header> getChallenges(
98 final HttpHost authhost,
99 final HttpResponse response,
100 final HttpContext context) throws MalformedChallengeException {
101 if (response == null) {
102 throw new IllegalArgumentException("HTTP response may not be null");
103 }
104 Header[] headers = response.getHeaders(this.headerName);
105 Map<String, Header> map = new HashMap<String, Header>(headers.length);
106 for (Header header : headers) {
107 CharArrayBuffer buffer;
108 int pos;
109 if (header instanceof FormattedHeader) {
110 buffer = ((FormattedHeader) header).getBuffer();
111 pos = ((FormattedHeader) header).getValuePos();
112 } else {
113 String s = header.getValue();
114 if (s == null) {
115 throw new MalformedChallengeException("Header value is null");
116 }
117 buffer = new CharArrayBuffer(s.length());
118 buffer.append(s);
119 pos = 0;
120 }
121 while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) {
122 pos++;
123 }
124 int beginIndex = pos;
125 while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) {
126 pos++;
127 }
128 int endIndex = pos;
129 String s = buffer.substring(beginIndex, endIndex);
130 map.put(s.toLowerCase(Locale.US), header);
131 }
132 return map;
133 }
134
135 public Queue<AuthOption> select(
136 final Map<String, Header> challenges,
137 final HttpHost authhost,
138 final HttpResponse response,
139 final HttpContext context) throws MalformedChallengeException {
140 if (challenges == null) {
141 throw new IllegalArgumentException("Map of auth challenges may not be null");
142 }
143 if (authhost == null) {
144 throw new IllegalArgumentException("Host may not be null");
145 }
146 if (response == null) {
147 throw new IllegalArgumentException("HTTP response may not be null");
148 }
149 if (context == null) {
150 throw new IllegalArgumentException("HTTP context may not be null");
151 }
152
153 Queue<AuthOption> options = new LinkedList<AuthOption>();
154 AuthSchemeRegistry registry = (AuthSchemeRegistry) context.getAttribute(
155 ClientContext.AUTHSCHEME_REGISTRY);
156 if (registry == null) {
157 this.log.debug("Auth scheme registry not set in the context");
158 return options;
159 }
160 CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
161 ClientContext.CREDS_PROVIDER);
162 if (credsProvider == null) {
163 this.log.debug("Credentials provider not set in the context");
164 return options;
165 }
166
167 @SuppressWarnings("unchecked")
168 List<String> authPrefs = (List<String>) response.getParams().getParameter(this.prefParamName);
169 if (authPrefs == null) {
170 authPrefs = DEFAULT_SCHEME_PRIORITY;
171 }
172 if (this.log.isDebugEnabled()) {
173 this.log.debug("Authentication schemes in the order of preference: " + authPrefs);
174 }
175
176 for (String id: authPrefs) {
177 Header challenge = challenges.get(id.toLowerCase(Locale.US));
178 if (challenge != null) {
179 try {
180 AuthScheme authScheme = registry.getAuthScheme(id, response.getParams());
181 authScheme.processChallenge(challenge);
182
183 AuthScope authScope = new AuthScope(
184 authhost.getHostName(),
185 authhost.getPort(),
186 authScheme.getRealm(),
187 authScheme.getSchemeName());
188
189 Credentials credentials = credsProvider.getCredentials(authScope);
190 if (credentials != null) {
191 options.add(new AuthOption(authScheme, credentials));
192 }
193 } catch (IllegalStateException e) {
194 if (this.log.isWarnEnabled()) {
195 this.log.warn("Authentication scheme " + id + " not supported");
196
197 }
198 }
199 } else {
200 if (this.log.isDebugEnabled()) {
201 this.log.debug("Challenge for " + id + " authentication scheme not available");
202
203 }
204 }
205 }
206 return options;
207 }
208
209 public void authSucceeded(
210 final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) {
211 if (authhost == null) {
212 throw new IllegalArgumentException("Host may not be null");
213 }
214 if (authScheme == null) {
215 throw new IllegalArgumentException("Auth scheme may not be null");
216 }
217 if (context == null) {
218 throw new IllegalArgumentException("HTTP context may not be null");
219 }
220 if (isCachable(authScheme)) {
221 AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE);
222 if (authCache == null) {
223 authCache = new BasicAuthCache();
224 context.setAttribute(ClientContext.AUTH_CACHE, authCache);
225 }
226 if (this.log.isDebugEnabled()) {
227 this.log.debug("Caching '" + authScheme.getSchemeName() +
228 "' auth scheme for " + authhost);
229 }
230 authCache.put(authhost, authScheme);
231 }
232 }
233
234 protected boolean isCachable(final AuthScheme authScheme) {
235 if (authScheme == null || !authScheme.isComplete()) {
236 return false;
237 }
238 String schemeName = authScheme.getSchemeName();
239 return schemeName.equalsIgnoreCase(AuthPolicy.BASIC) ||
240 schemeName.equalsIgnoreCase(AuthPolicy.DIGEST);
241 }
242
243 public void authFailed(
244 final HttpHost authhost, final AuthScheme authScheme, final HttpContext context) {
245 if (authhost == null) {
246 throw new IllegalArgumentException("Host may not be null");
247 }
248 if (context == null) {
249 throw new IllegalArgumentException("HTTP context may not be null");
250 }
251 AuthCache authCache = (AuthCache) context.getAttribute(ClientContext.AUTH_CACHE);
252 if (authCache != null) {
253 if (this.log.isDebugEnabled()) {
254 this.log.debug("Clearing cached auth scheme for " + authhost);
255 }
256 authCache.remove(authhost);
257 }
258 }
259
260 }