зеркало из https://github.com/mozilla/pjs.git
make HTTP auth pluggable (#39781), r=gagan
This commit is contained in:
Родитель
efb5741989
Коммит
0d14cb0f41
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче