mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 794680 - Connect gecko to gaia identity ui. r=benadida, r=cjones
This commit is contained in:
parent
8545ec6439
commit
e4891fd894
186
b2g/chrome/content/identity.js
Normal file
186
b2g/chrome/content/identity.js
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
/* -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
// This JS shim contains the callbacks to fire DOMRequest events for
|
||||||
|
// navigator.pay API within the payment processor's scope.
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
let { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||||
|
"@mozilla.org/childprocessmessagemanager;1",
|
||||||
|
"nsIMessageSender");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||||
|
"@mozilla.org/uuid-generator;1",
|
||||||
|
"nsIUUIDGenerator");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Logger",
|
||||||
|
"resource://gre/modules/identity/LogUtils.jsm");
|
||||||
|
|
||||||
|
function log(...aMessageArgs) {
|
||||||
|
Logger.log.apply(Logger, ["injected identity.js"].concat(aMessageArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
log("\n\n======================= identity.js =======================\n\n");
|
||||||
|
|
||||||
|
// This script may be injected more than once into an iframe.
|
||||||
|
// Ensure we don't redefine contstants
|
||||||
|
if (typeof kIdentityJSLoaded === 'undefined') {
|
||||||
|
const kReceivedIdentityAssertion = "received-id-assertion";
|
||||||
|
const kIdentityDelegateWatch = "identity-delegate-watch";
|
||||||
|
const kIdentityDelegateRequest = "identity-delegate-request";
|
||||||
|
const kIdentityDelegateLogout = "identity-delegate-logout";
|
||||||
|
const kIdentityDelegateReady = "identity-delegate-ready";
|
||||||
|
const kIdentityDelegateFinished = "identity-delegate-finished";
|
||||||
|
const kIdentityControllerDoMethod = "identity-controller-doMethod";
|
||||||
|
const kIdentktyJSLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
var showUI = false;
|
||||||
|
var options = null;
|
||||||
|
var isLoaded = false;
|
||||||
|
var func = null;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Message back to the SignInToWebsite pipe. Message should be an
|
||||||
|
* object with the following keys:
|
||||||
|
*
|
||||||
|
* method: one of 'login', 'logout', 'ready'
|
||||||
|
* assertion: optional assertion
|
||||||
|
*/
|
||||||
|
function identityCall(message) {
|
||||||
|
sendAsyncMessage(kIdentityControllerDoMethod, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
function identityFinished() {
|
||||||
|
log("identity finished. closing dialog");
|
||||||
|
closeIdentityDialog(function notifySuccess() {
|
||||||
|
// get ready for next call with a reinit
|
||||||
|
func = null; options = null;
|
||||||
|
|
||||||
|
sendAsyncMessage(kIdentityDelegateFinished);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Notify the UI to close the dialog and return to the caller application
|
||||||
|
*/
|
||||||
|
function closeIdentityDialog(aCallback) {
|
||||||
|
let randomId = uuidgen.generateUUID().toString();
|
||||||
|
let id = kReceivedIdentityAssertion + "-" + randomId;
|
||||||
|
let browser = Services.wm.getMostRecentWindow("navigator:browser");
|
||||||
|
|
||||||
|
let detail = {
|
||||||
|
type: kReceivedIdentityAssertion,
|
||||||
|
id: id,
|
||||||
|
showUI: showUI
|
||||||
|
};
|
||||||
|
|
||||||
|
// In order to avoid race conditions, we wait for the UI to notify that
|
||||||
|
// it has successfully closed the identity flow and has recovered the
|
||||||
|
// caller app, before notifying the parent process.
|
||||||
|
content.addEventListener("mozContentEvent", function closeIdentityDialogFinished(evt) {
|
||||||
|
content.removeEventListener("mozContentEvent", closeIdentityDialogFinished);
|
||||||
|
|
||||||
|
if (evt.detail.id == id && aCallback) {
|
||||||
|
aCallback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
browser.shell.sendChromeEvent(detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* doInternalWatch - call the internal.watch api and relay the results
|
||||||
|
* up to the controller.
|
||||||
|
*/
|
||||||
|
function doInternalWatch() {
|
||||||
|
log("doInternalWatch:", options, isLoaded);
|
||||||
|
if (options && isLoaded) {
|
||||||
|
log("internal watch options:", options);
|
||||||
|
let BrowserID = content.wrappedJSObject.BrowserID;
|
||||||
|
BrowserID.internal.watch(function(aParams) {
|
||||||
|
log("sending watch method message:", aParams.method);
|
||||||
|
identityCall(aParams);
|
||||||
|
if (aParams.method === "ready") {
|
||||||
|
log("watch finished.");
|
||||||
|
identityFinished();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
JSON.stringify({loggedInUser: options.loggedInUser, origin: options.origin}),
|
||||||
|
function(...things) {
|
||||||
|
log("internal: ", things);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doInternalRequest() {
|
||||||
|
log("doInternalRequest:", options && isLoaded);
|
||||||
|
if (options && isLoaded) {
|
||||||
|
content.wrappedJSObject.BrowserID.internal.get(
|
||||||
|
options.origin,
|
||||||
|
function(assertion) {
|
||||||
|
if (assertion) {
|
||||||
|
log("request -> assertion, so do login");
|
||||||
|
identityCall({method:'login',assertion:assertion});
|
||||||
|
}
|
||||||
|
identityFinished();
|
||||||
|
},
|
||||||
|
options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function doInternalLogout(aOptions) {
|
||||||
|
log("doInternalLogout:", (options && isLoaded));
|
||||||
|
if (options && isLoaded) {
|
||||||
|
let BrowserID = content.wrappedJSObject.BrowserID;
|
||||||
|
log("logging you out of ", options.origin);
|
||||||
|
BrowserID.internal.logout(options.origin, function() {
|
||||||
|
identityCall({method:'logout'});
|
||||||
|
identityFinished();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener("DOMContentLoaded", function(e) {
|
||||||
|
content.addEventListener("load", function(e) {
|
||||||
|
isLoaded = true;
|
||||||
|
// bring da func
|
||||||
|
if (func) func();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// listen for request
|
||||||
|
addMessageListener(kIdentityDelegateRequest, function(aMessage) {
|
||||||
|
log("\n\n* * * * injected identity.js received", kIdentityDelegateRequest, "\n\n\n");
|
||||||
|
options = aMessage.json;
|
||||||
|
showUI = true;
|
||||||
|
func = doInternalRequest;
|
||||||
|
func();
|
||||||
|
});
|
||||||
|
|
||||||
|
// listen for watch
|
||||||
|
addMessageListener(kIdentityDelegateWatch, function(aMessage) {
|
||||||
|
log("\n\n* * * * injected identity.js received", kIdentityDelegateWatch, "\n\n\n");
|
||||||
|
options = aMessage.json;
|
||||||
|
showUI = false;
|
||||||
|
func = doInternalWatch;
|
||||||
|
func();
|
||||||
|
});
|
||||||
|
|
||||||
|
// listen for logout
|
||||||
|
addMessageListener(kIdentityDelegateLogout, function(aMessage) {
|
||||||
|
log("\n\n* * * * injected identity.js received", kIdentityDelegateLogout, "\n\n\n");
|
||||||
|
options = aMessage.json;
|
||||||
|
showUI = false;
|
||||||
|
func = doInternalLogout;
|
||||||
|
func();
|
||||||
|
});
|
352
b2g/components/SignInToWebsite.jsm
Normal file
352
b2g/components/SignInToWebsite.jsm
Normal file
@ -0,0 +1,352 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SignInToWebsite.jsm - UX Controller and means for accessing identity
|
||||||
|
* cookies on behalf of relying parties.
|
||||||
|
*
|
||||||
|
* Currently, the b2g security architecture isolates web applications
|
||||||
|
* so that each window has access only to a local cookie jar:
|
||||||
|
*
|
||||||
|
* To prevent Web apps from interfering with one another, each one is
|
||||||
|
* hosted on a separate domain, and therefore may only access the
|
||||||
|
* resources associated with its domain. These resources include
|
||||||
|
* things such as IndexedDB databases, cookies, offline storage,
|
||||||
|
* and so forth.
|
||||||
|
*
|
||||||
|
* -- https://developer.mozilla.org/en-US/docs/Mozilla/Firefox_OS/Security/Security_model
|
||||||
|
*
|
||||||
|
* As a result, an authentication system like Persona cannot share its
|
||||||
|
* cookie jar with multiple relying parties, and so would require a
|
||||||
|
* fresh login request in every window. This would not be a good
|
||||||
|
* experience.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* In order for navigator.id.request() to maintain state in a single
|
||||||
|
* cookie jar, we cause all Persona interactions to take place in a
|
||||||
|
* gaia context that is launched by the system application, with the
|
||||||
|
* result that Persona has a single cookie jar that all Relying
|
||||||
|
* Parties can use. Since of course those Relying Parties cannot
|
||||||
|
* reach into the system cookie jar, the Controller in this module
|
||||||
|
* provides a way to get messages and data to and fro between the
|
||||||
|
* Relying Party in its window context, and the Persona internal api
|
||||||
|
* in its context.
|
||||||
|
*
|
||||||
|
* On the Relying Party's side, say a web page invokes
|
||||||
|
* navigator.id.watch(), to register callbacks, and then
|
||||||
|
* navigator.id.request() to request an assertion. The navigator.id
|
||||||
|
* calls are provided by nsDOMIdentity. nsDOMIdentity messages down
|
||||||
|
* to the privileged DOMIdentity code (using cpmm and ppmm message
|
||||||
|
* managers). DOMIdentity stores the state of Relying Party flows
|
||||||
|
* using an Identity service (MinimalIdentity.jsm), and emits messages
|
||||||
|
* requesting Persona functions (doWatch, doReady, doLogout).
|
||||||
|
*
|
||||||
|
* The Identity service sends these observer messages to the
|
||||||
|
* Controller in this module, which in turn triggers gaia to open a
|
||||||
|
* window to host the Persona js. If user interaction is required,
|
||||||
|
* gaia will open the trusty UI. If user interaction is not required,
|
||||||
|
* and we only need to get to Persona functions, gaia will open a
|
||||||
|
* hidden iframe. In either case, a window is opened into which the
|
||||||
|
* controller causes the script identity.js to be injected. This
|
||||||
|
* script provides the glue between the in-page javascript and the
|
||||||
|
* pipe back down to the Controller, translating navigator.internal
|
||||||
|
* function callbacks into messages sent back to the Controller.
|
||||||
|
*
|
||||||
|
* As a result, a navigator.internal function in the hosted popup or
|
||||||
|
* iframe can call back to the injected identity.js (doReady, doLogin,
|
||||||
|
* or doLogout). identity.js callbacks send messages back through the
|
||||||
|
* pipe to the Controller. The controller invokes the corresponding
|
||||||
|
* function on the Identity Service (doReady, doLogin, or doLogout).
|
||||||
|
* The IdentityService calls the corresponding callback for the
|
||||||
|
* correct Relying Party, which causes DOMIdentity to send a message
|
||||||
|
* up to the Relying Party through nsDOMIdentity
|
||||||
|
* (Identity:RP:Watch:OnLogin etc.), and finally, nsDOMIdentity
|
||||||
|
* receives these messages and calls the original callback that the
|
||||||
|
* Relying Party registered (navigator.id.watch(),
|
||||||
|
* navigator.id.request(), or navigator.id.logout()).
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const EXPORTED_SYMBOLS = ["SignInToWebsiteController"];
|
||||||
|
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
const Cu = Components.utils;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
|
||||||
|
"resource://gre/modules/identity/MinimalIdentity.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "Logger",
|
||||||
|
"resource://gre/modules/identity/LogUtils.jsm");
|
||||||
|
|
||||||
|
// JS shim that contains the callback functions that
|
||||||
|
// live within the identity UI provisioning frame.
|
||||||
|
const kIdentityShimFile = "chrome://browser/content/identity.js";
|
||||||
|
|
||||||
|
// Type of MozChromeEvents to handle id dialogs.
|
||||||
|
const kOpenIdentityDialog = "open-id-dialog";
|
||||||
|
const kCloseIdentityDialog = "close-id-dialog";
|
||||||
|
|
||||||
|
// Observer messages to communicate to shim
|
||||||
|
const kIdentityDelegateWatch = "identity-delegate-watch";
|
||||||
|
const kIdentityDelegateRequest = "identity-delegate-request";
|
||||||
|
const kIdentityDelegateLogout = "identity-delegate-logout";
|
||||||
|
const kIdentityDelegateFinished = "identity-delegate-finished";
|
||||||
|
const kIdentityDelegateReady = "identity-delegate-ready";
|
||||||
|
|
||||||
|
const kIdentityControllerDoMethod = "identity-controller-doMethod";
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||||
|
"@mozilla.org/uuid-generator;1",
|
||||||
|
"nsIUUIDGenerator");
|
||||||
|
|
||||||
|
function log(...aMessageArgs) {
|
||||||
|
Logger.log.apply(Logger, ["SignInToWebsiteController"].concat(aMessageArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomId() {
|
||||||
|
return uuidgen.generateUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GaiaInterface encapsulates the our gaia functions. There are only two:
|
||||||
|
*
|
||||||
|
* getContent - return the current content window
|
||||||
|
* sendChromeEvent - send a chromeEvent from the browser shell
|
||||||
|
*/
|
||||||
|
let GaiaInterface = {
|
||||||
|
_getBrowser: function SignInToWebsiteController__getBrowser() {
|
||||||
|
return Services.wm.getMostRecentWindow("navigator:browser");
|
||||||
|
},
|
||||||
|
|
||||||
|
getContent: function SignInToWebsiteController_getContent() {
|
||||||
|
return this._getBrowser().getContentWindow();
|
||||||
|
},
|
||||||
|
|
||||||
|
sendChromeEvent: function SignInToWebsiteController_sendChromeEvent(detail) {
|
||||||
|
this._getBrowser().shell.sendChromeEvent(detail);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The Pipe abstracts the communcation channel between the Controller
|
||||||
|
* and the identity.js code running in the browser window.
|
||||||
|
*/
|
||||||
|
let Pipe = {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* communicate - launch a gaia window with certain options and
|
||||||
|
* provide a callback for handling messages.
|
||||||
|
*
|
||||||
|
* @param aRpOptions options describing the Relying Party's
|
||||||
|
* (dicitonary) call, such as origin and loggedInEmail.
|
||||||
|
*
|
||||||
|
* @param aGaiaOptions showUI: boolean
|
||||||
|
* (dictionary) message: name of the message to emit
|
||||||
|
* (request, logout, watch)
|
||||||
|
*
|
||||||
|
* @param aMessageCallback function to call on receipt of a
|
||||||
|
* (function) do-method message. These messages name
|
||||||
|
* a method ('login', 'logout', etc.) and
|
||||||
|
* carry optional parameters. The Pipe does
|
||||||
|
* not know what the messages mean; it is
|
||||||
|
* up to the caller to interpret them and
|
||||||
|
* act on them.
|
||||||
|
*/
|
||||||
|
communicate: function(aRpOptions, aGaiaOptions, aMessageCallback) {
|
||||||
|
log("open gaia dialog with options:", aGaiaOptions);
|
||||||
|
|
||||||
|
// This content variable is injected into the scope of
|
||||||
|
// kIdentityShimFile, where it is used to access the BrowserID object
|
||||||
|
// and its internal API.
|
||||||
|
let content = GaiaInterface.getContent();
|
||||||
|
|
||||||
|
if (!content) {
|
||||||
|
log("ERROR: what the what? no content window?");
|
||||||
|
// aErrorCb.onresult("NO_CONTENT_WINDOW");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare a message for gaia. The parameter showUI signals
|
||||||
|
// whether user interaction is needed. If it is, gaia will open a
|
||||||
|
// dialog; if not, a hidden iframe. In each case, BrowserID is
|
||||||
|
// available in the context.
|
||||||
|
let id = kOpenIdentityDialog + "-" + getRandomId();
|
||||||
|
let detail = {
|
||||||
|
type: kOpenIdentityDialog,
|
||||||
|
showUI: aGaiaOptions.showUI || false,
|
||||||
|
id: id
|
||||||
|
};
|
||||||
|
|
||||||
|
// When gaia signals back with a mozContentEvent containing the
|
||||||
|
// unique id we created, we know the window is ready. We then inject
|
||||||
|
// the magic javascript (kIdentityShimFile) that will give the content
|
||||||
|
// the superpowers it needs to communicate back with this code.
|
||||||
|
content.addEventListener("mozContentEvent", function getAssertion(evt) {
|
||||||
|
|
||||||
|
// Make sure the message is really for us
|
||||||
|
let msg = evt.detail;
|
||||||
|
if (msg.id != id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only need to catch the first mozContentEvent from the
|
||||||
|
// iframe or popup, so we remove the listener right away.
|
||||||
|
content.removeEventListener("mozContentEvent", getAssertion);
|
||||||
|
|
||||||
|
// Try to load the identity shim file containing the callbacks
|
||||||
|
// in the content script. This could be either the visible
|
||||||
|
// popup that the user interacts with, or it could be an invisible
|
||||||
|
// frame.
|
||||||
|
let frame = evt.detail.frame;
|
||||||
|
let frameLoader = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
|
||||||
|
let mm = frameLoader.messageManager;
|
||||||
|
try {
|
||||||
|
log("about to load frame script");
|
||||||
|
mm.loadFrameScript(kIdentityShimFile, true);
|
||||||
|
} catch (e) {
|
||||||
|
log("Error loading ", kIdentityShimFile, " as a frame script: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are two messages that the delegate can send back: a "do
|
||||||
|
// method" event, and a "finished" event. We pass the do-method
|
||||||
|
// events straight to the caller for interpretation and handling.
|
||||||
|
// If we receive a "finished" event, then the delegate is done, so
|
||||||
|
// we shut down the pipe and clean up.
|
||||||
|
mm.addMessageListener(kIdentityControllerDoMethod, aMessageCallback);
|
||||||
|
mm.addMessageListener(kIdentityDelegateFinished, function identityDelegateFinished(message) {
|
||||||
|
mm.removeMessageListener(kIdentityDelegateFinished, identityDelegateFinished);
|
||||||
|
mm.removeMessageListener(kIdentityControllerDoMethod, aMessageCallback);
|
||||||
|
});
|
||||||
|
|
||||||
|
mm.sendAsyncMessage(aGaiaOptions.message, aRpOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tell gaia to open the identity iframe or trusty popup
|
||||||
|
GaiaInterface.sendChromeEvent(detail);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The controller sits between the IdentityService used by DOMIdentity
|
||||||
|
* and a gaia process launches an (invisible) iframe or (visible)
|
||||||
|
* trusty UI. Using an injected js script (identity.js), the
|
||||||
|
* controller enables the gaia window to access the persona identity
|
||||||
|
* storage in the system cookie jar and send events back via the
|
||||||
|
* controller into IdentityService and DOM, and ultimately up to the
|
||||||
|
* Relying Party, which is open in a different window context.
|
||||||
|
*/
|
||||||
|
let SignInToWebsiteController = {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize the controller. To use a different gaia communication pipe,
|
||||||
|
* such as when mocking it in tests, pass aOptions.pipe.
|
||||||
|
*/
|
||||||
|
init: function SignInToWebsiteController_init(aOptions) {
|
||||||
|
aOptions = aOptions || {};
|
||||||
|
this.pipe = aOptions.pipe || Pipe;
|
||||||
|
Services.obs.addObserver(this, "identity-controller-watch", false);
|
||||||
|
Services.obs.addObserver(this, "identity-controller-request", false);
|
||||||
|
Services.obs.addObserver(this, "identity-controller-logout", false);
|
||||||
|
},
|
||||||
|
|
||||||
|
uninit: function SignInToWebsiteController_uninit() {
|
||||||
|
Services.obs.removeObserver(this, "identity-controller-watch");
|
||||||
|
Services.obs.removeObserver(this, "identity-controller-request");
|
||||||
|
Services.obs.removeObserver(this, "identity-controller-logout");
|
||||||
|
},
|
||||||
|
|
||||||
|
observe: function SignInToWebsiteController_observe(aSubject, aTopic, aData) {
|
||||||
|
log("observe: received", aTopic, "with", aData, "for", aSubject);
|
||||||
|
let options = null;
|
||||||
|
if (aSubject) {
|
||||||
|
options = aSubject.wrappedJSObject;
|
||||||
|
}
|
||||||
|
switch(aTopic) {
|
||||||
|
case "identity-controller-watch":
|
||||||
|
this.doWatch(options);
|
||||||
|
break;
|
||||||
|
case "identity-controller-request":
|
||||||
|
this.doRequest(options);
|
||||||
|
break;
|
||||||
|
case "identity-controller-logout":
|
||||||
|
this.doLogout(options);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Logger.reportError("SignInToWebsiteController", "Unknown observer notification:", aTopic);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* options: method required - name of method to invoke
|
||||||
|
* assertion optional
|
||||||
|
*/
|
||||||
|
_makeDoMethodCallback: function SignInToWebsiteController__makeDoMethodCallback(aRpId) {
|
||||||
|
return function SignInToWebsiteController_methodCallback(aOptions) {
|
||||||
|
log("doMethod:", aOptions);
|
||||||
|
let message = aOptions.json;
|
||||||
|
if (typeof message === 'string') {
|
||||||
|
message = JSON.parse(message);
|
||||||
|
}
|
||||||
|
switch(message.method) {
|
||||||
|
case "ready":
|
||||||
|
IdentityService.doReady(aRpId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "login":
|
||||||
|
IdentityService.doLogin(aRpId, message.assertion);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "logout":
|
||||||
|
IdentityService.doLogout(aRpId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
log("WARNING: wonky method call:", message.method);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
doWatch: function SignInToWebsiteController_doWatch(aOptions) {
|
||||||
|
// dom prevents watch from being called twice
|
||||||
|
log("doWatch:", aOptions);
|
||||||
|
var gaiaOptions = {
|
||||||
|
message: kIdentityDelegateWatch,
|
||||||
|
showUI: false
|
||||||
|
};
|
||||||
|
this.pipe.communicate(aOptions, gaiaOptions, this._makeDoMethodCallback(aOptions.rpId));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The website is requesting login so the user must choose an identity to use.
|
||||||
|
*/
|
||||||
|
doRequest: function SignInToWebsiteController_doRequest(aOptions) {
|
||||||
|
log("doRequest", aOptions);
|
||||||
|
// tell gaia to open the identity popup
|
||||||
|
var gaiaOptions = {
|
||||||
|
message: kIdentityDelegateRequest,
|
||||||
|
showUI: true
|
||||||
|
};
|
||||||
|
this.pipe.communicate(aOptions, gaiaOptions, this._makeDoMethodCallback(aOptions.rpId));
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
doLogout: function SignInToWebsiteController_doLogout(aOptions) {
|
||||||
|
log("doLogout", aOptions);
|
||||||
|
var gaiaOptions = {
|
||||||
|
message: kIdentityDelegateLogout,
|
||||||
|
showUI: false
|
||||||
|
};
|
||||||
|
this.pipe.communicate(aOptions, gaiaOptions, this._makeDoMethodCallback(aOptions.rpId));
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
134
b2g/components/test/unit/head_identity.js
Normal file
134
b2g/components/test/unit/head_identity.js
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
const Cu = Components.utils;
|
||||||
|
|
||||||
|
// The following boilerplate makes sure that XPCom calls
|
||||||
|
// that use the profile directory work.
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "MinimalIDService",
|
||||||
|
"resource://gre/modules/identity/MinimalIdentity.jsm",
|
||||||
|
"IdentityService");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this,
|
||||||
|
"Logger",
|
||||||
|
"resource://gre/modules/identity/LogUtils.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this,
|
||||||
|
"uuidGenerator",
|
||||||
|
"@mozilla.org/uuid-generator;1",
|
||||||
|
"nsIUUIDGenerator");
|
||||||
|
|
||||||
|
const TEST_URL = "https://myfavoriteflan.com";
|
||||||
|
const TEST_USER = "uumellmahaye1969@hotmail.com";
|
||||||
|
const TEST_PRIVKEY = "i-am-a-secret";
|
||||||
|
const TEST_CERT = "i~like~pie";
|
||||||
|
|
||||||
|
// The following are utility functions for Identity testing
|
||||||
|
|
||||||
|
function log(...aMessageArgs) {
|
||||||
|
Logger.log.apply(Logger, ["test"].concat(aMessageArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
function partial(fn) {
|
||||||
|
let args = Array.prototype.slice.call(arguments, 1);
|
||||||
|
return function() {
|
||||||
|
return fn.apply(this, args.concat(Array.prototype.slice.call(arguments)));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function uuid() {
|
||||||
|
return uuidGenerator.generateUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a mock "doc" object, which the Identity Service
|
||||||
|
// uses as a pointer back into the doc object
|
||||||
|
function mockDoc(aIdentity, aOrigin, aDoFunc) {
|
||||||
|
let mockedDoc = {};
|
||||||
|
mockedDoc.id = uuid();
|
||||||
|
mockedDoc.loggedInEmail = aIdentity;
|
||||||
|
mockedDoc.origin = aOrigin;
|
||||||
|
mockedDoc['do'] = aDoFunc;
|
||||||
|
mockedDoc.doReady = partial(aDoFunc, 'ready');
|
||||||
|
mockedDoc.doLogin = partial(aDoFunc, 'login');
|
||||||
|
mockedDoc.doLogout = partial(aDoFunc, 'logout');
|
||||||
|
mockedDoc.doError = partial(aDoFunc, 'error');
|
||||||
|
mockedDoc.doCancel = partial(aDoFunc, 'cancel');
|
||||||
|
mockedDoc.doCoffee = partial(aDoFunc, 'coffee');
|
||||||
|
|
||||||
|
return mockedDoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a mock "pipe" object that would normally communicate
|
||||||
|
// messages up to gaia (either the trusty ui or the hidden iframe),
|
||||||
|
// and convey messages back down from gaia to the controller through
|
||||||
|
// the message callback.
|
||||||
|
function mockPipe() {
|
||||||
|
let MockedPipe = {
|
||||||
|
communicate: function(aRpOptions, aGaiaOptions, aMessageCallback) {
|
||||||
|
switch (aGaiaOptions.message) {
|
||||||
|
case "identity-delegate-watch":
|
||||||
|
aMessageCallback({json: {method: "ready"}});
|
||||||
|
break;
|
||||||
|
case "identity-delegate-request":
|
||||||
|
aMessageCallback({json: {method: "login", assertion: TEST_CERT}});
|
||||||
|
break;
|
||||||
|
case "identity-delegate-logout":
|
||||||
|
aMessageCallback({json: {method: "logout"}});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw("what the what?? " + aGaiaOptions.message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return MockedPipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mimicking callback funtionality for ease of testing
|
||||||
|
// this observer auto-removes itself after the observe function
|
||||||
|
// is called, so this is meant to observe only ONE event.
|
||||||
|
function makeObserver(aObserveTopic, aObserveFunc) {
|
||||||
|
let observer = {
|
||||||
|
// nsISupports provides type management in C++
|
||||||
|
// nsIObserver is to be an observer
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
|
||||||
|
|
||||||
|
observe: function (aSubject, aTopic, aData) {
|
||||||
|
if (aTopic == aObserveTopic) {
|
||||||
|
Services.obs.removeObserver(observer, aObserveTopic);
|
||||||
|
aObserveFunc(aSubject, aTopic, aData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Services.obs.addObserver(observer, aObserveTopic, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a hook to set up the ID service with an identity with keypair and all
|
||||||
|
// when ready, invoke callback with the identity. It's there if we need it.
|
||||||
|
function setup_test_identity(identity, cert, cb) {
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
|
||||||
|
// takes a list of functions and returns a function that
|
||||||
|
// when called the first time, calls the first func,
|
||||||
|
// then the next time the second, etc.
|
||||||
|
function call_sequentially() {
|
||||||
|
let numCalls = 0;
|
||||||
|
let funcs = arguments;
|
||||||
|
|
||||||
|
return function() {
|
||||||
|
if (!funcs[numCalls]) {
|
||||||
|
let argString = Array.prototype.slice.call(arguments).join(",");
|
||||||
|
do_throw("Too many calls: " + argString);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
funcs[numCalls].apply(funcs[numCalls],arguments);
|
||||||
|
numCalls += 1;
|
||||||
|
};
|
||||||
|
}
|
162
b2g/components/test/unit/test_signintowebsite.js
Normal file
162
b2g/components/test/unit/test_signintowebsite.js
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
// Tests for b2g/components/SignInToWebsite.jsm
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "MinimalIDService",
|
||||||
|
"resource://gre/modules/identity/MinimalIdentity.jsm",
|
||||||
|
"IdentityService");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteController",
|
||||||
|
"resource://gre/modules/SignInToWebsite.jsm",
|
||||||
|
"SignInToWebsiteController");
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
|
||||||
|
|
||||||
|
function log(...aMessageArgs) {
|
||||||
|
Logger.log.apply(Logger, ["test_signintowebsite"].concat(aMessageArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_overall() {
|
||||||
|
do_check_neq(MinimalIDService, null);
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_mock_doc() {
|
||||||
|
do_test_pending();
|
||||||
|
let mockedDoc = mockDoc(null, TEST_URL, function(action, params) {
|
||||||
|
do_check_eq(action, 'coffee');
|
||||||
|
do_test_finished();
|
||||||
|
run_next_test();
|
||||||
|
});
|
||||||
|
|
||||||
|
// A smoke test to ensure that mockedDoc is functioning correctly.
|
||||||
|
// There is presently no doCoffee method in Persona.
|
||||||
|
mockedDoc.doCoffee();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_watch() {
|
||||||
|
do_test_pending();
|
||||||
|
|
||||||
|
setup_test_identity("pie@food.gov", TEST_CERT, function() {
|
||||||
|
let controller = SignInToWebsiteController;
|
||||||
|
|
||||||
|
let mockedDoc = mockDoc(null, TEST_URL, function(action, params) {
|
||||||
|
do_check_eq(action, 'ready');
|
||||||
|
controller.uninit();
|
||||||
|
do_test_finished();
|
||||||
|
run_next_test();
|
||||||
|
});
|
||||||
|
|
||||||
|
controller.init({pipe: mockPipe()});
|
||||||
|
|
||||||
|
MinimalIDService.RP.watch(mockedDoc, {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_request_login() {
|
||||||
|
do_test_pending();
|
||||||
|
|
||||||
|
setup_test_identity("flan@food.gov", TEST_CERT, function() {
|
||||||
|
let controller = SignInToWebsiteController;
|
||||||
|
|
||||||
|
let mockedDoc = mockDoc(null, TEST_URL, call_sequentially(
|
||||||
|
function(action, params) {
|
||||||
|
do_check_eq(action, 'ready');
|
||||||
|
do_check_eq(params, undefined);
|
||||||
|
},
|
||||||
|
function(action, params) {
|
||||||
|
do_check_eq(action, 'login');
|
||||||
|
do_check_eq(params, TEST_CERT);
|
||||||
|
controller.uninit();
|
||||||
|
do_test_finished();
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
controller.init({pipe: mockPipe()});
|
||||||
|
MinimalIDService.RP.watch(mockedDoc, {});
|
||||||
|
MinimalIDService.RP.request(mockedDoc.id, {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_request_logout() {
|
||||||
|
do_test_pending();
|
||||||
|
|
||||||
|
setup_test_identity("flan@food.gov", TEST_CERT, function() {
|
||||||
|
let controller = SignInToWebsiteController;
|
||||||
|
|
||||||
|
let mockedDoc = mockDoc(null, TEST_URL, call_sequentially(
|
||||||
|
function(action, params) {
|
||||||
|
do_check_eq(action, 'ready');
|
||||||
|
do_check_eq(params, undefined);
|
||||||
|
},
|
||||||
|
function(action, params) {
|
||||||
|
do_check_eq(action, 'logout');
|
||||||
|
do_check_eq(params, undefined);
|
||||||
|
controller.uninit();
|
||||||
|
do_test_finished();
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
controller.init({pipe: mockPipe()});
|
||||||
|
MinimalIDService.RP.watch(mockedDoc, {});
|
||||||
|
MinimalIDService.RP.logout(mockedDoc.id, {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_request_login_logout() {
|
||||||
|
do_test_pending();
|
||||||
|
|
||||||
|
setup_test_identity("unagi@food.gov", TEST_CERT, function() {
|
||||||
|
let controller = SignInToWebsiteController;
|
||||||
|
|
||||||
|
let mockedDoc = mockDoc(null, TEST_URL, call_sequentially(
|
||||||
|
function(action, params) {
|
||||||
|
do_check_eq(action, 'ready');
|
||||||
|
do_check_eq(params, undefined);
|
||||||
|
},
|
||||||
|
function(action, params) {
|
||||||
|
do_check_eq(action, 'login');
|
||||||
|
do_check_eq(params, TEST_CERT);
|
||||||
|
},
|
||||||
|
function(action, params) {
|
||||||
|
do_check_eq(action, 'logout');
|
||||||
|
do_check_eq(params, undefined);
|
||||||
|
controller.uninit();
|
||||||
|
do_test_finished();
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
,function(action, params) {
|
||||||
|
do_check_eq(action, 'ready');
|
||||||
|
do_test_finished();
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
));
|
||||||
|
|
||||||
|
controller.init({pipe: mockPipe()});
|
||||||
|
MinimalIDService.RP.watch(mockedDoc, {});
|
||||||
|
MinimalIDService.RP.request(mockedDoc.id, {});
|
||||||
|
MinimalIDService.RP.logout(mockedDoc.id, {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let TESTS = [
|
||||||
|
test_overall,
|
||||||
|
test_mock_doc,
|
||||||
|
test_watch,
|
||||||
|
test_request_login,
|
||||||
|
test_request_logout,
|
||||||
|
test_request_login_logout
|
||||||
|
];
|
||||||
|
|
||||||
|
TESTS.forEach(add_test);
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
run_next_test();
|
||||||
|
}
|
@ -3,3 +3,9 @@ head =
|
|||||||
tail =
|
tail =
|
||||||
|
|
||||||
[test_bug793310.js]
|
[test_bug793310.js]
|
||||||
|
|
||||||
|
[test_signintowebsite.js]
|
||||||
|
head = head_identity.js
|
||||||
|
tail =
|
||||||
|
|
||||||
|
|
||||||
|
@ -225,6 +225,7 @@
|
|||||||
@BINPATH@/components/hal.xpt
|
@BINPATH@/components/hal.xpt
|
||||||
@BINPATH@/components/html5.xpt
|
@BINPATH@/components/html5.xpt
|
||||||
@BINPATH@/components/htmlparser.xpt
|
@BINPATH@/components/htmlparser.xpt
|
||||||
|
@BINPATH@/components/identity.xpt
|
||||||
@BINPATH@/components/imglib2.xpt
|
@BINPATH@/components/imglib2.xpt
|
||||||
@BINPATH@/components/imgicon.xpt
|
@BINPATH@/components/imgicon.xpt
|
||||||
@BINPATH@/components/inspector.xpt
|
@BINPATH@/components/inspector.xpt
|
||||||
@ -492,6 +493,10 @@
|
|||||||
@BINPATH@/components/AppsService.js
|
@BINPATH@/components/AppsService.js
|
||||||
@BINPATH@/components/AppsService.manifest
|
@BINPATH@/components/AppsService.manifest
|
||||||
|
|
||||||
|
@BINPATH@/components/nsDOMIdentity.js
|
||||||
|
@BINPATH@/components/nsIDService.js
|
||||||
|
@BINPATH@/components/Identity.manifest
|
||||||
|
|
||||||
@BINPATH@/components/SystemMessageInternal.js
|
@BINPATH@/components/SystemMessageInternal.js
|
||||||
@BINPATH@/components/SystemMessageManager.js
|
@BINPATH@/components/SystemMessageManager.js
|
||||||
@BINPATH@/components/SystemMessageManager.manifest
|
@BINPATH@/components/SystemMessageManager.manifest
|
||||||
|
@ -13,12 +13,20 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
|
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
|
||||||
|
#ifdef MOZ_B2G_VERSION
|
||||||
|
"resource://gre/modules/identity/MinimalIdentity.jsm");
|
||||||
|
#else
|
||||||
"resource://gre/modules/identity/Identity.jsm");
|
"resource://gre/modules/identity/Identity.jsm");
|
||||||
|
#endif
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this,
|
XPCOMUtils.defineLazyModuleGetter(this,
|
||||||
"Logger",
|
"Logger",
|
||||||
"resource://gre/modules/identity/LogUtils.jsm");
|
"resource://gre/modules/identity/LogUtils.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||||
|
"@mozilla.org/parentprocessmessagemanager;1",
|
||||||
|
"nsIMessageListenerManager");
|
||||||
|
|
||||||
function log(...aMessageArgs) {
|
function log(...aMessageArgs) {
|
||||||
Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs));
|
Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs));
|
||||||
}
|
}
|
||||||
@ -122,9 +130,7 @@ let DOMIdentity = {
|
|||||||
|
|
||||||
// Target is the frame message manager that called us and is
|
// Target is the frame message manager that called us and is
|
||||||
// used to send replies back to the proper window.
|
// used to send replies back to the proper window.
|
||||||
let targetMM = aMessage.target
|
let targetMM = aMessage.target;
|
||||||
.QueryInterface(Ci.nsIFrameLoaderOwner)
|
|
||||||
.frameLoader.messageManager;
|
|
||||||
|
|
||||||
switch (aMessage.name) {
|
switch (aMessage.name) {
|
||||||
// RP
|
// RP
|
||||||
@ -165,16 +171,10 @@ let DOMIdentity = {
|
|||||||
// nsIObserver
|
// nsIObserver
|
||||||
observe: function DOMIdentity_observe(aSubject, aTopic, aData) {
|
observe: function DOMIdentity_observe(aSubject, aTopic, aData) {
|
||||||
switch (aTopic) {
|
switch (aTopic) {
|
||||||
case "domwindowopened":
|
|
||||||
case "domwindowclosed":
|
|
||||||
let win = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
||||||
.getInterface(Ci.nsIDOMWindow);
|
|
||||||
this._configureMessages(win, aTopic == "domwindowopened");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "xpcom-shutdown":
|
case "xpcom-shutdown":
|
||||||
Services.ww.unregisterNotification(this);
|
this._unsubscribeListeners();
|
||||||
Services.obs.removeObserver(this, "xpcom-shutdown");
|
Services.obs.removeObserver(this, "xpcom-shutdown");
|
||||||
|
Services.ww.unregisterNotification(this);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -190,20 +190,23 @@ let DOMIdentity = {
|
|||||||
_init: function DOMIdentity__init() {
|
_init: function DOMIdentity__init() {
|
||||||
Services.ww.registerNotification(this);
|
Services.ww.registerNotification(this);
|
||||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||||
|
this._subscribeListeners();
|
||||||
},
|
},
|
||||||
|
|
||||||
_configureMessages: function DOMIdentity__configureMessages(aWindow, aRegister) {
|
_subscribeListeners: function DOMIdentity__subscribeListeners() {
|
||||||
if (!aWindow.messageManager)
|
if (!ppmm) return;
|
||||||
return;
|
|
||||||
|
|
||||||
let func = aWindow.messageManager[aRegister ? "addMessageListener"
|
|
||||||
: "removeMessageListener"];
|
|
||||||
|
|
||||||
for (let message of this.messages) {
|
for (let message of this.messages) {
|
||||||
func.call(aWindow.messageManager, message, this);
|
ppmm.addMessageListener(message, this);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_unsubscribeListeners: function DOMIdentity__unsubscribeListeners() {
|
||||||
|
for (let message of this.messages) {
|
||||||
|
ppmm.removeMessageListener(message, this);
|
||||||
|
}
|
||||||
|
ppmm = null;
|
||||||
|
},
|
||||||
|
|
||||||
_resetFrameState: function(aContext) {
|
_resetFrameState: function(aContext) {
|
||||||
log("_resetFrameState: ", aContext.id);
|
log("_resetFrameState: ", aContext.id);
|
||||||
if (!aContext._mm) {
|
if (!aContext._mm) {
|
||||||
|
@ -17,10 +17,13 @@ EXTRA_COMPONENTS = \
|
|||||||
Identity.manifest \
|
Identity.manifest \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
EXTRA_JS_MODULES = \
|
EXTRA_PP_JS_MODULES = \
|
||||||
DOMIdentity.jsm \
|
DOMIdentity.jsm \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
|
EXTRA_JS_MODULES = \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
ifdef ENABLE_TESTS
|
ifdef ENABLE_TESTS
|
||||||
DIRS += tests
|
DIRS += tests
|
||||||
endif
|
endif
|
||||||
|
@ -17,6 +17,10 @@ const MAX_RP_CALLS = 100;
|
|||||||
Cu.import("resource://gre/modules/Services.jsm");
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||||
|
"@mozilla.org/childprocessmessagemanager;1",
|
||||||
|
"nsIMessageSender");
|
||||||
|
|
||||||
// This is the child process corresponding to nsIDOMIdentity.
|
// This is the child process corresponding to nsIDOMIdentity.
|
||||||
|
|
||||||
|
|
||||||
@ -483,10 +487,7 @@ nsDOMIdentityInternal.prototype = {
|
|||||||
|
|
||||||
this._log("init was called from " + aWindow.document.location);
|
this._log("init was called from " + aWindow.document.location);
|
||||||
|
|
||||||
this._mm = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
this._mm = cpmm;
|
||||||
.getInterface(Ci.nsIWebNavigation)
|
|
||||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
||||||
.getInterface(Ci.nsIContentFrameMessageManager);
|
|
||||||
|
|
||||||
// Setup listeners for messages from parent process.
|
// Setup listeners for messages from parent process.
|
||||||
this._messages = [
|
this._messages = [
|
||||||
|
@ -11,11 +11,6 @@ relativesrcdir = @relativesrcdir@
|
|||||||
|
|
||||||
include $(DEPTH)/config/autoconf.mk
|
include $(DEPTH)/config/autoconf.mk
|
||||||
|
|
||||||
MOCHITEST_FILES = \
|
# XXX bug 805602 update and restore mochitests
|
||||||
head_identity.js \
|
|
||||||
test_identity_idp_auth_basics.html \
|
|
||||||
test_identity_idp_prov_basics.html \
|
|
||||||
test_identity_rp_basics.html \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
include $(topsrcdir)/config/rules.mk
|
include $(topsrcdir)/config/rules.mk
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const Ci = SpecialPowers.Ci;
|
|
||||||
const Cu = SpecialPowers.Cu;
|
|
||||||
|
|
||||||
SpecialPowers.setBoolPref("toolkit.identity.debug", true);
|
|
||||||
SpecialPowers.setBoolPref("dom.identity.enabled", true);
|
|
||||||
|
|
||||||
const Services = Cu.import("resource://gre/modules/Services.jsm").Services;
|
|
||||||
const DOMIdentity = Cu.import("resource://gre/modules/DOMIdentity.jsm")
|
|
||||||
.DOMIdentity;
|
|
||||||
|
|
||||||
let util = SpecialPowers.getDOMWindowUtils(window);
|
|
||||||
let outerWinId = util.outerWindowID;
|
|
||||||
|
|
||||||
const identity = navigator.id || navigator.mozId;
|
|
||||||
|
|
||||||
let index = 0;
|
|
||||||
|
|
||||||
// mimicking callback funtionality for ease of testing
|
|
||||||
// this observer auto-removes itself after the observe function
|
|
||||||
// is called, so this is meant to observe only ONE event.
|
|
||||||
function makeObserver(aObserveTopic, aObserveFunc) {
|
|
||||||
function observe(aSubject, aTopic, aData) {
|
|
||||||
if (aTopic == aObserveTopic) {
|
|
||||||
aObserveFunc(aSubject, aTopic, aData);
|
|
||||||
Services.obs.removeObserver(this, aObserveTopic);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Services.obs.addObserver(observe, aObserveTopic, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function expectException(aFunc, msg, aErrorType="Error") {
|
|
||||||
info("Expecting an exception: " + msg);
|
|
||||||
msg = msg || "";
|
|
||||||
let caughtEx = null;
|
|
||||||
try {
|
|
||||||
aFunc();
|
|
||||||
} catch (ex) {
|
|
||||||
let exProto = Object.getPrototypeOf(ex);
|
|
||||||
// Don't count NS_* exceptions since they shouldn't be exposed to content
|
|
||||||
if (exProto.toString() == aErrorType
|
|
||||||
&& ex.toString().indexOf("NS_ERROR_FAILURE") == -1) {
|
|
||||||
caughtEx = ex;
|
|
||||||
} else {
|
|
||||||
ok(false, ex);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isnot(caughtEx, null, "Check for thrown exception.");
|
|
||||||
}
|
|
||||||
|
|
||||||
function next() {
|
|
||||||
if (!identity) {
|
|
||||||
todo(false, "DOM API is not available. Skipping tests.");
|
|
||||||
finish_tests();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (index >= steps.length) {
|
|
||||||
ok(false, "Shouldn't get here!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
let fn = steps[index];
|
|
||||||
info("Begin test " + index + " '" + steps[index].name + "'!");
|
|
||||||
fn();
|
|
||||||
} catch(ex) {
|
|
||||||
ok(false, "Caught exception", ex);
|
|
||||||
}
|
|
||||||
index += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function finish_tests() {
|
|
||||||
info("all done");
|
|
||||||
SpecialPowers.clearUserPref("toolkit.identity.debug");
|
|
||||||
SpecialPowers.clearUserPref("dom.identity.enabled");
|
|
||||||
SimpleTest.finish();
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<!-- Any copyright is dedicated to the Public Domain.
|
|
||||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Test for navigator.id identity provider (IDP) authentication basics</title>
|
|
||||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<script type="application/javascript;version=1.8" src="head_identity.js"></script>
|
|
||||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<a target="_blank">navigator.id identity provider (IDP) authentication basics</a>
|
|
||||||
<p id="display"></p>
|
|
||||||
<div id="content" style="display: none">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<pre id="test">
|
|
||||||
<script type="application/javascript;version=1.8">
|
|
||||||
|
|
||||||
"use strict"
|
|
||||||
|
|
||||||
let steps = [
|
|
||||||
// completeAuthentication tests
|
|
||||||
function completeAuthenticationExists() {
|
|
||||||
is(typeof(identity.completeAuthentication), "function",
|
|
||||||
"Check completeAuthentication is a function");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function completeAuthenticationOutsideFlow() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.completeAuthentication();
|
|
||||||
}, "Check completeAuthentication outside of an auth. flow");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
// raiseAuthenticationFailure tests
|
|
||||||
function raiseAuthenticationFailureExists() {
|
|
||||||
is(typeof(identity.raiseAuthenticationFailure), "function",
|
|
||||||
"Check raiseAuthenticationFailure is a function");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function raiseAuthenticationFailureNoArgs() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.raiseAuthenticationFailure();
|
|
||||||
}, "raiseAuthenticationFailure with no arguments");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
// beginAuthentication tests
|
|
||||||
function beginAuthenticationExists() {
|
|
||||||
is(typeof(identity.beginAuthentication), "function",
|
|
||||||
"Check beginAuthentication is a function");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function beginAuthenticationNoArgs() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.beginAuthentication();
|
|
||||||
}, "beginAuthentication with no arguments");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function beginAuthenticationInvalidArg() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.beginAuthentication(999);
|
|
||||||
}, "beginAuthentication with a non-function argument");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function beginAuthenticationArgs() {
|
|
||||||
function beginAuthenticationCb() {
|
|
||||||
throw "beginAuthentication callback shouldn't have been called outside of an "
|
|
||||||
+ "auth flow";
|
|
||||||
}
|
|
||||||
is(identity.beginAuthentication(beginAuthenticationCb), undefined,
|
|
||||||
"Check minimum beginAuthentication arguments");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
finish_tests,
|
|
||||||
];
|
|
||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
|
||||||
addLoadEvent(next);
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</pre>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,160 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<!-- Any copyright is dedicated to the Public Domain.
|
|
||||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Test for navigator.id identity provider (IDP) provisioning basics</title>
|
|
||||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<script type="application/javascript;version=1.8" src="head_identity.js"></script>
|
|
||||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<a target="_blank">navigator.id identity provider (IDP) provisioning basics</a>
|
|
||||||
<p id="display"></p>
|
|
||||||
<div id="content" style="display: none">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<pre id="test">
|
|
||||||
<script type="application/javascript;version=1.8">
|
|
||||||
|
|
||||||
"use strict"
|
|
||||||
|
|
||||||
let IDP = Cu.import("resource://gre/modules/identity/IdentityProvider.jsm").IdentityProvider;
|
|
||||||
|
|
||||||
function setupProv() {
|
|
||||||
info("setupProv");
|
|
||||||
// Add a provisioning flow so the DOM calls succeed
|
|
||||||
IDP._provisionFlows[outerWinId] = {
|
|
||||||
sandbox: {},
|
|
||||||
callback: function doCallback(aErr) {
|
|
||||||
info("provisioning callback: " + aErr);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetAndNext() {
|
|
||||||
info("resetAndNext");
|
|
||||||
// reset DOM state for the next test
|
|
||||||
// Give the flow some time to cross the IPC boundary
|
|
||||||
setTimeout(function() {
|
|
||||||
let provContext = IDP._provisionFlows[outerWinId];
|
|
||||||
if (!provContext) {
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
makeObserver("identity-DOM-state-reset", function() {
|
|
||||||
info("reset done");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
});
|
|
||||||
DOMIdentity._resetFrameState(provContext.caller);
|
|
||||||
}, 700);
|
|
||||||
}
|
|
||||||
|
|
||||||
let steps = [
|
|
||||||
// genKeyPair tests
|
|
||||||
function genKeyPairExists() {
|
|
||||||
is(typeof(identity.genKeyPair), "function",
|
|
||||||
"Check genKeyPair is a function");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function genKeyPairOutsideProv() {
|
|
||||||
expectException(function(){
|
|
||||||
identity.genKeyPair(function(){});
|
|
||||||
}, "Check genKeyPair outside of a prov. flow");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function genKeyPairNoArgs() {
|
|
||||||
setupProv();
|
|
||||||
identity.beginProvisioning(function() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.genKeyPair();
|
|
||||||
}, "genKeyPair with no arguments");
|
|
||||||
SimpleTest.executeSoon(resetAndNext);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
function genKeyPairInvalidArg() {
|
|
||||||
setupProv();
|
|
||||||
identity.beginProvisioning(function() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.genKeyPair(999);
|
|
||||||
}, "Check genKeyPair with non-function object argument");
|
|
||||||
SimpleTest.executeSoon(resetAndNext);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
// registerCertificate tests
|
|
||||||
function registerCertificateExists() {
|
|
||||||
is(typeof(identity.registerCertificate), "function",
|
|
||||||
"Check registerCertificate is a function");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function registerCertificateNoArgs() {
|
|
||||||
setupProv();
|
|
||||||
identity.beginProvisioning(function() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.registerCertificate();
|
|
||||||
}, "Check registerCertificate with no arguments");
|
|
||||||
});
|
|
||||||
SimpleTest.executeSoon(resetAndNext);
|
|
||||||
},
|
|
||||||
function registerCertificateOutsideProv() {
|
|
||||||
expectException(function(){
|
|
||||||
identity.registerCertificate("foo");
|
|
||||||
}, "Check registerCertificate outside of a prov. flow");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
// raiseProvisioningFailure tests
|
|
||||||
function raiseProvisioningFailureExists() {
|
|
||||||
is(typeof(identity.raiseProvisioningFailure), "function",
|
|
||||||
"Check raiseProvisioningFailure is a function");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function raiseProvisioningFailureNoArgs() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.raiseProvisioningFailure();
|
|
||||||
}, "raiseProvisioningFailure with no arguments");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function raiseProvisioningFailureWithReason() {
|
|
||||||
identity.raiseProvisioningFailure("my test reason");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
// beginProvisioning tests
|
|
||||||
function beginProvisioningExists() {
|
|
||||||
is(typeof(identity.beginProvisioning), "function",
|
|
||||||
"Check beginProvisioning is a function");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function beginProvisioningNoArgs() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.beginProvisioning();
|
|
||||||
}, "beginProvisioning with no arguments");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function beginProvisioningInvalidArg() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.beginProvisioning(999);
|
|
||||||
}, "beginProvisioning with a non-function argument");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function beginProvisioningArgs() {
|
|
||||||
function beginProvisioningCb() {
|
|
||||||
SimpleTest.executeSoon(resetAndNext);
|
|
||||||
}
|
|
||||||
is(identity.beginProvisioning(beginProvisioningCb), undefined,
|
|
||||||
"Check minimum beginProvisioning arguments");
|
|
||||||
},
|
|
||||||
|
|
||||||
finish_tests,
|
|
||||||
];
|
|
||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
|
||||||
addLoadEvent(next);
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</pre>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,177 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<!-- Any copyright is dedicated to the Public Domain.
|
|
||||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Test for navigator.id relying party (RP) basics</title>
|
|
||||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<script type="application/javascript;version=1.8" src="head_identity.js"></script>
|
|
||||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<a target="_blank">navigator.id RP basics</a>
|
|
||||||
<p id="display"></p>
|
|
||||||
<div id="content">
|
|
||||||
<button id='request'>request</button>
|
|
||||||
</div>
|
|
||||||
<pre id="test">
|
|
||||||
<script type="application/javascript;version=1.8">
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const RP = Cu.import("resource://gre/modules/identity/RelyingParty.jsm").RelyingParty;
|
|
||||||
|
|
||||||
function resetAndNext() {
|
|
||||||
// reset DOM state for the next test
|
|
||||||
makeObserver("identity-DOM-state-reset", function() {
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
});
|
|
||||||
// Give the flow some time to cross the IPC boundary
|
|
||||||
setTimeout(function() {
|
|
||||||
let rpContext = RP._rpFlows[outerWinId];
|
|
||||||
if (!rpContext) {
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
DOMIdentity._resetFrameState(rpContext);
|
|
||||||
}, 700);
|
|
||||||
}
|
|
||||||
|
|
||||||
let steps = [
|
|
||||||
function nonExistentProp() {
|
|
||||||
is(identity.foobarbaz, undefined, "Check that foobarbaz does not exist");
|
|
||||||
expectException(function() {
|
|
||||||
identity.foobarbaz()
|
|
||||||
}, "Check for exception calling non-existent method", "TypeError");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
// test request before watch throws an exception
|
|
||||||
function requestBeforeWatch() {
|
|
||||||
var button = document.getElementById('request');
|
|
||||||
button.addEventListener('click', function requestHandler() {
|
|
||||||
button.removeEventListener('click', requestHandler);
|
|
||||||
|
|
||||||
expectException(function() {
|
|
||||||
identity.request();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
// watch tests
|
|
||||||
function watchExists() {
|
|
||||||
is(typeof(identity.watch), "function", "Check watch is a function");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function watchNoArgs() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.watch();
|
|
||||||
}, "watch with no arguments");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function watchEmptyObj() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.watch({});
|
|
||||||
}, "watch with empty object argument");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function watchOnLoginBool() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.watch({onlogin: true});
|
|
||||||
}, "watch with invalid onlogin member");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function watchOnLoginLogoutBool() {
|
|
||||||
expectException(function() {
|
|
||||||
identity.watch({onlogin: true, onlogout: false});
|
|
||||||
}, "watch with invalid onlogin and onlogout members");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function watchMinimumArgs() {
|
|
||||||
function onLoginLogoutCb() {
|
|
||||||
throw "onlogin/onlogout callback shouldn't have been called";
|
|
||||||
}
|
|
||||||
is(identity.watch({onlogin: onLoginLogoutCb, onlogout: onLoginLogoutCb}),
|
|
||||||
undefined, "Check minimum watch argument members");
|
|
||||||
resetAndNext();
|
|
||||||
},
|
|
||||||
function watchOnReadyType() {
|
|
||||||
function onLoginLogoutCb() {
|
|
||||||
throw "onlogin/onlogout callback shouldn't have been called";
|
|
||||||
}
|
|
||||||
let options = {
|
|
||||||
onlogin: onLoginLogoutCb,
|
|
||||||
onlogout: onLoginLogoutCb,
|
|
||||||
onready: 999,
|
|
||||||
}
|
|
||||||
expectException(function() {
|
|
||||||
identity.watch(options)
|
|
||||||
}, "Check onready type");
|
|
||||||
resetAndNext();
|
|
||||||
},
|
|
||||||
function watchLoggedInEmailType() {
|
|
||||||
function onLoginLogoutCb() {
|
|
||||||
throw "onlogin/onlogout callback shouldn't have been called";
|
|
||||||
}
|
|
||||||
let options = {
|
|
||||||
onlogin: onLoginLogoutCb,
|
|
||||||
onlogout: onLoginLogoutCb,
|
|
||||||
loggedInEmail: {},
|
|
||||||
}
|
|
||||||
expectException(function() {
|
|
||||||
identity.watch(options)
|
|
||||||
}, "Check loggedInEmail type");
|
|
||||||
resetAndNext();
|
|
||||||
},
|
|
||||||
function watchOnReadyCalled() {
|
|
||||||
let onLogoutCalled = false;
|
|
||||||
let options = {
|
|
||||||
loggedInEmail: "loggedOut@user.com",
|
|
||||||
onlogin: function onLoginCb(assertion) {
|
|
||||||
throw "onlogin/onlogout callback shouldn't have been called";
|
|
||||||
},
|
|
||||||
onlogout: function onLogoutCb() {
|
|
||||||
is(arguments.length, 0, "Check onlogout argument length");
|
|
||||||
onLogoutCalled = true;
|
|
||||||
},
|
|
||||||
onready: function onReady() {
|
|
||||||
is(arguments.length, 0, "Check onready argument length");
|
|
||||||
ok(onLogoutCalled, "onlogout callback should be called before onready");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
is(identity.watch(options), undefined, "Check onready is called");
|
|
||||||
},
|
|
||||||
|
|
||||||
// request tests
|
|
||||||
function requestExists() {
|
|
||||||
is(typeof(identity.request), "function", "Check request is a function");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function requestNoArgs() {
|
|
||||||
is(identity.request(), undefined, "Check request with no arguments");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
function requestEmptyObj() {
|
|
||||||
is(identity.request({}), undefined, "Check request with empty object argument");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
// logout tests
|
|
||||||
function logoutExists() {
|
|
||||||
is(typeof(identity.logout), "function", "Check logout is a function");
|
|
||||||
SimpleTest.executeSoon(next);
|
|
||||||
},
|
|
||||||
|
|
||||||
finish_tests,
|
|
||||||
];
|
|
||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
|
||||||
addLoadEvent(next);
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</pre>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -358,6 +358,7 @@ pref("toolkit.telemetry.infoURL", "http://www.mozilla.com/legal/privacy/firefox.
|
|||||||
pref("toolkit.telemetry.debugSlowSql", false);
|
pref("toolkit.telemetry.debugSlowSql", false);
|
||||||
|
|
||||||
// Identity module
|
// Identity module
|
||||||
|
pref("toolkit.identity.enabled", false);
|
||||||
pref("toolkit.identity.debug", false);
|
pref("toolkit.identity.debug", false);
|
||||||
|
|
||||||
// Disable remote debugging protocol logging
|
// Disable remote debugging protocol logging
|
||||||
@ -3797,8 +3798,6 @@ pref("social.enabled", false);
|
|||||||
// observers (bug 780507).
|
// observers (bug 780507).
|
||||||
pref("dom.idle-observers-api.fuzz_time.disabled", true);
|
pref("dom.idle-observers-api.fuzz_time.disabled", true);
|
||||||
|
|
||||||
pref("toolkit.identity.debug", false);
|
|
||||||
|
|
||||||
// Setting that to true grant elevated privileges to apps that ask
|
// Setting that to true grant elevated privileges to apps that ask
|
||||||
// for them in their manifest.
|
// for them in their manifest.
|
||||||
pref("dom.mozApps.dev_mode", false);
|
pref("dom.mozApps.dev_mode", false);
|
||||||
|
@ -35,6 +35,7 @@ EXTRA_JS_MODULES = \
|
|||||||
IdentityStore.jsm \
|
IdentityStore.jsm \
|
||||||
jwcrypto.jsm \
|
jwcrypto.jsm \
|
||||||
LogUtils.jsm \
|
LogUtils.jsm \
|
||||||
|
MinimalIdentity.jsm \
|
||||||
RelyingParty.jsm \
|
RelyingParty.jsm \
|
||||||
Sandbox.jsm \
|
Sandbox.jsm \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
376
toolkit/identity/MinimalIdentity.jsm
Normal file
376
toolkit/identity/MinimalIdentity.jsm
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */
|
||||||
|
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This alternate implementation of IdentityService provides just the
|
||||||
|
* channels for navigator.id, leaving the certificate storage to a
|
||||||
|
* server-provided app.
|
||||||
|
*
|
||||||
|
* On b2g, the messages identity-controller-watch, -request, and
|
||||||
|
* -logout, are observed by the component SignInToWebsite.jsm.
|
||||||
|
*/
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const EXPORTED_SYMBOLS = ["IdentityService"];
|
||||||
|
|
||||||
|
const Cu = Components.utils;
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
const Cc = Components.classes;
|
||||||
|
const Cr = Components.results;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this,
|
||||||
|
"jwcrypto",
|
||||||
|
"resource://gre/modules/identity/jwcrypto.jsm");
|
||||||
|
|
||||||
|
function log(...aMessageArgs) {
|
||||||
|
Logger.log.apply(Logger, ["minimal core"].concat(aMessageArgs));
|
||||||
|
}
|
||||||
|
function reportError(...aMessageArgs) {
|
||||||
|
Logger.reportError.apply(Logger, ["core"].concat(aMessageArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
function IDService() {
|
||||||
|
Services.obs.addObserver(this, "quit-application-granted", false);
|
||||||
|
// Services.obs.addObserver(this, "identity-auth-complete", false);
|
||||||
|
|
||||||
|
// simplify, it's one object
|
||||||
|
this.RP = this;
|
||||||
|
this.IDP = this;
|
||||||
|
|
||||||
|
// keep track of flows
|
||||||
|
this._rpFlows = {};
|
||||||
|
this._authFlows = {};
|
||||||
|
this._provFlows = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
IDService.prototype = {
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
|
||||||
|
|
||||||
|
observe: function observe(aSubject, aTopic, aData) {
|
||||||
|
switch (aTopic) {
|
||||||
|
case "quit-application-granted":
|
||||||
|
Services.obs.removeObserver(this, "quit-application-granted");
|
||||||
|
// Services.obs.removeObserver(this, "identity-auth-complete");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse an email into username and domain if it is valid, else return null
|
||||||
|
*/
|
||||||
|
parseEmail: function parseEmail(email) {
|
||||||
|
var match = email.match(/^([^@]+)@([^@^/]+.[a-z]+)$/);
|
||||||
|
if (match) {
|
||||||
|
return {
|
||||||
|
username: match[1],
|
||||||
|
domain: match[2]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a listener for a given windowID as a result of a call to
|
||||||
|
* navigator.id.watch().
|
||||||
|
*
|
||||||
|
* @param aCaller
|
||||||
|
* (Object) an object that represents the caller document, and
|
||||||
|
* is expected to have properties:
|
||||||
|
* - id (unique, e.g. uuid)
|
||||||
|
* - loggedInEmail (string or null)
|
||||||
|
* - origin (string)
|
||||||
|
*
|
||||||
|
* and a bunch of callbacks
|
||||||
|
* - doReady()
|
||||||
|
* - doLogin()
|
||||||
|
* - doLogout()
|
||||||
|
* - doError()
|
||||||
|
* - doCancel()
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
watch: function watch(aRpCaller) {
|
||||||
|
log("watch: caller keys:", Object.keys(aRpCaller));
|
||||||
|
log("watch: rpcaller:", aRpCaller);
|
||||||
|
// store the caller structure and notify the UI observers
|
||||||
|
|
||||||
|
// note that, unfortunately, what here is loggedInEmail is called
|
||||||
|
// loggedInUser in the native API.
|
||||||
|
this._rpFlows[aRpCaller.id] = aRpCaller;
|
||||||
|
let options = {rpId: aRpCaller.id,
|
||||||
|
origin: aRpCaller.origin,
|
||||||
|
loggedInUser: aRpCaller.loggedInEmail || null};
|
||||||
|
log("sending identity-controller-watch:", options);
|
||||||
|
Services.obs.notifyObservers({wrappedJSObject: options},"identity-controller-watch", null);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initiate a login with user interaction as a result of a call to
|
||||||
|
* navigator.id.request().
|
||||||
|
*
|
||||||
|
* @param aRPId
|
||||||
|
* (integer) the id of the doc object obtained in .watch()
|
||||||
|
*
|
||||||
|
* @param aOptions
|
||||||
|
* (Object) options including privacyPolicy, termsOfService
|
||||||
|
*/
|
||||||
|
request: function request(aRPId, aOptions) {
|
||||||
|
log("request: rpId:", aRPId);
|
||||||
|
let rp = this._rpFlows[aRPId];
|
||||||
|
|
||||||
|
// Notify UX to display identity picker.
|
||||||
|
// Pass the doc id to UX so it can pass it back to us later.
|
||||||
|
let options = {rpId: aRPId, origin: rp.origin};
|
||||||
|
Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-request", null);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a user wishes to logout of a site (for instance, when clicking
|
||||||
|
* on an in-content logout button).
|
||||||
|
*
|
||||||
|
* @param aRpCallerId
|
||||||
|
* (integer) the id of the doc object obtained in .watch()
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
logout: function logout(aRpCallerId) {
|
||||||
|
log("logout: RP caller id:", aRpCallerId);
|
||||||
|
let rp = this._rpFlows[aRpCallerId];
|
||||||
|
|
||||||
|
let options = {rpId: aRpCallerId, origin: rp.origin};
|
||||||
|
Services.obs.notifyObservers({wrappedJSObject: options}, "identity-controller-logout", null);
|
||||||
|
},
|
||||||
|
|
||||||
|
/*
|
||||||
|
* once the UI-and-display-logic components have received
|
||||||
|
* notifications, they call back with direct invocation of the
|
||||||
|
* following functions (doLogin, doLogout, or doReady)
|
||||||
|
*/
|
||||||
|
|
||||||
|
doLogin: function doLogin(aRpCallerId, aAssertion) {
|
||||||
|
let rp = this._rpFlows[aRpCallerId];
|
||||||
|
if (!rp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rp.doLogin(aAssertion);
|
||||||
|
},
|
||||||
|
|
||||||
|
doLogout: function doLogout(aRpCallerId) {
|
||||||
|
let rp = this._rpFlows[aRpCallerId];
|
||||||
|
if (!rp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rp.doLogout();
|
||||||
|
},
|
||||||
|
|
||||||
|
doReady: function doReady(aRpCallerId) {
|
||||||
|
let rp = this._rpFlows[aRpCallerId];
|
||||||
|
if (!rp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
rp.doReady();
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX Bug 804229: Implement Identity Provider Functions
|
||||||
|
*
|
||||||
|
* Stubs for Identity Provider functions follow
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the provisioning iframe sandbox has called navigator.id.beginProvisioning()
|
||||||
|
*
|
||||||
|
* @param aCaller
|
||||||
|
* (object) the iframe sandbox caller with all callbacks and
|
||||||
|
* other information. Callbacks include:
|
||||||
|
* - doBeginProvisioningCallback(id, duration_s)
|
||||||
|
* - doGenKeyPairCallback(pk)
|
||||||
|
*/
|
||||||
|
beginProvisioning: function beginProvisioning(aCaller) {
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the provisioning iframe sandbox has called
|
||||||
|
* navigator.id.raiseProvisioningFailure()
|
||||||
|
*
|
||||||
|
* @param aProvId
|
||||||
|
* (int) the identifier of the provisioning flow tied to that sandbox
|
||||||
|
* @param aReason
|
||||||
|
*/
|
||||||
|
raiseProvisioningFailure: function raiseProvisioningFailure(aProvId, aReason) {
|
||||||
|
reportError("Provisioning failure", aReason);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When navigator.id.genKeyPair is called from provisioning iframe sandbox.
|
||||||
|
* Generates a keypair for the current user being provisioned.
|
||||||
|
*
|
||||||
|
* @param aProvId
|
||||||
|
* (int) the identifier of the provisioning caller tied to that sandbox
|
||||||
|
*
|
||||||
|
* It is an error to call genKeypair without receiving the callback for
|
||||||
|
* the beginProvisioning() call first.
|
||||||
|
*/
|
||||||
|
genKeyPair: function genKeyPair(aProvId) {
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When navigator.id.registerCertificate is called from provisioning iframe
|
||||||
|
* sandbox.
|
||||||
|
*
|
||||||
|
* Sets the certificate for the user for which a certificate was requested
|
||||||
|
* via a preceding call to beginProvisioning (and genKeypair).
|
||||||
|
*
|
||||||
|
* @param aProvId
|
||||||
|
* (integer) the identifier of the provisioning caller tied to that
|
||||||
|
* sandbox
|
||||||
|
*
|
||||||
|
* @param aCert
|
||||||
|
* (String) A JWT representing the signed certificate for the user
|
||||||
|
* being provisioned, provided by the IdP.
|
||||||
|
*/
|
||||||
|
registerCertificate: function registerCertificate(aProvId, aCert) {
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The authentication frame has called navigator.id.beginAuthentication
|
||||||
|
*
|
||||||
|
* IMPORTANT: the aCaller is *always* non-null, even if this is called from
|
||||||
|
* a regular content page. We have to make sure, on every DOM call, that
|
||||||
|
* aCaller is an expected authentication-flow identifier. If not, we throw
|
||||||
|
* an error or something.
|
||||||
|
*
|
||||||
|
* @param aCaller
|
||||||
|
* (object) the authentication caller
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
beginAuthentication: function beginAuthentication(aCaller) {
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The auth frame has called navigator.id.completeAuthentication
|
||||||
|
*
|
||||||
|
* @param aAuthId
|
||||||
|
* (int) the identifier of the authentication caller tied to that sandbox
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
completeAuthentication: function completeAuthentication(aAuthId) {
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The auth frame has called navigator.id.cancelAuthentication
|
||||||
|
*
|
||||||
|
* @param aAuthId
|
||||||
|
* (int) the identifier of the authentication caller
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
cancelAuthentication: function cancelAuthentication(aAuthId) {
|
||||||
|
},
|
||||||
|
|
||||||
|
// methods for chrome and add-ons
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discover the IdP for an identity
|
||||||
|
*
|
||||||
|
* @param aIdentity
|
||||||
|
* (string) the email we're logging in with
|
||||||
|
*
|
||||||
|
* @param aCallback
|
||||||
|
* (function) callback to invoke on completion
|
||||||
|
* with first-positional parameter the error.
|
||||||
|
*/
|
||||||
|
_discoverIdentityProvider: function _discoverIdentityProvider(aIdentity, aCallback) {
|
||||||
|
// XXX bug 767610 - validate email address call
|
||||||
|
// When that is available, we can remove this custom parser
|
||||||
|
var parsedEmail = this.parseEmail(aIdentity);
|
||||||
|
if (parsedEmail === null) {
|
||||||
|
return aCallback("Could not parse email: " + aIdentity);
|
||||||
|
}
|
||||||
|
log("_discoverIdentityProvider: identity:", aIdentity, "domain:", parsedEmail.domain);
|
||||||
|
|
||||||
|
this._fetchWellKnownFile(parsedEmail.domain, function fetchedWellKnown(err, idpParams) {
|
||||||
|
// idpParams includes the pk, authorization url, and
|
||||||
|
// provisioning url.
|
||||||
|
|
||||||
|
// XXX bug 769861 follow any authority delegations
|
||||||
|
// if no well-known at any point in the delegation
|
||||||
|
// fall back to browserid.org as IdP
|
||||||
|
return aCallback(err, idpParams);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the well-known file from the domain.
|
||||||
|
*
|
||||||
|
* @param aDomain
|
||||||
|
*
|
||||||
|
* @param aScheme
|
||||||
|
* (string) (optional) Protocol to use. Default is https.
|
||||||
|
* This is necessary because we are unable to test
|
||||||
|
* https.
|
||||||
|
*
|
||||||
|
* @param aCallback
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
_fetchWellKnownFile: function _fetchWellKnownFile(aDomain, aCallback, aScheme='https') {
|
||||||
|
// XXX bug 769854 make tests https and remove aScheme option
|
||||||
|
let url = aScheme + '://' + aDomain + "/.well-known/browserid";
|
||||||
|
log("_fetchWellKnownFile:", url);
|
||||||
|
|
||||||
|
// this appears to be a more successful way to get at xmlhttprequest (which supposedly will close with a window
|
||||||
|
let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||||
|
.createInstance(Ci.nsIXMLHttpRequest);
|
||||||
|
|
||||||
|
// XXX bug 769865 gracefully handle being off-line
|
||||||
|
// XXX bug 769866 decide on how to handle redirects
|
||||||
|
req.open("GET", url, true);
|
||||||
|
req.responseType = "json";
|
||||||
|
req.mozBackgroundRequest = true;
|
||||||
|
req.onload = function _fetchWellKnownFile_onload() {
|
||||||
|
if (req.status < 200 || req.status >= 400) {
|
||||||
|
log("_fetchWellKnownFile", url, ": server returned status:", req.status);
|
||||||
|
return aCallback("Error");
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let idpParams = req.response;
|
||||||
|
|
||||||
|
// Verify that the IdP returned a valid configuration
|
||||||
|
if (! (idpParams.provisioning &&
|
||||||
|
idpParams.authentication &&
|
||||||
|
idpParams['public-key'])) {
|
||||||
|
let errStr= "Invalid well-known file from: " + aDomain;
|
||||||
|
log("_fetchWellKnownFile:", errStr);
|
||||||
|
return aCallback(errStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
let callbackObj = {
|
||||||
|
domain: aDomain,
|
||||||
|
idpParams: idpParams,
|
||||||
|
};
|
||||||
|
log("_fetchWellKnownFile result: ", callbackObj);
|
||||||
|
// Yay. Valid IdP configuration for the domain.
|
||||||
|
return aCallback(null, callbackObj);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
reportError("_fetchWellKnownFile", "Bad configuration from", aDomain, err);
|
||||||
|
return aCallback(err.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
req.onerror = function _fetchWellKnownFile_onerror() {
|
||||||
|
log("_fetchWellKnownFile", "ERROR:", req.status, req.statusText);
|
||||||
|
log("ERROR: _fetchWellKnownFile:", err);
|
||||||
|
return aCallback("Error");
|
||||||
|
};
|
||||||
|
req.send(null);
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
let IdentityService = new IDService();
|
@ -10,11 +10,6 @@ relativesrcdir = @relativesrcdir@
|
|||||||
|
|
||||||
include $(DEPTH)/config/autoconf.mk
|
include $(DEPTH)/config/autoconf.mk
|
||||||
|
|
||||||
MOCHITEST_FILES = \
|
# XXX bug 805602 update and restore mochitests
|
||||||
head_identity.js \
|
|
||||||
test_authentication.html \
|
|
||||||
test_provisioning.html \
|
|
||||||
test_relying_party.html \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
include $(topsrcdir)/config/rules.mk
|
include $(topsrcdir)/config/rules.mk
|
||||||
|
@ -1,221 +0,0 @@
|
|||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
/** Helper functions for identity mochitests **/
|
|
||||||
/** Please keep functions in-sync with unit/head_identity.js **/
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const Cc = SpecialPowers.Cc;
|
|
||||||
const Ci = SpecialPowers.Ci;
|
|
||||||
const Cu = SpecialPowers.Cu;
|
|
||||||
|
|
||||||
const TEST_URL = "http://mochi.test:8888";
|
|
||||||
const TEST_URL2 = "https://myfavoritebaconinacan.com";
|
|
||||||
const TEST_USER = "user@example.com";
|
|
||||||
const TEST_PRIVKEY = "fake-privkey";
|
|
||||||
const TEST_CERT = "fake-cert";
|
|
||||||
const TEST_IDPPARAMS = {
|
|
||||||
domain: "example.com",
|
|
||||||
idpParams: {
|
|
||||||
authentication: "/foo/authenticate.html",
|
|
||||||
provisioning: "/foo/provision.html"
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const Services = Cu.import("resource://gre/modules/Services.jsm").Services;
|
|
||||||
|
|
||||||
// Set the debug pref before loading other modules
|
|
||||||
SpecialPowers.setBoolPref("toolkit.identity.debug", true);
|
|
||||||
SpecialPowers.setBoolPref("dom.identity.enabled", true);
|
|
||||||
|
|
||||||
// Shutdown the UX if it exists so that it won't interfere with tests by also responding to
|
|
||||||
// observer notifications.
|
|
||||||
try {
|
|
||||||
const SignInToWebsiteUX = Cu.import("resource:///modules/SignInToWebsite.jsm").SignInToWebsiteUX;
|
|
||||||
if (SignInToWebsiteUX) {
|
|
||||||
SignInToWebsiteUX.uninit();
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
// The module doesn't exist
|
|
||||||
}
|
|
||||||
|
|
||||||
const jwcrypto = Cu.import("resource://gre/modules/identity/jwcrypto.jsm").jwcrypto;
|
|
||||||
const IdentityStore = Cu.import("resource://gre/modules/identity/IdentityStore.jsm").IdentityStore;
|
|
||||||
const RelyingParty = Cu.import("resource://gre/modules/identity/RelyingParty.jsm").RelyingParty;
|
|
||||||
const XPCOMUtils = Cu.import("resource://gre/modules/XPCOMUtils.jsm").XPCOMUtils;
|
|
||||||
const IDService = Cu.import("resource://gre/modules/identity/Identity.jsm").IdentityService;
|
|
||||||
const IdentityProvider = Cu.import("resource://gre/modules/identity/IdentityProvider.jsm").IdentityProvider;
|
|
||||||
|
|
||||||
const identity = navigator.id || navigator.mozId;
|
|
||||||
|
|
||||||
function do_check_null(aVal, aMsg) {
|
|
||||||
is(aVal, null, aMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
function do_timeout(aDelay, aFunc) {
|
|
||||||
setTimeout(aFunc, aDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
function get_idstore() {
|
|
||||||
return IdentityStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a mock "watch" object, which the Identity Service
|
|
||||||
function mock_watch(aIdentity, aDoFunc) {
|
|
||||||
function partial(fn) {
|
|
||||||
let args = Array.prototype.slice.call(arguments, 1);
|
|
||||||
return function() {
|
|
||||||
return fn.apply(this, args.concat(Array.prototype.slice.call(arguments)));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let mockedWatch = {};
|
|
||||||
mockedWatch.loggedInEmail = aIdentity;
|
|
||||||
mockedWatch['do'] = aDoFunc;
|
|
||||||
mockedWatch.onready = partial(aDoFunc, 'ready');
|
|
||||||
mockedWatch.onlogin = partial(aDoFunc, 'login');
|
|
||||||
mockedWatch.onlogout = partial(aDoFunc, 'logout');
|
|
||||||
|
|
||||||
return mockedWatch;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mimicking callback funtionality for ease of testing
|
|
||||||
// this observer auto-removes itself after the observe function
|
|
||||||
// is called, so this is meant to observe only ONE event.
|
|
||||||
function makeObserver(aObserveTopic, aObserveFunc) {
|
|
||||||
function observe(aSubject, aTopic, aData) {
|
|
||||||
if (aTopic == aObserveTopic) {
|
|
||||||
|
|
||||||
Services.obs.removeObserver(this, aObserveTopic);
|
|
||||||
try {
|
|
||||||
aObserveFunc(SpecialPowers.wrap(aSubject), aTopic, aData);
|
|
||||||
} catch (ex) {
|
|
||||||
ok(false, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Services.obs.addObserver(observe, aObserveTopic, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up the ID service with an identity with keypair and all
|
|
||||||
// when ready, invoke callback with the identity
|
|
||||||
function setup_test_identity(identity, cert, cb) {
|
|
||||||
// set up the store so that we're supposed to be logged in
|
|
||||||
let store = get_idstore();
|
|
||||||
|
|
||||||
function keyGenerated(err, kpo) {
|
|
||||||
store.addIdentity(identity, kpo, cert);
|
|
||||||
cb();
|
|
||||||
};
|
|
||||||
|
|
||||||
jwcrypto.generateKeyPair("DS160", keyGenerated);
|
|
||||||
}
|
|
||||||
|
|
||||||
// takes a list of functions and returns a function that
|
|
||||||
// when called the first time, calls the first func,
|
|
||||||
// then the next time the second, etc.
|
|
||||||
function call_sequentially() {
|
|
||||||
let numCalls = 0;
|
|
||||||
let funcs = arguments;
|
|
||||||
|
|
||||||
return function() {
|
|
||||||
if (!funcs[numCalls]) {
|
|
||||||
let argString = Array.prototype.slice.call(arguments).join(",");
|
|
||||||
ok(false, "Too many calls: " + argString);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
funcs[numCalls].apply(funcs[numCalls], arguments);
|
|
||||||
numCalls += 1;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Setup a provisioning workflow with appropriate callbacks
|
|
||||||
*
|
|
||||||
* identity is the email we're provisioning.
|
|
||||||
*
|
|
||||||
* afterSetupCallback is required.
|
|
||||||
*
|
|
||||||
* doneProvisioningCallback is optional, if the caller
|
|
||||||
* wants to be notified when the whole provisioning workflow is done
|
|
||||||
*
|
|
||||||
* frameCallbacks is optional, contains the callbacks that the sandbox
|
|
||||||
* frame would provide in response to DOM calls.
|
|
||||||
*/
|
|
||||||
function setup_provisioning(identity, afterSetupCallback, doneProvisioningCallback, callerCallbacks) {
|
|
||||||
IDService.reset();
|
|
||||||
|
|
||||||
let util = SpecialPowers.getDOMWindowUtils(window);
|
|
||||||
|
|
||||||
let provId = util.outerWindowID;
|
|
||||||
IDService.IDP._provisionFlows[provId] = {
|
|
||||||
identity : identity,
|
|
||||||
idpParams: TEST_IDPPARAMS,
|
|
||||||
callback: function(err) {
|
|
||||||
if (doneProvisioningCallback)
|
|
||||||
doneProvisioningCallback(err);
|
|
||||||
},
|
|
||||||
sandbox: {
|
|
||||||
// Emulate the free() method on the iframe sandbox
|
|
||||||
free: function() {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let caller = {};
|
|
||||||
caller.id = callerCallbacks.id = provId;
|
|
||||||
caller.doBeginProvisioningCallback = function(id, duration_s) {
|
|
||||||
if (callerCallbacks && callerCallbacks.beginProvisioningCallback)
|
|
||||||
callerCallbacks.beginProvisioningCallback(id, duration_s);
|
|
||||||
};
|
|
||||||
caller.doGenKeyPairCallback = function(pk) {
|
|
||||||
if (callerCallbacks && callerCallbacks.genKeyPairCallback)
|
|
||||||
callerCallbacks.genKeyPairCallback(pk);
|
|
||||||
};
|
|
||||||
|
|
||||||
afterSetupCallback(callerCallbacks);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetState() {
|
|
||||||
IDService.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
function cleanup() {
|
|
||||||
resetState();
|
|
||||||
SpecialPowers.clearUserPref("toolkit.identity.debug");
|
|
||||||
SpecialPowers.clearUserPref("dom.identity.enabled");
|
|
||||||
// Re-init the UX that we uninit
|
|
||||||
try {
|
|
||||||
const SignInToWebsiteUX = Cu.import("resource:///modules/SignInToWebsite.jsm").SignInToWebsiteUX;
|
|
||||||
if (SignInToWebsiteUX) {
|
|
||||||
SignInToWebsiteUX.init();
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
// The module doesn't exist
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var TESTS = [];
|
|
||||||
|
|
||||||
function run_next_test() {
|
|
||||||
if (!identity) {
|
|
||||||
todo(false, "DOM API is not available. Skipping tests.");
|
|
||||||
cleanup();
|
|
||||||
SimpleTest.finish();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (TESTS.length) {
|
|
||||||
let test = TESTS.shift();
|
|
||||||
info(test.name);
|
|
||||||
try {
|
|
||||||
test();
|
|
||||||
} catch (ex) {
|
|
||||||
ok(false, ex);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
cleanup();
|
|
||||||
info("all done");
|
|
||||||
SimpleTest.finish();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,161 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<!--
|
|
||||||
Test of Identity Provider (IDP) Authentication using the DOM APIs
|
|
||||||
-->
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Test of Identity Provider (IDP) Authentication using the DOM APIs</title>
|
|
||||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
|
||||||
<script type="application/javascript;version=1.8" src="head_identity.js"></script>
|
|
||||||
</head>
|
|
||||||
<body onload="run_next_test()">
|
|
||||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=753238">Test of Identity Provider (IDP) Authentication using the DOM APIs</a>
|
|
||||||
<p id="display"></p>
|
|
||||||
<div id="content" style="display: none">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<pre id="test">
|
|
||||||
<script type="application/javascript;version=1.8">
|
|
||||||
|
|
||||||
/** Test of Identity Provider (IDP) Authentication using the DOM APIs **/
|
|
||||||
/** Most tests are ported from test_authentication.js */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
|
||||||
|
|
||||||
const DOMIdentity = Cu.import("resource://gre/modules/DOMIdentity.jsm")
|
|
||||||
.DOMIdentity;
|
|
||||||
let outerWinId = SpecialPowers.getDOMWindowUtils(window).outerWindowID;
|
|
||||||
|
|
||||||
function run_next_auth_test() {
|
|
||||||
// Reset the DOM state then run the next test
|
|
||||||
let provContext = IdentityProvider._provisionFlows[outerWinId];
|
|
||||||
if (provContext && provContext.caller) {
|
|
||||||
makeObserver("identity-DOM-state-reset", function() {
|
|
||||||
SimpleTest.executeSoon(run_next_test);
|
|
||||||
});
|
|
||||||
DOMIdentity._resetFrameState(provContext.caller);
|
|
||||||
} else {
|
|
||||||
SimpleTest.executeSoon(run_next_test);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_begin_authentication_flow() {
|
|
||||||
let _provId = null;
|
|
||||||
|
|
||||||
// set up a watch, to be consistent
|
|
||||||
let mockedDoc = mock_watch(null, function(action, params) {});
|
|
||||||
identity.watch(mockedDoc);
|
|
||||||
|
|
||||||
// The identity-auth notification is sent up to the UX from the
|
|
||||||
// _doAuthentication function. Be ready to receive it and call
|
|
||||||
// beginAuthentication
|
|
||||||
makeObserver("identity-auth", function (aSubject, aTopic, aData) {
|
|
||||||
isnot(aSubject, null);
|
|
||||||
|
|
||||||
is(aSubject.wrappedJSObject.provId, _provId);
|
|
||||||
|
|
||||||
run_next_auth_test();
|
|
||||||
});
|
|
||||||
setup_provisioning(
|
|
||||||
TEST_USER,
|
|
||||||
function(caller) {
|
|
||||||
_provId = caller.id;
|
|
||||||
identity.beginProvisioning(caller.beginProvisioningCallback);
|
|
||||||
}, function() {},
|
|
||||||
{
|
|
||||||
beginProvisioningCallback: function(email, duration_s) {
|
|
||||||
// let's say this user needs to authenticate
|
|
||||||
IDService.IDP._doAuthentication(_provId, TEST_IDPPARAMS);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_complete_authentication_flow() {
|
|
||||||
let _provId = null;
|
|
||||||
let _authId = null;
|
|
||||||
let id = TEST_USER;
|
|
||||||
|
|
||||||
let callbacksFired = false;
|
|
||||||
let topicObserved = false;
|
|
||||||
|
|
||||||
// The result of authentication should be a successful login
|
|
||||||
IDService.reset();
|
|
||||||
|
|
||||||
let mockedDoc = mock_watch(null, call_sequentially(
|
|
||||||
function(action, params) {
|
|
||||||
is(action, 'ready');
|
|
||||||
is(params, undefined);
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
identity.watch(mockedDoc);
|
|
||||||
|
|
||||||
// A mock calling context
|
|
||||||
let authCaller = {
|
|
||||||
doBeginAuthenticationCallback: function doBeginAuthCallback(aIdentity) {
|
|
||||||
is(aIdentity, TEST_USER);
|
|
||||||
identity.completeAuthentication();
|
|
||||||
},
|
|
||||||
|
|
||||||
doError: function(err) {
|
|
||||||
ok(false, "doError called: " + err);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
makeObserver("identity-auth-complete", function (aSubject, aTopic, aData) {
|
|
||||||
info("identity-auth-complete");
|
|
||||||
is(aSubject.wrappedJSObject.identity, TEST_USER);
|
|
||||||
|
|
||||||
run_next_test();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a provisioning flow for our auth flow to attach to
|
|
||||||
setup_provisioning(
|
|
||||||
TEST_USER,
|
|
||||||
function(provFlow) {
|
|
||||||
_provId = provFlow.id;
|
|
||||||
info("after setup: " + _provId);
|
|
||||||
identity.beginProvisioning(provFlow.beginProvisioningCallback);
|
|
||||||
},
|
|
||||||
function() {
|
|
||||||
info("doneProvisioningCallback");
|
|
||||||
|
|
||||||
// let's say this user needs to authenticate
|
|
||||||
IDService.IDP._doAuthentication(_provId, TEST_IDPPARAMS);
|
|
||||||
|
|
||||||
// test_begin_authentication_flow verifies that the right
|
|
||||||
// message is sent to the UI. So that works. Moving on,
|
|
||||||
// the UI calls setAuthenticationFlow ...
|
|
||||||
_authId = outerWinId;
|
|
||||||
IDService.IDP.setAuthenticationFlow(_authId, _provId);
|
|
||||||
IDService.IDP._provisionFlows[_provId].rpId = outerWinId;
|
|
||||||
|
|
||||||
// ... then the UI calls beginAuthentication ...
|
|
||||||
authCaller.id = _authId;
|
|
||||||
IDService.IDP._provisionFlows[_provId].caller = authCaller;
|
|
||||||
identity.beginAuthentication(function bac() {
|
|
||||||
info("beginAuthentication callback");
|
|
||||||
identity.completeAuthentication();
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beginProvisioningCallback: function(email, duration_s) {
|
|
||||||
info("beginProvisioningCallback");
|
|
||||||
|
|
||||||
identity.raiseProvisioningFailure("Prov. failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TESTS.push(test_begin_authentication_flow);
|
|
||||||
TESTS.push(test_complete_authentication_flow);
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</pre>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,285 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<!--
|
|
||||||
Test of Identity Provider (IDP) Provisioning using the DOM APIs
|
|
||||||
-->
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Test of Identity Provider (IDP) Provisioning using the DOM APIs</title>
|
|
||||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
|
||||||
<script type="application/javascript;version=1.8" src="head_identity.js"></script>
|
|
||||||
</head>
|
|
||||||
<body onload="run_next_test()">
|
|
||||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=753238">Test of Identity Provider (IDP) Provisioning using the DOM APIs</a>
|
|
||||||
<p id="display"></p>
|
|
||||||
<div id="content" style="display: none">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<pre id="test">
|
|
||||||
<script type="application/javascript;version=1.8">
|
|
||||||
|
|
||||||
/** Test of Identity Provider (IDP) Provisioning using the DOM APIs **/
|
|
||||||
/** Most tests are ported from test_provisioning.js */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
|
||||||
|
|
||||||
const DOMIdentity = Cu.import("resource://gre/modules/DOMIdentity.jsm")
|
|
||||||
.DOMIdentity;
|
|
||||||
let outerWinId = SpecialPowers.getDOMWindowUtils(window).outerWindowID;
|
|
||||||
|
|
||||||
function check_provision_flow_done(provId) {
|
|
||||||
do_check_null(IdentityProvider._provisionFlows[provId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allow specifying aProvFlow so we can reset after the _provisionFlows is cleaned up.
|
|
||||||
*/
|
|
||||||
function run_next_prov_test(aProvFlow) {
|
|
||||||
// Reset the DOM state then run the next test
|
|
||||||
let provContext = aProvFlow || IdentityProvider._provisionFlows[outerWinId];
|
|
||||||
if (provContext && provContext.caller) {
|
|
||||||
makeObserver("identity-DOM-state-reset", function() {
|
|
||||||
SimpleTest.executeSoon(run_next_test);
|
|
||||||
});
|
|
||||||
DOMIdentity._resetFrameState(provContext.caller);
|
|
||||||
} else {
|
|
||||||
SimpleTest.executeSoon(run_next_test);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_begin_provisioning() {
|
|
||||||
setup_provisioning(
|
|
||||||
TEST_USER,
|
|
||||||
function(caller) {
|
|
||||||
// call .beginProvisioning()
|
|
||||||
// TODO: should probably throw outside of a prov. sandbox?
|
|
||||||
identity.beginProvisioning(caller.beginProvisioningCallback);
|
|
||||||
}, function() {},
|
|
||||||
{
|
|
||||||
beginProvisioningCallback: function(email, duration_s) {
|
|
||||||
is(email, TEST_USER);
|
|
||||||
ok(duration_s > 0);
|
|
||||||
ok(duration_s <= (24 * 3600));
|
|
||||||
|
|
||||||
run_next_prov_test();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_raise_provisioning_failure() {
|
|
||||||
let _callerId = null;
|
|
||||||
|
|
||||||
setup_provisioning(
|
|
||||||
TEST_USER,
|
|
||||||
function(caller) {
|
|
||||||
// call .beginProvisioning()
|
|
||||||
_callerId = caller.id;
|
|
||||||
identity.beginProvisioning(caller.beginProvisioningCallback);
|
|
||||||
}, function(err) {
|
|
||||||
// this should be invoked with a populated error
|
|
||||||
isnot(err, null);
|
|
||||||
ok(err.indexOf("can't authenticate this email") > -1);
|
|
||||||
|
|
||||||
run_next_prov_test();
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beginProvisioningCallback: function(email, duration_s) {
|
|
||||||
// raise the failure as if we can't provision this email
|
|
||||||
identity.raiseProvisioningFailure("can't authenticate this email");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_genkeypair_before_begin_provisioning() {
|
|
||||||
setup_provisioning(
|
|
||||||
TEST_USER,
|
|
||||||
function(caller) {
|
|
||||||
try {
|
|
||||||
// call genKeyPair without beginProvisioning
|
|
||||||
identity.genKeyPair(caller.genKeyPairCallback);
|
|
||||||
} catch (ex) {
|
|
||||||
ok(ex, "Caught exception for calling genKeyPair without beginProvisioning: " + ex);
|
|
||||||
run_next_prov_test();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// expect this to be called with an error
|
|
||||||
function(err) {
|
|
||||||
ok(false, "Shoudn't reach here as DOM code should have caught the problem");
|
|
||||||
|
|
||||||
run_next_prov_test();
|
|
||||||
},
|
|
||||||
{
|
|
||||||
// this should not be called at all!
|
|
||||||
genKeyPairCallback: function(pk) {
|
|
||||||
// a test that will surely fail because we shouldn't be here.
|
|
||||||
ok(false);
|
|
||||||
|
|
||||||
run_next_prov_test();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_genkeypair() {
|
|
||||||
let _callerId = null;
|
|
||||||
|
|
||||||
function gkpCallback(kp) {
|
|
||||||
isnot(kp, null);
|
|
||||||
|
|
||||||
// yay!
|
|
||||||
run_next_prov_test();
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_provisioning(
|
|
||||||
TEST_USER,
|
|
||||||
function(caller) {
|
|
||||||
_callerId = caller.id;
|
|
||||||
identity.beginProvisioning(caller.beginProvisioningCallback);
|
|
||||||
},
|
|
||||||
function(err) {
|
|
||||||
// should not be called!
|
|
||||||
ok(false);
|
|
||||||
|
|
||||||
run_next_prov_test();
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beginProvisioningCallback: function(email, time_s) {
|
|
||||||
identity.genKeyPair(gkpCallback);
|
|
||||||
},
|
|
||||||
genKeyPairCallback: gkpCallback,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we've already ensured that genkeypair can't be called
|
|
||||||
// before beginProvisioning, so this test should be enough
|
|
||||||
// to ensure full sequential call of the 3 APIs.
|
|
||||||
function test_register_certificate_before_genkeypair() {
|
|
||||||
let _callerID = null;
|
|
||||||
|
|
||||||
setup_provisioning(
|
|
||||||
TEST_USER,
|
|
||||||
function(caller) {
|
|
||||||
// do the right thing for beginProvisioning
|
|
||||||
_callerID = caller.id;
|
|
||||||
identity.beginProvisioning(caller.beginProvisioningCallback);
|
|
||||||
},
|
|
||||||
// expect this to be called with an error
|
|
||||||
function(err) {
|
|
||||||
ok(false, "Shoudn't reach here as DOM code should have caught the problem");
|
|
||||||
|
|
||||||
run_next_prov_test();
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beginProvisioningCallback: function(email, duration_s) {
|
|
||||||
try {
|
|
||||||
// now we try to register cert but no keygen has been done
|
|
||||||
identity.registerCertificate("fake-cert");
|
|
||||||
} catch (ex) {
|
|
||||||
ok(ex, "Caught exception for calling genKeyPair without beginProvisioning: " + ex);
|
|
||||||
run_next_prov_test();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_register_certificate() {
|
|
||||||
let _callerId = null;
|
|
||||||
let provFlow = null;
|
|
||||||
|
|
||||||
function gkpCallback(pk) {
|
|
||||||
// Hold on to the provFlow so we have access to .caller to cleanup later
|
|
||||||
provFlow = IdentityProvider._provisionFlows[outerWinId];
|
|
||||||
|
|
||||||
identity.registerCertificate("fake-cert-42");
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_provisioning(
|
|
||||||
TEST_USER,
|
|
||||||
function(caller) {
|
|
||||||
_callerId = caller.id;
|
|
||||||
identity.beginProvisioning(caller.beginProvisioningCallback);
|
|
||||||
},
|
|
||||||
function(err) {
|
|
||||||
// we should be cool!
|
|
||||||
do_check_null(err);
|
|
||||||
|
|
||||||
// check that the cert is there
|
|
||||||
let identity = get_idstore().fetchIdentity(TEST_USER);
|
|
||||||
isnot(identity,null);
|
|
||||||
is(identity.cert, "fake-cert-42");
|
|
||||||
|
|
||||||
SimpleTest.executeSoon(function check_done() {
|
|
||||||
// cleanup will happen after the callback is called
|
|
||||||
check_provision_flow_done(_callerId);
|
|
||||||
|
|
||||||
run_next_prov_test(provFlow);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beginProvisioningCallback: function(email, duration_s) {
|
|
||||||
identity.genKeyPair(gkpCallback);
|
|
||||||
},
|
|
||||||
genKeyPairCallback: gkpCallback,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_get_assertion_after_provision() {
|
|
||||||
let _callerId = null;
|
|
||||||
let provFlow = null;
|
|
||||||
|
|
||||||
function gkpCallback(pk) {
|
|
||||||
// Hold on to the provFlow so we have access to .caller to cleanup later
|
|
||||||
provFlow = IdentityProvider._provisionFlows[outerWinId];
|
|
||||||
identity.registerCertificate("fake-cert-42");
|
|
||||||
}
|
|
||||||
|
|
||||||
setup_provisioning(
|
|
||||||
TEST_USER,
|
|
||||||
function(caller) {
|
|
||||||
_callerId = caller.id;
|
|
||||||
identity.beginProvisioning(caller.beginProvisioningCallback);
|
|
||||||
},
|
|
||||||
function(err) {
|
|
||||||
// we should be cool!
|
|
||||||
do_check_null(err);
|
|
||||||
|
|
||||||
// check that the cert is there
|
|
||||||
let identity = get_idstore().fetchIdentity(TEST_USER);
|
|
||||||
isnot(identity,null);
|
|
||||||
is(identity.cert, "fake-cert-42");
|
|
||||||
|
|
||||||
SimpleTest.executeSoon(function check_done() {
|
|
||||||
// cleanup will happen after the callback is called
|
|
||||||
check_provision_flow_done(_callerId);
|
|
||||||
|
|
||||||
run_next_prov_test(provFlow);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{
|
|
||||||
beginProvisioningCallback: function(email, duration_s) {
|
|
||||||
identity.genKeyPair(gkpCallback);
|
|
||||||
},
|
|
||||||
genKeyPairCallback: gkpCallback,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
TESTS.push(test_genkeypair_before_begin_provisioning);
|
|
||||||
TESTS.push(test_begin_provisioning);
|
|
||||||
TESTS.push(test_raise_provisioning_failure);
|
|
||||||
TESTS.push(test_register_certificate_before_genkeypair);
|
|
||||||
TESTS.push(test_genkeypair);
|
|
||||||
TESTS.push(test_register_certificate);
|
|
||||||
TESTS.push(test_get_assertion_after_provision);
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</pre>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1,229 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<!--
|
|
||||||
Test of Relying Party (RP) using the DOM APIs
|
|
||||||
-->
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Test of Relying Party (RP) using the DOM APIs</title>
|
|
||||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
|
||||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
|
||||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
|
||||||
<script type="application/javascript;version=1.8" src="head_identity.js"></script>
|
|
||||||
</head>
|
|
||||||
<body onload="run_next_test()">
|
|
||||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=753238">Test of Relying Party (RP) using the DOM APIs</a>
|
|
||||||
<p id="display"></p>
|
|
||||||
<div id="content">
|
|
||||||
<button id='request'>request</button>
|
|
||||||
</div>
|
|
||||||
<pre id="test">
|
|
||||||
<script type="application/javascript;version=1.8">
|
|
||||||
|
|
||||||
/** Test of Relying Party (RP) using the DOM APIs **/
|
|
||||||
/** Most tests are ported from test_relying_party.js */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
SimpleTest.waitForExplicitFinish();
|
|
||||||
|
|
||||||
const DOMIdentity = Cu.import("resource://gre/modules/DOMIdentity.jsm")
|
|
||||||
.DOMIdentity;
|
|
||||||
let outerWinId = SpecialPowers.getDOMWindowUtils(window).outerWindowID;
|
|
||||||
|
|
||||||
// Reset the DOM state then run the next test
|
|
||||||
function run_next_rp_test() {
|
|
||||||
let rpContext = RelyingParty._rpFlows[outerWinId];
|
|
||||||
if (rpContext) {
|
|
||||||
makeObserver("identity-DOM-state-reset", function() {
|
|
||||||
SimpleTest.executeSoon(run_next_test);
|
|
||||||
});
|
|
||||||
DOMIdentity._resetFrameState(rpContext);
|
|
||||||
} else {
|
|
||||||
SimpleTest.executeSoon(run_next_test);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_watch_loggedin_ready() {
|
|
||||||
resetState();
|
|
||||||
|
|
||||||
let id = TEST_USER;
|
|
||||||
setup_test_identity(id, TEST_CERT, function() {
|
|
||||||
let store = get_idstore();
|
|
||||||
|
|
||||||
// set it up so we're supposed to be logged in to TEST_URL
|
|
||||||
store.setLoginState(TEST_URL, true, id);
|
|
||||||
identity.watch(mock_watch(id, function(action, params) {
|
|
||||||
is(action, 'ready');
|
|
||||||
is(params, undefined);
|
|
||||||
|
|
||||||
run_next_rp_test();
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_watch_loggedin_login() {
|
|
||||||
resetState();
|
|
||||||
|
|
||||||
let id = TEST_USER;
|
|
||||||
setup_test_identity(id, TEST_CERT, function() {
|
|
||||||
let store = get_idstore();
|
|
||||||
|
|
||||||
// set it up so we're supposed to be logged in to TEST_URL
|
|
||||||
store.setLoginState(TEST_URL, true, id);
|
|
||||||
|
|
||||||
// check for first a login() call, then a ready() call
|
|
||||||
identity.watch(mock_watch(null, call_sequentially(
|
|
||||||
function(action, params) {
|
|
||||||
is(action, 'login');
|
|
||||||
isnot(params, null);
|
|
||||||
},
|
|
||||||
function(action, params) {
|
|
||||||
is(action, 'ready');
|
|
||||||
do_check_null(params);
|
|
||||||
|
|
||||||
run_next_rp_test();
|
|
||||||
}
|
|
||||||
)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_watch_loggedin_logout() {
|
|
||||||
resetState();
|
|
||||||
|
|
||||||
let id = TEST_USER;
|
|
||||||
let other_id = "otherid@foo.com";
|
|
||||||
setup_test_identity(other_id, TEST_CERT, function() {
|
|
||||||
setup_test_identity(id, TEST_CERT, function() {
|
|
||||||
let store = get_idstore();
|
|
||||||
|
|
||||||
// set it up so we're supposed to be logged in to TEST_URL
|
|
||||||
// with id, not other_id
|
|
||||||
store.setLoginState(TEST_URL, true, id);
|
|
||||||
|
|
||||||
// this should cause a login with an assertion for id,
|
|
||||||
// not for other_id
|
|
||||||
identity.watch(mock_watch(other_id, call_sequentially(
|
|
||||||
function(action, params) {
|
|
||||||
is(action, 'login');
|
|
||||||
isnot(params, null);
|
|
||||||
},
|
|
||||||
function(action, params) {
|
|
||||||
is(action, 'ready');
|
|
||||||
do_check_null(params);
|
|
||||||
|
|
||||||
run_next_rp_test();
|
|
||||||
}
|
|
||||||
)));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_watch_notloggedin_ready() {
|
|
||||||
resetState();
|
|
||||||
|
|
||||||
identity.watch(mock_watch(null, function(action, params) {
|
|
||||||
is(action, 'ready');
|
|
||||||
is(params, undefined);
|
|
||||||
|
|
||||||
run_next_rp_test();
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_watch_notloggedin_logout() {
|
|
||||||
resetState();
|
|
||||||
|
|
||||||
identity.watch(mock_watch(TEST_USER, call_sequentially(
|
|
||||||
function(action, params) {
|
|
||||||
is(action, 'logout');
|
|
||||||
is(params, undefined);
|
|
||||||
|
|
||||||
let store = get_idstore();
|
|
||||||
do_check_null(store.getLoginState(TEST_URL));
|
|
||||||
},
|
|
||||||
function(action, params) {
|
|
||||||
is(action, 'ready');
|
|
||||||
is(params, undefined);
|
|
||||||
run_next_rp_test();
|
|
||||||
}
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_request() {
|
|
||||||
// set up a watch, to be consistent
|
|
||||||
let mockedDoc = mock_watch(null, function(action, params) {
|
|
||||||
// We're not checking anything here at the moment.
|
|
||||||
});
|
|
||||||
|
|
||||||
identity.watch(mockedDoc);
|
|
||||||
|
|
||||||
// be ready for the UX identity-request notification
|
|
||||||
makeObserver("identity-request", function (aSubject, aTopic, aData) {
|
|
||||||
isnot(aSubject, null);
|
|
||||||
|
|
||||||
run_next_rp_test();
|
|
||||||
});
|
|
||||||
|
|
||||||
var button = document.getElementById('request');
|
|
||||||
button.addEventListener('click', function requestHandler() {
|
|
||||||
button.removeEventListener('click', requestHandler);
|
|
||||||
identity.request();
|
|
||||||
});
|
|
||||||
|
|
||||||
synthesizeMouseAtCenter(button, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
function test_logout() {
|
|
||||||
resetState();
|
|
||||||
|
|
||||||
let id = TEST_USER;
|
|
||||||
setup_test_identity(id, TEST_CERT, function() {
|
|
||||||
let store = get_idstore();
|
|
||||||
|
|
||||||
// set it up so we're supposed to be logged in to TEST_URL
|
|
||||||
store.setLoginState(TEST_URL, true, id);
|
|
||||||
|
|
||||||
let doLogout;
|
|
||||||
let mockedDoc = mock_watch(id, call_sequentially(
|
|
||||||
function(action, params) {
|
|
||||||
is(action, 'ready');
|
|
||||||
is(params, undefined);
|
|
||||||
|
|
||||||
SimpleTest.executeSoon(doLogout);
|
|
||||||
},
|
|
||||||
function(action, params) {
|
|
||||||
is(action, 'logout');
|
|
||||||
is(params, undefined);
|
|
||||||
},
|
|
||||||
function(action, params) {
|
|
||||||
is(action, 'ready');
|
|
||||||
is(params, undefined);
|
|
||||||
|
|
||||||
run_next_rp_test();
|
|
||||||
}));
|
|
||||||
|
|
||||||
doLogout = function() {
|
|
||||||
makeObserver("identity-login-state-changed", function (aSubject, aTopic, aData) {
|
|
||||||
isnot(aSubject.wrappedJSObject.rpId, null, "Check rpId is not null");
|
|
||||||
is(aData, null, "Check identity changed to nobody");
|
|
||||||
|
|
||||||
ok(!store.getLoginState(TEST_URL).isLoggedIn, "Check isLoggedIn is false");
|
|
||||||
is(store.getLoginState(TEST_URL).email, TEST_USER, "Check notification email");
|
|
||||||
});
|
|
||||||
identity.logout();
|
|
||||||
};
|
|
||||||
|
|
||||||
identity.watch(mockedDoc);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
TESTS = TESTS.concat([test_watch_loggedin_ready, test_watch_loggedin_login, test_watch_loggedin_logout]);
|
|
||||||
TESTS = TESTS.concat([test_watch_notloggedin_ready, test_watch_notloggedin_logout]);
|
|
||||||
TESTS.push(test_request);
|
|
||||||
TESTS.push(test_logout);
|
|
||||||
|
|
||||||
</script>
|
|
||||||
</pre>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -46,8 +46,8 @@ function test_select_identity() {
|
|||||||
let mockedDoc = mock_doc(null, TEST_URL, call_sequentially(
|
let mockedDoc = mock_doc(null, TEST_URL, call_sequentially(
|
||||||
function(action, params) {
|
function(action, params) {
|
||||||
// ready emitted from first watch() call
|
// ready emitted from first watch() call
|
||||||
do_check_eq(action, 'ready');
|
do_check_eq(action, 'ready');
|
||||||
do_check_null(params);
|
do_check_null(params);
|
||||||
},
|
},
|
||||||
// first the login call
|
// first the login call
|
||||||
function(action, params) {
|
function(action, params) {
|
||||||
|
95
toolkit/identity/tests/unit/test_minimalidentity.js
Normal file
95
toolkit/identity/tests/unit/test_minimalidentity.js
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "MinimalIDService",
|
||||||
|
"resource://gre/modules/identity/MinimalIdentity.jsm",
|
||||||
|
"IdentityService");
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
|
||||||
|
|
||||||
|
function log(...aMessageArgs) {
|
||||||
|
Logger.log.apply(Logger, ["test_minimalidentity"].concat(aMessageArgs));
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_overall() {
|
||||||
|
do_check_neq(MinimalIDService, null);
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_mock_doc() {
|
||||||
|
do_test_pending();
|
||||||
|
let mockedDoc = mock_doc(null, TEST_URL, function(action, params) {
|
||||||
|
do_check_eq(action, 'coffee');
|
||||||
|
do_test_finished();
|
||||||
|
run_next_test();
|
||||||
|
});
|
||||||
|
|
||||||
|
mockedDoc.doCoffee();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test that the "identity-controller-watch" signal is emitted correctly
|
||||||
|
*/
|
||||||
|
function test_watch() {
|
||||||
|
do_test_pending();
|
||||||
|
|
||||||
|
let mockedDoc = mock_doc(null, TEST_URL);
|
||||||
|
makeObserver("identity-controller-watch", function (aSubject, aTopic, aData) {
|
||||||
|
do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
|
||||||
|
do_check_eq(aSubject.wrappedJSObject.origin, TEST_URL);
|
||||||
|
do_test_finished();
|
||||||
|
run_next_test();
|
||||||
|
});
|
||||||
|
|
||||||
|
MinimalIDService.RP.watch(mockedDoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test that the "identity-controller-request" signal is emitted correctly
|
||||||
|
*/
|
||||||
|
function test_request() {
|
||||||
|
do_test_pending();
|
||||||
|
|
||||||
|
let mockedDoc = mock_doc(null, TEST_URL);
|
||||||
|
makeObserver("identity-controller-request", function (aSubject, aTopic, aData) {
|
||||||
|
do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
|
||||||
|
do_check_eq(aSubject.wrappedJSObject.origin, TEST_URL);
|
||||||
|
do_test_finished();
|
||||||
|
run_next_test();
|
||||||
|
});
|
||||||
|
|
||||||
|
MinimalIDService.RP.watch(mockedDoc);
|
||||||
|
MinimalIDService.RP.request(mockedDoc.id, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Test that the "identity-controller-logout" signal is emitted correctly
|
||||||
|
*/
|
||||||
|
function test_logout() {
|
||||||
|
do_test_pending();
|
||||||
|
|
||||||
|
let mockedDoc = mock_doc(null, TEST_URL);
|
||||||
|
makeObserver("identity-controller-logout", function (aSubject, aTopic, aData) {
|
||||||
|
do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
|
||||||
|
do_test_finished();
|
||||||
|
run_next_test();
|
||||||
|
});
|
||||||
|
|
||||||
|
MinimalIDService.RP.watch(mockedDoc);
|
||||||
|
MinimalIDService.RP.logout(mockedDoc.id, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let TESTS = [
|
||||||
|
test_overall,
|
||||||
|
test_mock_doc,
|
||||||
|
test_watch,
|
||||||
|
test_request,
|
||||||
|
test_logout
|
||||||
|
];
|
||||||
|
|
||||||
|
TESTS.forEach(add_test);
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
run_next_test();
|
||||||
|
}
|
@ -4,8 +4,9 @@ tail = tail_identity.js
|
|||||||
|
|
||||||
# Test load modules first so syntax failures are caught early.
|
# Test load modules first so syntax failures are caught early.
|
||||||
[test_load_modules.js]
|
[test_load_modules.js]
|
||||||
[test_log_utils.js]
|
[test_minimalidentity.js]
|
||||||
|
|
||||||
|
[test_log_utils.js]
|
||||||
[test_authentication.js]
|
[test_authentication.js]
|
||||||
[test_crypto_service.js]
|
[test_crypto_service.js]
|
||||||
[test_identity.js]
|
[test_identity.js]
|
||||||
|
Loading…
Reference in New Issue
Block a user