From ca155b4e16d15d5ffd62d20ce42a7516c1f9c49c Mon Sep 17 00:00:00 2001 From: Matěj Cepl Date: Sun, 4 Sep 2011 02:02:36 +0200 Subject: Fix passwords and XML-RPC logins. Make switching off XML-RPC per-domain not per-user (fixes #107). Login just once per session and domain (fixes #113). --- data/lib/bzpage.js | 2 +- data/lib/collectingMetadata.js | 3 +- data/lib/rpcutils.js | 22 ------ data/rhlib/addAttachmentRow.js | 4 +- lib/libbugzilla.js | 156 ++++++++++++++++++++++++----------------- 5 files changed, 95 insertions(+), 92 deletions(-) diff --git a/data/lib/bzpage.js b/data/lib/bzpage.js index 43cf5cb..f3f5e27 100644 --- a/data/lib/bzpage.js +++ b/data/lib/bzpage.js @@ -39,7 +39,7 @@ self.on('message', function onMessage(msg) { equivalentComponents = ("equivalentComponents" in constantData) ? constantData.equivalentComponents : null; generateButtons(msg.data.instPkgs, msg.data.kNodes); - JSONRPCLogin(completeInit); + completeInit(); break; case "Error": alert("Error " + msg.data); diff --git a/data/lib/collectingMetadata.js b/data/lib/collectingMetadata.js index 00b973d..47407e8 100644 --- a/data/lib/collectingMetadata.js +++ b/data/lib/collectingMetadata.js @@ -157,7 +157,8 @@ AttachList.prototype.addCheckXorgLogLink = function addCheckXorgLogLink() { */ AttachList.prototype.markBadAttachments = function markBadAttachments() { if (!constantData.passwordState.passAvailable) { - console.log("markBadAttachments : No password, no XML-RPC calls; sorry"); + console.log("markBadAttachments : No XML-RPC calls for " + + location.protocol + "//" + location.hostname); return null; } diff --git a/data/lib/rpcutils.js b/data/lib/rpcutils.js index d29fd68..ee8804a 100644 --- a/data/lib/rpcutils.js +++ b/data/lib/rpcutils.js @@ -2,28 +2,6 @@ //http://www.opensource.org/licenses/mit-license.php "use strict"; -/* -We should first login and then we shouldn't bother with it. -It could be interesting to know how many logins per second are bad. - */ - -function JSONRPCLogin(callback) { - if (!constantData.passwordState.password) { - return; - } - - console.myDebug("JSONRPCLogin: passObj = " + - constantData.passwordState.toSource()); - makeJSONRPCCall("User.login", { - login: getLogin(), - password: constantData.passwordState.password, - remember: false - }, function(logResult) { - callback(); - }); -}; - - // Make a JSONL-RPC call ... most of the business logic should stay in the // content script // http://json-rpc.org/wd/JSON-RPC-1-1-WD-20060807.html diff --git a/data/rhlib/addAttachmentRow.js b/data/rhlib/addAttachmentRow.js index 6fab1bd..20541dc 100644 --- a/data/rhlib/addAttachmentRow.js +++ b/data/rhlib/addAttachmentRow.js @@ -37,8 +37,8 @@ function addAttachment(data, callback, param) { var params = []; if (!constantData.passwordState.passAvailable) { - console - .error("addAttachment : No password, no XML-RPC calls; sorry"); + console.log("addAttachment: No XML-RPC calls for " + + location.protocol + "//" + location.hostname); return null; } diff --git a/lib/libbugzilla.js b/lib/libbugzilla.js index 62df80b..d03cd3c 100644 --- a/lib/libbugzilla.js +++ b/lib/libbugzilla.js @@ -22,12 +22,11 @@ var copiedAttributes = [ "queryButton", "upstreamButton", "parseAbrtBacktraces", "suspiciousComponents", "verboseInlineHistory" ]; -var passwords = {}; // hash of passwords indexed by a hostname var config = exports.config = {}; -var debugOption = false; +var debugOption = null; function Message(cmd, data) { - console.log("Message: cmd = " + cmd + ", data = " + data); + debug("Message: cmd = " + cmd + ", data = " + data); this.cmd = cmd; this.data = data; } @@ -84,53 +83,67 @@ function getRealBugNoSlow(bugNo, location, callback) { function getPassword(login, domain, callback) { var passPrompt = "Enter your Bugzilla password " + "for accessing JSONRPC services"; - var switchPrompt = "Do you want to switch off features " + - "requiring password completely"; - var prefName = BTSPrefNS+"withoutPassowrd"; + var switchPrompt = "Do you want to switch off XML-RPC " + + "for domain "; + var prefName = BTSPrefNS+"withoutPassowrd", prefValue = []; var retObject = { password: null, // password string or null if no password provided - withoutPass: false // whether user doesn't want to use password at all + withoutPass: [] // whether user doesn't want to use password at all }; - passUtils.search({ - username: login, - url: domain, - realm: BTSPassRealm, - onComplete: function onComplete([credential]) { - if (credential) { - // We found the password, just go ahead and use it - retObject.password = credential.password; - callback(retObject); - } - else { - // We don't have a stored password, ask for one - var passwordText = prompts.promptPassword(passPrompt); - if (passwordText && passwordText.length > 0) { - // Right, we've got it … store it and then use it. - retObject.password = passwordText; - passUtils.store({ - username: login, - password: passwordText, - url: domain, - realm: BTSPassRealm, - onComplete: function onComplete() { - callback(retObject); - } - }); + if (preferences.has(prefName)) { + prefValue = JSON.parse(preferences.get(prefName, null)); + debug("getPassword: prefValue = " + prefValue); + if ((prefValue === true) || (prefValue === false)) { + console.log("Clearing previous scheme of " + prefName + + " preference " + prefValue + "."); + preferences.set(prefName, JSON.stringify([])); + prefValue = []; + } + } + + if (prefValue.indexOf(domain) == -1) { + passUtils.search({ + username: login, + url: domain, + realm: BTSPassRealm, + onComplete: function onComplete([credential]) { + if (credential) { + // We found the password, just go ahead and use it + retObject.password = credential.password; + callback(retObject); } else { - // We don't have password, and user haven't entered one? - // Does he want to live passwordless? - var switchOff = prompts.promptYesNoCancel(switchPrompt); - if (switchOff) { - preferences.set(prefName,true); + // We don't have a stored password, ask for one + var passwordText = prompts.promptPassword(passPrompt); + if (passwordText && passwordText.length > 0) { + // Right, we've got it … store it and then use it. + retObject.password = passwordText; + passUtils.store({ + username: login, + password: passwordText, + url: domain, + realm: BTSPassRealm, + onComplete: function onComplete() { + callback(retObject); + } + }); + } + else { + // We don't have password, and user haven't entered one? + // Does he want to live passwordless for this domain? + var switchOff = prompts.promptYesNoCancel(switchPrompt + domain + "?"); + if (switchOff) { + prefValue.push(domain); + preferences.set(prefName, JSON.stringify(prefValue)); + } + retObject.withoutPass = prefValue; + callback(retObject); } - retObject.withoutPass = switchOff; - callback(retObject); } } - } - }); + }); + } } // Change URL of the configuration JSON file @@ -214,9 +227,8 @@ exports.getInstalledPackages = function getInstalledPackages(locationLoginObj, c // In order to avoid sending whole password to the content script, // we are sending just these two Booleans. config.constantData.passwordState = { - password: passwObj.password, - passAvailable: (passwObj.password !== null), - withoutPass: passwObj.withoutPass + passAvailable: (passwObj.password !== null), + withoutPass: passwObj.withoutPass.indexOf(passDomain) === -1 }; callback(new Message("CreateButtons", { @@ -281,28 +293,40 @@ exports.createUpstreamBug = function createUpstreamBug(urlStr, subjectStr, comme }); }; -exports.makeJSONRPCCallWithLogin = function makeJSONRPCCallWithLogin(url, method, - params, login, callback) { - var urlObj = urlMod.URL(url); - getPassword(login, - urlObj.scheme + "://" + urlObj.host, - function (passObj) { - if (!passObj.password) { - return; +function loginToAllBugzillas(callback) { + var callbacksCounter = 0, bugzilla = ""; + if ("enabledPackages" in config.gJSONData.configData) { + for (bugzillaHost in config.gJSONData.configData.enabledPackages) { + passUtils.search({ + url: "https://" + bugzillaHost, + realm: BTSPassRealm, + onComplete: function onComplete(credentials) { + credentials.forEach(function(credential) { + debug("credential = " + credential.toSource()); + // function makeJSONRPCCall(url, method, params, callback) + makeJSONRPCCall(credential.url + "/jsonrpc.cgi", + "User.login", { + login: credential.username, + password: credential.password, + remember: false + }, function(logResult) { + debug("Logging as " + credential.username + " to " + credential.url); + callbacksCounter--; + if (callbacksCounter <= 0) { + debug("All logins done!"); + callback(); + } + }); + callbacksCounter++; + }); + }, + onError: function onError() { + console.error("No credentials were found for " + bugzillaHost + "!"); } - - debug("makeJSONRPCCallWithLogin: passObj = " + - passObj.toSource()); - makeJSONRPCCall(url, "User.login", { - login: login, - password: passObj.password, - remember: false - }, function(logResult) { - makeJSONRPCCall(url, method, params, callback); - }); - } - ); -}; + }); + } + } +} // Make a JSONL-RPC call ... most of the business logic should stay in the // content script @@ -406,7 +430,7 @@ exports.initialize = function initialize(config, callback) { logger.initialize(); } } - callback(); + loginToAllBugzillas(callback); } }).get(); } -- cgit