Bug 1221320 - XMLHttpRequest authentication should not require auth prompt dialog, r=honzab.moz

This commit is contained in:
Andrea Marchesini 2015-12-30 18:47:55 +00:00
parent aa49b603d7
commit be6fc8d36f
11 changed files with 100 additions and 122 deletions

View File

@ -178,72 +178,6 @@ static void AddLoadFlags(nsIRequest *request, nsLoadFlags newFlags)
request->SetLoadFlags(flags);
}
//-----------------------------------------------------------------------------
// XMLHttpRequestAuthPrompt
//-----------------------------------------------------------------------------
class XMLHttpRequestAuthPrompt : public nsIAuthPrompt
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIAUTHPROMPT
XMLHttpRequestAuthPrompt();
protected:
virtual ~XMLHttpRequestAuthPrompt();
};
NS_IMPL_ISUPPORTS(XMLHttpRequestAuthPrompt, nsIAuthPrompt)
XMLHttpRequestAuthPrompt::XMLHttpRequestAuthPrompt()
{
MOZ_COUNT_CTOR(XMLHttpRequestAuthPrompt);
}
XMLHttpRequestAuthPrompt::~XMLHttpRequestAuthPrompt()
{
MOZ_COUNT_DTOR(XMLHttpRequestAuthPrompt);
}
NS_IMETHODIMP
XMLHttpRequestAuthPrompt::Prompt(const char16_t* aDialogTitle,
const char16_t* aText,
const char16_t* aPasswordRealm,
uint32_t aSavePassword,
const char16_t* aDefaultText,
char16_t** aResult,
bool* aRetval)
{
*aRetval = false;
return NS_OK;
}
NS_IMETHODIMP
XMLHttpRequestAuthPrompt::PromptUsernameAndPassword(const char16_t* aDialogTitle,
const char16_t* aDialogText,
const char16_t* aPasswordRealm,
uint32_t aSavePassword,
char16_t** aUser,
char16_t** aPwd,
bool* aRetval)
{
*aRetval = false;
return NS_OK;
}
NS_IMETHODIMP
XMLHttpRequestAuthPrompt::PromptPassword(const char16_t* aDialogTitle,
const char16_t* aText,
const char16_t* aPasswordRealm,
uint32_t aSavePassword,
char16_t** aPwd,
bool* aRetval)
{
*aRetval = false;
return NS_OK;
}
/////////////////////////////////////////////
NS_IMPL_CYCLE_COLLECTION_CLASS(nsXHREventTarget)
@ -2901,6 +2835,10 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)
mChannel->GetNotificationCallbacks(getter_AddRefs(mNotificationCallbacks));
mChannel->SetNotificationCallbacks(this);
if (internalHttpChannel) {
internalHttpChannel->SetBlockAuthPrompt(ShouldBlockAuthPrompt());
}
// Start reading from the channel
// Because of bug 682305, we can't let listener be the XHR object itself
// because JS wouldn't be able to use it. So create a listener around 'this'.
@ -3521,59 +3459,6 @@ nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
}
else if (aIID.Equals(NS_GET_IID(nsIAuthPrompt)) ||
aIID.Equals(NS_GET_IID(nsIAuthPrompt2))) {
nsCOMPtr<nsIURI> uri;
rv = mChannel->GetURI(getter_AddRefs(uri));
NS_ENSURE_SUCCESS(rv, rv);
// Verify that it's ok to prompt for credentials here, per spec
// http://xhr.spec.whatwg.org/#the-send%28%29-method
bool showPrompt = true;
// If authentication fails, XMLHttpRequest origin and
// the request URL are same origin, ...
/* Disabled - bug: 799540
if (IsCrossSiteCORSRequest()) {
showPrompt = false;
}
*/
// ... Authorization is not in the list of author request headers, ...
if (showPrompt) {
for (uint32_t i = 0, len = mModifiedRequestHeaders.Length(); i < len; ++i) {
if (mModifiedRequestHeaders[i].header.
LowerCaseEqualsLiteral("authorization")) {
showPrompt = false;
break;
}
}
}
// ... request username is null, and request password is null,
if (showPrompt) {
nsCString username;
rv = uri->GetUsername(username);
NS_ENSURE_SUCCESS(rv, rv);
nsCString password;
rv = uri->GetPassword(password);
NS_ENSURE_SUCCESS(rv, rv);
if (!username.IsEmpty() || !password.IsEmpty()) {
showPrompt = false;
}
}
// ... user agents should prompt the end user for their username and password.
if (!showPrompt) {
RefPtr<XMLHttpRequestAuthPrompt> prompt = new XMLHttpRequestAuthPrompt();
if (!prompt)
return NS_ERROR_OUT_OF_MEMORY;
return prompt->QueryInterface(aIID, aResult);
}
nsCOMPtr<nsIPromptFactory> wwatch =
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
@ -3721,6 +3606,45 @@ nsXMLHttpRequest::EnsureXPCOMifier()
return newRef.forget();
}
bool
nsXMLHttpRequest::ShouldBlockAuthPrompt()
{
// Verify that it's ok to prompt for credentials here, per spec
// http://xhr.spec.whatwg.org/#the-send%28%29-method
for (uint32_t i = 0, len = mModifiedRequestHeaders.Length(); i < len; ++i) {
if (mModifiedRequestHeaders[i].header.
LowerCaseEqualsLiteral("authorization")) {
return true;
}
}
nsCOMPtr<nsIURI> uri;
nsresult rv = mChannel->GetURI(getter_AddRefs(uri));
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
// Also skip if a username and/or password is provided in the URI.
nsCString username;
rv = uri->GetUsername(username);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
nsCString password;
rv = uri->GetPassword(password);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
if (!username.IsEmpty() || !password.IsEmpty()) {
return true;
}
return false;
}
NS_IMPL_ISUPPORTS(nsXMLHttpRequest::nsHeaderVisitor, nsIHttpHeaderVisitor)
NS_IMETHODIMP nsXMLHttpRequest::

