Bug 776171 - Don't delete cached credentials when having ident from URI, XHR auth didn't work after first time, r=cbiesinger

--HG--
rename : toolkit/components/passwordmgr/test/authenticate.sjs => toolkit/components/passwordmgr/test/auth2/authenticate.sjs
This commit is contained in:
Honza Bambas 2012-10-24 16:05:12 +02:00
parent 30a201f94a
commit 90b33162dd
5 changed files with 322 additions and 7 deletions

View File

@ -718,13 +718,15 @@ nsHttpChannelAuthProvider::GetCredentialsForChallenge(const char *challenge,
if (identityInvalid) {
if (entry) {
if (ident->Equals(entry->Identity())) {
LOG((" clearing bad auth cache entry\n"));
// 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());
entry = nullptr;
ident->Clear();
if (!identFromURI) {
LOG((" clearing bad auth cache entry\n"));
// 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());
entry = nullptr;
ident->Clear();
}
}
else if (!identFromURI ||
nsCRT::strcmp(ident->User(),

View File

@ -13,6 +13,7 @@ include $(topsrcdir)/config/config.mk
DIRS = \
browser \
auth2 \
$(NULL)
# Module name for xpcshell tests.
@ -43,6 +44,7 @@ MOCHITEST_FILES = \
test_bug_427033.html \
test_bug_444968.html \
test_bug_627616.html \
test_bug_776171.html \
test_master_password.html \
test_master_password_cleanup.html \
test_maxforms_1.html \

View File

@ -0,0 +1,17 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DEPTH = ../../../../..
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
MOCHITEST_FILES = \
authenticate.sjs \
$(NULL)
include $(topsrcdir)/config/rules.mk

View File

@ -0,0 +1,220 @@
function handleRequest(request, response)
{
try {
reallyHandleRequest(request, response);
} catch (e) {
response.setStatusLine("1.0", 200, "AlmostOK");
response.write("Error handling request: " + e);
}
}
function reallyHandleRequest(request, response) {
var match;
var requestAuth = true, requestProxyAuth = true;
// Allow the caller to drive how authentication is processed via the query.
// Eg, http://localhost:8888/authenticate.sjs?user=foo&realm=bar
// The extra ? allows the user/pass/realm checks to succeed if the name is
// at the beginning of the query string.
var query = "?" + request.queryString;
var expected_user = "", expected_pass = "", realm = "mochitest";
var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy";
var huge = false, plugin = false, anonymous = false;
var authHeaderCount = 1;
// user=xxx
match = /[^_]user=([^&]*)/.exec(query);
if (match)
expected_user = match[1];
// pass=xxx
match = /[^_]pass=([^&]*)/.exec(query);
if (match)
expected_pass = match[1];
// realm=xxx
match = /[^_]realm=([^&]*)/.exec(query);
if (match)
realm = match[1];
// proxy_user=xxx
match = /proxy_user=([^&]*)/.exec(query);
if (match)
proxy_expected_user = match[1];
// proxy_pass=xxx
match = /proxy_pass=([^&]*)/.exec(query);
if (match)
proxy_expected_pass = match[1];
// proxy_realm=xxx
match = /proxy_realm=([^&]*)/.exec(query);
if (match)
proxy_realm = match[1];
// huge=1
match = /huge=1/.exec(query);
if (match)
huge = true;
// plugin=1
match = /plugin=1/.exec(query);
if (match)
plugin = true;
// multiple=1
match = /multiple=([^&]*)/.exec(query);
if (match)
authHeaderCount = match[1]+0;
// anonymous=1
match = /anonymous=1/.exec(query);
if (match)
anonymous = true;
// Look for an authentication header, if any, in the request.
//
// EG: Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
//
// This test only supports Basic auth. The value sent by the client is
// "username:password", obscured with base64 encoding.
var actual_user = "", actual_pass = "", authHeader, authPresent = false;
if (request.hasHeader("Authorization")) {
authPresent = true;
authHeader = request.getHeader("Authorization");
match = /Basic (.+)/.exec(authHeader);
if (match.length != 2)
throw "Couldn't parse auth header: " + authHeader;
var userpass = base64ToString(match[1]); // no atob() :-(
match = /(.*):(.*)/.exec(userpass);
if (match.length != 3)
throw "Couldn't decode auth header: " + userpass;
actual_user = match[1];
actual_pass = match[2];
}
var proxy_actual_user = "", proxy_actual_pass = "";
if (request.hasHeader("Proxy-Authorization")) {
authHeader = request.getHeader("Proxy-Authorization");
match = /Basic (.+)/.exec(authHeader);
if (match.length != 2)
throw "Couldn't parse auth header: " + authHeader;
var userpass = base64ToString(match[1]); // no atob() :-(
match = /(.*):(.*)/.exec(userpass);
if (match.length != 3)
throw "Couldn't decode auth header: " + userpass;
proxy_actual_user = match[1];
proxy_actual_pass = match[2];
}
// Don't request authentication if the credentials we got were what we
// expected.
if (expected_user == actual_user &&
expected_pass == actual_pass) {
requestAuth = false;
}
if (proxy_expected_user == proxy_actual_user &&
proxy_expected_pass == proxy_actual_pass) {
requestProxyAuth = false;
}
if (anonymous) {
if (authPresent) {
response.setStatusLine("1.0", 400, "Unexpected authorization header found");
} else {
response.setStatusLine("1.0", 200, "Authorization header not found");
}
} else {
if (requestProxyAuth) {
response.setStatusLine("1.0", 407, "Proxy authentication required");
for (i = 0; i < authHeaderCount; ++i)
response.setHeader("Proxy-Authenticate", "basic realm=\"" + proxy_realm + "\"", true);
} else if (requestAuth) {
response.setStatusLine("1.0", 401, "Authentication required");
for (i = 0; i < authHeaderCount; ++i)
response.setHeader("WWW-Authenticate", "basic realm=\"" + realm + "\"", true);
} else {
response.setStatusLine("1.0", 200, "OK");
}
}
response.setHeader("Content-Type", "application/xhtml+xml", false);
response.write("<html xmlns='http://www.w3.org/1999/xhtml'>");
response.write("<p>Login: <span id='ok'>" + (requestAuth ? "FAIL" : "PASS") + "</span></p>\n");
response.write("<p>Proxy: <span id='proxy'>" + (requestProxyAuth ? "FAIL" : "PASS") + "</span></p>\n");
response.write("<p>Auth: <span id='auth'>" + authHeader + "</span></p>\n");
response.write("<p>User: <span id='user'>" + actual_user + "</span></p>\n");
response.write("<p>Pass: <span id='pass'>" + actual_pass + "</span></p>\n");
if (huge) {
response.write("<div style='display: none'>");
for (i = 0; i < 100000; i++) {
response.write("123456789\n");
}
response.write("</div>");
response.write("<span id='footnote'>This is a footnote after the huge content fill</span>");
}
if (plugin) {
response.write("<embed id='embedtest' style='width: 400px; height: 100px;' " +
"type='application/x-test'></embed>\n");
}
response.write("</html>");
}
// base64 decoder
//
// Yoinked from extensions/xml-rpc/src/nsXmlRpcClient.js because btoa()
// doesn't seem to exist. :-(
/* Convert Base64 data to a string */
const toBinaryTable = [
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
];
const base64Pad = '=';
function base64ToString(data) {
var result = '';
var leftbits = 0; // number of bits decoded, but yet to be appended
var leftdata = 0; // bits decoded, but yet to be appended
// Convert one by one.
for (var i = 0; i < data.length; i++) {
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
var padding = (data[i] == base64Pad);
// Skip illegal characters and whitespace
if (c == -1) continue;
// Collect data into leftdata, update bitcount
leftdata = (leftdata << 6) | c;
leftbits += 6;
// If we have 8 or more bits, append 8 bits to the result
if (leftbits >= 8) {
leftbits -= 8;
// Append if not padding.
if (!padding)
result += String.fromCharCode((leftdata >> leftbits) & 0xff);
leftdata &= (1 << leftbits) - 1;
}
}
// If there are any bits left, the base64 string was corrupted
if (leftbits)
throw Components.Exception('Corrupted base64 string');
return result;
}

View File

@ -0,0 +1,74 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=776171
-->
<head>
<title>Test for Bug 776171</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="startTest()">
<script class="testbody" type="text/javascript">
/**
* This test checks we correctly ignore authentication entry
* for a subpath and use creds from the URL when provided when XHR
* is used with filled user name and password.
*
* 1. connect auth2/authenticate.sjs that excepts user1:pass1 password
* 2. connect a dummy URL at the same path
* 3. connect authenticate.sjs that again expects user1:pass1 password
* in this case, however, we have an entry without an identity
* for this path (that is a parent for auth2 path in the first step)
*/
SimpleTest.waitForExplicitFinish();
function clearAuthCache()
{
var authMgr = SpecialPowers.Cc['@mozilla.org/network/http-auth-manager;1']
.getService(Components.interfaces.nsIHttpAuthManager);
authMgr.clearAll();
}
function doxhr(URL, user, pass, next)
{
var xhr = new XMLHttpRequest();
if (user && pass)
xhr.open("POST", URL, true, user, pass);
else
xhr.open("POST", URL, true);
xhr.onload = function()
{
is(xhr.status, 200, "Got status 200");
next();
}
xhr.onerror = function()
{
ok(false, "request passed");
finishTest();
}
xhr.send();
}
function startTest()
{
clearAuthCache();
doxhr("auth2/authenticate.sjs?user=user1&pass=pass1&realm=realm1", "user1", "pass1", function() {
doxhr("auth2", null, null, function() {
doxhr("authenticate.sjs?user=user1&pass=pass1&realm=realm1", "user1", "pass1", finishTest);
});
});
}
function finishTest()
{
clearAuthCache();
SimpleTest.finish();
}
</script>
</body>
</html>