KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > HTTPClient > AuthorizationModule


1 /*
2  * @(#)AuthorizationModule.java 0.3-2 18/06/1999
3  *
4  * This file is part of the HTTPClient package
5  * Copyright (C) 1996-1999 Ronald Tschalär
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free
19  * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20  * MA 02111-1307, USA
21  *
22  * For questions, suggestions, bug-reports, enhancement-requests etc.
23  * I may be contacted at:
24  *
25  * ronald@innovation.ch
26  *
27  */

28
29 package HTTPClient;
30
31 import java.io.IOException JavaDoc;
32 import java.net.ProtocolException JavaDoc;
33 import java.util.Hashtable JavaDoc;
34
35
36 /**
37  * This module handles authentication requests. Authentication info is
38  * preemptively sent if any suitable candidate info is available. If a
39  * request returns with an appropriate status (401 or 407) then the
40  * necessary info is sought from the AuthenticationInfo class.
41  *
42  * @version 0.3-2 18/06/1999
43  * @author Ronald Tschalär
44  */

45
46 class AuthorizationModule implements HTTPClientModule, GlobalConstants
47 {
48     /** This holds the current Proxy-Authorization-Info for each
49         HTTPConnection */

50     private static Hashtable JavaDoc proxy_cntxt_list = new Hashtable JavaDoc();
51
52     /** counters for challenge and auth-info lists */
53     private int auth_lst_idx,
54         prxy_lst_idx,
55         auth_scm_idx,
56         prxy_scm_idx;
57
58     /** the last auth info sent, if any */
59     private AuthorizationInfo auth_sent;
60     private AuthorizationInfo prxy_sent;
61
62     /** is the info in auth_sent a preemtive guess or the result of a 4xx */
63     private boolean auth_from_4xx;
64     private boolean prxy_from_4xx;
65
66     /** guard against bugs on both our side and the server side */
67     private int num_tries;
68
69
70     // Constructors
71

72     /**
73      * Initialize counters for challenge and auth-info lists.
74      */

75     AuthorizationModule()
76     {
77     auth_lst_idx = 0;
78     prxy_lst_idx = 0;
79     auth_scm_idx = 0;
80     prxy_scm_idx = 0;
81
82     auth_sent = null;
83     prxy_sent = null;
84
85     auth_from_4xx = false;
86     prxy_from_4xx = false;
87
88     num_tries = 0;
89     }
90
91
92     // Methods
93

94     /**
95      * Invoked by the HTTPClient.
96      */

97     public int requestHandler(Request req, Response[] resp)
98     {
99     HTTPConnection con = req.getConnection();
100     AuthorizationHandler auth_handler = AuthorizationInfo.getAuthHandler();
101     AuthorizationInfo guess;
102
103
104     // Preemptively send proxy authorization info
105

106     Proxy: if (con.getProxyHost() != null && !prxy_from_4xx)
107     {
108         Hashtable JavaDoc proxy_auth_list = Util.getList(proxy_cntxt_list,
109                          req.getConnection().getContext());
110         guess = (AuthorizationInfo) proxy_auth_list.get(
111                     con.getProxyHost()+":"+con.getProxyPort());
112         if (guess == null) break Proxy;
113
114         if (auth_handler != null)
115         {
116         try
117             { guess = auth_handler.fixupAuthInfo(guess, req, null, null); }
118         catch (AuthSchemeNotImplException atnie)
119             { break Proxy; }
120         if (guess == null) break Proxy;
121         }
122
123         int idx;
124         NVPair[] hdrs = req.getHeaders();
125         for (idx=0; idx<hdrs.length; idx++)
126         {
127         if (hdrs[idx].getName().equalsIgnoreCase("Proxy-Authorization"))
128             break; // already set
129
}
130         if (idx == hdrs.length) // add proxy-auth header
131
{
132         hdrs = Util.resizeArray(hdrs, idx+1);
133         req.setHeaders(hdrs);
134         }
135
136         hdrs[idx] = new NVPair("Proxy-Authorization", guess.toString());
137
138         prxy_sent = guess;
139         prxy_from_4xx = false;
140
141         if (DebugMods)
142         System.err.println("AuthM: Preemptively sending " +
143                    "Proxy-Authorization '" + guess + "'");
144     }
145
146
147     // Preemptively send authorization info
148

149     Auth: if (!auth_from_4xx)
150     {
151         guess = AuthorizationInfo.findBest(req);
152         if (guess == null) break Auth;
153
154         if (auth_handler != null)
155         {
156         try
157             { guess = auth_handler.fixupAuthInfo(guess, req, null, null); }
158         catch (AuthSchemeNotImplException atnie)
159             { break Auth; }
160         if (guess == null) break Auth;
161         }
162
163         int idx;
164         NVPair[] hdrs = req.getHeaders();
165         for (idx=0; idx<hdrs.length; idx++)
166         {
167         if (hdrs[idx].getName().equalsIgnoreCase("Authorization"))
168             break; // already set
169
}
170         if (idx == hdrs.length) // add auth header
171
{
172         hdrs = Util.resizeArray(hdrs, idx+1);
173         req.setHeaders(hdrs);
174         }
175
176         hdrs[idx] = new NVPair("Authorization", guess.toString());
177
178         auth_sent = guess;
179         auth_from_4xx = false;
180
181         if (DebugMods)
182         System.err.println("AuthM: Preemptively sending Authorization '"
183                    + guess + "'");
184     }
185
186     return REQ_CONTINUE;
187     }
188
189
190     /**
191      * Invoked by the HTTPClient.
192      */

193     public void responsePhase1Handler(Response resp, RoRequest req)
194         throws IOException JavaDoc
195     {
196     /* If auth info successful update path list. Note: if we
197      * preemptively sent auth info we don't actually know if
198      * it was necessary. Therefore we don't update the path
199      * list in this case; this prevents it from being
200      * contaminated. If the info was necessary, then the next
201      * time we access this resource we will again guess the
202      * same info and send it.
203      */

204     if (resp.getStatusCode() != 401 && resp.getStatusCode() != 407)
205     {
206         if (auth_sent != null && auth_from_4xx)
207         {
208         try
209         {
210             AuthorizationInfo.getAuthorization(auth_sent, req, resp,
211                     false).addPath(req.getRequestURI());
212         }
213         catch (AuthSchemeNotImplException asnie)
214             { /* shouldn't happen */ }
215         }
216
217         // reset guard if not an auth challenge
218
num_tries = 0;
219     }
220
221     auth_from_4xx = false;
222     prxy_from_4xx = false;
223
224     if (resp.getHeader("WWW-Authenticate") == null)
225     {
226         auth_lst_idx = 0;
227         auth_scm_idx = 0;
228     }
229
230     if (resp.getHeader("Proxy-Authenticate") == null)
231     {
232         prxy_lst_idx = 0;
233         prxy_scm_idx = 0;
234     }
235     }
236
237
238     /**
239      * Invoked by the HTTPClient.
240      */

241     public int responsePhase2Handler(Response resp, Request req)
242         throws IOException JavaDoc, AuthSchemeNotImplException
243     {
244     // Let the AuthHandler handle any Authentication headers.
245

246     AuthorizationHandler h = AuthorizationInfo.getAuthHandler();
247     if (h != null)
248         h.handleAuthHeaders(resp, req, auth_sent, prxy_sent);
249
250
251     // handle 401 and 407 response codes
252

253     int sts = resp.getStatusCode();
254     switch(sts)
255     {
256         case 401: // Unauthorized
257
case 407: // Proxy Authentication Required
258

259         // guard against infinite retries due to bugs
260

261         num_tries++;
262         if (num_tries > 10)
263             throw new ProtocolException JavaDoc("Bug in authorization handling: server refused the given info 10 times");
264
265
266         if (DebugMods) System.err.println("AuthM: Handling status: " +
267                         sts + " " + resp.getReasonLine());
268
269
270         // handle WWW-Authenticate
271

272         int[] idx_arr = { auth_lst_idx, // hack to pass by ref
273
auth_scm_idx};
274         auth_sent = setAuthHeaders(resp.getHeader("WWW-Authenticate"),
275                        req, resp, "Authorization", idx_arr,
276                        auth_sent);
277         if (auth_sent != null)
278             auth_from_4xx = true;
279         auth_lst_idx = idx_arr[0];
280         auth_scm_idx = idx_arr[1];
281
282
283         // handle Proxy-Authenticate
284

285         idx_arr[0] = prxy_lst_idx; // hack to pass by ref
286
idx_arr[1] = prxy_scm_idx;
287         prxy_sent = setAuthHeaders(resp.getHeader("Proxy-Authenticate"),
288                        req, resp, "Proxy-Authorization",
289                        idx_arr, prxy_sent);
290         if (prxy_sent != null)
291             prxy_from_4xx = true;
292         prxy_lst_idx = idx_arr[0];
293         prxy_scm_idx = idx_arr[1];
294
295         if (prxy_sent != null)
296         {
297             HTTPConnection con = req.getConnection();
298             Util.getList(proxy_cntxt_list, con.getContext())
299             .put(con.getProxyHost()+":"+con.getProxyPort(),
300                  prxy_sent);
301         }
302
303
304         if (req.getStream() == null &&
305             (auth_sent != null || prxy_sent != null))
306         {
307             try { resp.getInputStream().close(); }
308             catch (IOException JavaDoc ioe) { }
309
310             if (DebugMods)
311             {
312             if (auth_sent != null)
313                 System.err.println("AuthM: Resending request " +
314                            "with Authorization '" +
315                            auth_sent + "'");
316             else
317                 System.err.println("AuthM: Resending request " +
318                            "with Proxy-Authorization '" +
319                            prxy_sent + "'");
320             }
321
322             return RSP_REQUEST;
323         }
324
325
326         // check for headers
327

328         if (auth_sent == null && prxy_sent == null &&
329             resp.getHeader("WWW-Authenticate") == null &&
330             resp.getHeader("Proxy-Authenticate") == null)
331         {
332             if (resp.getStatusCode() == 401)
333             throw new ProtocolException JavaDoc("Missing WWW-Authenticate header");
334             else
335             throw new ProtocolException JavaDoc("Missing Proxy-Authenticate header");
336         }
337
338         if (DebugMods)
339         {
340             if (req.getStream() == null)
341             System.err.println("AuthM: status " + sts + " not " +
342                        "handled - request has an output " +
343                        "stream");
344             else
345             System.err.println("AuthM: No Auth Info found - " +
346                        "status " + sts + " not handled");
347         }
348
349         return RSP_CONTINUE;
350
351         default:
352
353         return RSP_CONTINUE;
354     }
355     }
356
357
358     /**
359      * Invoked by the HTTPClient.
360      */

361     public void responsePhase3Handler(Response resp, RoRequest req)
362     {
363     }
364
365
366     /**
367      * Invoked by the HTTPClient.
368      */

369     public void trailerHandler(Response resp, RoRequest req) throws IOException JavaDoc
370     {
371     // Let the AuthHandler handle any Authentication headers.
372

373     AuthorizationHandler h = AuthorizationInfo.getAuthHandler();
374     if (h != null)
375         h.handleAuthTrailers(resp, req, auth_sent, prxy_sent);
376     }
377
378
379     /**
380      * Handles authentication requests and sets the authorization headers.
381      * It tries to retrieve the neccessary parameters from AuthorizationInfo,
382      * and failing that calls the AuthHandler. Handles multiple authentication
383      * headers.
384      *
385      * @param auth_str the authentication header field returned by the server.
386      * @param req the Request used
387      * @param resp the full Response received
388      * @param header the header name to use in the new headers array.
389      * @param idx_arr an array of indicies holding the state of where we
390      * are when handling multiple authorization headers.
391      * @param prev the previous auth info sent, or null if none
392      * @return the new credentials, or null if none found
393      * @exception ProtocolException if <var>auth_str</var> is null.
394      * @exception AuthSchemeNotImplException if thrown by the AuthHandler.
395      */

396     private AuthorizationInfo setAuthHeaders(String JavaDoc auth_str, Request req,
397                          RoResponse resp, String JavaDoc header,
398                          int[] idx_arr,
399                          AuthorizationInfo prev)
400     throws ProtocolException JavaDoc, AuthSchemeNotImplException
401     {
402     if (auth_str == null) return null;
403
404     // get the list of challenges the server sent
405
AuthorizationInfo[] challenge =
406             AuthorizationInfo.parseAuthString(auth_str, req, resp);
407
408     if (DebugMods)
409     {
410         System.err.println("AuthM: parsed " + challenge.length +
411                    " challenges:");
412         for (int idx=0; idx<challenge.length; idx++)
413         System.err.println("AuthM: Challenge " + challenge[idx]);
414     }
415
416
417     /* some servers expect a 401 to invalidate sent credentials.
418      * However, only do this for Basic scheme (because e.g. digest
419      * "stale" handling will fail otherwise)
420      */

421     if (prev != null && prev.getScheme().equalsIgnoreCase("Basic"))
422     {
423         for (int idx=0; idx<challenge.length; idx++)
424         if (prev.getRealm().equals(challenge[idx].getRealm()) &&
425             prev.getScheme().equalsIgnoreCase(challenge[idx].getScheme()))
426             AuthorizationInfo.removeAuthorization(prev,
427                           req.getConnection().getContext());
428     }
429
430     AuthorizationInfo credentials = null;
431     AuthorizationHandler auth_handler = AuthorizationInfo.getAuthHandler();
432
433     // try next auth challenge in list
434
while (credentials == null && idx_arr[0] != -1)
435     {
436         credentials =
437         AuthorizationInfo.getAuthorization(challenge[idx_arr[0]], req,
438                            resp, false);
439         if (auth_handler != null && credentials != null)
440         credentials = auth_handler.fixupAuthInfo(credentials, req,
441                         challenge[idx_arr[0]], resp);
442         if (++idx_arr[0] == challenge.length)
443         idx_arr[0] = -1;
444     }
445
446
447     // if we don't have any credentials then prompt the user
448
if (credentials == null)
449     {
450         for (int idx=0; idx<challenge.length; idx++)
451         {
452         try
453         {
454             credentials = AuthorizationInfo.queryAuthHandler(
455                         challenge[idx_arr[1]], req, resp);
456         }
457         catch (AuthSchemeNotImplException atnie)
458         {
459             if (idx == challenge.length-1)
460             throw atnie;
461             continue;
462         }
463         break;
464         }
465         if (++idx_arr[1] == challenge.length)
466         idx_arr[1] = 0;
467     }
468
469     // if we still don't have any credentials then give up
470
if (credentials == null)
471         return null;
472
473     // find auth info
474
int auth_idx;
475     NVPair[] hdrs = req.getHeaders();
476     for (auth_idx=0; auth_idx<hdrs.length; auth_idx++)
477     {
478         if (hdrs[auth_idx].getName().equalsIgnoreCase(header))
479         break;
480     }
481
482     // add credentials to headers
483
if (auth_idx == hdrs.length)
484     {
485         hdrs = Util.resizeArray(hdrs, auth_idx+1);
486         req.setHeaders(hdrs);
487     }
488     hdrs[auth_idx] = new NVPair(header, credentials.toString());
489
490     return credentials;
491     }
492 }
493
494
Popular Tags