Bug 737955 - Deleting an online storage account should wipe out old credentials. Dropbox now automatically logs out after OAuth keys received. r+a=bienvenu.
authorMike Conley <mconley@mozilla.com>
Tue, 27 Mar 2012 09:54:01 -0400
changeset 11137 57bfc276a84bec5fd49a2fc7912884fec1c4d69c
parent 11136 af3f77dda5cfb244a038a3d6e2aa804709e6fbb8
child 11138 788690fde22b0f6bc6783c20ef368f77b5b0f7a6
push id463
push userbugzilla@standard8.plus.com
push dateTue, 24 Apr 2012 17:34:51 +0000
treeherdercomm-beta@e53588e8f7b0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs737955
Bug 737955 - Deleting an online storage account should wipe out old credentials. Dropbox now automatically logs out after OAuth keys received. r+a=bienvenu.
mail/components/cloudfile/cloudFileAccounts.js
mail/components/cloudfile/nsDropbox.js
mail/test/mozmill/cloudfile/test-cloudfile-backend-dropbox.js
mail/test/mozmill/shared-modules/test-cloudfile-backend-helpers.js
mail/test/mozmill/shared-modules/test-cloudfile-dropbox-helpers.js
--- a/mail/components/cloudfile/cloudFileAccounts.js
+++ b/mail/components/cloudfile/cloudFileAccounts.js
@@ -177,18 +177,27 @@ var cloudFileAccounts = {
   getAccount: function(aKey) {
     let type = Services.prefs.QueryInterface(Ci.nsIPrefBranch)
                        .getCharPref(ACCOUNT_ROOT + aKey + ".type");
     return this._getInitedProviderForType(aKey, type);
   },
 
   removeAccount: function(aKeyOrAccount) {
     let key = this._ensureKey(aKeyOrAccount);
+
     let type = Services.prefs.QueryInterface(Ci.nsIPrefBranch)
                        .deleteBranch(ACCOUNT_ROOT + key);
+
+    // Destroy any secret tokens for this accountKey.
+    let logins = Services.logins
+                         .findLogins({}, PWDMGR_HOST, null, "");
+    for each (let login in logins) {
+      if (login.username == key)
+        Services.logins.removeLogin(login);
+    }
   },
 
   get accounts() {
     return [this.getAccount(key)
             for each (key in this._accountKeys)
             if (this.getAccount(key) != null)];
   },
 
--- a/mail/components/cloudfile/nsDropbox.js
+++ b/mail/components/cloudfile/nsDropbox.js
@@ -28,16 +28,17 @@ const kDeletePath = "fileops/delete/?roo
 const kAppKey = "7xkhuze09iqkghm";
 const kAppSecret = "3i5kwjkt74rkkjc";
 const kSharesPath = "shares/sandbox/";
 const kFilesPutPath = "files_put/sandbox/";
 
 var gServerUrl = "https://api.dropbox.com/1/";
 var gContentUrl = "https://api-content.dropbox.com/1/";
 var gAuthUrl = "https://www.dropbox.com/1/";
+var gLogoutUrl = "https://www.dropbox.com/logout";
 
 function wwwFormUrlEncode(aStr) {
   return encodeURIComponent(aStr).replace(/!/g, '%21')
                                  .replace(/'/g, '%27')
                                  .replace(/\(/g, '%28')
                                  .replace(/\)/g, '%29')
                                  .replace(/\*/g, '%2A');
 }
@@ -406,16 +407,17 @@ nsDropbox.prototype = {
   /**
    * This function is used by our testing framework to override the default
    * URL's that nsDropbox connects to.
    */
   overrideUrls : function nsDropbox_overrideUrls(aNumUrls, aUrls) {
     gServerUrl = aUrls[0];
     gContentUrl = aUrls[1];
     gAuthUrl = aUrls[2];
+    gLogoutUrl = aUrls[3];
   },
 
   /**
    * logon to the dropbox account.
    *
    * @param successCallback - called if logon is successful
    * @param failureCallback - called back on error.
    * @param aWithUI if false, logon fails if it would have needed to put up UI.
@@ -425,24 +427,49 @@ nsDropbox.prototype = {
   logon: function nsDropbox_logon(successCallback, failureCallback, aWithUI) {
     let authToken = this._cachedAuthToken;
     let authSecret = this._cachedAuthSecret;
     if (!aWithUI && (!authToken.length || !authSecret.length)) {
       failureCallback();
       return;
     }
 
-    this._connection = new OAuth(this.displayName, gServerUrl, gAuthUrl, authToken, authSecret,
-                                 kAppKey, kAppSecret);
+    this._connection = new OAuth(this.displayName, gServerUrl, gAuthUrl,
+                                 authToken, authSecret, kAppKey, kAppSecret);
     this._connection.connect(
       function () {
         this.log.info("success connecting");
         this._loggedIn = true;
         this._cachedAuthToken = this._connection.token;
         this._cachedAuthSecret = this._connection.tokenSecret;
+
+        // Attempt to end the session we just opened to get these tokens...
+        let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+                    .createInstance(Ci.nsIXMLHttpRequest);
+        xhr.mozBackgroundRequest = true;
+        xhr.open("GET", gLogoutUrl);
+        xhr.onerror = function(aProgressEvent) {
+          this.log.error("Could not end authorization session!");
+          this.log.error("Status was: " + aProgressEvent.target.status);
+          this.log.error("Message was: " + aProgressEvent.target.statusText);
+        }.bind(this);
+
+        xhr.onload = function(aRequest) {
+          if (aRequest.target.status == 200)
+            this.log.info("Successfully ended authorization session.");
+          else {
+            this.log.error("Could not end authorization session!");
+            this.log.error("Status was: " + aRequest.target.status);
+            this.log.error("Message was: " + aRequest.target.statusText);
+          }
+        }.bind(this);
+
+        this.log.info("Sending logout request to: " + gLogoutUrl);
+        xhr.send();
+
         successCallback();
       }.bind(this),
       function () {
         this.log.info("failed connecting");
         failureCallback();
       }.bind(this),
       true);
   },
--- a/mail/test/mozmill/cloudfile/test-cloudfile-backend-dropbox.js
+++ b/mail/test/mozmill/cloudfile/test-cloudfile-backend-dropbox.js
@@ -172,17 +172,17 @@ function test_deleting_uploads() {
   gServer.planForGetFileURL(kFilename,
                                 {url: "http://www.example.com/someFile"});
   let requestObserver = gObsManager.create("test_deleting_uploads - upload 1");
   provider.uploadFile(file, requestObserver);
   mc.waitFor(function() requestObserver.success);
 
   // Try deleting a file
   let obs = new ObservationRecorder();
-  obs.planFor(kDeleteFile)
+  obs.planFor(kDeleteFile);
   Services.obs.addObserver(obs, kDeleteFile, false);
 
   gServer.planForDeleteFile(kFilename);
   let deleteObserver = gObsManager.create("test_deleting_uploads - delete 1");
   provider.deleteFile(file, deleteObserver);
   mc.waitFor(function() deleteObserver.success);
 
   // Check to make sure the file was deleted on the server
@@ -207,8 +207,22 @@ function test_create_existing_account() 
       done = true;
     },
   }
 
   provider.createExistingAccount(myObs);
   mc.waitFor(function() done);
 }
 
+/**
+ * Test that completing the OAuth procedure results in an attempt to logout.
+ */
+function test_oauth_complete_causes_logout() {
+  let provider = gServer.getPreparedBackend("someNewAccount");
+  let dummyObs = gObsManager.create("test_oauth_complete_causes_logout");
+  let obs = new ObservationRecorder();
+  obs.planFor(kLogout);
+  Services.obs.addObserver(obs, kLogout, false);
+  provider.createExistingAccount(dummyObs);
+  mc.waitFor(function() dummyObs.success);
+  mc.waitFor(function() 1 == obs.numSightings(kLogout));
+  Services.obs.removeObserver(obs, kLogout);
+}
--- a/mail/test/mozmill/shared-modules/test-cloudfile-backend-helpers.js
+++ b/mail/test/mozmill/shared-modules/test-cloudfile-backend-helpers.js
@@ -11,29 +11,31 @@ const MODULE_NAME = 'cloudfile-backend-h
 const RELATIVE_ROOT = '../shared-modules';
 const MODULE_REQUIRES = ['folder-display-helpers'];
 
 const kUserAuthRequested = "cloudfile:auth";
 const kUserDataRequested = "cloudfile:user";
 const kUploadFile = "cloudfile:uploadFile";
 const kGetFileURL = "cloudfile:getFileURL";
 const kDeleteFile = "cloudfile:deleteFile";
+const kLogout = "cloudfile:logout";
 
 Cu.import('resource://mozmill/stdlib/os.js', os);
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 
 var fdh;
 
 function installInto(module) {
   setupModule(module);
   module.kUserAuthRequested = kUserAuthRequested;
   module.kUserDataRequested = kUserDataRequested;
   module.kUploadFile = kUploadFile;
   module.kGetFileURL = kGetFileURL;
   module.kDeleteFile = kDeleteFile;
+  module.kLogout = kLogout;
   module.SimpleRequestObserverManager = SimpleRequestObserverManager;
   module.SimpleRequestObserver = SimpleRequestObserver;
 }
 
 function setupModule(module) {
   fdh = collector.getModule('folder-display-helpers');
 }
 
--- a/mail/test/mozmill/shared-modules/test-cloudfile-dropbox-helpers.js
+++ b/mail/test/mozmill/shared-modules/test-cloudfile-dropbox-helpers.js
@@ -25,16 +25,18 @@ const kContentURL = kServerRoot + kConte
 const kAuthURL = kServerRoot + kAuthPath;
 const kOAuthTokenPath = "oauth/request_token";
 const kOAuthAuthorizePath = "oauth/authorize";
 const kOAuthAccessTokenPath = "oauth/access_token";
 const kUserInfoPath = "account/info";
 const kPutFilePath = "files_put/sandbox/";
 const kSharesPath = "shares/sandbox/";
 const kDeletePath = "fileops/delete/";
+const kLogoutPath = "/logout";
+const kLogoutURL = kServerRoot + kLogoutPath;
 
 const kDefaultConfig = {
   port: kDefaultServerPort
 }
 
 const kAuthTokenString = "oauth_token=requestkey&oauth_token_secret=requestsecret";
 
 const kDefaultUser = {
@@ -99,17 +101,17 @@ function MockDropboxServer() {}
 MockDropboxServer.prototype = {
   _server: null,
   _toDelete: [],
 
   getPreparedBackend: function MDBS_getPreparedBackend(aAccountKey) {
     let dropbox = Cc["@mozilla.org/mail/dropbox;1"]
                   .getService(Ci.nsIMsgCloudFileProvider);
 
-    let urls = [kServerURL, kContentURL, kAuthURL];
+    let urls = [kServerURL, kContentURL, kAuthURL, kLogoutURL];
     dropbox.overrideUrls(urls.length, urls);
     dropbox.init(aAccountKey);
     return dropbox;
   },
 
   init: function MDBS_init(aConfig) {
     this._config = kDefaultConfig;
 
@@ -242,16 +244,20 @@ MockDropboxServer.prototype = {
                                              kAuthTokenString);
 
     this._server.registerPathHandler(kServerPath + kOAuthTokenPath,
                                      authFunc);
     this._server.registerPathHandler(kServerPath + kOAuthAccessTokenPath,
                                      authFunc);
     this._server.registerPathHandler(kAuthPath + kOAuthAuthorizePath,
                                      this._authHandler);
+
+    let logoutFunc = this._noteAndReturnString("cloudfile:logout", "",
+                                               "Successfully logged out!");
+    this._server.registerPathHandler(kLogoutPath, logoutFunc);
   },
 
   _authHandler: function MDBS__authHandler(meta, response) {
     response.setStatusLine(null, 302, "Found");
     response.setHeader("Location", "http://oauthcallback.local/",
                        false);
   },
 }