Bug 793749 - Add support for the PLAINTEXT signature method to oauth.jsm, r=bienvenu,florian,a=Standard8 SEAMONKEY_2_13b5_BUILD1 SEAMONKEY_2_13b5_RELEASE
authorTom Thompson <tom@spideroak.com>
Mon, 24 Sep 2012 18:51:52 +0200
changeset 13018 5f3a3fa7a27fabd8510b2d11809239c5341ef9a6
parent 13017 753ed57d0fc9b1256c6c40a6f22139c5da124c9e
child 13019 88fec24ba7c5c6e715fd1b0466b2151ace9bb771
child 13021 d586bc78036a66590ac50d536ff16570aef15358
child 13025 fd1c642374463e2b8dc30042595d53d41028295e
push id679
push userbugzilla@standard8.plus.com
push dateTue, 25 Sep 2012 20:22:24 +0000
treeherdercomm-beta@5f3a3fa7a27f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbienvenu, florian, Standard8
bugs793749
Bug 793749 - Add support for the PLAINTEXT signature method to oauth.jsm, r=bienvenu,florian,a=Standard8
mail/base/modules/oauth.jsm
--- a/mail/base/modules/oauth.jsm
+++ b/mail/base/modules/oauth.jsm
@@ -12,27 +12,34 @@ const {classes: Cc, interfaces: Ci, resu
 
 var EXPORTED_SYMBOLS = ["OAuth"];
 
 Cu.import("resource:///modules/http.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/gloda/log4moz.js");
 
-function OAuth(aDisplayName, aBaseUri, aAuthUri, aAuthToken, aAuthTokenSecret, aAppKey, aAppSecret)
+function OAuth(aDisplayName, aBaseUri, aAuthUri, aAuthToken, aAuthTokenSecret,
+               aAppKey, aAppSecret, aSignatureMethod, aTempCredentialsMethod,
+               aAuthorizeMethod, aRequestCredentialsMethod)
 {
   this._userInfo = {};
   this.displayName = aDisplayName;
   this.baseURI = aBaseUri;
   this.authURI = aAuthUri;
   this.token = aAuthToken;
   this.tokenSecret = aAuthTokenSecret;
   this.consumerKey = aAppKey;
   this.consumerSecret = aAppSecret;
   this.log = Log4Moz.getConfiguredLogger("TBOAuth");
+
+  this.signatureMethod = aSignatureMethod || "HMAC-SHA1";
+  this.tempCredentialsMethod = aTempCredentialsMethod || "oauth/request_token";
+  this.authorizeMethod = aAuthorizeMethod || "oauth/authorize";
+  this.requestCredentialsMethod = aRequestCredentialsMethod || "oauth/access_token";
 }
 
 OAuth.prototype = {
   consumerKey: "",
   consumerSecret: "",
   completionURI: "http://oauthcallback.local/",
   baseURI: "",
   token : "",
@@ -87,17 +94,17 @@ OAuth.prototype = {
     const kNonceLength = 6;
     let nonce = "";
     for (let i = 0; i < kNonceLength; ++i)
       nonce += kChars[Math.floor(Math.random() * kChars.length)];
 
     let params = (aOAuthParams || []).concat([
       ["oauth_consumer_key", this.consumerKey],
       ["oauth_nonce", nonce],
-      ["oauth_signature_method", "HMAC-SHA1"],
+      ["oauth_signature_method", this.signatureMethod],
       ["oauth_token", this.token],
       ["oauth_timestamp", Math.floor(((new Date()).getTime()) / 1000)],
       ["oauth_version", "1.0"]
     ]);
 
     // encodeURI *AND* % escape characters that encodeURIComponent doesn't.
     function percentEncode(aString)
       encodeURIComponent(aString).replace(/\!|\*|\'|\(|\)/g, function(m)
@@ -109,37 +116,48 @@ OAuth.prototype = {
     let queryIndex = url.indexOf("?");
     if (queryIndex != -1) {
       urlSpec = url.slice(0, queryIndex);
       dataParams = url.slice(queryIndex + 1).split("&")
                       .map(function(p) p.split("=").map(percentEncode));
     }
     this.log.info("in sign and send url = " + url + "\nurlSpec = " + urlSpec);
     this.log.info("dataParams = " + dataParams);
-    let signatureKey = this.consumerSecret + "&" + this.tokenSecret;
-    let signatureBase =
-      aMethod + "&" + encodeURIComponent(urlSpec) + "&" +
-      params.concat(dataParams)
-            .sort(function(a,b) (a[0] < b[0]) ? -1 : (a[0] > b[0]) ? 1 : 0)
-            .map(function(p) p.map(percentEncode).join("%3D"))
-            .join("%26");
+
+    let signature;
+    if (this.signatureMethod === "HMAC-SHA1") {
+      let signatureKey = this.consumerSecret + "&" + this.tokenSecret;
+      let signatureBase =
+        aMethod + "&" + encodeURIComponent(urlSpec) + "&" +
+        params.concat(dataParams)
+              .sort(function(a,b) (a[0] < b[0]) ? -1 : (a[0] > b[0]) ? 1 : 0)
+              .map(function(p) p.map(percentEncode).join("%3D"))
+              .join("%26");
 
-    this.log.info("sig base = " + signatureBase);
-    let keyFactory = Cc["@mozilla.org/security/keyobjectfactory;1"]
-                     .getService(Ci.nsIKeyObjectFactory);
-    let hmac =
-      Cc["@mozilla.org/security/hmac;1"].createInstance(Ci.nsICryptoHMAC);
-    hmac.init(hmac.SHA1,
-              keyFactory.keyFromString(Ci.nsIKeyObject.HMAC, signatureKey));
-    // No UTF-8 encoding, special chars are already escaped.
-    let bytes = [b.charCodeAt() for each (b in signatureBase)];
-    hmac.update(bytes, bytes.length);
-    let signature = hmac.finish(true);
+      this.log.info("sig base = " + signatureBase);
+      let keyFactory = Cc["@mozilla.org/security/keyobjectfactory;1"]
+                       .getService(Ci.nsIKeyObjectFactory);
+      let hmac =
+        Cc["@mozilla.org/security/hmac;1"].createInstance(Ci.nsICryptoHMAC);
+      hmac.init(hmac.SHA1,
+                keyFactory.keyFromString(Ci.nsIKeyObject.HMAC, signatureKey));
+      // No UTF-8 encoding, special chars are already escaped.
+      let bytes = [b.charCodeAt() for each (b in signatureBase)];
+      hmac.update(bytes, bytes.length);
+      signature = encodeURIComponent(hmac.finish(true));
+    }
+    else if (this.signatureMethod == "PLAINTEXT") {
+      signature = percentEncode(percentEncode(this.consumerSecret) + "&" +
+                                percentEncode(this.tokenSecret));
+    }
+    else {
+      throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+    }
 
-    params.push(["oauth_signature", encodeURIComponent(signature)]);
+    params.push(["oauth_signature", signature]);
 
     let authorization =
       "OAuth " + params.map(function (p) p[0] + "=\"" + p[1] + "\"").join(", ");
     let headers = (aHeaders || []).concat([["Authorization", authorization]]);
     return doXHRequest(url, headers, aPOSTData, aOnLoad, aOnError, aThis, aMethod);
   },
   _parseURLData: function(aData) {
     let result = {};
@@ -158,17 +176,17 @@ OAuth.prototype = {
     let newText = this._pendingData + text.slice(this._receivedLength);
     this.log.info("Received data: " + newText);
     let messages = newText.split(/\r\n?/);
   },
 
   requestToken: function() {
     let oauthParams =
       [["oauth_callback", encodeURIComponent(this.completionURI)]];
-    this.signAndSend("oauth/request_token",
+    this.signAndSend(this.tempCredentialsMethod,
                      null, "POST", null,
                      this.onRequestTokenReceived,
                      this.connectFailureCallback, this,
                      oauthParams);
   },
   onRequestTokenReceived: function(aData) {
     this.log.info("Received request token.");
     let data = this._parseURLData(aData);
@@ -179,17 +197,17 @@ OAuth.prototype = {
     }
     this.token = data.oauth_token;
     this.tokenSecret = data.oauth_token_secret;
 
     this.requestAuthorization();
   },
   requestAuthorization: function() {
     this.log.info("requesting oauth");
-    const url = this.authURI + "oauth/authorize?oauth_token=";
+    const url = this.authURI + this.authorizeMethod + "?oauth_token=";
     this._browserRequest =
       { promptText : "auth prompt",
         account: this,
         url: url + this.token + "&oauth_callback=" + this.completionURI,
         _active: true,
         iconURI : "",  // would be nice to set this from the oauth user...
         cancelled: function() {
           if (!this._active)
@@ -250,22 +268,24 @@ OAuth.prototype = {
 
     this._browserRequest._active = false;
     if ("_listener" in this._browserRequest)
       this._browserRequest._listener._cleanUp();
     delete this._browserRequest;
   },
   onAuthorizationReceived: function(aData) {
     this.log.info("authorization received");
-    this.requestAccessToken();
+    let data = this._parseURLData(aData);
+
+    this.requestAccessToken([["oauth_verifier", data.oauth_verifier]]);
   },
-  requestAccessToken: function() {
-    this.signAndSend("oauth/access_token", null, "POST", null,
+  requestAccessToken: function(aVerifier) {
+    this.signAndSend(this.requestCredentialsMethod, null, "POST", null,
                      this.onAccessTokenReceived, this.connectFailureCallback, this,
-                     null);
+                     aVerifier);
   },
   onAccessTokenReceived: function(aData) {
     this.log.info("Received access token. urlData = " + aData);
     let result = this._parseURLData(aData);
 
     this.token = result.oauth_token;
     this.tokenSecret = result.oauth_token_secret;
     this.connectSuccessCallback();