|
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.client; |
|
29 |
|
30 import java.util.Arrays; |
|
31 import java.util.Collection; |
|
32 import java.util.Collections; |
|
33 import java.util.HashMap; |
|
34 import java.util.List; |
|
35 import java.util.Locale; |
|
36 import java.util.Map; |
|
37 |
|
38 import ch.boye.httpclientandroidlib.androidextra.HttpClientAndroidLog; |
|
39 /* LogFactory removed by HttpClient for Android script. */ |
|
40 import ch.boye.httpclientandroidlib.FormattedHeader; |
|
41 import ch.boye.httpclientandroidlib.Header; |
|
42 import ch.boye.httpclientandroidlib.HttpResponse; |
|
43 import ch.boye.httpclientandroidlib.annotation.Immutable; |
|
44 import ch.boye.httpclientandroidlib.auth.AuthScheme; |
|
45 import ch.boye.httpclientandroidlib.auth.AuthSchemeRegistry; |
|
46 import ch.boye.httpclientandroidlib.auth.AuthenticationException; |
|
47 import ch.boye.httpclientandroidlib.auth.MalformedChallengeException; |
|
48 import ch.boye.httpclientandroidlib.client.AuthenticationHandler; |
|
49 import ch.boye.httpclientandroidlib.client.params.AuthPolicy; |
|
50 import ch.boye.httpclientandroidlib.client.protocol.ClientContext; |
|
51 import ch.boye.httpclientandroidlib.protocol.HTTP; |
|
52 import ch.boye.httpclientandroidlib.protocol.HttpContext; |
|
53 import ch.boye.httpclientandroidlib.util.CharArrayBuffer; |
|
54 |
|
55 /** |
|
56 * Base class for {@link AuthenticationHandler} implementations. |
|
57 * |
|
58 * @since 4.0 |
|
59 */ |
|
60 @Immutable |
|
61 public abstract class AbstractAuthenticationHandler implements AuthenticationHandler { |
|
62 |
|
63 public HttpClientAndroidLog log = new HttpClientAndroidLog(getClass()); |
|
64 |
|
65 private static final List<String> DEFAULT_SCHEME_PRIORITY = |
|
66 Collections.unmodifiableList(Arrays.asList(new String[] { |
|
67 AuthPolicy.SPNEGO, |
|
68 AuthPolicy.NTLM, |
|
69 AuthPolicy.DIGEST, |
|
70 AuthPolicy.BASIC |
|
71 })); |
|
72 |
|
73 public AbstractAuthenticationHandler() { |
|
74 super(); |
|
75 } |
|
76 |
|
77 protected Map<String, Header> parseChallenges( |
|
78 final Header[] headers) throws MalformedChallengeException { |
|
79 |
|
80 Map<String, Header> map = new HashMap<String, Header>(headers.length); |
|
81 for (Header header : headers) { |
|
82 CharArrayBuffer buffer; |
|
83 int pos; |
|
84 if (header instanceof FormattedHeader) { |
|
85 buffer = ((FormattedHeader) header).getBuffer(); |
|
86 pos = ((FormattedHeader) header).getValuePos(); |
|
87 } else { |
|
88 String s = header.getValue(); |
|
89 if (s == null) { |
|
90 throw new MalformedChallengeException("Header value is null"); |
|
91 } |
|
92 buffer = new CharArrayBuffer(s.length()); |
|
93 buffer.append(s); |
|
94 pos = 0; |
|
95 } |
|
96 while (pos < buffer.length() && HTTP.isWhitespace(buffer.charAt(pos))) { |
|
97 pos++; |
|
98 } |
|
99 int beginIndex = pos; |
|
100 while (pos < buffer.length() && !HTTP.isWhitespace(buffer.charAt(pos))) { |
|
101 pos++; |
|
102 } |
|
103 int endIndex = pos; |
|
104 String s = buffer.substring(beginIndex, endIndex); |
|
105 map.put(s.toLowerCase(Locale.ENGLISH), header); |
|
106 } |
|
107 return map; |
|
108 } |
|
109 |
|
110 /** |
|
111 * Returns default list of auth scheme names in their order of preference. |
|
112 * |
|
113 * @return list of auth scheme names |
|
114 */ |
|
115 protected List<String> getAuthPreferences() { |
|
116 return DEFAULT_SCHEME_PRIORITY; |
|
117 } |
|
118 |
|
119 /** |
|
120 * Returns default list of auth scheme names in their order of preference |
|
121 * based on the HTTP response and the current execution context. |
|
122 * |
|
123 * @param response HTTP response. |
|
124 * @param context HTTP execution context. |
|
125 * |
|
126 * @since 4.1 |
|
127 */ |
|
128 protected List<String> getAuthPreferences( |
|
129 final HttpResponse response, |
|
130 final HttpContext context) { |
|
131 return getAuthPreferences(); |
|
132 } |
|
133 |
|
134 public AuthScheme selectScheme( |
|
135 final Map<String, Header> challenges, |
|
136 final HttpResponse response, |
|
137 final HttpContext context) throws AuthenticationException { |
|
138 |
|
139 AuthSchemeRegistry registry = (AuthSchemeRegistry) context.getAttribute( |
|
140 ClientContext.AUTHSCHEME_REGISTRY); |
|
141 if (registry == null) { |
|
142 throw new IllegalStateException("AuthScheme registry not set in HTTP context"); |
|
143 } |
|
144 |
|
145 Collection<String> authPrefs = getAuthPreferences(response, context); |
|
146 if (authPrefs == null) { |
|
147 authPrefs = DEFAULT_SCHEME_PRIORITY; |
|
148 } |
|
149 |
|
150 if (this.log.isDebugEnabled()) { |
|
151 this.log.debug("Authentication schemes in the order of preference: " |
|
152 + authPrefs); |
|
153 } |
|
154 |
|
155 AuthScheme authScheme = null; |
|
156 for (String id: authPrefs) { |
|
157 Header challenge = challenges.get(id.toLowerCase(Locale.ENGLISH)); |
|
158 |
|
159 if (challenge != null) { |
|
160 if (this.log.isDebugEnabled()) { |
|
161 this.log.debug(id + " authentication scheme selected"); |
|
162 } |
|
163 try { |
|
164 authScheme = registry.getAuthScheme(id, response.getParams()); |
|
165 break; |
|
166 } catch (IllegalStateException e) { |
|
167 if (this.log.isWarnEnabled()) { |
|
168 this.log.warn("Authentication scheme " + id + " not supported"); |
|
169 // Try again |
|
170 } |
|
171 } |
|
172 } else { |
|
173 if (this.log.isDebugEnabled()) { |
|
174 this.log.debug("Challenge for " + id + " authentication scheme not available"); |
|
175 // Try again |
|
176 } |
|
177 } |
|
178 } |
|
179 if (authScheme == null) { |
|
180 // If none selected, something is wrong |
|
181 throw new AuthenticationException( |
|
182 "Unable to respond to any of these challenges: " |
|
183 + challenges); |
|
184 } |
|
185 return authScheme; |
|
186 } |
|
187 |
|
188 } |