Bug 1028398 - FxA will silently provide user's email to privileged apps in 2.0. Part 3: Tests. r=jedp, a=2.0+
authorFernando Jiménez <ferjmoreno@gmail.com>
Fri, 11 Jul 2014 16:13:32 +0200
changeset 207891 5de9080323053eda62e7a04b03e47a5279b9e881
parent 207890 76080ed1f8debe59488647f45c76632c09f500ab
child 207892 aecef7260c8def7b7bb1aa908093b78f0f5a04a4
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjedp, 2
bugs1028398
milestone32.0a2
Bug 1028398 - FxA will silently provide user's email to privileged apps in 2.0. Part 3: Tests. r=jedp, a=2.0+
dom/identity/tests/mochitest/file_declareAudience.html
dom/identity/tests/mochitest/file_syntheticEvents.html
dom/identity/tests/mochitest/test_declareAudience.html
dom/identity/tests/mochitest/test_rpHasValidCallbacks.html
dom/identity/tests/mochitest/test_syntheticEvents.html
--- a/dom/identity/tests/mochitest/file_declareAudience.html
+++ b/dom/identity/tests/mochitest/file_declareAudience.html
@@ -17,39 +17,57 @@
 <body>
     <div id='test'>
 <script type="application/javascript;version=1.8">
 
   function postResults(message) {
     window.realParent.postMessage(JSON.stringify(message), "*");
   }
 
