/* * @(#)AuthorizationInfo.java 0.3-3 06/05/2001 * * This file is part of the HTTPClient package * Copyright (C) 1996-2001 Ronald Tschalär * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307, USA * * For questions, suggestions, bug-reports, enhancement-requests etc. * I may be contacted at: * * ronald@innovation.ch * * The HTTPClient's home page is located at: * * http://www.innovation.ch/java/HTTPClient/ * */ package HTTPClient; import java.io.IOException; import java.net.ProtocolException; import java.util.Vector; import java.util.Hashtable; import java.util.Enumeration; /** * Holds the information for an authorization response. * *
There are 7 fields which make up this class: host, port, scheme, * realm, cookie, params, and extra_info. The host and port select which * server the info will be sent to. The realm is server specified string * which groups various URLs under a given server together and which is * used to select the correct info when a server issues an auth challenge; * for schemes which don't use a realm (such as "NTLM", "PEM", and * "Kerberos") the realm must be the empty string (""). The scheme is the * authorization scheme used (such as "Basic" or "Digest"). * *
There are basically two formats used for the Authorization header, * the one used by the "Basic" scheme and derivatives, and the one used by * the "Digest" scheme and derivatives. The first form contains just the * the scheme and a "cookie": * *
Authorization: Basic aGVsbG86d29ybGQ=* * The second form contains the scheme followed by a number of parameters * in the form of name=value pairs: * *
Authorization: Digest username="hello", realm="test", nonce="42", ...* * The two fields "cookie" and "params" correspond to these two forms. * toString() is used by the AuthorizationModule * when generating the Authorization header and will format the info * accordingly. Note that "cookie" and "params" are mutually exclusive: if * the cookie field is non-null then toString() will generate the first * form; otherwise it will generate the second form. * *
In some schemes "extra" information needs to be kept which doesn't * appear directly in the Authorization header. An example of this are the * A1 and A2 strings in the Digest scheme. Since all elements in the params * field will appear in the Authorization header this field can't be used * for storing such info. This is what the extra_info field is for. It is * an arbitrary object which can be manipulated by the corresponding * setExtraInfo() and getExtraInfo() methods, but which will not be printed * by toString(). * *
The addXXXAuthorization(), removeXXXAuthorization(), and * getAuthorization() methods manipulate and query an internal list of * AuthorizationInfo instances. There can be only one instance per host, * port, scheme, and realm combination (see equals()). * * @version 0.3-3 06/05/2001 * @author Ronald Tschalär * @since V0.1 */ public class AuthorizationInfo implements Cloneable { // class fields /** Holds the list of lists of authorization info structures */ private static Hashtable CntxtList = new Hashtable(); /** A pointer to the handler to be called when we need authorization info */ private static AuthorizationHandler AuthHandler = new DefaultAuthHandler(); static { CntxtList.put(HTTPConnection.getDefaultContext(), new Hashtable()); } // the instance oriented stuff /** the host (lowercase) */ private String host; /** the port */ private int port; /** the scheme. (e.g. "Basic") * Note: don't lowercase because some buggy servers use a case-sensitive * match */ private String scheme; /** the realm */ private String realm; /** the string used for the "Basic", "NTLM", and other authorization * schemes which don't use parameters */ private String cookie; /** any parameters */ private NVPair[] auth_params = new NVPair[0]; /** additional info which won't be displayed in the toString() */ private Object extra_info = null; /** a list of paths where this realm has been known to be required */ private String[] paths = new String[0]; // Constructors /** * Creates an new info structure for the specified host and port. * * @param host the host * @param port the port */ AuthorizationInfo(String host, int port) { this.host = host.trim().toLowerCase(); this.port = port; } /** * Creates a new info structure for the specified host and port with the * specified scheme, realm, params. The cookie is set to null. * * @param host the host * @param port the port * @param scheme the scheme * @param realm the realm * @param params the parameters as an array of name/value pairs, or null * @param info arbitrary extra info, or null */ public AuthorizationInfo(String host, int port, String scheme, String realm, NVPair params[], Object info) { this.scheme = scheme.trim(); this.host = host.trim().toLowerCase(); this.port = port; this.realm = realm; this.cookie = null; if (params != null) auth_params = Util.resizeArray(params, params.length); this.extra_info = info; } /** * Creates a new info structure for the specified host and port with the * specified scheme, realm and cookie. The params is set to a zero-length * array, and the extra_info is set to null. * * @param host the host * @param port the port * @param scheme the scheme * @param realm the realm * @param cookie for the "Basic" scheme this is the base64-encoded * username/password; for the "NTLM" scheme this is the * base64-encoded username/password message. */ public AuthorizationInfo(String host, int port, String scheme, String realm, String cookie) { this.scheme = scheme.trim(); this.host = host.trim().toLowerCase(); this.port = port; this.realm = realm; if (cookie != null) this.cookie = cookie.trim(); else this.cookie = null; } /** * Creates a new copy of the given AuthorizationInfo. * * @param templ the info to copy */ AuthorizationInfo(AuthorizationInfo templ) { this.scheme = templ.scheme; this.host = templ.host; this.port = templ.port; this.realm = templ.realm; this.cookie = templ.cookie; this.auth_params = Util.resizeArray(templ.auth_params, templ.auth_params.length); this.extra_info = templ.extra_info; } // Class Methods /** * Set's the authorization handler. This handler is called whenever * the server requests authorization and no entry for the requested * scheme and realm can be found in the list. The handler must implement * the AuthorizationHandler interface. * *
If no handler is set then a {@link DefaultAuthHandler default * handler} is used. This handler currently only handles the "Basic" and * "Digest" schemes and brings up a popup which prompts for the username * and password. * *
The default handler can be disabled by setting the auth handler to
* null.
*
* @param handler the new authorization handler
* @return the old authorization handler
* @see AuthorizationHandler
*/
public static AuthorizationHandler
setAuthHandler(AuthorizationHandler handler)
{
AuthorizationHandler tmp = AuthHandler;
AuthHandler = handler;
return tmp;
}
/**
* Get's the current authorization handler.
*
* @return the current authorization handler, or null if none is set.
* @see AuthorizationHandler
*/
public static AuthorizationHandler getAuthHandler()
{
return AuthHandler;
}
/**
* Searches for the authorization info using the given host, port,
* scheme and realm. The context is the default context.
*
* @param host the host
* @param port the port
* @param scheme the scheme
* @param realm the realm
* @return a pointer to the authorization data or null if not found
*/
public static AuthorizationInfo getAuthorization(
String host, int port,
String scheme, String realm)
{
return getAuthorization(host, port, scheme, realm,
HTTPConnection.getDefaultContext());
}
/**
* Searches for the authorization info in the given context using the
* given host, port, scheme and realm.
*
* @param host the host
* @param port the port
* @param scheme the scheme
* @param realm the realm
* @param context the context this info is associated with
* @return a pointer to the authorization data or null if not found
*/
public static synchronized AuthorizationInfo getAuthorization(
String host, int port,
String scheme, String realm,
Object context)
{
Hashtable AuthList = Util.getList(CntxtList, context);
AuthorizationInfo auth_info =
new AuthorizationInfo(host, port, scheme, realm, (NVPair[]) null,
null);
return (AuthorizationInfo) AuthList.get(auth_info);
}
/**
* Queries the AuthHandler for authorization info. It also adds this
* info to the list.
*
* @param auth_info any info needed by the AuthHandler; at a minimum the
* host, scheme and realm should be set.
* @param req the request which initiated this query
* @param resp the full response
* @return a structure containing the requested info, or null if either
* no AuthHandler is set or the user canceled the request.
* @exception AuthSchemeNotImplException if this is thrown by
* the AuthHandler.
*/
static AuthorizationInfo queryAuthHandler(AuthorizationInfo auth_info,
RoRequest req, RoResponse resp)
throws AuthSchemeNotImplException, IOException
{
if (AuthHandler == null)
return null;
AuthorizationInfo new_info =
AuthHandler.getAuthorization(auth_info, req, resp);
if (new_info != null)
{
if (req != null)
addAuthorization((AuthorizationInfo) new_info.clone(),
req.getConnection().getContext());
else
addAuthorization((AuthorizationInfo) new_info.clone(),
HTTPConnection.getDefaultContext());
}
return new_info;
}
/**
* Searches for the authorization info using the host, port, scheme and
* realm from the given info struct. If not found it queries the
* AuthHandler (if set).
*
* @param auth_info the AuthorizationInfo
* @param req the request which initiated this query
* @param resp the full response
* @param query_auth_h if true, query the auth-handler if no info found.
* @return a pointer to the authorization data or null if not found
* @exception AuthSchemeNotImplException If thrown by the AuthHandler.
*/
static synchronized AuthorizationInfo getAuthorization(
AuthorizationInfo auth_info, RoRequest req,
RoResponse resp, boolean query_auth_h)
throws AuthSchemeNotImplException, IOException
{
Hashtable AuthList;
if (req != null)
AuthList = Util.getList(CntxtList, req.getConnection().getContext());
else
AuthList = Util.getList(CntxtList, HTTPConnection.getDefaultContext());
AuthorizationInfo new_info =
(AuthorizationInfo) AuthList.get(auth_info);
if (new_info == null && query_auth_h)
new_info = queryAuthHandler(auth_info, req, resp);
return new_info;
}
/**
* Searches for the authorization info given a host, port, scheme and
* realm. Queries the AuthHandler if not found in list.
*
* @param host the host
* @param port the port
* @param scheme the scheme
* @param realm the realm
* @param req the request which initiated this query
* @param resp the full response
* @param query_auth_h if true, query the auth-handler if no info found.
* @return a pointer to the authorization data or null if not found
* @exception AuthSchemeNotImplException If thrown by the AuthHandler.
*/
static AuthorizationInfo getAuthorization(String host, int port,
String scheme, String realm,
RoRequest req, RoResponse resp,
boolean query_auth_h)
throws AuthSchemeNotImplException, IOException
{
return getAuthorization(new AuthorizationInfo(host, port, scheme,
realm, (NVPair[]) null, null),
req, resp, query_auth_h);
}
/**
* Adds an authorization entry to the list using the default context.
* If an entry for the specified scheme and realm already exists then
* its cookie and params are replaced with the new data.
*
* @param auth_info the AuthorizationInfo to add
*/
public static void addAuthorization(AuthorizationInfo auth_info)
{
addAuthorization(auth_info, HTTPConnection.getDefaultContext());
}
/**
* Adds an authorization entry to the list. If an entry for the
* specified scheme and realm already exists then its cookie and
* params are replaced with the new data.
*
* @param auth_info the AuthorizationInfo to add
* @param context the context to associate this info with
*/
public static void addAuthorization(AuthorizationInfo auth_info,
Object context)
{
Hashtable AuthList = Util.getList(CntxtList, context);
// merge path list
AuthorizationInfo old_info =
(AuthorizationInfo) AuthList.get(auth_info);
if (old_info != null)
{
int ol = old_info.paths.length,
al = auth_info.paths.length;
if (al == 0)
auth_info.paths = old_info.paths;
else
{
auth_info.paths = Util.resizeArray(auth_info.paths, al+ol);
System.arraycopy(old_info.paths, 0, auth_info.paths, al, ol);
}
}
AuthList.put(auth_info, auth_info);
}
/**
* Adds an authorization entry to the list using the default context.
* If an entry for the specified scheme and realm already exists then
* its cookie and params are replaced with the new data.
*
* @param host the host
* @param port the port
* @param scheme the scheme
* @param realm the realm
* @param cookie the cookie
* @param params an array of name/value pairs of parameters
* @param info arbitrary extra auth info
*/
public static void addAuthorization(String host, int port, String scheme,
String realm, String cookie,
NVPair params[], Object info)
{
addAuthorization(host, port, scheme, realm, cookie, params, info,
HTTPConnection.getDefaultContext());
}
/**
* Adds an authorization entry to the list. If an entry for the
* specified scheme and realm already exists then its cookie and
* params are replaced with the new data.
*
* @param host the host
* @param port the port
* @param scheme the scheme
* @param realm the realm
* @param cookie the cookie
* @param params an array of name/value pairs of parameters
* @param info arbitrary extra auth info
* @param context the context to associate this info with
*/
public static void addAuthorization(String host, int port, String scheme,
String realm, String cookie,
NVPair params[], Object info,
Object context)
{
AuthorizationInfo auth =
new AuthorizationInfo(host, port, scheme, realm, cookie);
if (params != null && params.length > 0)
auth.auth_params = Util.resizeArray(params, params.length);
auth.extra_info = info;
addAuthorization(auth, context);
}
/**
* Adds an authorization entry for the "Basic" authorization scheme to
* the list using the default context. If an entry already exists for
* the "Basic" scheme and the specified realm then it is overwritten.
*
* @param host the host
* @param port the port
* @param realm the realm
* @param user the username
* @param passwd the password
*/
public static void addBasicAuthorization(String host, int port,
String realm, String user,
String passwd)
{
addAuthorization(host, port, "Basic", realm,
Codecs.base64Encode(user + ":" + passwd),
(NVPair[]) null, null);
}
/**
* Adds an authorization entry for the "Basic" authorization scheme to
* the list. If an entry already exists for the "Basic" scheme and the
* specified realm then it is overwritten.
*
* @param host the host
* @param port the port
* @param realm the realm
* @param user the username
* @param passwd the password
* @param context the context to associate this info with
*/
public static void addBasicAuthorization(String host, int port,
String realm, String user,
String passwd, Object context)
{
addAuthorization(host, port, "Basic", realm,
Codecs.base64Encode(user + ":" + passwd),
(NVPair[]) null, null, context);
}
/**
* Adds an authorization entry for the "Digest" authorization scheme to
* the list using the default context. If an entry already exists for the
* "Digest" scheme and the specified realm then it is overwritten.
*
* @param host the host
* @param port the port
* @param realm the realm
* @param user the username
* @param passwd the password
*/
public static void addDigestAuthorization(String host, int port,
String realm, String user,
String passwd)
{
addDigestAuthorization(host, port, realm, user, passwd,
HTTPConnection.getDefaultContext());
}
/**
* Adds an authorization entry for the "Digest" authorization scheme to
* the list. If an entry already exists for the "Digest" scheme and the
* specified realm then it is overwritten.
*
* @param host the host
* @param port the port
* @param realm the realm
* @param user the username
* @param passwd the password
* @param context the context to associate this info with
*/
public static void addDigestAuthorization(String host, int port,
String realm, String user,
String passwd, Object context)
{
AuthorizationInfo prev =
getAuthorization(host, port, "Digest", realm, context);
NVPair[] params;
if (prev == null)
{
params = new NVPair[4];
params[0] = new NVPair("username", user);
params[1] = new NVPair("uri", "");
params[2] = new NVPair("nonce", "");
params[3] = new NVPair("response", "");
}
else
{
params = prev.getParams();
for (int idx=0; idx