make HTTP auth pluggable (#39781), r=gagan

This commit is contained in:
shaver%mozilla.org 2000-06-02 00:23:56 +00:00
Родитель efb5741989
Коммит 0d14cb0f41
8 изменённых файлов: 269 добавлений и 86 удалений

Просмотреть файл

@ -2,6 +2,7 @@
# This is a list of local files which get copied to the mozilla:dist directory
#
nsIAuthenticator.idl
nsIChannel.idl
nsIFileTransportService.idl
nsIPrompt.idl

Просмотреть файл

@ -29,6 +29,7 @@ include $(DEPTH)/config/autoconf.mk
MODULE = necko
XPIDLSRCS = \
nsIAuthenticator.idl \
nsIFileStreams.idl \
nsIRequest.idl \
nsIChannel.idl \

Просмотреть файл

@ -31,6 +31,7 @@ EXPORTS = \
$(NULL)
XPIDLSRCS = \
.\nsIAuthenticator.idl \
.\nsIChannel.idl \
.\nsIFileStreams.idl \
.\nsIFileTransportService.idl \

Просмотреть файл

@ -0,0 +1,78 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* The contents of this file are subject to the Mozilla Public
* License Version 1.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla code.
*
* The Initial Developer of the Original Code is Zero-Knowledge Systems,
* Inc. Portions created by Zero-Knowledge are Copyright (C) 2000
* Zero-Knowledge Systems, Inc. All Rights Reserved.
*
* Contributor(s):
*/
#include "nsISupports.idl"
#include "nsIPrompt.idl"
#include "nsIURL.idl"
[scriptable,uuid(adf74d2a-1dd1-11b2-b129-f3154be37959)]
interface nsIAuthenticator : nsISupports
{
/**
* @param uri The URI for which authentication is required.
* @param uri The protocol, for selecting authentication-style. Variant
* protocols, such as http/https and imap/imaps will likely all
* use the primary protocol name ("http", "imap"), to reduce
*
* @param challenge The complete value of the WWW-Authenticate header from
* the response.
* @param username The username provided in the prehost portion of the URI,
* if any.
* @param password The password provided in the prehost portion of the URI,
* if any.
* @param prompter The standard prompter for interacting with the user.
* @param
* @return The complete authentication value to return. For Basic HTTP
* auth, as an example, the returned string would be
* "Basic BASE64(<user>:<pass>)". The format is dependent on the
* provided protocol parameter.
*/
string authenticate(in nsIURI uri, in string protocol, in string challenge,
in wstring username, in wstring password,
in nsIPrompt prompter);
/**
* No interaction with the user required. This indicates that the
* authenticate method can be called with a null prompter argument, though
* calling code should strive to provide it if at all possible. (While
* interaction with the user may not be _required_, it might still be
* desire
*/
const PRUint32 INTERACTION_NONE = 0;
/**
* Standard username and optional password required, and the caller should
* prompt for it.
*/
const PRUint32 INTERACTION_STANDARD = 1;
/**
* Custom interaction required: Mozilla must provide a non-null prompter
* argument when calling authenticate.
*/
const PRUint32 INTERACTION_CUSTOM = 2;
/**
* What kind of interaction with the user does this authentication method
* require?
*/
readonly attribute PRUint32 interactionType;
};

Просмотреть файл

@ -24,6 +24,7 @@
#include "nsIGenericFactory.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsICategoryManager.h"
#include "nsIOService.h"
#include "nsNetModuleMgr.h"
#include "nsFileTransportService.h"
@ -78,12 +79,50 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsMIMEInfoImpl);
#include "nsIHTTPProtocolHandler.h"
#include "nsHTTPHandler.h"
#include "nsHTTPSHandler.h"
#include "nsBasicAuth.h"
NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsHTTPHandler, Init);
//NS_GENERIC_FACTORY_CONSTRUCTOR(nsHTTPSHandler);
#define NS_HTTPS_HANDLER_FACTORY_CID { 0xd2771480, 0xcac4, 0x11d3, { 0x8c, 0xaf, 0x0, 0x0, 0x64, 0x65, 0x73, 0x74 } }
NS_GENERIC_FACTORY_CONSTRUCTOR(nsBasicAuth);
#define NS_BASICAUTH_CID { 0xd5c9bc48, 0x1dd1, 0x11b2, { 0x9a, 0x0b, 0xf7, 0x3f, 0x59, 0x53, 0x19, 0xae } }
#define NS_BASICAUTH_PROGID "mozilla.network.http-basic-auth.1"
/* XXX this should all be data-driven, via NS_IMPL_GETMODULE_WITH_CATEGORIES */
static nsresult
RegisterBasicAuth(nsIComponentManager *aCompMgr, nsIFile *aPath,
const char *registryLocation, const char *componentType)
{
nsresult rv;
nsCOMPtr<nsICategoryManager> catman =
do_GetService(NS_CATEGORYMANAGER_PROGID, &rv);
if (NS_FAILED(rv)) return rv;
nsXPIDLCString previous;
return catman->AddCategoryEntry("http-auth", "Basic", NS_BASICAUTH_PROGID,
PR_TRUE, PR_TRUE, getter_Copies(previous));
}
static nsresult
UnregisterBasicAuth(nsIComponentManager *aCompMgr, nsIFile *aPath,
const char *registryLocation)
{
nsresult rv;
nsCOMPtr<nsICategoryManager> catman =
do_GetService(NS_CATEGORYMANAGER_PROGID, &rv);
if (NS_FAILED(rv)) return rv;
nsXPIDLCString basicAuth;
rv = catman->GetCategoryEntry("http-auth", "Basic",
getter_Copies(basicAuth));
if (NS_FAILED(rv)) return rv;
// only unregister if we're the current Basic-auth handler
if (!strcmp(basicAuth, NS_BASICAUTH_PROGID))
return catman->DeleteCategoryEntry("http-auth", "Basic", PR_TRUE,
getter_Copies(basicAuth));
return NS_OK;
}
///////////////////////////////////////////////////////////////////////////////
#include "nsFileChannel.h"
@ -534,6 +573,13 @@ static nsModuleComponentInfo gNetModuleInfo[] = {
NS_HTTPS_HANDLER_FACTORY_CID,
NS_NETWORK_PROTOCOL_PROGID_PREFIX "https",
nsHTTPSHandler::Create },
{ "Basic Auth Encoder",
NS_BASICAUTH_CID,
NS_BASICAUTH_PROGID,
nsBasicAuthConstructor,
RegisterBasicAuth,
UnregisterBasicAuth
},
// from netwerk/protocol/data:
{ "Data Protocol Handler",

Просмотреть файл

@ -37,32 +37,59 @@ nsBasicAuth::~nsBasicAuth()
}
nsresult
nsBasicAuth::Authenticate(nsIURI* i_URI,
const char* iChallenge,
const char* iUserPass,
char** oResult)
nsBasicAuth::Authenticate(nsIURI* i_URI, const char *protocol,
const char* iChallenge, const PRUnichar* iUser,
const PRUnichar* iPass, nsIPrompt *prompt,
char **oResult)
{
nsresult rv = NS_ERROR_FAILURE;
// Assert that iChallenge starts with Basic. TODO
// Then do the conversion
if (oResult && iUserPass)
{
char* tempBuff = nsnull;
tempBuff = PL_Base64Encode(iUserPass, 0, tempBuff);
if (!tempBuff)
return NS_ERROR_FAILURE; // ??
// STRING USE WARNING: perhaps |authString| should be an |nsCAutoString|? -- scc
nsString authString; authString.AssignWithConversion("Basic ");
authString.AppendWithConversion(tempBuff);
*oResult = authString.ToNewCString();
PR_Free(tempBuff);
rv = NS_OK;
// we only know how to deal with Basic auth for http.
PRBool isBasicAuth = !strncmp(iChallenge, "Basic ", 6);
NS_ASSERTION(isBasicAuth, "nsBasicAuth called for non-Basic auth");
if (!isBasicAuth)
return NS_ERROR_INVALID_ARG;
PRBool isHTTPAuth = !strncmp(protocol, "http", 4);
NS_ASSERTION(isHTTPAuth, "nsBasicAuth called for non-http auth");
if (!isHTTPAuth)
return NS_ERROR_INVALID_ARG;
if (!oResult || !iUser)
return NS_ERROR_NULL_POINTER;
// we work with ASCII around these here parts
nsCAutoString cUser, cPass;
cUser.AssignWithConversion(iUser);
if (iPass) {
cPass.AssignWithConversion(iPass);
}
return rv;
char* tempBuff = (char *)nsAllocator::Alloc(cUser.Length() +
iPass ? (cPass.Length() + 2)
: 1);
if (!tempBuff)
return NS_ERROR_OUT_OF_MEMORY;
strcpy(tempBuff, cUser.GetBuffer());
if (iPass) {
strcat(tempBuff, ":");
strcat(tempBuff, cPass.GetBuffer());
}
char *base64Buff = PL_Base64Encode(tempBuff, 0, nsnull);
if (!base64Buff) {
nsAllocator::Free(tempBuff);
return NS_ERROR_FAILURE; // ??
}
nsCAutoString authString("Basic "); // , 6 + strlen(base64Buff));
authString.Append(base64Buff);
*oResult = authString.ToNewCString();
PR_Free(base64Buff);
nsAllocator::Free(tempBuff);
return NS_OK;
}
NS_IMPL_ADDREF(nsBasicAuth);
NS_IMPL_RELEASE(nsBasicAuth);
nsresult
nsBasicAuth::GetInteractionType(PRUint32 *aType)
{
*aType = nsIAuthenticator::INTERACTION_STANDARD;
return NS_OK;
}
NS_IMPL_QUERY_INTERFACE0(nsBasicAuth);
NS_IMPL_ISUPPORTS1(nsBasicAuth, nsIAuthenticator);

Просмотреть файл

@ -24,38 +24,27 @@
#define _nsBasicAuth_h_
#include "nsISupports.h"
#include "nsIAuthenticator.h"
/*
The nsBasicAuth class converts a username:password string
to a Base-64 encoded version. If you want to do other kind
of encoding (MD5, Digest) there should really be a super
class that does the Authenticate function. Will add that later...
-Gagan Saksena 08/17/1999
*/
/*
* The nsBasicAuth class produces HTTP Basic-auth responses for a username/
* (optional)password pair, BASE64("user:pass").
*/
class nsIURI;
class nsBasicAuth : public nsISupports
class nsBasicAuth : public nsIAuthenticator
{
public:
// Constructor and Destructor
nsBasicAuth();
virtual ~nsBasicAuth();
// Functions from nsISupports
NS_DECL_ISUPPORTS
// Authenticate-- the actual method
static nsresult Authenticate(
nsIURI* iUri,
const char* i_Challenge, // "Basic realm='....'"
const char* i_UserPassString, // username:password
char** o_Output);
private:
// Functions from nsISupports
NS_DECL_ISUPPORTS
// Functions from nsIAuthenticator
NS_DECL_NSIAUTHENTICATOR
};
#endif // _nsBasicAuth_h_

Просмотреть файл

@ -56,9 +56,8 @@
// FIXME - Temporary include. Delete this when cache is enabled on all
// platforms
#include "nsIPref.h"
#include "nsIAuthenticator.h"
// Once other kinds of auth are up change TODO
#include "nsBasicAuth.h"
static NS_DEFINE_CID(kProxyObjectManagerCID, NS_PROXYEVENT_MANAGER_CID);
#include "nsProxiedService.h"
@ -1793,6 +1792,7 @@ nsHTTPChannel::Authenticate(const char *iChallenge, PRBool iProxyAuth)
NS_WITH_SERVICE(nsIIOService, serv, kIOServiceCID, &rv);
if (NS_FAILED(rv)) return rv;
//flush out existing records of this URI in authengine-
nsAuthEngine* pEngine;
if (NS_SUCCEEDED(mHandler->GetAuthEngine(&pEngine)))
@ -1801,78 +1801,118 @@ nsHTTPChannel::Authenticate(const char *iChallenge, PRBool iProxyAuth)
}
// Determine the new username password combination to use
char* newUserPass = nsnull;
nsAutoString user, passwd;
if (!mAuthTriedWithPrehost && !iProxyAuth) // Proxy auth's never in prehost
{
nsXPIDLCString prehost;
/* XXX utility routine to get user/pass from prehost? */
if (NS_SUCCEEDED(rv = mURI->GetPreHost(getter_Copies(prehost))))
{
if ((const char*)prehost) {
rv = serv->Unescape(prehost, &newUserPass);
if (NS_FAILED(rv)) return rv;
nsXPIDLCString newUserPass;
if ((const char*)prehost) {
/* XXX should we be un-escaping? 4.x didn't. */
rv = serv->Unescape(prehost, getter_Copies(newUserPass));
if (NS_FAILED(rv)) return rv;
const char *colonBlow = strchr(newUserPass, ':');
if (colonBlow) {
// "user:pass"
user.AssignWithConversion(newUserPass,
colonBlow - (const char *)newUserPass);
passwd.AssignWithConversion(colonBlow + 1);
} else {
/* just "user" */
user.AssignWithConversion(newUserPass);
}
}
}
}
// Couldnt get one from prehost or has already been tried so...ask
if (!newUserPass || !*newUserPass)
nsCAutoString authType;
nsXPIDLCString authString;
const char *space = strchr(iChallenge, ' ');
if (space) {
authType.Assign(iChallenge, space - iChallenge);
} else {
authType.Assign(iChallenge);
}
#ifdef DEBUG_shaver
fprintf(stderr, "Auth type: \"%s\"\n", authType.GetBuffer());
#endif
nsCOMPtr<nsIAuthenticator> auth =
do_GetServiceFromCategory("http-auth", authType, &rv);
if (NS_FAILED(rv))
// XXX report "Authentication-type not supported: %s"
return rv;
nsXPIDLString userBuf, passwdBuf;
// save me, waterson!
*getter_Copies(userBuf) = user.ToNewUnicode();
*getter_Copies(passwdBuf) = passwd.ToNewUnicode();
PRUint32 interactionType;
rv = auth->GetInteractionType(&interactionType);
if (NS_FAILED(rv))
interactionType = nsIAuthenticator::INTERACTION_STANDARD;
// Couldnt get one from prehost
if (!user.Length() &&
interactionType == nsIAuthenticator::INTERACTION_STANDARD)
{
/*
Throw a modal dialog box asking for
username, password. Prefill (!?!)
Throw a modal dialog box asking for
username, password. Prefill (!?!)
*/
if (!mPrompter)
return rv;
PRUnichar *user=nsnull, *passwd=nsnull;
return NS_ERROR_FAILURE;
PRBool retval = PR_FALSE;
//TODO localize it!
nsAutoString message; message.AssignWithConversion("Enter username for ");
// later on change to only show realm and then host's info.
// later on change to only show realm and then host's info.
message.AppendWithConversion(iChallenge);
// Get url
nsXPIDLCString urlCString;
mURI->GetPrePath(getter_Copies(urlCString));
nsAutoString prePath = NS_ConvertToString(urlCString); // XXX i18n
rv = mPrompter->PromptUsernameAndPassword(nsnull,
message.GetUnicode(),
prePath.GetUnicode(),
PR_FALSE,
&user,
&passwd,
getter_Copies(userBuf),
getter_Copies(passwdBuf),
&retval);
if (NS_SUCCEEDED(rv) && (retval))
{
nsAutoString temp(user);
if (passwd) {
temp.AppendWithConversion(':');
temp += passwd;
}
CRTFREEIF(newUserPass);
newUserPass = temp.ToNewCString();
}
else
if (NS_FAILED(rv))
return rv;
}
// Construct the auth string request header based on info provided.
nsXPIDLCString authString;
// change this later to include other kinds of authentication. TODO
if (NS_FAILED(rv = nsBasicAuth::Authenticate(
mURI,
NS_STATIC_CAST(const char*, iChallenge),
NS_STATIC_CAST(const char*, newUserPass),
getter_Copies(authString))))
return rv; // Failed to construct an authentication string.
if (!userBuf[0] &&
(interactionType == nsIAuthenticator::INTERACTION_STANDARD ||
interactionType == nsIAuthenticator::INTERACTION_NONE)) {
/* can't proceed without at least a username, can we? */
return NS_ERROR_FAILURE;
}
if (NS_FAILED(rv) ||
NS_FAILED(rv = auth->Authenticate(mURI, "http", iChallenge,
userBuf, passwdBuf, mPrompter,
getter_Copies(authString))))
return rv;
#ifdef DEBUG_shaver
fprintf(stderr, "Auth string: %s\n", (const char *)authString);
#endif
// Construct a new channel
// For security/privacy purposes, a response to an authenticated request is
// not cached, except perhaps in the memory cache.
// XXX if we had username and passwd in user-auth, and the interaction
// XXX was standard or none, then it's safe to cache, I think (shaver)
mLoadAttributes |= nsIChannel::INHIBIT_PERSISTENT_CACHING;
// This smells like a clone function... maybe there is a