mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 794680 - Connect gecko to b2g identity ui. r=benadida
This commit is contained in:
parent
a03f8d4021
commit
9563e27ce0
@ -35,6 +35,7 @@ EXTRA_PP_COMPONENTS = \
|
||||
|
||||
EXTRA_JS_MODULES = \
|
||||
TelURIParser.jsm \
|
||||
SignInToWebsite.jsm \
|
||||
$(NULL)
|
||||
|
||||
TEST_DIRS = \
|
||||
|
168
b2g/components/SignInToWebsite.jsm
Normal file
168
b2g/components/SignInToWebsite.jsm
Normal file
@ -0,0 +1,168 @@
|
||||
/* 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/. */
|
||||
|
||||
//
|
||||
// the B2G version of Identity UX
|
||||
//
|
||||
|
||||
"use strict";
|
||||
|
||||
const EXPORTED_SYMBOLS = ["SignInToWebsiteController"];
|
||||
|
||||
const Cc = Components.classes;
|
||||
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.
|
||||
// (NOT THE SAME THING AS IdP)
|
||||
const kIdentityShimFile = "chrome://browser/content/identity.js";
|
||||
|
||||
// Type of MozChromEvents to handle payment dialogs.
|
||||
const kOpenIdentityDialog = "open-id-dialog";
|
||||
const kCloseIdentityDialog = "close-id-dialog";
|
||||
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "uuidgen",
|
||||
"@mozilla.org/uuid-generator;1",
|
||||
"nsIUUIDGenerator");
|
||||
|
||||
function log(...aMessageArgs) {
|
||||
Logger.log.apply(Logger, ["SignInToWebsiteController"].concat(aMessageArgs));
|
||||
}
|
||||
|
||||
let SignInToWebsiteController = {
|
||||
|
||||
init: function SignInToWebsiteController_init() {
|
||||
log("SignInToWebsiteController: init");
|
||||
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;
|
||||
}
|
||||
},
|
||||
|
||||
getRandomId: function getRandomId() {
|
||||
return uuidgen.generateUUID().toString();
|
||||
},
|
||||
|
||||
doWatch: function SignInToWebsiteController_doWatch(aOptions) {
|
||||
// for now, just say we're ready
|
||||
// this._openDialogAndSendMessage(aOptions.rpId, "identity-delegate-watch", aOptions);
|
||||
IdentityService.doReady(aOptions.rpId);
|
||||
},
|
||||
|
||||
/**
|
||||
* The website is requesting login so the user must choose an identity to use.
|
||||
*/
|
||||
doRequest: function SignInToWebsiteController_doRequest(aOptions) {
|
||||
log("request options", aOptions);
|
||||
this._openDialogAndSendMessage(aOptions.rpId, "identity-delegate-request", aOptions);
|
||||
},
|
||||
|
||||
/*
|
||||
*
|
||||
*/
|
||||
doLogout: function SignInToWebsiteController_doLogout(aOptions) {
|
||||
log("logout options", aOptions);
|
||||
IdentityService.doLogout(aOptions.rpId);
|
||||
},
|
||||
|
||||
// FIXME: add a callback when the response from dialog is received
|
||||
// so watch can call doReady, while request doesn't.
|
||||
_openDialogAndSendMessage: function SignInToWebsiteController_openDialogAndSendMessage(aRpId, aMessage, aOptions) {
|
||||
let browser = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let content = browser.getContentWindow();
|
||||
if (!content) {
|
||||
// aErrorCb.onresult("NO_CONTENT_WINDOW");
|
||||
return;
|
||||
}
|
||||
|
||||
// prepare the message to be sent to gaia
|
||||
let id = kOpenIdentityDialog + "-" + this.getRandomId();
|
||||
let detail = {
|
||||
type: kOpenIdentityDialog,
|
||||
id: id
|
||||
};
|
||||
|
||||
log("id before is ", id);
|
||||
// wait for Gaia to send us a message back
|
||||
content.addEventListener("mozContentEvent", function getAssertion(evt) {
|
||||
log("id after is ", id);
|
||||
let msg = evt.detail;
|
||||
if (msg.id != id) {
|
||||
log("mozContentEvent. evt.detail.id != ", id, msg);
|
||||
content.removeEventListener("mozContentEvent", getAssertion);
|
||||
return;
|
||||
}
|
||||
|
||||
// Try to load the identity shim file containing the callbacks
|
||||
// in the content script.
|
||||
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);
|
||||
}
|
||||
|
||||
mm.addMessageListener("identity-delegate-return-assertion", function(message) {
|
||||
log("back with assertion", message.json);
|
||||
if (message.json.assertion)
|
||||
IdentityService.doLogin(aRpId, message.json.assertion);
|
||||
|
||||
IdentityService.doReady(aRpId);
|
||||
});
|
||||
|
||||
// send the options down
|
||||
log("sending message" , aMessage, aOptions);
|
||||
mm.sendAsyncMessage(aMessage, aOptions);
|
||||
|
||||
log("done load frame script");
|
||||
|
||||
content.removeEventListener("mozContentEvent", getAssertion);
|
||||
});
|
||||
|
||||
browser.shell.sendChromeEvent(detail);
|
||||
log("sent", detail, "to gaia");
|
||||
}
|
||||
|
||||
};
|
118
b2g/components/test/unit/head_identity.js
Normal file
118
b2g/components/test/unit/head_identity.js
Normal file
@ -0,0 +1,118 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
const Cr = Components.results;
|
||||
|
||||
// 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, "Identity",
|
||||
"resource://gre/modules/identity/Identity.jsm",
|
||||
"Identity");
|
||||
|
||||
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-am-a-cert";
|
||||
|
||||
// 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 mock_doc(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;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
aObserveFunc(aSubject, aTopic, aData);
|
||||
Services.obs.removeObserver(observer, aObserveTopic);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Services.obs.addObserver(observer, 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(",");
|
||||
do_throw("Too many calls: " + argString);
|
||||
return;
|
||||
}
|
||||
funcs[numCalls].apply(funcs[numCalls],arguments);
|
||||
numCalls += 1;
|
||||
};
|
||||
}
|
45
b2g/components/test/unit/test_signintowebsite.js
Normal file
45
b2g/components/test/unit/test_signintowebsite.js
Normal file
@ -0,0 +1,45 @@
|
||||
"use strict";
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MinimalIDService",
|
||||
"resource://gre/modules/identity/MinimalIdentity.jsm",
|
||||
"IdentityService");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsite",
|
||||
"resource://gre/b2g/components/SignInToWebsite.jsm",
|
||||
"SignInToWebsite");
|
||||
|
||||
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 = mock_doc(null, TEST_URL, function(action, params) {
|
||||
do_check_eq(action, 'coffee');
|
||||
do_test_finished();
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
mockedDoc.doCoffee();
|
||||
}
|
||||
|
||||
// XXX bug 800085 complete these tests - mock the gaia content object
|
||||
// so we can round trip through the SignInToWebsiteController
|
||||
|
||||
let TESTS = [
|
||||
test_overall,
|
||||
test_mock_doc,
|
||||
];
|
||||
|
||||
TESTS.forEach(add_test);
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
@ -3,3 +3,9 @@ head =
|
||||
tail =
|
||||
|
||||
[test_bug793310.js]
|
||||
|
||||
[test_signintowebsite.js]
|
||||
head = head_identity.js
|
||||
tail =
|
||||
|
||||
|
||||
|
@ -12,13 +12,23 @@ let EXPORTED_SYMBOLS = ["DOMIdentity"];
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
/*
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
|
||||
"resource://gre/modules/identity/Identity.jsm");
|
||||
*/
|
||||
|
||||
// minimal Identity.jsm
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
|
||||
"resource://gre/modules/identity/MinimalIdentity.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this,
|
||||
"Logger",
|
||||
"resource://gre/modules/identity/LogUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||
"@mozilla.org/parentprocessmessagemanager;1",
|
||||
"nsIMessageListenerManager");
|
||||
|
||||
function log(...aMessageArgs) {
|
||||
Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs));
|
||||
}
|
||||
@ -118,13 +128,15 @@ RPWatchContext.prototype = {
|
||||
let DOMIdentity = {
|
||||
// nsIMessageListener
|
||||
receiveMessage: function DOMIdentity_receiveMessage(aMessage) {
|
||||
log("**received message", aMessage);
|
||||
let msg = aMessage.json;
|
||||
|
||||
// Target is the frame message manager that called us and is
|
||||
// used to send replies back to the proper window.
|
||||
let targetMM = aMessage.target
|
||||
/* let targetMM = aMessage.target
|
||||
.QueryInterface(Ci.nsIFrameLoaderOwner)
|
||||
.frameLoader.messageManager;
|
||||
.frameLoader.messageManager;*/
|
||||
let targetMM = aMessage.target;
|
||||
|
||||
switch (aMessage.name) {
|
||||
// RP
|
||||
@ -199,8 +211,11 @@ let DOMIdentity = {
|
||||
let func = aWindow.messageManager[aRegister ? "addMessageListener"
|
||||
: "removeMessageListener"];
|
||||
|
||||
log("in _configureMessages *****", aRegister, this.messages);
|
||||
|
||||
for (let message of this.messages) {
|
||||
func.call(aWindow.messageManager, message, this);
|
||||
// func.call(aWindow.messageManager, message, this);
|
||||
ppmm.addMessageListener(message, this);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -17,6 +17,10 @@ const MAX_RP_CALLS = 100;
|
||||
Cu.import("resource://gre/modules/Services.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.
|
||||
|
||||
|
||||
@ -483,10 +487,12 @@ nsDOMIdentityInternal.prototype = {
|
||||
|
||||
this._log("init was called from " + aWindow.document.location);
|
||||
|
||||
this._mm = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
/*this._mm = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIContentFrameMessageManager);
|
||||
*/
|
||||
this._mm = cpmm;
|
||||
|
||||
// Setup listeners for messages from parent process.
|
||||
this._messages = [
|
||||
|
@ -35,6 +35,7 @@ EXTRA_JS_MODULES = \
|
||||
IdentityStore.jsm \
|
||||
jwcrypto.jsm \
|
||||
LogUtils.jsm \
|
||||
MinimalIdentity.jsm \
|
||||
RelyingParty.jsm \
|
||||
Sandbox.jsm \
|
||||
$(NULL)
|
||||
|
370
toolkit/identity/MinimalIdentity.jsm
Normal file
370
toolkit/identity/MinimalIdentity.jsm
Normal file
@ -0,0 +1,370 @@
|
||||
/* -*- 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
|
||||
*/
|
||||
|
||||
"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");
|
||||
this.shutdown();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
shutdown: function shutdown() {
|
||||
log("shutdown");
|
||||
Services.obs.removeObserver(this, "quit-application-granted");
|
||||
},
|
||||
|
||||
/**
|
||||
* 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;
|
||||
},
|
||||
|
||||
// RP stuff
|
||||
/**
|
||||
* 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: ", aRpCaller);
|
||||
|
||||
// store the caller structure and notify the UI observers
|
||||
this._rpFlows[aRpCaller.id] = aRpCaller;
|
||||
let options = {rpId: aRpCaller.id, origin: aRpCaller.origin};
|
||||
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);
|
||||
Services.obs.notifyObservers({wrappedJSObject: {rpId: aRpCallerId}},
|
||||
"identity-controller-logout",
|
||||
null);
|
||||
|
||||
},
|
||||
|
||||
// once the UI-and-display-logic components have received notifications, they call back with
|
||||
// direct invocation of the following.
|
||||
|
||||
/**
|
||||
* make login happen with an assertion
|
||||
*/
|
||||
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();
|
||||
},
|
||||
|
||||
|
||||
// IdP
|
||||
|
||||
/**
|
||||
* 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
|
||||
b * (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();
|
@ -13,6 +13,6 @@ include $(DEPTH)/config/autoconf.mk
|
||||
MODULE = test_identity
|
||||
XPCSHELL_TESTS = unit
|
||||
|
||||
DIRS = chrome mochitest
|
||||
DIRS = chrome mochitest
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -46,8 +46,8 @@ function test_select_identity() {
|
||||
let mockedDoc = mock_doc(null, TEST_URL, call_sequentially(
|
||||
function(action, params) {
|
||||
// ready emitted from first watch() call
|
||||
do_check_eq(action, 'ready');
|
||||
do_check_null(params);
|
||||
do_check_eq(action, 'ready');
|
||||
do_check_null(params);
|
||||
},
|
||||
// first the login call
|
||||
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.js]
|
||||
[test_log_utils.js]
|
||||
[test_minimalidentity.js]
|
||||
|
||||
[test_log_utils.js]
|
||||
[test_authentication.js]
|
||||
[test_crypto_service.js]
|
||||
[test_identity.js]
|
||||
|
Loading…
Reference in New Issue
Block a user