-  function onready() {
-    navigator.mozId.request();
-  }
-
-  function onlogin(backedAssertion) {
-    postResults({success: true, backedAssertion: backedAssertion});
-  }
-
-  function onerror(error) {
-    postResults({success: false, error: error});
-  }
+  onmessage = function(event) {
+    try {
+      navigator.mozId.watch({
+        wantIssuer: "firefox-accounts",
+        audience: event.data.audience,
+        onready: function() {
+          try {
+            navigator.mozId.request();
+          } catch(e) {
+            postResults({
+              success: false,
+              error: e,
+              appIndex: event.data.appIndex
+            });
+          }
+        },
+        onlogin: function(backedAssertion) {
+          postResults({
+            success: true,
+            backedAssertion: backedAssertion,
+            appIndex: event.data.appIndex
+          });
+        },
+        onerror: function(error) {
+          postResults({
+            success: false,
+            error: error,
+            appIndex: event.data.appIndex
+          });
+        },
 
-  onmessage = function(event) {
-    navigator.mozId.watch({
-      wantIssuer: "firefox-accounts",
-      audience: event.data.audience,
-      onready: onready,
-      onlogin: onlogin,
-      onerror: onerror,
-
-      // onlogout will actually be called every time watch() is invoked,
-      // because fxa will find no signed-in user and so trigger logout.
-      // For this test, though, we don't care and just ignore logout.
-      onlogout: function () {},
-    });
+        // onlogout will actually be called every time watch() is invoked,
+        // because fxa will find no signed-in user and so trigger logout.
+        // For this test, though, we don't care and just ignore logout.
+        onlogout: function () {},
+      });
+    } catch (e) {
+      postResults({
+        success: false,
+        error: e,
+        appIndex: event.data.appIndex
+      });
+    }
   };
 
 </script>
 </div>
 </body>
 </html>
--- a/dom/identity/tests/mochitest/file_syntheticEvents.html
+++ b/dom/identity/tests/mochitest/file_syntheticEvents.html
@@ -18,34 +18,44 @@
 <body>
     <div id='test'>
 <script type="application/javascript;version=1.8">
 
   function postResults(message) {
     window.realParent.postMessage(JSON.stringify(message), "*");
   }
 
-  function onready() {
-    navigator.mozId.request();
-  }
-
-  function onlogin(backedAssertion) {
-    postResults({success: true, backedAssertion: backedAssertion});
-  }
-
-  function onerror(error) {
-    postResults({success: false, error: error});
-  }
-
-  onmessage = function(message) {
+  onmessage = function(event) {
     navigator.mozId.watch({
-      wantIssuer: message.data.wantIssuer,
-      onready: onready,
-      onerror: onerror,
-      onlogin: onlogin,
+      wantIssuer: event.data.wantIssuer,
+      onready: function() {
+        try {
+          navigator.mozId.request();
+        } catch(e) {
+          postResults({
+            success: false,
+            error: e,
+            appIndex: event.data.appIndex
+          });
+        }
+      },
+      onlogin: function(backedAssertion) {
+        postResults({
+          success: true,
+          backedAssertion: backedAssertion,
+          appIndex: event.data.appIndex
+        });
+      },
+      onerror: function(error) {
+        postResults({
+          success: false,
+          error: error,
+          appIndex: event.data.appIndex
+        });
+      },
       onlogout: function() {},
     });
   };
 
 </script>
 </div>
 </body>
 </html>
--- a/dom/identity/tests/mochitest/test_declareAudience.html
+++ b/dom/identity/tests/mochitest/test_declareAudience.html
@@ -97,136 +97,151 @@ let apps = [
     title: "a privileged app, which may not have an audience other than its origin",
     manifest: "https://example.com/manifest_priv.webapp",
     appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_PRIVILEGED,
     origin: "https://example.com",
     wantAudience: "https://i-like-pie.com",
     uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_declareAudience.html",
     expected: {
       success: false,
-      underprivileged: false,
+      underprivileged: true,
     },
   },
   {
     title: "a privileged app, which may declare an audience the same as its origin",
     manifest: "https://example.com/manifest_priv.webapp",
     appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_PRIVILEGED,
     origin: "https://example.com",
     wantAudience: "https://example.com",
     uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_declareAudience.html",
     expected: {
       success: true,
+      underprivileged: false,
     },
   },
   {
     title: "a certified app, which may do whatever it damn well pleases",
     manifest: "https://example.com/manifest_cert.webapp",
     appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_CERTIFIED,
     origin: "https://example.com",
     wantAudience: "https://whatever-i-want.com",
     uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_declareAudience.html",
     expected: {
       success: true,
     },
   },
 ];
 
-let appIndex = 0;
-let expectedErrors = 0;
-let receivedErrors = [];
+let eventsReceived = 0;
 let testRunner = runTest();
 
 // Successful tests will send exactly one message.  But for error tests, we may
 // have more than one message from the onerror handler in the client.  So we keep
 // track of received errors; once they reach the expected count, we are done.
 function receiveMessage(event) {
   let result = JSON.parse(event.data);
-  let app = apps[appIndex];
+  let app = apps[result.appIndex];
+  if (app.received) {
+    return;
+  }
+  apps[result.appIndex].received = true;
+
   let expected = app.expected;
 
+  let expectedErrors = 0;
+  let receivedErrors = [];
+
+  if (expected.underprivileged) {
+    expectedErrors += 1;
+  }
+  if (expected.nopermission) {
+    expectedErrors += 1;
+  }
+
   is(result.success, expected.success,
      "Assertion request succeeds");
 
   if (expected.success) {
     // Confirm that the assertion audience and origin are as expected
     let components = extractAssertionComponents(result.backedAssertion);
     is(components.payload.aud, app.wantAudience || app.origin,
        "Got desired assertion audience");
-
   } else {
     receivedErrors.push(result.error);
   }
 
-  if (receivedErrors.length === expectedErrors) {
+
+  ok(receivedErrors.length === expectedErrors,
+     "Received errors should be equal to expected errors");
 
-    if (expected.underprivileged) {
-      ok(receivedErrors.indexOf("ERROR_NOT_AUTHORIZED_FOR_FIREFOX_ACCOUNTS") > -1,
-         "Expect a complaint that this app cannot use FxA.");
-    }
-    if (!expected.success) {
-      ok(receivedErrors.indexOf("ERROR_INVALID_ASSERTION_AUDIENCE") > -1,
-         "Expect an error getting an assertion");
-    }
+  if (!expected.success && expected.underprivileged) {
+    ok(receivedErrors.indexOf("ERROR_INVALID_ASSERTION_AUDIENCE") > -1,
+       "Expect an error getting an assertion");
+  }
+
+  eventsReceived += 1;
 
-    appIndex += 1;
+  if (eventsReceived === apps.length) {
+    window.removeEventListener("message", receiveMessage);
 
-    if (appIndex === apps.length) {
-      window.removeEventListener("message", receiveMessage);
-
-      FirefoxAccounts.fxAccountsManager = originalManager;
+    FirefoxAccounts.fxAccountsManager = originalManager;
 
-      SimpleTest.finish();
-      return;
-    }
+    SimpleTest.finish();
 
-    testRunner.next();
+    return;
   }
+
+  testRunner.next();
 }
 
 window.addEventListener("message", receiveMessage, false, true);
 
 function runTest() {
-  for (let app of apps) {
-    dump("** Testing " + app.title + "\n");
-    // Set up state for message handler
-    expectedErrors = 0;
-    receivedErrors = [];
-    if (!app.expected.success) {
-      expectedErrors += 1;
-    }
-    if (app.expected.underprivileged) {
-      expectedErrors += 1;
-    }
+  let index;
+  for (let i = 0; i < apps.length; i++) {
+    let app = apps[i];
+    dump("\n\n** Testing " + app.title + "\n");
 
     let iframe = document.createElement("iframe");
 
     iframe.setAttribute("mozapp", app.manifest);
     iframe.setAttribute("mozbrowser", "true");
     iframe.src = app.uri;
 
     document.getElementById("content").appendChild(iframe);
 
-    iframe.addEventListener("load", function onLoad() {
-      iframe.removeEventListener("load", onLoad);
+    index = i;
+    (function(_index) {
+      iframe.addEventListener("load", function onLoad() {
+        iframe.removeEventListener("load", onLoad);
 
-      let principal = iframe.contentDocument.nodePrincipal;
-      is(principal.appStatus, app.appStatus,
-         "Iframe's document.nodePrincipal has expected appStatus");
+        SpecialPowers.addPermission(
+          "firefox-accounts",
+          SpecialPowers.Ci.nsIPermissionManager.ALLOW_ACTION,
+          iframe.contentDocument
+        );
+
+        let principal = iframe.contentDocument.nodePrincipal;
+        is(principal.appStatus, app.appStatus,
+           "Iframe's document.nodePrincipal has expected appStatus");
 
-      // Because the <iframe mozapp> can't parent its way back to us, we
-      // provide this handle to our window so it can postMessage to us.
-      iframe.contentWindow.wrappedJSObject.realParent = window;
+        // Because the <iframe mozapp> can't parent its way back to us, we
+        // provide this handle to our window so it can postMessage to us.
+        iframe.contentWindow.wrappedJSObject.realParent = window;
 
-      // Test what we want to test, viz. whether or not the app can request
-      // an assertion with an audience the same as or different from its
-      // origin.  The client will post back its success or failure in procuring
-      // an identity assertion from Firefox Accounts.
-      iframe.contentWindow.postMessage({audience: app.wantAudience}, "*");
-    }, false);
-
+        // Test what we want to test, viz. whether or not the app can request
+        // an assertion with an audience the same as or different from its
+        // origin.  The client will post back its success or failure in procuring
+        // an identity assertion from Firefox Accounts.
+        iframe.contentWindow.postMessage({
+          audience: app.wantAudience,
+          appIndex: _index
+        }, "*");
+      }, false);
+    })(index);
     yield undefined;
   }
 }
 
 function extractAssertionComponents(backedAssertion) {
   let [_, signedObject] = backedAssertion.split("~");
   let parts = signedObject.split(".");
 
@@ -254,19 +269,18 @@ function base64UrlDecode(s) {
 
 SpecialPowers.pushPrefEnv({"set":
   [
     ["dom.mozBrowserFramesEnabled", true],
     ["dom.identity.enabled", true],
     ["identity.fxaccounts.enabled", true],
     ["toolkit.identity.debug", true],
     ["dom.identity.syntheticEventsOk", true],
-
-    ["security.apps.privileged.CSP.default", ""],
-    ["security.apps.certified.CSP.default", ""],
+    ["security.apps.privileged.CSP.default", "'inline-script';"],
+    ["security.apps.certified.CSP.default", "'inline-script';"],
   ]},
   function() {
     testRunner.next();
   }
 );
 
 
 </script>
--- a/dom/identity/tests/mochitest/test_rpHasValidCallbacks.html
+++ b/dom/identity/tests/mochitest/test_rpHasValidCallbacks.html
@@ -80,16 +80,18 @@ let testRunner = runTest();
 
 // Enable the identity systems and use verbose logging
 SpecialPowers.pushPrefEnv({'set': [
     ['dom.identity.enabled', true],               // navigator.mozId
     ['identity.fxaccounts.enabled', true],        // fx accounts
     ['dom.identity.syntheticEventsOk', true],     // so we can call request()
     ['toolkit.identity.debug', true],             // verbose identity logging
     ['browser.dom.window.dump.enabled', true],
+    ["security.apps.privileged.CSP.default", "'inline-script';"],
+    ["security.apps.certified.CSP.default", "'inline-script';"],
   ]},
   function () { testRunner.next(); }
 );
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/identity/tests/mochitest/test_syntheticEvents.html
+++ b/dom/identity/tests/mochitest/test_syntheticEvents.html
@@ -76,29 +76,16 @@ let apps = [
     expected: {
       success: false,
       errors: [
         "ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT",
       ],
     },
   },
   {
-    title: "an installed app, which must may not use firefox accounts",
-    manifest: "https://example.com/manifest.webapp",
-    origin: "https://example.com",
-    uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_syntheticEvents.html",
-    wantIssuer: "firefox-accounts",
-    expected: {
-      success: false,
-      errors: [
-        "ERROR_NOT_AUTHORIZED_FOR_FIREFOX_ACCOUNTS",
-      ],
-    },
-  },
-  {
     title: "a privileged app, which may use synthetic events",
     manifest: "https://example.com/manifest_priv.webapp",
     origin: "https://example.com",
     uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_syntheticEvents.html",
     wantIssuer: "firefox-accounts",
     expected: {
       success: true,
     },
@@ -110,97 +97,110 @@ let apps = [
     uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_syntheticEvents.html",
     wantIssuer: "firefox-accounts",
     expected: {
       success: true,
     },
   },
 ];
 
-let appIndex = 0;
+let eventsReceived = 0;
 let testRunner = runTest();
-let receivedErrors = [];
 
 function receiveMessage(event) {
   dump("** Received response: " + event.data + "\n");
   let result = JSON.parse(event.data);
-  let app = apps[appIndex];
+  let app = apps[result.appIndex];
+  if (app.received) {
+    return;
+  }
+  apps[result.appIndex].received = true;
+
   let expected = app.expected;
+  let receivedErrors = [];
 
   is(result.success, expected.success,
     "Assertion request " + (expected.success ? "succeeds" : "fails"));
 
   if (result.error) {
     receivedErrors.push(result.error);
   }
 
-  if (receivedErrors.length === (expected.errors || []).length) {
-    receivedErrors.forEach((error) => {
-      ok(expected.errors.indexOf(error) > -1,
-         "Received " + error + ".  " +
-         "Expected errors are: " + JSON.stringify(expected.errors));
-    });
+  ok(receivedErrors.length === (expected.errors || []).length,
+     "Received errors should be equal to expected errors");
 
-    appIndex += 1;
+  receivedErrors.forEach((error) => {
+    ok(expected.errors.indexOf(error) > -1,
+       "Received " + error + ".  " +
+       "Expected errors are: " + JSON.stringify(expected.errors));
+  });
 
-    if (appIndex === apps.length) {
-      window.removeEventListener("message", receiveMessage);
+  eventsReceived += 1;
 
-      // Remove mock from DOMIdentity
-      DOMIdentity._mockIdentityService = null;
+  if (eventsReceived === apps.length) {
+    window.removeEventListener("message", receiveMessage);
 
-      // Restore original fxa manager
-      FirefoxAccounts.fxAccountsManager = originalManager;
+    FirefoxAccounts.fxAccountsManager = originalManager;
 
-      SimpleTest.finish();
-      return;
-    }
+    SimpleTest.finish();
 
-    testRunner.next();
+    return;
   }
+
+  testRunner.next();
 }
 
 window.addEventListener("message", receiveMessage, false, true);
 
 function runTest() {
-  for (let app of apps) {
+  let index;
+  for (let i = 0; i < apps.length; i++) {
+    let app = apps[i];
     dump("** Testing " + app.title + "\n");
 
-    receivedErrors = [];
-
     let iframe = document.createElement("iframe");
 
     iframe.setAttribute("mozapp", app.manifest);
     iframe.setAttribute("mozbrowser", "true");
     iframe.src = app.uri;
 
     document.getElementById("content").appendChild(iframe);
 
-    iframe.addEventListener("load", function onLoad() {
-      iframe.removeEventListener("load", onLoad);
+    index = i;
+    (function(_index) {
+      iframe.addEventListener("load", function onLoad() {
+        iframe.removeEventListener("load", onLoad);
 
-      // Because the <iframe mozapp> can't parent its way back to us, we
-      // provide this handle to our window so it can postMessage to us.
-      iframe.contentWindow.wrappedJSObject.realParent = window;
-      iframe.contentWindow.postMessage({wantIssuer: app.wantIssuer}, "*");
-    }, false);
+        SpecialPowers.addPermission(
+          "firefox-accounts",
+          SpecialPowers.Ci.nsIPermissionManager.ALLOW_ACTION,
+          iframe.contentDocument
+        );
 
+        // Because the <iframe mozapp> can't parent its way back to us, we
+        // provide this handle to our window so it can postMessage to us.
+        iframe.contentWindow.wrappedJSObject.realParent = window;
+        iframe.contentWindow.postMessage({
+          wantIssuer: app.wantIssuer,
+          appIndex: _index
+        }, "*");
+      }, false);
+    })(index);
     yield undefined;
   }
 }
 
 SpecialPowers.pushPrefEnv({"set":
   [
     ["dom.mozBrowserFramesEnabled", true],
     ["dom.identity.enabled", true],
     ["identity.fxaccounts.enabled", true],
     ["toolkit.identity.debug", true],
-
-    ["security.apps.privileged.CSP.default", ""],
-    ["security.apps.certified.CSP.default", ""],
+    ["security.apps.privileged.CSP.default", "'inline-script';"],
+    ["security.apps.certified.CSP.default", "'inline-script';"],
   ]},
   function() {
     testRunner.next();
   }
 );
 
 
 </script>