View File

@ -796,6 +796,8 @@ protected:
void ResetResponse();
bool ShouldBlockAuthPrompt();
struct RequestHeader
{
nsCString header;

View File

@ -415,7 +415,6 @@ skip-if = buildapp == 'b2g' || toolkit == 'android' # b2g(clipboard undefined) b
[test_bug276037-1.html]
[test_bug276037-2.xhtml]
[test_bug282547.html]
skip-if = e10s
[test_bug28293.html]
[test_bug28293.xhtml]
[test_bug298064.html]

View File

@ -115,6 +115,7 @@ struct HttpChannelOpenArgs
nsCString schedulingContextID;
OptionalCorsPreflightArgs preflightArgs;
uint32_t initialRwin;
bool blockAuthPrompt;
bool suspendAfterSynthesizeResponse;
bool allowStaleCacheContent;
};

View File

@ -87,6 +87,7 @@ HttpBaseChannel::HttpBaseChannel()
, mAllRedirectsSameOrigin(true)
, mAllRedirectsPassTimingAllowCheck(true)
, mResponseCouldBeSynthesized(false)
, mBlockAuthPrompt(false)
, mAllowStaleCacheContent(false)
, mSuspendCount(0)
, mInitialRwin(0)
@ -3158,6 +3159,26 @@ HttpBaseChannel::SetCorsPreflightParameters(const nsTArray<nsCString>& aUnsafeHe
mUnsafeHeaders = aUnsafeHeaders;
}
NS_IMETHODIMP
HttpBaseChannel::GetBlockAuthPrompt(bool* aValue)
{
if (!aValue) {
return NS_ERROR_FAILURE;
}
*aValue = mBlockAuthPrompt;
return NS_OK;
}
NS_IMETHODIMP
HttpBaseChannel::SetBlockAuthPrompt(bool aValue)
{
ENSURE_CALLED_BEFORE_CONNECT();
mBlockAuthPrompt = aValue;
return NS_OK;
}
// static
nsresult
HttpBaseChannel::GetSecureUpgradedURI(nsIURI* aURI, nsIURI** aUpgradedURI)

View File

@ -126,6 +126,8 @@ public:
NS_IMETHOD SetContentLength(int64_t aContentLength) override;
NS_IMETHOD Open(nsIInputStream **aResult) override;
NS_IMETHOD Open2(nsIInputStream **aResult) override;
NS_IMETHOD GetBlockAuthPrompt(bool* aValue) override;
NS_IMETHOD SetBlockAuthPrompt(bool aValue) override;
// nsIEncodedChannel
NS_IMETHOD GetApplyConversion(bool *value) override;
@ -431,6 +433,8 @@ protected:
// True if this channel was intercepted and could receive a synthesized response.
uint32_t mResponseCouldBeSynthesized : 1;
uint32_t mBlockAuthPrompt : 1;
// If true, we behave as if the LOAD_FROM_CACHE flag has been set.
// Used to enforce that flag's behavior but not expose it externally.
uint32_t mAllowStaleCacheContent : 1;

