зеркало из https://github.com/mozilla/gecko-dev.git
Bug 971379 - FxA should accept synthetic events from certified apps. r=ferjm
This commit is contained in:
Родитель
8129654c83
Коммит
dfd9d50b6c
|
@ -167,6 +167,18 @@ this.DOMIdentity = {
|
|||
_serviceContexts: new Map(),
|
||||
_mmContexts: new Map(),
|
||||
|
||||
/*
|
||||
* Mockable, for testing
|
||||
*/
|
||||
_mockIdentityService: null,
|
||||
get IdentityService() {
|
||||
if (this._mockIdentityService) {
|
||||
log("Using a mocked identity service");
|
||||
return this._mockIdentityService;
|
||||
}
|
||||
return IdentityService;
|
||||
},
|
||||
|
||||
/*
|
||||
* Create a new RPWatchContext, and update the context maps.
|
||||
*/
|
||||
|
@ -199,7 +211,7 @@ this.DOMIdentity = {
|
|||
}
|
||||
log("WARNING: Firefox Accounts is not enabled; Defaulting to BrowserID");
|
||||
}
|
||||
return IdentityService;
|
||||
return this.IdentityService;
|
||||
},
|
||||
|
||||
/*
|
||||
|
|
|
@ -44,6 +44,8 @@ const ERRORS = {
|
|||
"Only privileged and certified apps may use Firefox Accounts",
|
||||
"ERROR_INVALID_ASSERTION_AUDIENCE":
|
||||
"Assertion audience may not differ from origin",
|
||||
"ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT":
|
||||
"The request() method may only be invoked when handling user input",
|
||||
};
|
||||
|
||||
function nsDOMIdentity(aIdentityInternal) {
|
||||
|
@ -178,17 +180,6 @@ nsDOMIdentity.prototype = {
|
|||
|
||||
request: function nsDOMIdentity_request(aOptions = {}) {
|
||||
this._log("request: " + JSON.stringify(aOptions));
|
||||
let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
// The only time we permit calling of request() outside of a user
|
||||
// input handler is when we are handling the (deprecated) get() or
|
||||
// getVerifiedEmail() calls, which make use of an RP context
|
||||
// marked as _internal.
|
||||
if (this.nativeEventsRequired && !util.isHandlingUserInput && !aOptions._internal) {
|
||||
this._log("request: rejecting non-native event");
|
||||
return;
|
||||
}
|
||||
|
||||
// Has the caller called watch() before this?
|
||||
if (!this._rpWatcher) {
|
||||
|
@ -198,8 +189,32 @@ nsDOMIdentity.prototype = {
|
|||
throw new Error("navigator.id.request called too many times");
|
||||
}
|
||||
|
||||
let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
let message = this.DOMIdentityMessage(aOptions);
|
||||
|
||||
// We permit calling of request() outside of a user input handler only when
|
||||
// we are handling the (deprecated) get() or getVerifiedEmail() calls,
|
||||
// which make use of an RP context marked as _internal, or when a certified
|
||||
// app is calling.
|
||||
//
|
||||
// XXX Bug 982460 - grant the same privilege to packaged apps
|
||||
|
||||
if (!aOptions._internal &&
|
||||
this._appStatus !== Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
|
||||
|
||||
// If the caller is not special in one of those ways, see if the user has
|
||||
// preffed on 'syntheticEventsOk' (useful for testing); otherwise, if
|
||||
// this is a non-native event, reject it.
|
||||
let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
if (!util.isHandlingUserInput && this.nativeEventsRequired) {
|
||||
message.errors.push("ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT");
|
||||
}
|
||||
}
|
||||
|
||||
// Report and fail hard on any errors.
|
||||
if (message.errors.length) {
|
||||
this.reportErrors(message);
|
||||
|
@ -620,7 +635,8 @@ nsDOMIdentity.prototype = {
|
|||
|
||||
// Currently, we only permit certified and privileged apps to use
|
||||
// Firefox Accounts.
|
||||
if (this._appStatus !== principal.APP_STATUS_PRIVILEGED &&
|
||||
if (aOptions.wantIssuer == "firefox-accounts" &&
|
||||
this._appStatus !== principal.APP_STATUS_PRIVILEGED &&
|
||||
this._appStatus !== principal.APP_STATUS_CERTIFIED) {
|
||||
message.errors.push("ERROR_NOT_AUTHORIZED_FOR_FIREFOX_ACCOUNTS");
|
||||
}
|
||||
|
@ -648,7 +664,7 @@ nsDOMIdentity.prototype = {
|
|||
// Replace any audience supplied by the RP with one that has been sanitised
|
||||
message.audience = _audience;
|
||||
|
||||
this._log("Generated message: " + JSON.stringify(message));
|
||||
this._log("DOMIdentityMessage: " + JSON.stringify(message));
|
||||
|
||||
return message;
|
||||
},
|
||||
|
|
|
@ -2,5 +2,8 @@
|
|||
|
||||
support-files=
|
||||
file_declareAudience.html
|
||||
file_syntheticEvents.html
|
||||
|
||||
[test_declareAudience.html]
|
||||
[test_syntheticEvents.html]
|
||||
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
<!--
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<!--
|
||||
Certified and privileged apps can call mozId outside an event handler
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=971379
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test app for bug 971379</title>
|
||||
</head>
|
||||
|
||||
<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) {
|
||||
navigator.mozId.watch({
|
||||
wantIssuer: message.data.wantIssuer,
|
||||
onready: onready,
|
||||
onerror: onerror,
|
||||
onlogin: onlogin,
|
||||
onlogout: function() {},
|
||||
});
|
||||
};
|
||||
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,209 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=971379
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Certified/packaged apps may use synthetic events with FXA -- Bug 971379</title>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=971379">Mozilla Bug 971379</a>
|
||||
<p id="display"></p>
|
||||
<div id="content">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript;version=1.8">
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
Components.utils.import("resource://gre/modules/Promise.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/DOMIdentity.jsm");
|
||||
Components.utils.import("resource://gre/modules/identity/jwcrypto.jsm");
|
||||
Components.utils.import("resource://gre/modules/identity/FirefoxAccounts.jsm");
|
||||
|
||||
// Mock the Firefox Accounts manager to give a dummy assertion, just to confirm
|
||||
// that we're making the trip through the dom/identity and toolkit/identity
|
||||
// plumbing.
|
||||
function MockFXAManager() {}
|
||||
MockFXAManager.prototype = {
|
||||
getAssertion: function() {
|
||||
return Promise.resolve("here~you.go.dude");
|
||||
}
|
||||
};
|
||||
|
||||
let originalManager = FirefoxAccounts.fxAccountsManager;
|
||||
FirefoxAccounts.fxAccountsManager = new MockFXAManager();
|
||||
|
||||
// Mock IdentityService (Persona) so we can test request() while not handling
|
||||
// user input on an installed app. Since in this test suite, we have only this
|
||||
// one test for Persona, we additionally cause request() to throw if invoked, as
|
||||
// added security that nsDOMIdentity did not emit a request message.
|
||||
let MockIdentityService = function() {
|
||||
this.RP = this;
|
||||
this.contexts = {};
|
||||
}
|
||||
MockIdentityService.prototype = {
|
||||
watch: function(context) {
|
||||
this.contexts[context.id] = context;
|
||||
context.doReady();
|
||||
},
|
||||
|
||||
request: function(message) {
|
||||
ok(false, "nsDOMIdentity should block Persona request() in this test suite");
|
||||
},
|
||||
};
|
||||
DOMIdentity._mockIdentityService = new MockIdentityService();
|
||||
|
||||
// The manifests for these apps are all declared in
|
||||
// /testing/profiles/webapps_mochitest.json. They are injected into the profile
|
||||
// by /testing/mochitest/runtests.py with the appropriate appStatus. So we don't
|
||||
// have to manually install any apps.
|
||||
let apps = [
|
||||
{
|
||||
title: "an installed app, which must request() in a native event",
|
||||
manifest: "https://example.com/manifest.webapp",
|
||||
origin: "https://example.com",
|
||||
uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_syntheticEvents.html",
|
||||
wantIssuer: "", // default to persona
|
||||
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 not use synthetic events (until bug 982460 lands)",
|
||||
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: false,
|
||||
errors: [
|
||||
"ERROR_REQUEST_WHILE_NOT_HANDLING_USER_INPUT",
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
title: "a certified app, which may use synthetic events",
|
||||
manifest: "https://example.com/manifest_cert.webapp",
|
||||
origin: "https://example.com",
|
||||
uri: "https://example.com/chrome/dom/identity/tests/mochitest/file_syntheticEvents.html",
|
||||
wantIssuer: "firefox-accounts",
|
||||
expected: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
let appIndex = 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 expected = app.expected;
|
||||
|
||||
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));
|
||||
});
|
||||
|
||||
appIndex += 1;
|
||||
|
||||
if (appIndex === apps.length) {
|
||||
window.removeEventListener("message", receiveMessage);
|
||||
|
||||
// Remove mock from DOMIdentity
|
||||
DOMIdentity._mockIdentityService = null;
|
||||
|
||||
// Restore original fxa manager
|
||||
FirefoxAccounts.fxAccountsManager = originalManager;
|
||||
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
|
||||
testRunner.next();
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener("message", receiveMessage, false, true);
|
||||
|
||||
function runTest() {
|
||||
for (let app of apps) {
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
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", ""],
|
||||
]},
|
||||
function() {
|
||||
testRunner.next();
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
Загрузка…
Ссылка в новой задаче