From fbf5889d742c7f250bb5dd76dddd016250d5e942 Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Wed, 5 Dec 2012 02:33:20 -0500 Subject: [PATCH] Bug 814247 - Add auth cache jars for separate apps. r=mayhemer --- .../mochitest/browserElement_Auth.js | 84 +++++++++++++-- dom/plugins/base/nsNPAPIPlugin.cpp | 6 +- netwerk/protocol/http/nsHttpAuthCache.cpp | 101 ++++++++++++++++-- netwerk/protocol/http/nsHttpAuthCache.h | 26 ++++- netwerk/protocol/http/nsHttpAuthManager.cpp | 24 ++++- .../http/nsHttpChannelAuthProvider.cpp | 51 ++++++++- netwerk/protocol/http/nsIHttpAuthManager.idl | 22 +++- netwerk/test/unit/test_auth_jar.js | 52 +++++++++ netwerk/test/unit/xpcshell.ini | 1 + 9 files changed, 341 insertions(+), 26 deletions(-) create mode 100644 netwerk/test/unit/test_auth_jar.js diff --git a/dom/browser-element/mochitest/browserElement_Auth.js b/dom/browser-element/mochitest/browserElement_Auth.js index a3c59a67f00e..1a1b7fb86819 100644 --- a/dom/browser-element/mochitest/browserElement_Auth.js +++ b/dom/browser-element/mochitest/browserElement_Auth.js @@ -40,7 +40,7 @@ function testHttpAuthCancel(e) { iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) { iframe.removeEventListener("mozbrowsertitlechange", onTitleChange); iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFail); - is(e.detail, 'http auth failed'); + is(e.detail, 'http auth failed', 'expected authentication to fail'); iframe.addEventListener('mozbrowserusernameandpasswordrequired', testHttpAuth); SimpleTest.executeSoon(function() { // Use absolute path because we need to specify host. @@ -48,8 +48,8 @@ function testHttpAuthCancel(e) { }); }); - is(e.detail.realm, 'http_realm'); - is(e.detail.host, 'http://test'); + is(e.detail.realm, 'http_realm', 'expected realm matches'); + is(e.detail.host, 'http://test', 'expected host matches'); e.preventDefault(); SimpleTest.executeSoon(function() { @@ -66,12 +66,12 @@ function testHttpAuth(e) { iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) { iframe.removeEventListener("mozbrowsertitlechange", onTitleChange); iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFail); - is(e.detail, 'http auth success'); - SimpleTest.executeSoon(testFinish); + is(e.detail, 'http auth success', 'expect authentication to succeed'); + SimpleTest.executeSoon(testAuthJarNoInterfere); }); - is(e.detail.realm, 'http_realm'); - is(e.detail.host, 'http://test'); + is(e.detail.realm, 'http_realm', 'expected realm matches'); + is(e.detail.host, 'http://test', 'expected host matches'); e.preventDefault(); SimpleTest.executeSoon(function() { @@ -79,7 +79,77 @@ function testHttpAuth(e) { }); } +function testAuthJarNoInterfere(e) { + var authMgr = SpecialPowers.Cc['@mozilla.org/network/http-auth-manager;1'] + .getService(SpecialPowers.Ci.nsIHttpAuthManager); + var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(SpecialPowers.Ci.nsIScriptSecurityManager); + var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + var uri = ioService.newURI("http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs", null, null); + + // Set a bunch of auth data that should not conflict with the correct auth data already + // stored in the cache. + var principal = secMan.getAppCodebasePrincipal(uri, 1, false); + authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm', + 'tests/dom/browser-element/mochitest/file_http_401_response.sjs', + '', 'httpuser', 'wrongpass', false, principal); + principal = secMan.getAppCodebasePrincipal(uri, 1, true); + authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm', + 'tests/dom/browser-element/mochitest/file_http_401_response.sjs', + '', 'httpuser', 'wrongpass', false, principal); + principal = secMan.getAppCodebasePrincipal(uri, secMan.NO_APP_ID, false); + authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm', + 'tests/dom/browser-element/mochitest/file_http_401_response.sjs', + '', 'httpuser', 'wrongpass', false, principal); + + // Will authenticate with correct password, prompt should not be + // called again. + iframe.addEventListener("mozbrowserusernameandpasswordrequired", testFail); + iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) { + iframe.removeEventListener("mozbrowsertitlechange", onTitleChange); + iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFail); + is(e.detail, 'http auth success', 'expected authentication success'); + SimpleTest.executeSoon(testAuthJarInterfere); + }); + + // Once more with feeling. Ensure that our new auth data doesn't interfere with this mozbrowser's + // auth data. + iframe.src = 'http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs'; +} + +function testAuthJarInterfere(e) { + var authMgr = SpecialPowers.Cc['@mozilla.org/network/http-auth-manager;1'] + .getService(SpecialPowers.Ci.nsIHttpAuthManager); + var secMan = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"] + .getService(SpecialPowers.Ci.nsIScriptSecurityManager); + var ioService = SpecialPowers.Cc["@mozilla.org/network/io-service;1"] + .getService(SpecialPowers.Ci.nsIIOService); + var uri = ioService.newURI("http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs", null, null); + + // Set some auth data that should overwrite the successful stored details. + var principal = secMan.getAppCodebasePrincipal(uri, secMan.NO_APP_ID, true); + authMgr.setAuthIdentity('http', 'test', -1, 'basic', 'http_realm', + 'tests/dom/browser-element/mochitest/file_http_401_response.sjs', + '', 'httpuser', 'wrongpass', false, principal); + + // Will authenticate with correct password, prompt should not be + // called again. + iframe.addEventListener("mozbrowserusernameandpasswordrequired", testFinish); + iframe.addEventListener("mozbrowsertitlechange", function onTitleChange(e) { + iframe.removeEventListener("mozbrowsertitlechange", onTitleChange); + iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFinish); + SimpleTest.execute(testFail); + }); + + // Once more with feeling. Ensure that our new auth data interferes with this mozbrowser's + // auth data. + iframe.src = 'http://test/tests/dom/browser-element/mochitest/file_http_401_response.sjs'; +} + function testFinish() { + iframe.removeEventListener("mozbrowserusernameandpasswordrequired", testFinish); + // Clear login information stored in password manager. var authMgr = SpecialPowers.Cc['@mozilla.org/network/http-auth-manager;1'] .getService(SpecialPowers.Ci.nsIHttpAuthManager); diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index 75a1b980feca..ebd79381a3cc 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -2771,12 +2771,16 @@ _getauthenticationinfo(NPP instance, const char *protocol, const char *host, bool authPrivate = false; GetPrivacyFromNPP(instance, &authPrivate); + nsIDocument *doc = GetDocumentFromNPP(instance); + NS_ENSURE_TRUE(doc, NPERR_GENERIC_ERROR); + nsIPrincipal *principal = doc->NodePrincipal(); + nsAutoString unused, uname16, pwd16; if (NS_FAILED(authManager->GetAuthIdentity(proto, nsDependentCString(host), port, nsDependentCString(scheme), nsDependentCString(realm), EmptyCString(), unused, uname16, - pwd16, authPrivate))) { + pwd16, authPrivate, principal))) { return NPERR_GENERIC_ERROR; } diff --git a/netwerk/protocol/http/nsHttpAuthCache.cpp b/netwerk/protocol/http/nsHttpAuthCache.cpp index 3d34506bcf84..0109f8238ed5 100644 --- a/netwerk/protocol/http/nsHttpAuthCache.cpp +++ b/netwerk/protocol/http/nsHttpAuthCache.cpp @@ -9,11 +9,20 @@ #include "nsString.h" #include "nsCRT.h" #include "prprf.h" +#include "mozIApplicationClearPrivateDataParams.h" +#include "nsIObserverService.h" +#include "mozilla/Services.h" +#include "nsNetUtil.h" static inline void -GetAuthKey(const char *scheme, const char *host, int32_t port, nsCString &key) +GetAuthKey(const char *scheme, const char *host, int32_t port, uint32_t appId, bool inBrowserElement, nsCString &key) { - key.Assign(scheme); + key.Truncate(); + key.AppendInt(appId); + key.Append(':'); + key.AppendInt(inBrowserElement); + key.Append(':'); + key.Append(scheme); key.AppendLiteral("://"); key.Append(host); key.Append(':'); @@ -41,13 +50,23 @@ StrEquivalent(const PRUnichar *a, const PRUnichar *b) nsHttpAuthCache::nsHttpAuthCache() : mDB(nullptr) + , mObserver(new AppDataClearObserver(this)) { + nsCOMPtr obsSvc = mozilla::services::GetObserverService(); + if (obsSvc) { + obsSvc->AddObserver(mObserver, "webapps-clear-data", false); + } } nsHttpAuthCache::~nsHttpAuthCache() { if (mDB) ClearAll(); + nsCOMPtr obsSvc = mozilla::services::GetObserverService(); + if (obsSvc) { + obsSvc->RemoveObserver(mObserver, "webapps-clear-data"); + mObserver->mOwner = nullptr; + } } nsresult @@ -71,13 +90,15 @@ nsHttpAuthCache::GetAuthEntryForPath(const char *scheme, const char *host, int32_t port, const char *path, + uint32_t appId, + bool inBrowserElement, nsHttpAuthEntry **entry) { LOG(("nsHttpAuthCache::GetAuthEntryForPath [key=%s://%s:%d path=%s]\n", scheme, host, port, path)); nsAutoCString key; - nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key); + nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key); if (!node) return NS_ERROR_NOT_AVAILABLE; @@ -90,6 +111,8 @@ nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme, const char *host, int32_t port, const char *realm, + uint32_t appId, + bool inBrowserElement, nsHttpAuthEntry **entry) { @@ -97,7 +120,7 @@ nsHttpAuthCache::GetAuthEntryForDomain(const char *scheme, scheme, host, port, realm)); nsAutoCString key; - nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key); + nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key); if (!node) return NS_ERROR_NOT_AVAILABLE; @@ -113,6 +136,8 @@ nsHttpAuthCache::SetAuthEntry(const char *scheme, const char *realm, const char *creds, const char *challenge, + uint32_t appId, + bool inBrowserElement, const nsHttpAuthIdentity *ident, nsISupports *metadata) { @@ -127,7 +152,7 @@ nsHttpAuthCache::SetAuthEntry(const char *scheme, } nsAutoCString key; - nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, key); + nsHttpAuthNode *node = LookupAuthNode(scheme, host, port, appId, inBrowserElement, key); if (!node) { // create a new entry node and set the given entry @@ -149,13 +174,15 @@ void nsHttpAuthCache::ClearAuthEntry(const char *scheme, const char *host, int32_t port, - const char *realm) + const char *realm, + uint32_t appId, + bool inBrowserElement) { if (!mDB) return; nsAutoCString key; - GetAuthKey(scheme, host, port, key); + GetAuthKey(scheme, host, port, appId, inBrowserElement, key); PL_HashTableRemove(mDB, key.get()); } @@ -179,12 +206,14 @@ nsHttpAuthNode * nsHttpAuthCache::LookupAuthNode(const char *scheme, const char *host, int32_t port, + uint32_t appId, + bool inBrowserElement, nsCString &key) { if (!mDB) return nullptr; - GetAuthKey(scheme, host, port, key); + GetAuthKey(scheme, host, port, appId, inBrowserElement, key); return (nsHttpAuthNode *) PL_HashTableLookup(mDB, key.get()); } @@ -232,6 +261,62 @@ PLHashAllocOps nsHttpAuthCache::gHashAllocOps = nsHttpAuthCache::FreeEntry }; +NS_IMPL_ISUPPORTS1(nsHttpAuthCache::AppDataClearObserver, nsIObserver) + +NS_IMETHODIMP +nsHttpAuthCache::AppDataClearObserver::Observe(nsISupports *subject, + const char * topic, + const PRUnichar * data_unicode) +{ + NS_ENSURE_TRUE(mOwner, NS_ERROR_NOT_AVAILABLE); + + nsCOMPtr params = + do_QueryInterface(subject); + if (!params) { + NS_ERROR("'webapps-clear-data' notification's subject should be a mozIApplicationClearPrivateDataParams"); + return NS_ERROR_UNEXPECTED; + } + + uint32_t appId; + bool browserOnly; + + nsresult rv = params->GetAppId(&appId); + NS_ENSURE_SUCCESS(rv, rv); + rv = params->GetBrowserOnly(&browserOnly); + NS_ENSURE_SUCCESS(rv, rv); + + MOZ_ASSERT(appId != NECKO_UNKNOWN_APP_ID); + mOwner->ClearAppData(appId, browserOnly); + return NS_OK; +} + +static int +RemoveEntriesForApp(PLHashEntry *entry, int32_t number, void *arg) +{ + nsDependentCString key(static_cast(entry->key)); + nsAutoCString* prefix = static_cast(arg); + if (StringBeginsWith(key, *prefix)) { + return HT_ENUMERATE_NEXT | HT_ENUMERATE_REMOVE; + } + return HT_ENUMERATE_NEXT; +} + +void +nsHttpAuthCache::ClearAppData(uint32_t appId, bool browserOnly) +{ + if (!mDB) { + return; + } + nsAutoCString keyPrefix; + keyPrefix.AppendInt(appId); + keyPrefix.Append(':'); + if (browserOnly) { + keyPrefix.AppendInt(browserOnly); + keyPrefix.Append(':'); + } + PL_HashTableEnumerateEntries(mDB, RemoveEntriesForApp, &keyPrefix); +} + //----------------------------------------------------------------------------- // nsHttpAuthIdentity //----------------------------------------------------------------------------- diff --git a/netwerk/protocol/http/nsHttpAuthCache.h b/netwerk/protocol/http/nsHttpAuthCache.h index 7e371190df39..62960f96c593 100644 --- a/netwerk/protocol/http/nsHttpAuthCache.h +++ b/netwerk/protocol/http/nsHttpAuthCache.h @@ -15,6 +15,8 @@ #include "nsCOMPtr.h" #include "plhash.h" #include "nsCRT.h" +#include "nsIObserver.h" + struct nsHttpAuthPath { @@ -180,6 +182,8 @@ public: const char *host, int32_t port, const char *path, + uint32_t appId, + bool inBrowserElement, nsHttpAuthEntry **entry); // |scheme|, |host|, and |port| are required @@ -189,6 +193,8 @@ public: const char *host, int32_t port, const char *realm, + uint32_t appId, + bool inBrowserElement, nsHttpAuthEntry **entry); // |scheme|, |host|, and |port| are required @@ -203,13 +209,17 @@ public: const char *realm, const char *credentials, const char *challenge, + uint32_t appId, + bool inBrowserElement, const nsHttpAuthIdentity *ident, nsISupports *metadata); void ClearAuthEntry(const char *scheme, const char *host, int32_t port, - const char *realm); + const char *realm, + uint32_t appId, + bool inBrowserElement); // expire all existing auth list entries including proxy auths. nsresult ClearAll(); @@ -218,6 +228,8 @@ private: nsHttpAuthNode *LookupAuthNode(const char *scheme, const char *host, int32_t port, + uint32_t appId, + bool inBrowserElement, nsCString &key); // hash table allocation functions @@ -227,9 +239,21 @@ private: static void FreeEntry(void *, PLHashEntry *he, unsigned flag); static PLHashAllocOps gHashAllocOps; + + class AppDataClearObserver : public nsIObserver { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + AppDataClearObserver(nsHttpAuthCache* aOwner) : mOwner(aOwner) {} + virtual ~AppDataClearObserver() {} + nsHttpAuthCache* mOwner; + }; + + void ClearAppData(uint32_t appId, bool browserOnly); private: PLHashTable *mDB; // "host:port" --> nsHttpAuthNode + nsRefPtr mObserver; }; #endif // nsHttpAuthCache_h__ diff --git a/netwerk/protocol/http/nsHttpAuthManager.cpp b/netwerk/protocol/http/nsHttpAuthManager.cpp index 0e3cfca1d36e..2ad7d12680ba 100644 --- a/netwerk/protocol/http/nsHttpAuthManager.cpp +++ b/netwerk/protocol/http/nsHttpAuthManager.cpp @@ -8,6 +8,7 @@ #include "nsHttpAuthManager.h" #include "nsReadableUtils.h" #include "nsNetUtil.h" +#include "nsIPrincipal.h" NS_IMPL_ISUPPORTS1(nsHttpAuthManager, nsIHttpAuthManager) @@ -56,22 +57,32 @@ nsHttpAuthManager::GetAuthIdentity(const nsACString & aScheme, nsAString & aUserDomain, nsAString & aUserName, nsAString & aUserPassword, - bool aIsPrivate) + bool aIsPrivate, + nsIPrincipal* aPrincipal) { nsHttpAuthCache* auth_cache = aIsPrivate ? mPrivateAuthCache : mAuthCache; nsHttpAuthEntry * entry = nullptr; nsresult rv; + uint32_t appId = NECKO_NO_APP_ID; + bool inBrowserElement = false; + if (aPrincipal) { + appId = aPrincipal->GetAppId(); + inBrowserElement = aPrincipal->GetIsInBrowserElement(); + } + if (!aPath.IsEmpty()) rv = auth_cache->GetAuthEntryForPath(PromiseFlatCString(aScheme).get(), PromiseFlatCString(aHost).get(), aPort, PromiseFlatCString(aPath).get(), + appId, inBrowserElement, &entry); else rv = auth_cache->GetAuthEntryForDomain(PromiseFlatCString(aScheme).get(), PromiseFlatCString(aHost).get(), aPort, PromiseFlatCString(aRealm).get(), + appId, inBrowserElement, &entry); if (NS_FAILED(rv)) @@ -95,12 +106,20 @@ nsHttpAuthManager::SetAuthIdentity(const nsACString & aScheme, const nsAString & aUserDomain, const nsAString & aUserName, const nsAString & aUserPassword, - bool aIsPrivate) + bool aIsPrivate, + nsIPrincipal* aPrincipal) { nsHttpAuthIdentity ident(PromiseFlatString(aUserDomain).get(), PromiseFlatString(aUserName).get(), PromiseFlatString(aUserPassword).get()); + uint32_t appId = NECKO_NO_APP_ID; + bool inBrowserElement = false; + if (aPrincipal) { + appId = aPrincipal->GetAppId(); + inBrowserElement = aPrincipal->GetIsInBrowserElement(); + } + nsHttpAuthCache* auth_cache = aIsPrivate ? mPrivateAuthCache : mAuthCache; return auth_cache->SetAuthEntry(PromiseFlatCString(aScheme).get(), PromiseFlatCString(aHost).get(), @@ -109,6 +128,7 @@ nsHttpAuthManager::SetAuthIdentity(const nsACString & aScheme, PromiseFlatCString(aRealm).get(), nullptr, // credentials nullptr, // challenge + appId, inBrowserElement, &ident, nullptr); // metadata } diff --git a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp index 84db6717c2fe..e6a3ed018702 100644 --- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp +++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp @@ -20,6 +20,23 @@ #include "nsIDNSService.h" #include "nsNetCID.h" #include "nsIDNSRecord.h" +#include "nsNetUtil.h" + +static void +GetAppIdAndBrowserStatus(nsIChannel* aChan, uint32_t* aAppId, bool* aInBrowserElem) +{ + nsCOMPtr loadContext; + if (aChan) { + NS_QueryNotificationCallbacks(aChan, loadContext); + } + if (!loadContext) { + *aAppId = NECKO_NO_APP_ID; + *aInBrowserElem = false; + } else { + loadContext->GetAppId(aAppId); + loadContext->GetIsInBrowserElement(aInBrowserElem); + } +} nsHttpChannelAuthProvider::nsHttpChannelAuthProvider() : mAuthChannel(nullptr) @@ -372,6 +389,11 @@ nsHttpChannelAuthProvider::GenCredsAndSetEntry(nsIHttpAuthenticator *auth, // this getter never fails nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate); + nsCOMPtr chan = do_QueryInterface(mAuthChannel); + uint32_t appId; + bool isInBrowserElement; + GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); + // create a cache entry. we do this even though we don't yet know that // these credentials are valid b/c we need to avoid prompting the user // more than once in case the credentials are valid. @@ -381,6 +403,7 @@ nsHttpChannelAuthProvider::GenCredsAndSetEntry(nsIHttpAuthenticator *auth, rv = authCache->SetAuthEntry(scheme, host, port, directory, realm, saveCreds ? *result : nullptr, saveChallenge ? challenge : nullptr, + appId, isInBrowserElement, saveIdentity ? &ident : nullptr, sessionState); return rv; @@ -694,6 +717,11 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, return NS_ERROR_NOT_AVAILABLE; } + nsCOMPtr chan = do_QueryInterface(mAuthChannel); + uint32_t appId; + bool isInBrowserElement; + GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); + // // if we already tried some credentials for this transaction, then // we need to possibly clear them from the cache, unless the credentials @@ -702,7 +730,8 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, // nsHttpAuthEntry *entry = nullptr; authCache->GetAuthEntryForDomain(scheme.get(), host, port, - realm.get(), &entry); + realm.get(), appId, + isInBrowserElement, &entry); // hold reference to the auth session state (in case we clear our // reference to the entry). @@ -732,7 +761,8 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge, // ok, we've already tried this user identity, so clear the // corresponding entry from the auth cache. authCache->ClearAuthEntry(scheme.get(), host, - port, realm.get()); + port, realm.get(), + appId, isInBrowserElement); entry = nullptr; ident->Clear(); } @@ -1057,10 +1087,17 @@ NS_IMETHODIMP nsHttpChannelAuthProvider::OnAuthAvailable(nsISupports *aContext, nsAutoCString realm; ParseRealm(mCurrentChallenge.get(), realm); + nsCOMPtr chan = do_QueryInterface(mAuthChannel); + uint32_t appId; + bool isInBrowserElement; + GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); + nsHttpAuthCache *authCache = gHttpHandler->AuthCache(mIsPrivate); nsHttpAuthEntry *entry = nullptr; authCache->GetAuthEntryForDomain(scheme.get(), host, port, - realm.get(), &entry); + realm.get(), appId, + isInBrowserElement, + &entry); nsCOMPtr sessionStateGrip; if (entry) @@ -1292,7 +1329,13 @@ nsHttpChannelAuthProvider::SetAuthorizationHeader(nsHttpAuthCache *authCache, continuationState = &mAuthContinuationState; } - rv = authCache->GetAuthEntryForPath(scheme, host, port, path, &entry); + nsCOMPtr chan = do_QueryInterface(mAuthChannel); + uint32_t appId; + bool isInBrowserElement; + GetAppIdAndBrowserStatus(chan, &appId, &isInBrowserElement); + + rv = authCache->GetAuthEntryForPath(scheme, host, port, path, + appId, isInBrowserElement, &entry); if (NS_SUCCEEDED(rv)) { // if we are trying to add a header for origin server auth and if the // URL contains an explicit username, then try the given username first. diff --git a/netwerk/protocol/http/nsIHttpAuthManager.idl b/netwerk/protocol/http/nsIHttpAuthManager.idl index 5ec0495e8cfe..582ea60b614d 100644 --- a/netwerk/protocol/http/nsIHttpAuthManager.idl +++ b/netwerk/protocol/http/nsIHttpAuthManager.idl @@ -5,6 +5,8 @@ #include "nsISupports.idl" +interface nsIPrincipal; + /** * nsIHttpAuthManager * @@ -19,7 +21,7 @@ * Java client fetches content from a HTTP site that the user * has already logged into. */ -[scriptable, uuid(1301b517-ac72-48f6-a781-70c196eaaf3d)] +[scriptable, uuid(54f90444-c52b-4d2d-8916-c59a2bb25938)] interface nsIHttpAuthManager : nsISupports { /** @@ -45,6 +47,12 @@ interface nsIHttpAuthManager : nsISupports * return value containing user name. * @param aUserPassword * return value containing user password. + * @param aIsPrivate + * whether to look up a private or public identity (they are + * stored separately, for use by private browsing) + * @param aPrincipal + * the principal from which to derive information about which + * app/mozbrowser is in use for this request */ void getAuthIdentity(in ACString aScheme, in ACString aHost, @@ -55,7 +63,8 @@ interface nsIHttpAuthManager : nsISupports out AString aUserDomain, out AString aUserName, out AString aUserPassword, - [optional] in bool aIsPrivate); + [optional] in bool aIsPrivate, + [optional] in nsIPrincipal aPrincipal); /** * Store auth identity. @@ -80,6 +89,12 @@ interface nsIHttpAuthManager : nsISupports * optional string containing user name. * @param aUserPassword * optional string containing user password. + * @param aIsPrivate + * whether to store a private or public identity (they are + * stored separately, for use by private browsing) + * @param aPrincipal + * the principal from which to derive information about which + * app/mozbrowser is in use for this request */ void setAuthIdentity(in ACString aScheme, in ACString aHost, @@ -90,7 +105,8 @@ interface nsIHttpAuthManager : nsISupports in AString aUserDomain, in AString aUserName, in AString aUserPassword, - [optional] in boolean aIsPrivate); + [optional] in boolean aIsPrivate, + [optional] in nsIPrincipal aPrincipal); /** * Clear all auth cache. diff --git a/netwerk/test/unit/test_auth_jar.js b/netwerk/test/unit/test_auth_jar.js new file mode 100644 index 000000000000..c91251ad4902 --- /dev/null +++ b/netwerk/test/unit/test_auth_jar.js @@ -0,0 +1,52 @@ +const Cc = Components.classes; +const Ci = Components.interfaces; +Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +function createURI(s) { + let service = Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService); + return service.newURI(s, null, null); +} + +function run_test() { + var secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager); + const kURI1 = "http://example.com"; + var app1 = secMan.getAppCodebasePrincipal(createURI(kURI1), 1, false); + var app10 = secMan.getAppCodebasePrincipal(createURI(kURI1), 10, false); + var app1browser = secMan.getAppCodebasePrincipal(createURI(kURI1), 1, true); + + var am = Cc["@mozilla.org/network/http-auth-manager;1"]. + getService(Ci.nsIHttpAuthManager); + am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user", "pass", false, app1); + am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user3", "pass3", false, app1browser); + am.setAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", "example.com", "user2", "pass2", false, app10); + + let subject = { + appId: 1, + browserOnly: true, + QueryInterface: XPCOMUtils.generateQI([Ci.mozIApplicationClearPrivateDataParams]) + }; + Services.obs.notifyObservers(subject, "webapps-clear-data", null); + + var domain = {value: ""}, user = {value: ""}, pass = {value: ""}; + try { + am.getAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", domain, user, pass, false, app1browser); + do_check_false(true); // no identity should be present + } catch (x) { + do_check_eq(domain.value, ""); + do_check_eq(user.value, ""); + do_check_eq(pass.value, ""); + } + + am.getAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", domain, user, pass, false, app1); + do_check_eq(domain.value, "example.com"); + do_check_eq(user.value, "user"); + do_check_eq(pass.value, "pass"); + + + am.getAuthIdentity("http", "a.example.com", -1, "basic", "realm", "", domain, user, pass, false, app10); + do_check_eq(domain.value, "example.com"); + do_check_eq(user.value, "user2"); + do_check_eq(pass.value, "pass2"); +} \ No newline at end of file diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index e3957b880ee5..a467b182b1fe 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -9,6 +9,7 @@ tail = [test_URIs.js] [test_aboutblank.js] [test_assoc.js] +[test_auth_jar.js] [test_auth_proxy.js] [test_authentication.js] # Bug 675039: test hangs consistently on Android