Bug 422135: fix login manager's nsIAuthPrompt implementation to deal with NS_GetAuthKey-generated realms, r=dolske/standard8

This commit is contained in:
gavin@gavinsharp.com 2008-03-18 11:08:54 -07:00
parent 5cc5ed0b04
commit 070fda67ee
2 changed files with 155 additions and 80 deletions

View File

@ -215,46 +215,49 @@ LoginManagerPrompter.prototype = {
*/
promptUsernameAndPassword : function (aDialogTitle, aText, aPasswordRealm,
aSavePassword, aUsername, aPassword) {
this.log("===== promptUsernameAndPassword() called =====");
if (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_FOR_SESSION)
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
var selectedLogin = null;
var checkBox = { value : false };
var checkBoxLabel = null;
var hostname = this._getFormattedHostname(aPasswordRealm);
var [hostname, realm, unused] = this._getRealmInfo(aPasswordRealm);
this.log("===== promptUsernameAndPassword() called =====");
// If hostname is null, we can't save this login.
if (hostname) {
var canRememberLogin = (aSavePassword ==
Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) &&
this._pwmgr.getLoginSavingEnabled(hostname);
var canRememberLogin = (aSavePassword ==
Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) &&
this._pwmgr.getLoginSavingEnabled(hostname);
// if checkBoxLabel is null, the checkbox won't be shown at all.
if (canRememberLogin)
checkBoxLabel = this._getLocalizedString("rememberPassword");
// if checkBoxLabel is null, the checkbox won't be shown at all.
if (canRememberLogin)
checkBoxLabel = this._getLocalizedString("rememberPassword");
// Look for existing logins.
var foundLogins = this._pwmgr.findLogins({}, hostname, null,
realm);
// Look for existing logins.
var foundLogins = this._pwmgr.findLogins({}, hostname, null,
aPasswordRealm);
// XXX Like the original code, we can't deal with multiple
// account selection. (bug 227632)
if (foundLogins.length > 0) {
selectedLogin = foundLogins[0];
// XXX Like the original code, we can't deal with multiple
// account selection. (bug 227632)
if (foundLogins.length > 0) {
selectedLogin = foundLogins[0];
// If the caller provided a username, try to use it. If they
// provided only a password, this will try to find a password-only
// login (or return null if none exists).
if (aUsername.value)
selectedLogin = this._repickSelectedLogin(foundLogins,
aUsername.value);
// If the caller provided a username, try to use it. If they
// provided only a password, this will try to find a password-only
// login (or return null if none exists).
if (aUsername.value)
selectedLogin = this._repickSelectedLogin(foundLogins,
aUsername.value);
if (selectedLogin) {
checkBox.value = true;
aUsername.value = selectedLogin.username;
// If the caller provided a password, prefer it.
if (!aPassword.value)
aPassword.value = selectedLogin.password;
if (selectedLogin) {
checkBox.value = true;
aUsername.value = selectedLogin.username;
// If the caller provided a password, prefer it.
if (!aPassword.value)
aPassword.value = selectedLogin.password;
}
}
}
@ -262,14 +265,12 @@ LoginManagerPrompter.prototype = {
aDialogTitle, aText, aUsername, aPassword,
checkBoxLabel, checkBox);
if (!ok || !checkBox.value)
if (!ok || !checkBox.value || !hostname)
return ok;
var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
createInstance(Ci.nsILoginInfo);
newLogin.init(hostname, null, aPasswordRealm,
aUsername.value, aPassword.value,
newLogin.init(hostname, null, realm, aUsername.value, aPassword.value,
"", "");
// XXX We can't prompt with multiple logins yet (bug 227632), so
@ -281,11 +282,11 @@ LoginManagerPrompter.prototype = {
// changed, save as a new login.
if (!selectedLogin) {
// add as new
this.log("New login seen for " + aPasswordRealm);
this.log("New login seen for " + realm);
this._pwmgr.addLogin(newLogin);
} else if (aPassword.value != selectedLogin.password) {
// update password
this.log("Updating password for " + aPasswordRealm);
this.log("Updating password for " + realm);
this._pwmgr.modifyLogin(selectedLogin, newLogin);
} else {
this.log("Login unchanged, no further action needed.");
@ -306,54 +307,56 @@ LoginManagerPrompter.prototype = {
* allows it, then the password will be saved in the database.
*/
promptPassword : function (aDialogTitle, aText, aPasswordRealm,
aSavePassword, aPassword) {
aSavePassword, aPassword) {
this.log("===== promptPassword called() =====");
if (aSavePassword == Ci.nsIAuthPrompt.SAVE_PASSWORD_FOR_SESSION)
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
var checkBox = { value : false };
var checkBoxLabel = null;
var [hostname, username, pathname] = this._decomposeURI(aPasswordRealm);
var newRealm = hostname + pathname;
var [hostname, realm, username] = this._getRealmInfo(aPasswordRealm);
this.log("===== promptPassword called() =====");
var canRememberLogin = (aSavePassword ==
Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) &&
this._pwmgr.getLoginSavingEnabled(hostname);
// if checkBoxLabel is null, the checkbox won't be shown at all.
if (canRememberLogin)
checkBoxLabel = this._getLocalizedString("rememberPassword");
if (!aPassword.value) {
// Look for existing logins.
var foundLogins = this._pwmgr.findLogins({}, hostname, null,
newRealm);
// XXX Like the original code, we can't deal with multiple
// account selection (bug 227632). We can deal with finding the
// account based on the supplied username - but in this case we'll
// just return the first match.
for (var i = 0; i < foundLogins.length; ++i) {
if (foundLogins[i].username == username) {
aPassword.value = foundLogins[i].password;
// wallet returned straight away, so this mimics that code
return true;
}
}
// If hostname is null, we can't save this login.
if (hostname) {
var canRememberLogin = (aSavePassword ==
Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY) &&
this._pwmgr.getLoginSavingEnabled(hostname);
// if checkBoxLabel is null, the checkbox won't be shown at all.
if (canRememberLogin)
checkBoxLabel = this._getLocalizedString("rememberPassword");
if (!aPassword.value) {
// Look for existing logins.
var foundLogins = this._pwmgr.findLogins({}, hostname, null,
realm);
// XXX Like the original code, we can't deal with multiple
// account selection (bug 227632). We can deal with finding the
// account based on the supplied username - but in this case we'll
// just return the first match.
for (var i = 0; i < foundLogins.length; ++i) {
if (foundLogins[i].username == username) {
aPassword.value = foundLogins[i].password;
// wallet returned straight away, so this mimics that code
return true;
}
}
}
}
var ok = this._promptService.promptPassword(this._window, aDialogTitle,
aText, aPassword,
checkBoxLabel, checkBox);
if (ok && checkBox.value) {
if (ok && checkBox.value && hostname) {
var newLogin = Cc["@mozilla.org/login-manager/loginInfo;1"].
createInstance(Ci.nsILoginInfo);
newLogin.init(hostname, null, newRealm, username,
newLogin.init(hostname, null, realm, username,
aPassword.value, "", "");
this.log("New login seen for " + newRealm);
this.log("New login seen for " + realm);
this._pwmgr.addLogin(newLogin);
}
@ -361,8 +364,36 @@ LoginManagerPrompter.prototype = {
return ok;
},
/* ---------- nsIAuthPrompt helpers ---------- */
/**
* Given aRealmString, such as "http://user@example.com/foo", returns an
* array of:
* - the formatted hostname
* - the realm (hostname + path)
* - the username, if present
*
* If aRealmString is in the format produced by NS_GetAuthKey for HTTP[S]
* channels, e.g. "example.com:80 (httprealm)", null is returned for all
* arguments to let callers know the login can't be saved because we don't
* know whether it's http or https.
*/
_getRealmInfo : function (aRealmString) {
var httpRealm = /^.+ \(.+\)$/;
if (httpRealm.test(aRealmString))
return [null, null, null];
var uri = this._ioService.newURI(aRealmString, null, null);
var pathname = "";
if (uri.path != "/")
pathname = uri.path;
var formattedHostname = this._getFormattedHostname(uri);
return [formattedHostname, formattedHostname + pathname, uri.username];
},
/* ---------- nsIAuthPrompt2 prompts ---------- */
@ -977,20 +1008,6 @@ LoginManagerPrompter.prototype = {
return hostname;
},
/**
* Extracts a hostname, username and a pathname from a string based URI
* via a standard URI implementation.
*/
_decomposeURI: function (aURIString) {
var uri = this._ioService.newURI(aURIString, null, null);
var pathname = "";
if (uri.path != "/")
pathname = uri.path;
return [this._getFormattedHostname(uri), uri.username, pathname];
},
/*
* _getAuthTarget
*

View File

@ -145,6 +145,12 @@ function handleDialog(doc, testNum) {
passfield.setAttribute("value", "secret");
break;
case 30:
case 31:
is(password, "", "Checking provided password");
passfield.setAttribute("value", "fill2pass");
break;
case 100:
is(username, "inuser", "Checking provided username");
is(password, "inpass", "Checking provided password");
@ -188,6 +194,14 @@ function handleDialog(doc, testNum) {
passfield.setAttribute("value", "user2pass");
break;
case 120:
case 121:
is(username, "", "Checking filled username");
is(password, "", "Checking filled password");
userfield.setAttribute("value", "fill2user");
passfield.setAttribute("value", "fill2pass");
break;
case 500:
is(username, "inuser", "Checking unfilled username");
is(password, "inpass", "Checking unfilled password");
@ -493,6 +507,27 @@ is(pword.value, "user2pass", "Checking returned password");
// XXX test saving a password with Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY
// ===== test 30 =====
// We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
testNum = 30;
pword.value = null;
startCallbackTimer();
isOk = prompter1.promptPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)",
Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, pword);
ok(isOk, "Checking dialog return value (accept)");
is(pword.value, "fill2pass", "Checking returned password");
// ===== test 31 =====
// We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
testNum++;
pword.value = null;
startCallbackTimer();
isOk = prompter1.promptPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)",
Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, pword);
ok(isOk, "Checking dialog return value (accept)");
is(pword.value, "fill2pass", "Checking returned password");
// ===== test 100 =====
testNum = 100;
uname.value = "inuser";
@ -585,6 +620,29 @@ ok(isOk, "Checking dialog return value (accept)");
is(uname.value, "user2name", "Checking returned username");
is(pword.value, "user2pass", "Checking returned password");
// ===== test 120 =====
// We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
testNum = 120;
uname.value = null;
pword.value = null;
startCallbackTimer();
isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)",
Ci.nsIAuthPrompt.SAVE_PASSWORD_NEVER, uname, pword);
ok(isOk, "Checking dialog return value (accept)");
is(uname.value, "fill2user", "Checking returned username");
is(pword.value, "fill2pass", "Checking returned password");
// ===== test 121 =====
// We don't pre-fill or save for NS_GetAuthKey-generated realms, but we should still prompt
testNum++;
uname.value = null;
pword.value = null;
startCallbackTimer();
isOk = prompter1.promptUsernameAndPassword(dialogTitle(), dialogText, "example2.com:80 (somerealm)",
Ci.nsIAuthPrompt.SAVE_PASSWORD_PERMANENTLY, uname, pword);
ok(isOk, "Checking dialog return value (accept)");
is(uname.value, "fill2user", "Checking returned username");
is(pword.value, "fill2pass", "Checking returned password");
var channel1 = Cc["@mozilla.org/network/io-service;1"].