View File

@ -1964,6 +1964,8 @@ HttpChannelChild::ContinueAsyncOpen()
}
openArgs.cacheKey() = cacheKey;
openArgs.blockAuthPrompt() = mBlockAuthPrompt;
openArgs.allowStaleCacheContent() = mAllowStaleCacheContent;
nsresult rv = mozilla::ipc::LoadInfoToLoadInfoArgs(mLoadInfo, &openArgs.loadInfo());

View File

@ -131,7 +131,8 @@ HttpChannelParent::Init(const HttpChannelCreationArgs& aArgs)
a.loadInfo(), a.synthesizedResponseHead(),
a.synthesizedSecurityInfoSerialization(),
a.cacheKey(), a.schedulingContextID(), a.preflightArgs(),
a.initialRwin(), a.suspendAfterSynthesizeResponse(),
a.initialRwin(), a.blockAuthPrompt(),
a.suspendAfterSynthesizeResponse(),
a.allowStaleCacheContent());
}
case HttpChannelCreationArgs::THttpChannelConnectArgs:
@ -265,6 +266,7 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
const nsCString& aSchedulingContextID,
const OptionalCorsPreflightArgs& aCorsPreflightArgs,
const uint32_t& aInitialRwin,
const bool& aBlockAuthPrompt,
const bool& aSuspendAfterSynthesizeResponse,
const bool& aAllowStaleCacheContent)
{
@ -430,6 +432,7 @@ HttpChannelParent::DoAsyncOpen( const URIParams& aURI,
mChannel->SetAllowSpdy(allowSpdy);
mChannel->SetAllowAltSvc(allowAltSvc);
mChannel->SetInitialRwin(aInitialRwin);
mChannel->SetBlockAuthPrompt(aBlockAuthPrompt);
nsCOMPtr<nsIApplicationCacheChannel> appCacheChan =
do_QueryObject(mChannel);

View File

@ -137,6 +137,7 @@ protected:
const nsCString& aSchedulingContextID,
const OptionalCorsPreflightArgs& aCorsPreflightArgs,
const uint32_t& aInitialRwin,
const bool& aBlockAuthPrompt,
const bool& aSuspendAfterSynthesizeResponse,
const bool& aAllowStaleCacheContent);

View File

@ -12,6 +12,7 @@
#include "nsNetUtil.h"
#include "nsHttpHandler.h"
#include "nsIHttpAuthenticator.h"
#include "nsIHttpChannelInternal.h"
#include "nsIAuthPrompt2.h"
#include "nsIAuthPromptProvider.h"
#include "nsIInterfaceRequestor.h"
@ -817,6 +818,18 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge,
bool
nsHttpChannelAuthProvider::BlockPrompt()
{
// Verify that it's ok to prompt for credentials here, per spec
// http://xhr.spec.whatwg.org/#the-send%28%29-method
nsCOMPtr<nsIHttpChannelInternal> chanInternal = do_QueryInterface(mAuthChannel);
MOZ_ASSERT(chanInternal);
bool skipAuthentication = false;
nsresult rv = chanInternal->GetBlockAuthPrompt(&skipAuthentication);
if (NS_SUCCEEDED(rv) && skipAuthentication) {
return true;
}
nsCOMPtr<nsIChannel> chan = do_QueryInterface(mAuthChannel);
nsCOMPtr<nsILoadInfo> loadInfo;
chan->GetLoadInfo(getter_AddRefs(loadInfo));

View File

@ -39,7 +39,7 @@ interface nsIHttpUpgradeListener : nsISupports
* using any feature exposed by this interface, be aware that this interface
* will change and you will be broken. You have been warned.
*/
[scriptable, uuid(f292a080-f2f6-41d6-8aa4-71337e477360)]
[scriptable, uuid(01b8296a-e206-4e5f-acab-82bd8b6a900c)]
interface nsIHttpChannelInternal : nsISupports
{
/**
@ -264,4 +264,12 @@ interface nsIHttpChannelInternal : nsISupports
*/
[noscript, notxpcom, nostdcall]
void setCorsPreflightParameters(in StringArrayRef unsafeHeaders);
/**
* When set to true, the channel will not pop any authentication prompts up
* to the user. When provided or cached credentials lead to an
* authentication failure, that failure will be propagated to the channel
* listener. Must be called before opening the channel, otherwise throws.
*/
attribute boolean blockAuthPrompt;
};