import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo

This commit is contained in:
Roy Tam 2018-01-19 03:59:58 +08:00
commit dcd9973243
150858 changed files with 23884658 additions and 0 deletions

View file

@ -0,0 +1,7 @@
"use strict";
module.exports = {
"extends": [
"../../../../testing/xpcshell/xpcshell.eslintrc.js"
]
};

View file

@ -0,0 +1,5 @@
{
"public-key": {"algorithm":"RS","n":"65718905405105134410187227495885391609221288015566078542117409373192106382993306537273677557482085204736975067567111831005921322991127165013340443563713385983456311886801211241492470711576322130577278575529202840052753612576061450560588102139907846854501252327551303482213505265853706269864950437458242988327","e":"65537"},
"authentication": "/browserid/sign_in.html",
"provisioning": "/browserid/provision.html"
}

View file

@ -0,0 +1,5 @@
{
"public-key": {"algorithm":"RS","n":"65718905405105134410187227495885391609221288015566078542117409373192106382993306537273677557482085204736975067567111831005921322991127165013340443563713385983456311886801211241492470711576322130577278575529202840052753612576061450560588102139907846854501252327551303482213505265853706269864950437458242988327","e":"65537"},
"authentication": "/browserid/sign_in.html",
// missing "provisioning"
}

View file

@ -0,0 +1,256 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cu = Components.utils;
var Cr = Components.results;
Cu.import("resource://testing-common/httpd.js");
// XXX until bug 937114 is fixed
Cu.importGlobalProperties(["atob"]);
// 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, "jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "IDService",
"resource://gre/modules/identity/Identity.jsm",
"IdentityService");
XPCOMUtils.defineLazyModuleGetter(this,
"IdentityStore",
"resource://gre/modules/identity/IdentityStore.jsm");
XPCOMUtils.defineLazyModuleGetter(this,
"Logger",
"resource://gre/modules/identity/LogUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this,
"uuidGenerator",
"@mozilla.org/uuid-generator;1",
"nsIUUIDGenerator");
const TEST_MESSAGE_MANAGER = "Mr McFeeley";
const TEST_URL = "https://myfavoritebacon.com";
const TEST_URL2 = "https://myfavoritebaconinacan.com";
const TEST_USER = "user@mozilla.com";
const TEST_PRIVKEY = "fake-privkey";
const TEST_CERT = "fake-cert";
const TEST_ASSERTION = "fake-assertion";
const TEST_IDPPARAMS = {
domain: "myfavoriteflan.com",
authentication: "/foo/authenticate.html",
provisioning: "/foo/provision.html"
};
// The following are utility functions for Identity testing
function log(...aMessageArgs) {
Logger.log.apply(Logger, ["test"].concat(aMessageArgs));
}
function get_idstore() {
return IdentityStore;
}
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();
}
function base64UrlDecode(s) {
s = s.replace(/-/g, "+");
s = s.replace(/_/g, "/");
// Replace padding if it was stripped by the sender.
// See http://tools.ietf.org/html/rfc4648#section-4
switch (s.length % 4) {
case 0:
break; // No pad chars in this case
case 2:
s += "==";
break; // Two pad chars
case 3:
s += "=";
break; // One pad char
default:
throw new InputException("Illegal base64url string!");
}
// With correct padding restored, apply the standard base64 decoder
return atob(s);
}
// 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.loggedInUser = aIdentity;
mockedDoc.origin = aOrigin;
mockedDoc["do"] = aDoFunc;
mockedDoc._mm = TEST_MESSAGE_MANAGER;
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");
mockedDoc.childProcessShutdown = partial(aDoFunc, "child-process-shutdown");
mockedDoc.RP = mockedDoc;
return mockedDoc;
}
function mock_fxa_rp(aIdentity, aOrigin, aDoFunc) {
let mockedDoc = {};
mockedDoc.id = uuid();
mockedDoc.emailHint = aIdentity;
mockedDoc.origin = aOrigin;
mockedDoc.wantIssuer = "firefox-accounts";
mockedDoc._mm = TEST_MESSAGE_MANAGER;
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.childProcessShutdown = partial(aDoFunc, "child-process-shutdown");
mockedDoc.RP = mockedDoc;
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;
};
}
/*
* 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 provId = uuid();
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 = 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(caller);
}
// Switch debug messages on by default
var initialPrefDebugValue = false;
try {
initialPrefDebugValue = Services.prefs.getBoolPref("toolkit.identity.debug");
} catch (noPref) {}
Services.prefs.setBoolPref("toolkit.identity.debug", true);
// Switch on firefox accounts
var initialPrefFXAValue = false;
try {
initialPrefFXAValue = Services.prefs.getBoolPref("identity.fxaccounts.enabled");
} catch (noPref) {}
Services.prefs.setBoolPref("identity.fxaccounts.enabled", true);
// after execution, restore prefs
do_register_cleanup(function() {
log("restoring prefs to their initial values");
Services.prefs.setBoolPref("toolkit.identity.debug", initialPrefDebugValue);
Services.prefs.setBoolPref("identity.fxaccounts.enabled", initialPrefFXAValue);
});

View file

@ -0,0 +1,8 @@
// pre-emptively shut down to clear resources
if (typeof IdentityService !== "undefined") {
IdentityService.shutdown();
} else if (typeof IDService !== "undefined") {
IDService.shutdown();
}

View file

@ -0,0 +1,159 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "IDService",
"resource://gre/modules/identity/Identity.jsm",
"IdentityService");
XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm");
function test_begin_authentication_flow() {
do_test_pending();
let _provId = null;
// set up a watch, to be consistent
let mockedDoc = mock_doc(null, TEST_URL, function(action, params) {});
IDService.RP.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) {
do_check_neq(aSubject, null);
do_check_eq(aSubject.wrappedJSObject.provId, _provId);
do_test_finished();
run_next_test();
});
setup_provisioning(
TEST_USER,
function(caller) {
_provId = caller.id;
IDService.IDP.beginProvisioning(caller);
}, function() {},
{
beginProvisioningCallback: function(email, duration_s) {
// let's say this user needs to authenticate
IDService.IDP._doAuthentication(_provId, {idpParams:TEST_IDPPARAMS});
}
}
);
}
function test_complete_authentication_flow() {
do_test_pending();
let _provId = null;
let _authId = null;
let id = TEST_USER;
let callbacksFired = false;
let loginStateChanged = false;
let identityAuthComplete = false;
// The result of authentication should be a successful login
IDService.reset();
setup_test_identity(id, TEST_CERT, function() {
// set it up so we're supposed to be logged in to TEST_URL
get_idstore().setLoginState(TEST_URL, true, id);
// When we authenticate, our ready callback will be fired.
// At the same time, a separate topic will be sent up to the
// the observer in the UI. The test is complete when both
// events have occurred.
let mockedDoc = mock_doc(id, TEST_URL, call_sequentially(
function(action, params) {
do_check_eq(action, 'ready');
do_check_eq(params, undefined);
// if notification already received by observer, test is done
callbacksFired = true;
if (loginStateChanged && identityAuthComplete) {
do_test_finished();
run_next_test();
}
}
));
makeObserver("identity-auth-complete", function(aSubject, aTopic, aData) {
identityAuthComplete = true;
do_test_finished();
run_next_test();
});
makeObserver("identity-login-state-changed", function (aSubject, aTopic, aData) {
do_check_neq(aSubject, null);
do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
do_check_eq(aData, id);
// if callbacks in caller doc already fired, test is done.
loginStateChanged = true;
if (callbacksFired && identityAuthComplete) {
do_test_finished();
run_next_test();
}
});
IDService.RP.watch(mockedDoc);
// Create a provisioning flow for our auth flow to attach to
setup_provisioning(
TEST_USER,
function(provFlow) {
_provId = provFlow.id;
IDService.IDP.beginProvisioning(provFlow);
}, function() {},
{
beginProvisioningCallback: function(email, duration_s) {
// let's say this user needs to authenticate
IDService.IDP._doAuthentication(_provId, {idpParams: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 = uuid();
IDService.IDP.setAuthenticationFlow(_authId, _provId);
// ... then the UI calls beginAuthentication ...
authCaller.id = _authId;
IDService.IDP._provisionFlows[_provId].caller = authCaller;
IDService.IDP.beginAuthentication(authCaller);
}
}
);
});
// A mock calling context
let authCaller = {
doBeginAuthenticationCallback: function doBeginAuthenticationCallback(identity) {
do_check_eq(identity, TEST_USER);
// completeAuthentication will emit "identity-auth-complete"
IDService.IDP.completeAuthentication(_authId);
},
doError: function(err) {
log("OW! My doError callback hurts!", err);
},
};
}
var TESTS = [];
TESTS.push(test_begin_authentication_flow);
TESTS.push(test_complete_authentication_flow);
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,122 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
const idService = Cc["@mozilla.org/identity/crypto-service;1"]
.getService(Ci.nsIIdentityCryptoService);
const ALG_DSA = "DS160";
const ALG_RSA = "RS256";
const BASE64_URL_ENCODINGS = [
// The vectors from RFC 4648 are very silly, but we may as well include them.
["", ""],
["f", "Zg=="],
["fo", "Zm8="],
["foo", "Zm9v"],
["foob", "Zm9vYg=="],
["fooba", "Zm9vYmE="],
["foobar", "Zm9vYmFy"],
// It's quite likely you could get a string like this in an assertion audience
["i-like-pie.com", "aS1saWtlLXBpZS5jb20="],
// A few extra to be really sure
["andré@example.com", "YW5kcsOpQGV4YW1wbGUuY29t"],
["πόλλ' οἶδ' ἀλώπηξ, ἀλλ' ἐχῖνος ἓν μέγα",
"z4DPjM67zrsnIM6_4by2zrQnIOG8gM67z47PgM63zr4sIOG8gM67zrsnIOG8kM-H4b-Wzr3Ov8-CIOG8k869IM68zq3Os86x"],
];
// When the output of an operation is a
function do_check_eq_or_slightly_less(x, y) {
do_check_true(x >= y - (3 * 8));
}
function test_base64_roundtrip() {
let message = "Attack at dawn!";
let encoded = idService.base64UrlEncode(message);
let decoded = base64UrlDecode(encoded);
do_check_neq(message, encoded);
do_check_eq(decoded, message);
run_next_test();
}
function test_dsa() {
idService.generateKeyPair(ALG_DSA, function (rv, keyPair) {
log("DSA generateKeyPair finished ", rv);
do_check_true(Components.isSuccessCode(rv));
do_check_eq(typeof keyPair.sign, "function");
do_check_eq(keyPair.keyType, ALG_DSA);
do_check_eq_or_slightly_less(keyPair.hexDSAGenerator.length, 1024 / 8 * 2);
do_check_eq_or_slightly_less(keyPair.hexDSAPrime.length, 1024 / 8 * 2);
do_check_eq_or_slightly_less(keyPair.hexDSASubPrime.length, 160 / 8 * 2);
do_check_eq_or_slightly_less(keyPair.hexDSAPublicValue.length, 1024 / 8 * 2);
// XXX: test that RSA parameters throw the correct error
log("about to sign with DSA key");
keyPair.sign("foo", function (rv2, signature) {
log("DSA sign finished ", rv2, signature);
do_check_true(Components.isSuccessCode(rv2));
do_check_true(signature.length > 1);
// TODO: verify the signature with the public key
run_next_test();
});
});
}
function test_rsa() {
idService.generateKeyPair(ALG_RSA, function (rv, keyPair) {
log("RSA generateKeyPair finished ", rv);
do_check_true(Components.isSuccessCode(rv));
do_check_eq(typeof keyPair.sign, "function");
do_check_eq(keyPair.keyType, ALG_RSA);
do_check_eq_or_slightly_less(keyPair.hexRSAPublicKeyModulus.length,
2048 / 8);
do_check_true(keyPair.hexRSAPublicKeyExponent.length > 1);
log("about to sign with RSA key");
keyPair.sign("foo", function (rv2, signature) {
log("RSA sign finished ", rv2, signature);
do_check_true(Components.isSuccessCode(rv2));
do_check_true(signature.length > 1);
run_next_test();
});
});
}
function test_base64UrlEncode() {
for (let [source, target] of BASE64_URL_ENCODINGS) {
do_check_eq(target, idService.base64UrlEncode(source));
}
run_next_test();
}
function test_base64UrlDecode() {
let utf8Converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
utf8Converter.charset = "UTF-8";
// We know the encoding of our inputs - on conversion back out again, make
// sure they're the same.
for (let [source, target] of BASE64_URL_ENCODINGS) {
let result = utf8Converter.ConvertToUnicode(base64UrlDecode(target));
result += utf8Converter.Finish();
do_check_eq(source, result);
}
run_next_test();
}
add_test(test_base64_roundtrip);
add_test(test_dsa);
add_test(test_rsa);
add_test(test_base64UrlEncode);
add_test(test_base64UrlDecode);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,270 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://gre/modules/DOMIdentity.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FirefoxAccounts",
"resource://gre/modules/identity/FirefoxAccounts.jsm");
// Make the profile dir available; this is necessary so that
// services/fxaccounts/FxAccounts.jsm can read and write its signed-in user
// data.
do_get_profile();
function MockFXAManager() {
this.signedInUser = true;
}
MockFXAManager.prototype = {
getAssertion: function(audience) {
let result = this.signedInUser ? TEST_ASSERTION : null;
return Promise.resolve(result);
},
signOut: function() {
this.signedInUser = false;
return Promise.resolve(null);
},
signIn: function(user) {
this.signedInUser = user;
return Promise.resolve(user);
},
}
var originalManager = FirefoxAccounts.fxAccountsManager;
FirefoxAccounts.fxAccountsManager = new MockFXAManager();
do_register_cleanup(() => {
log("restoring fxaccountsmanager");
FirefoxAccounts.fxAccountsManager = originalManager;
});
function withNobodySignedIn() {
return FirefoxAccounts.fxAccountsManager.signOut();
}
function withSomebodySignedIn() {
return FirefoxAccounts.fxAccountsManager.signIn('Pertelote');
}
function test_overall() {
do_check_neq(FirefoxAccounts, null);
run_next_test();
}
function test_mock() {
do_test_pending();
withSomebodySignedIn().then(() => {
FirefoxAccounts.fxAccountsManager.getAssertion().then(assertion => {
do_check_eq(assertion, TEST_ASSERTION);
do_test_finished();
run_next_test();
});
});
}
function test_watch_signed_in() {
do_test_pending();
let received = [];
let mockedRP = mock_fxa_rp(null, TEST_URL, function(method, data) {
received.push([method, data]);
if (method == "ready") {
// confirm that we were signed in and then ready was called
do_check_eq(received.length, 2);
do_check_eq(received[0][0], "login");
do_check_eq(received[0][1], TEST_ASSERTION);
do_check_eq(received[1][0], "ready");
do_test_finished();
run_next_test();
}
});
withSomebodySignedIn().then(() => {
FirefoxAccounts.RP.watch(mockedRP);
});
}
function test_watch_signed_out() {
do_test_pending();
let received = [];
let mockedRP = mock_fxa_rp(null, TEST_URL, function(method) {
received.push(method);
if (method == "ready") {
// confirm that we were signed out and then ready was called
do_check_eq(received.length, 2);
do_check_eq(received[0], "logout");
do_check_eq(received[1], "ready");
do_test_finished();
run_next_test();
}
});
withNobodySignedIn().then(() => {
FirefoxAccounts.RP.watch(mockedRP);
});
}
function test_request() {
do_test_pending();
let received = [];
let mockedRP = mock_fxa_rp(null, TEST_URL, function(method, data) {
received.push([method, data]);
// On watch(), we are signed out. Then we call request().
if (received.length === 2) {
do_check_eq(received[0][0], "logout");
do_check_eq(received[1][0], "ready");
// Pretend request() showed ux and the user signed in
withSomebodySignedIn().then(() => {
FirefoxAccounts.RP.request(mockedRP.id);
});
}
if (received.length === 3) {
do_check_eq(received[2][0], "login");
do_check_eq(received[2][1], TEST_ASSERTION);
do_test_finished();
run_next_test();
}
});
// First, call watch() with nobody signed in
withNobodySignedIn().then(() => {
FirefoxAccounts.RP.watch(mockedRP);
});
}
function test_logout() {
do_test_pending();
let received = [];
let mockedRP = mock_fxa_rp(null, TEST_URL, function(method) {
received.push(method);
// At first, watch() signs us in automatically. Then we sign out.
if (received.length === 2) {
do_check_eq(received[0], "login");
do_check_eq(received[1], "ready");
FirefoxAccounts.RP.logout(mockedRP.id);
}
if (received.length === 3) {
do_check_eq(received[2], "logout");
do_test_finished();
run_next_test();
}
});
// First, call watch()
withSomebodySignedIn().then(() => {
FirefoxAccounts.RP.watch(mockedRP);
});
}
function test_error() {
do_test_pending();
let received = [];
// Mock the fxAccountsManager so that getAssertion rejects its promise and
// triggers our onerror handler. (This is the method that's used internally
// by FirefoxAccounts.RP.request().)
let originalGetAssertion = FirefoxAccounts.fxAccountsManager.getAssertion;
FirefoxAccounts.fxAccountsManager.getAssertion = function(audience) {
return Promise.reject(new Error("barf!"));
};
let mockedRP = mock_fxa_rp(null, TEST_URL, function(method, message) {
// We will immediately receive an error, due to watch()'s attempt
// to getAssertion().
do_check_eq(method, "error");
do_check_true(/barf/.test(message));
// Put things back the way they were
FirefoxAccounts.fxAccountsManager.getAssertion = originalGetAssertion;
do_test_finished();
run_next_test();
});
// First, call watch()
withSomebodySignedIn().then(() => {
FirefoxAccounts.RP.watch(mockedRP);
});
}
function test_child_process_shutdown() {
do_test_pending();
let rpCount = FirefoxAccounts.RP._rpFlows.size;
makeObserver("identity-child-process-shutdown", (aTopic, aSubject, aData) => {
// Last of all, the shutdown observer message will be fired.
// This takes place after the RP has a chance to delete flows
// and clean up.
do_check_eq(FirefoxAccounts.RP._rpFlows.size, rpCount);
do_test_finished();
run_next_test();
});
let mockedRP = mock_fxa_rp(null, TEST_URL, (method) => {
// We should enter this function for 'ready' and 'child-process-shutdown'.
// After we have a chance to do our thing, the shutdown observer message
// will fire and be caught by the function above.
do_check_eq(FirefoxAccounts.RP._rpFlows.size, rpCount + 1);
switch (method) {
case "ready":
DOMIdentity._childProcessShutdown("my message manager");
break;
case "child-process-shutdown":
// We have to call this explicitly because there's no real
// dom window here.
FirefoxAccounts.RP.childProcessShutdown(mockedRP._mm);
break;
default:
break;
}
});
mockedRP._mm = "my message manager";
withSomebodySignedIn().then(() => {
FirefoxAccounts.RP.watch(mockedRP);
});
// fake a dom window context
DOMIdentity.newContext(mockedRP, mockedRP._mm);
}
var TESTS = [
test_overall,
test_mock,
test_watch_signed_in,
test_watch_signed_out,
test_request,
test_logout,
test_error,
test_child_process_shutdown,
];
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,114 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "IDService",
"resource://gre/modules/identity/Identity.jsm",
"IdentityService");
function test_overall() {
do_check_neq(IDService, 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();
}
function test_add_identity() {
IDService.reset();
IDService.addIdentity(TEST_USER);
let identities = IDService.RP.getIdentitiesForSite(TEST_URL);
do_check_eq(identities.result.length, 1);
do_check_eq(identities.result[0], TEST_USER);
run_next_test();
}
function test_select_identity() {
do_test_pending();
IDService.reset();
let id = "ishtar@mockmyid.com";
setup_test_identity(id, TEST_CERT, function() {
let gotAssertion = false;
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);
},
// first the login call
function(action, params) {
do_check_eq(action, 'login');
do_check_neq(params, null);
// XXX - check that the assertion is for the right email
gotAssertion = true;
},
// then the ready call
function(action, params) {
do_check_eq(action, 'ready');
do_check_null(params);
// we should have gotten the assertion already
do_check_true(gotAssertion);
do_test_finished();
run_next_test();
}));
// register the callbacks
IDService.RP.watch(mockedDoc);
// register the request UX observer
makeObserver("identity-request", function (aSubject, aTopic, aData) {
// do the select identity
// we expect this to succeed right away because of test_identity
// so we don't mock network requests or otherwise
IDService.selectIdentity(aSubject.wrappedJSObject.rpId, id);
});
// do the request
IDService.RP.request(mockedDoc.id, {});
});
}
function test_parse_good_email() {
var parsed = IDService.parseEmail('prime-minister@jed.gov');
do_check_eq(parsed.username, 'prime-minister');
do_check_eq(parsed.domain, 'jed.gov');
run_next_test();
}
function test_parse_bogus_emails() {
do_check_eq(null, IDService.parseEmail('@evil.org'));
do_check_eq(null, IDService.parseEmail('foo@bar@baz.com'));
do_check_eq(null, IDService.parseEmail('you@wellsfargo.com/accounts/transfer?to=dolske&amt=all'));
run_next_test();
}
var TESTS = [test_overall, test_mock_doc];
TESTS.push(test_add_identity);
TESTS.push(test_select_identity);
TESTS.push(test_parse_good_email);
TESTS.push(test_parse_bogus_emails);
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,46 @@
"use strict";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/identity/IdentityUtils.jsm');
function test_check_deprecated() {
let options = {
id: 123,
loggedInEmail: "jed@foo.com",
pies: 42
};
do_check_true(checkDeprecated(options, "loggedInEmail"));
do_check_false(checkDeprecated(options, "flans"));
run_next_test();
}
function test_check_renamed() {
let options = {
id: 123,
loggedInEmail: "jed@foo.com",
pies: 42
};
checkRenamed(options, "loggedInEmail", "loggedInUser");
// It moves loggedInEmail to loggedInUser
do_check_false(!!options.loggedInEmail);
do_check_eq(options.loggedInUser, "jed@foo.com");
run_next_test();
}
var TESTS = [
test_check_deprecated,
test_check_renamed
];
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,281 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict"
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, "IDService",
"resource://gre/modules/identity/Identity.jsm",
"IdentityService");
XPCOMUtils.defineLazyModuleGetter(this, "jwcrypto",
"resource://gre/modules/identity/jwcrypto.jsm");
XPCOMUtils.defineLazyServiceGetter(this,
"CryptoService",
"@mozilla.org/identity/crypto-service;1",
"nsIIdentityCryptoService");
const RP_ORIGIN = "http://123done.org";
const INTERNAL_ORIGIN = "browserid://";
const SECOND_MS = 1000;
const MINUTE_MS = SECOND_MS * 60;
const HOUR_MS = MINUTE_MS * 60;
function test_sanity() {
do_test_pending();
jwcrypto.generateKeyPair("DS160", function(err, kp) {
do_check_null(err);
do_test_finished();
run_next_test();
});
}
function test_generate() {
do_test_pending();
jwcrypto.generateKeyPair("DS160", function(err, kp) {
do_check_null(err);
do_check_neq(kp, null);
do_test_finished();
run_next_test();
});
}
function test_get_assertion() {
do_test_pending();
jwcrypto.generateKeyPair(
"DS160",
function(err, kp) {
jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN, (err2, backedAssertion) => {
do_check_null(err2);
do_check_eq(backedAssertion.split("~").length, 2);
do_check_eq(backedAssertion.split(".").length, 3);
do_test_finished();
run_next_test();
});
});
}
function test_rsa() {
do_test_pending();
function checkRSA(err, kpo) {
do_check_neq(kpo, undefined);
log(kpo.serializedPublicKey);
let pk = JSON.parse(kpo.serializedPublicKey);
do_check_eq(pk.algorithm, "RS");
/* TODO
do_check_neq(kpo.sign, null);
do_check_eq(typeof kpo.sign, "function");
do_check_neq(kpo.userID, null);
do_check_neq(kpo.url, null);
do_check_eq(kpo.url, INTERNAL_ORIGIN);
do_check_neq(kpo.exponent, null);
do_check_neq(kpo.modulus, null);
// TODO: should sign be async?
let sig = kpo.sign("This is a message to sign");
do_check_neq(sig, null);
do_check_eq(typeof sig, "string");
do_check_true(sig.length > 1);
*/
do_test_finished();
run_next_test();
}
jwcrypto.generateKeyPair("RS256", checkRSA);
}
function test_dsa() {
do_test_pending();
function checkDSA(err, kpo) {
do_check_neq(kpo, undefined);
log(kpo.serializedPublicKey);
let pk = JSON.parse(kpo.serializedPublicKey);
do_check_eq(pk.algorithm, "DS");
/* TODO
do_check_neq(kpo.sign, null);
do_check_eq(typeof kpo.sign, "function");
do_check_neq(kpo.userID, null);
do_check_neq(kpo.url, null);
do_check_eq(kpo.url, INTERNAL_ORIGIN);
do_check_neq(kpo.generator, null);
do_check_neq(kpo.prime, null);
do_check_neq(kpo.subPrime, null);
do_check_neq(kpo.publicValue, null);
let sig = kpo.sign("This is a message to sign");
do_check_neq(sig, null);
do_check_eq(typeof sig, "string");
do_check_true(sig.length > 1);
*/
do_test_finished();
run_next_test();
}
jwcrypto.generateKeyPair("DS160", checkDSA);
}
function test_get_assertion_with_offset() {
do_test_pending();
// Use an arbitrary date in the past to ensure we don't accidentally pass
// this test with current dates, missing offsets, etc.
let serverMsec = Date.parse("Tue Oct 31 2000 00:00:00 GMT-0800");
// local clock skew
// clock is 12 hours fast; -12 hours offset must be applied
let localtimeOffsetMsec = -1 * 12 * HOUR_MS;
let localMsec = serverMsec - localtimeOffsetMsec;
jwcrypto.generateKeyPair(
"DS160",
function(err, kp) {
jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN,
{ duration: MINUTE_MS,
localtimeOffsetMsec: localtimeOffsetMsec,
now: localMsec},
function(err2, backedAssertion) {
do_check_null(err2);
// properly formed
let cert;
let assertion;
[cert, assertion] = backedAssertion.split("~");
do_check_eq(cert, "fake-cert");
do_check_eq(assertion.split(".").length, 3);
let components = extractComponents(assertion);
// Expiry is within two minutes, corrected for skew
let exp = parseInt(components.payload.exp, 10);
do_check_true(exp - serverMsec === MINUTE_MS);
do_test_finished();
run_next_test();
}
);
}
);
}
function test_assertion_lifetime() {
do_test_pending();
jwcrypto.generateKeyPair(
"DS160",
function(err, kp) {
jwcrypto.generateAssertion("fake-cert", kp, RP_ORIGIN,
{duration: MINUTE_MS},
function(err2, backedAssertion) {
do_check_null(err2);
// properly formed
let cert;
let assertion;
[cert, assertion] = backedAssertion.split("~");
do_check_eq(cert, "fake-cert");
do_check_eq(assertion.split(".").length, 3);
let components = extractComponents(assertion);
// Expiry is within one minute, as we specified above
let exp = parseInt(components.payload.exp, 10);
do_check_true(Math.abs(Date.now() - exp) > 50 * SECOND_MS);
do_check_true(Math.abs(Date.now() - exp) <= MINUTE_MS);
do_test_finished();
run_next_test();
}
);
}
);
}
function test_audience_encoding_bug972582() {
let audience = "i-like-pie.com";
jwcrypto.generateKeyPair(
"DS160",
function(err, kp) {
do_check_null(err);
jwcrypto.generateAssertion("fake-cert", kp, audience,
function(err2, backedAssertion) {
do_check_null(err2);
let [cert, assertion] = backedAssertion.split("~");
let components = extractComponents(assertion);
do_check_eq(components.payload.aud, audience);
do_test_finished();
run_next_test();
}
);
}
);
}
// End of tests
// Helper function follow
function extractComponents(signedObject) {
if (typeof(signedObject) != 'string') {
throw new Error("malformed signature " + typeof(signedObject));
}
let parts = signedObject.split(".");
if (parts.length != 3) {
throw new Error("signed object must have three parts, this one has " + parts.length);
}
let headerSegment = parts[0];
let payloadSegment = parts[1];
let cryptoSegment = parts[2];
let header = JSON.parse(base64UrlDecode(headerSegment));
let payload = JSON.parse(base64UrlDecode(payloadSegment));
// Ensure well-formed header
do_check_eq(Object.keys(header).length, 1);
do_check_true(!!header.alg);
// Ensure well-formed payload
for (let field of ["exp", "aud"]) {
do_check_true(!!payload[field]);
}
return {header: header,
payload: payload,
headerSegment: headerSegment,
payloadSegment: payloadSegment,
cryptoSegment: cryptoSegment};
}
var TESTS = [
test_sanity,
test_generate,
test_get_assertion,
test_get_assertion_with_offset,
test_assertion_lifetime,
test_audience_encoding_bug972582,
];
TESTS = TESTS.concat([test_rsa, test_dsa]);
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,20 @@
/* 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/. */
const modules = [
"Identity.jsm",
"IdentityProvider.jsm",
"IdentityStore.jsm",
"jwcrypto.jsm",
"RelyingParty.jsm",
"Sandbox.jsm",
];
function run_test() {
for (let m of modules) {
let resource = "resource://gre/modules/identity/" + m;
Components.utils.import(resource, {});
do_print("loaded " + resource);
}
}

View file

@ -0,0 +1,74 @@
"use strict";
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import('resource://gre/modules/Services.jsm');
Cu.import('resource://gre/modules/identity/LogUtils.jsm');
function toggle_debug() {
do_test_pending();
function Wrapper() {
this.init();
}
Wrapper.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
observe: function observe(aSubject, aTopic, aData) {
if (aTopic === "nsPref:changed") {
// race condition?
do_check_eq(Logger._debug, true);
do_test_finished();
run_next_test();
}
},
init: function() {
Services.prefs.addObserver('toolkit.identity.debug', this, false);
}
};
var wrapper = new Wrapper();
Services.prefs.setBoolPref('toolkit.identity.debug', true);
}
// test that things don't break
function logAlias(...args) {
Logger.log.apply(Logger, ["log alias"].concat(args));
}
function reportErrorAlias(...args) {
Logger.reportError.apply(Logger, ["report error alias"].concat(args));
}
function test_log() {
Logger.log("log test", "I like pie");
do_test_finished();
run_next_test();
}
function test_reportError() {
Logger.reportError("log test", "We are out of pies!!!");
do_test_finished();
run_next_test();
}
function test_wrappers() {
logAlias("I like potatoes");
do_test_finished();
reportErrorAlias("Too much red bull");
}
var TESTS = [
// XXX fix me
// toggle_debug,
test_log,
test_reportError,
test_wrappers,
];
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,223 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "MinimalIDService",
"resource://gre/modules/identity/MinimalIdentity.jsm",
"IdentityService");
Cu.import("resource://gre/modules/identity/LogUtils.jsm");
Cu.import("resource://gre/modules/DOMIdentity.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.id, 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.id, 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 forceAuthentication flag can be sent
*/
function test_request_forceAuthentication() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL);
makeObserver("identity-controller-request", function (aSubject, aTopic, aData) {
do_check_eq(aSubject.wrappedJSObject.id, mockedDoc.id);
do_check_eq(aSubject.wrappedJSObject.origin, TEST_URL);
do_check_eq(aSubject.wrappedJSObject.forceAuthentication, true);
do_test_finished();
run_next_test();
});
MinimalIDService.RP.watch(mockedDoc);
MinimalIDService.RP.request(mockedDoc.id, {forceAuthentication: true});
}
/*
* Test that the issuer can be forced
*/
function test_request_forceIssuer() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL);
makeObserver("identity-controller-request", function (aSubject, aTopic, aData) {
do_check_eq(aSubject.wrappedJSObject.id, mockedDoc.id);
do_check_eq(aSubject.wrappedJSObject.origin, TEST_URL);
do_check_eq(aSubject.wrappedJSObject.issuer, "https://jed.gov");
do_test_finished();
run_next_test();
});
MinimalIDService.RP.watch(mockedDoc);
MinimalIDService.RP.request(mockedDoc.id, {issuer: "https://jed.gov"});
}
/*
* 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.id, mockedDoc.id);
do_test_finished();
run_next_test();
});
MinimalIDService.RP.watch(mockedDoc);
MinimalIDService.RP.logout(mockedDoc.id, {});
}
/*
* Test that logout() before watch() fails gently
*/
function test_logoutBeforeWatch() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL);
makeObserver("identity-controller-logout", function() {
do_throw("How can we logout when watch was not called?");
});
MinimalIDService.RP.logout(mockedDoc.id, {});
do_test_finished();
run_next_test();
}
/*
* Test that request() before watch() fails gently
*/
function test_requestBeforeWatch() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL);
makeObserver("identity-controller-request", function() {
do_throw("How can we request when watch was not called?");
});
MinimalIDService.RP.request(mockedDoc.id, {});
do_test_finished();
run_next_test();
}
/*
* Test that internal unwatch() before watch() fails gently
*/
function test_unwatchBeforeWatch() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL);
MinimalIDService.RP.unwatch(mockedDoc.id, {});
do_test_finished();
run_next_test();
}
/*
* Test that the RP flow is cleaned up on child process shutdown
*/
function test_childProcessShutdown() {
do_test_pending();
let UNIQUE_MESSAGE_MANAGER = "i am a beautiful snowflake";
let initialRPCount = Object.keys(MinimalIDService.RP._rpFlows).length;
let mockedDoc = mock_doc(null, TEST_URL, (action, params) => {
if (action == "child-process-shutdown") {
// since there's no actual dom window connection, we have to
// do this bit manually here.
MinimalIDService.RP.childProcessShutdown(UNIQUE_MESSAGE_MANAGER);
}
});
mockedDoc._mm = UNIQUE_MESSAGE_MANAGER;
makeObserver("identity-controller-watch", function (aSubject, aTopic, aData) {
DOMIdentity._childProcessShutdown(UNIQUE_MESSAGE_MANAGER);
});
makeObserver("identity-child-process-shutdown", (aTopic, aSubject, aData) => {
do_check_eq(Object.keys(MinimalIDService.RP._rpFlows).length, initialRPCount);
do_test_finished();
run_next_test();
});
// fake a dom window context
DOMIdentity.newContext(mockedDoc, UNIQUE_MESSAGE_MANAGER);
MinimalIDService.RP.watch(mockedDoc);
}
var TESTS = [
test_overall,
test_mock_doc,
test_watch,
test_request,
test_request_forceAuthentication,
test_request_forceIssuer,
test_logout,
test_logoutBeforeWatch,
test_requestBeforeWatch,
test_unwatchBeforeWatch,
test_childProcessShutdown,
];
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,114 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* By their nature, these tests duplicate some of the functionality of
* other tests for Identity, RelyingParty, and IdentityProvider.
*
* In particular, "identity-auth-complete" and
* "identity-login-state-changed" are tested in test_authentication.js
*/
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "IDService",
"resource://gre/modules/identity/Identity.jsm",
"IdentityService");
function test_smoke() {
do_check_neq(IDService, null);
run_next_test();
}
function test_identity_request() {
// In response to navigator.id.request(), initiate a login with user
// interaction by notifying observers of 'identity-request'
do_test_pending();
IDService.reset();
let id = "landru@mockmyid.com";
setup_test_identity(id, TEST_CERT, function() {
// deliberately adding a trailing final slash on the domain
// to test path composition
let mockedDoc = mock_doc(null, "http://jed.gov/", function() {});
// by calling watch() we create an rp flow.
IDService.RP.watch(mockedDoc);
// register the request UX observer
makeObserver("identity-request", function (aSubject, aTopic, aData) {
do_check_eq(aTopic, "identity-request");
do_check_eq(aData, null);
// check that all the URLs are properly resolved
let subj = aSubject.wrappedJSObject;
do_check_eq(subj.privacyPolicy, "http://jed.gov/pp.html");
do_check_eq(subj.termsOfService, "http://jed.gov/tos.html");
do_test_finished();
run_next_test();
});
let requestOptions = {
privacyPolicy: "/pp.html",
termsOfService: "/tos.html"
};
IDService.RP.request(mockedDoc.id, requestOptions);
});
}
function test_identity_auth() {
// see test_authentication.js for "identity-auth-complete"
// and "identity-login-state-changed"
do_test_pending();
let _provId = "bogus";
// Simulate what would be returned by IDService._fetchWellKnownFile
// for a given domain.
let idpParams = {
domain: "myfavoriteflan.com",
idpParams: {
authentication: "/foo/authenticate.html",
provisioning: "/foo/provision.html"
}
};
// Create an RP flow
let mockedDoc = mock_doc(null, TEST_URL, function(action, params) {});
IDService.RP.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) {
do_check_neq(aSubject, null);
do_check_eq(aTopic, "identity-auth");
do_check_eq(aData, "https://myfavoriteflan.com/foo/authenticate.html");
do_check_eq(aSubject.wrappedJSObject.provId, _provId);
do_test_finished();
run_next_test();
});
// Even though our provisioning flow id is bogus, IdentityProvider
// won't look at it until farther along in the authentication
// process. So this test can pass with a fake provId.
IDService.IDP._doAuthentication(_provId, idpParams);
}
var TESTS = [
test_smoke,
test_identity_request,
test_identity_auth,
];
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,242 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
Cu.import("resource://gre/modules/identity/IdentityProvider.jsm");
function check_provision_flow_done(provId) {
do_check_null(IdentityProvider._provisionFlows[provId]);
}
function test_begin_provisioning() {
do_test_pending();
setup_provisioning(
TEST_USER,
function(caller) {
// call .beginProvisioning()
IdentityProvider.beginProvisioning(caller);
}, function() {},
{
beginProvisioningCallback: function(email, duration_s) {
do_check_eq(email, TEST_USER);
do_check_true(duration_s > 0);
do_check_true(duration_s <= (24 * 3600));
do_test_finished();
run_next_test();
}
});
}
function test_raise_provisioning_failure() {
do_test_pending();
let _callerId = null;
setup_provisioning(
TEST_USER,
function(caller) {
// call .beginProvisioning()
_callerId = caller.id;
IdentityProvider.beginProvisioning(caller);
}, function(err) {
// this should be invoked with a populated error
do_check_neq(err, null);
do_check_true(err.indexOf("can't authenticate this email") > -1);
do_test_finished();
run_next_test();
},
{
beginProvisioningCallback: function(email, duration_s) {
// raise the failure as if we can't provision this email
IdentityProvider.raiseProvisioningFailure(_callerId, "can't authenticate this email");
}
});
}
function test_genkeypair_before_begin_provisioning() {
do_test_pending();
setup_provisioning(
TEST_USER,
function(caller) {
// call genKeyPair without beginProvisioning
IdentityProvider.genKeyPair(caller.id);
},
// expect this to be called with an error
function(err) {
do_check_neq(err, null);
do_test_finished();
run_next_test();
},
{
// this should not be called at all!
genKeyPairCallback: function(pk) {
// a test that will surely fail because we shouldn't be here.
do_check_true(false);
do_test_finished();
run_next_test();
}
}
);
}
function test_genkeypair() {
do_test_pending();
let _callerId = null;
setup_provisioning(
TEST_USER,
function(caller) {
_callerId = caller.id;
IdentityProvider.beginProvisioning(caller);
},
function(err) {
// should not be called!
do_check_true(false);
do_test_finished();
run_next_test();
},
{
beginProvisioningCallback: function(email, time_s) {
IdentityProvider.genKeyPair(_callerId);
},
genKeyPairCallback: function(kp) {
do_check_neq(kp, null);
// yay!
do_test_finished();
run_next_test();
}
}
);
}
// 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() {
do_test_pending();
let _callerID = null;
setup_provisioning(
TEST_USER,
function(caller) {
// do the right thing for beginProvisioning
_callerID = caller.id;
IdentityProvider.beginProvisioning(caller);
},
// expect this to be called with an error
function(err) {
do_check_neq(err, null);
do_test_finished();
run_next_test();
},
{
beginProvisioningCallback: function(email, duration_s) {
// now we try to register cert but no keygen has been done
IdentityProvider.registerCertificate(_callerID, "fake-cert");
}
}
);
}
function test_register_certificate() {
do_test_pending();
let _callerId = null;
setup_provisioning(
TEST_USER,
function(caller) {
_callerId = caller.id;
IdentityProvider.beginProvisioning(caller);
},
function(err) {
// we should be cool!
do_check_null(err);
// check that the cert is there
let identity = get_idstore().fetchIdentity(TEST_USER);
do_check_neq(identity, null);
do_check_eq(identity.cert, "fake-cert-42");
do_execute_soon(function check_done() {
// cleanup will happen after the callback is called
check_provision_flow_done(_callerId);
do_test_finished();
run_next_test();
});
},
{
beginProvisioningCallback: function(email, duration_s) {
IdentityProvider.genKeyPair(_callerId);
},
genKeyPairCallback: function(pk) {
IdentityProvider.registerCertificate(_callerId, "fake-cert-42");
}
}
);
}
function test_get_assertion_after_provision() {
do_test_pending();
let _callerId = null;
setup_provisioning(
TEST_USER,
function(caller) {
_callerId = caller.id;
IdentityProvider.beginProvisioning(caller);
},
function(err) {
// we should be cool!
do_check_null(err);
// check that the cert is there
let identity = get_idstore().fetchIdentity(TEST_USER);
do_check_neq(identity, null);
do_check_eq(identity.cert, "fake-cert-42");
do_execute_soon(function check_done() {
// cleanup will happen after the callback is called
check_provision_flow_done(_callerId);
do_test_finished();
run_next_test();
});
},
{
beginProvisioningCallback: function(email, duration_s) {
IdentityProvider.genKeyPair(_callerId);
},
genKeyPairCallback: function(pk) {
IdentityProvider.registerCertificate(_callerId, "fake-cert-42");
}
}
);
}
var TESTS = [];
TESTS.push(test_begin_provisioning);
TESTS.push(test_raise_provisioning_failure);
TESTS.push(test_genkeypair_before_begin_provisioning);
TESTS.push(test_genkeypair);
TESTS.push(test_register_certificate_before_genkeypair);
TESTS.push(test_register_certificate);
TESTS.push(test_get_assertion_after_provision);
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,255 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "RelyingParty",
"resource://gre/modules/identity/RelyingParty.jsm");
function resetState() {
get_idstore().reset();
RelyingParty.reset();
}
function test_watch_loggedin_ready() {
do_test_pending();
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);
RelyingParty.watch(mock_doc(id, TEST_URL, function(action, params) {
do_check_eq(action, 'ready');
do_check_eq(params, undefined);
do_test_finished();
run_next_test();
}));
});
}
function test_watch_loggedin_login() {
do_test_pending();
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
RelyingParty.watch(mock_doc(null, TEST_URL, call_sequentially(
function(action, params) {
do_check_eq(action, 'login');
do_check_neq(params, null);
},
function(action, params) {
do_check_eq(action, 'ready');
do_check_null(params);
do_test_finished();
run_next_test();
}
)));
});
}
function test_watch_loggedin_logout() {
do_test_pending();
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
RelyingParty.watch(mock_doc(other_id, TEST_URL, call_sequentially(
function(action, params) {
do_check_eq(action, 'login');
do_check_neq(params, null);
},
function(action, params) {
do_check_eq(action, 'ready');
do_check_null(params);
do_test_finished();
run_next_test();
}
)));
});
});
}
function test_watch_notloggedin_ready() {
do_test_pending();
resetState();
RelyingParty.watch(mock_doc(null, TEST_URL, function(action, params) {
do_check_eq(action, 'ready');
do_check_eq(params, undefined);
do_test_finished();
run_next_test();
}));
}
function test_watch_notloggedin_logout() {
do_test_pending();
resetState();
RelyingParty.watch(mock_doc(TEST_USER, TEST_URL, call_sequentially(
function(action, params) {
do_check_eq(action, 'logout');
do_check_eq(params, undefined);
let store = get_idstore();
do_check_null(store.getLoginState(TEST_URL));
},
function(action, params) {
do_check_eq(action, 'ready');
do_check_eq(params, undefined);
do_test_finished();
run_next_test();
}
)));
}
function test_request() {
do_test_pending();
// set up a watch, to be consistent
let mockedDoc = mock_doc(null, TEST_URL, function(action, params) {
// this isn't going to be called for now
// XXX but it is called - is that bad?
});
RelyingParty.watch(mockedDoc);
// be ready for the UX identity-request notification
makeObserver("identity-request", function (aSubject, aTopic, aData) {
do_check_neq(aSubject, null);
do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
do_test_finished();
run_next_test();
});
RelyingParty.request(mockedDoc.id, {});
}
/*
* ensure the forceAuthentication param can be passed through
*/
function test_request_forceAuthentication() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL, function(action, params) {});
RelyingParty.watch(mockedDoc);
makeObserver("identity-request", function(aSubject, aTopic, aData) {
do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
do_check_eq(aSubject.wrappedJSObject.forceAuthentication, true);
do_test_finished();
run_next_test();
});
RelyingParty.request(mockedDoc.id, {forceAuthentication: true});
}
/*
* ensure the issuer can be forced
*/
function test_request_forceIssuer() {
do_test_pending();
let mockedDoc = mock_doc(null, TEST_URL, function(action, params) {});
RelyingParty.watch(mockedDoc);
makeObserver("identity-request", function(aSubject, aTopic, aData) {
do_check_eq(aSubject.wrappedJSObject.rpId, mockedDoc.id);
do_check_eq(aSubject.wrappedJSObject.issuer, "https://ozten.co.uk");
do_test_finished();
run_next_test();
});
RelyingParty.request(mockedDoc.id, {issuer: "https://ozten.co.uk"});
}
function test_logout() {
do_test_pending();
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_doc(id, TEST_URL, call_sequentially(
function(action, params) {
do_check_eq(action, 'ready');
do_check_eq(params, undefined);
do_timeout(100, doLogout);
},
function(action, params) {
do_check_eq(action, 'logout');
do_check_eq(params, undefined);
},
function(action, params) {
do_check_eq(action, 'ready');
do_check_eq(params, undefined);
do_test_finished();
run_next_test();
}));
doLogout = function() {
RelyingParty.logout(mockedDoc.id);
do_check_false(store.getLoginState(TEST_URL).isLoggedIn);
do_check_eq(store.getLoginState(TEST_URL).email, TEST_USER);
};
RelyingParty.watch(mockedDoc);
});
}
var TESTS = [
test_watch_loggedin_ready,
test_watch_loggedin_login,
test_watch_loggedin_logout,
test_watch_notloggedin_ready,
test_watch_notloggedin_logout,
test_request,
test_request_forceAuthentication,
test_request_forceIssuer,
test_logout,
];
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,64 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "IDService",
"resource://gre/modules/identity/Identity.jsm",
"IdentityService");
function test_id_store() {
// XXX - this is ugly, peaking in like this into IDService
// probably should instantiate our own.
var store = get_idstore();
// try adding an identity
store.addIdentity(TEST_USER, TEST_PRIVKEY, TEST_CERT);
do_check_neq(store.getIdentities()[TEST_USER], null);
do_check_eq(store.getIdentities()[TEST_USER].cert, TEST_CERT);
// does fetch identity work?
do_check_neq(store.fetchIdentity(TEST_USER), null);
do_check_eq(store.fetchIdentity(TEST_USER).cert, TEST_CERT);
// clear the cert should keep the identity but not the cert
store.clearCert(TEST_USER);
do_check_neq(store.getIdentities()[TEST_USER], null);
do_check_null(store.getIdentities()[TEST_USER].cert);
// remove it should remove everything
store.removeIdentity(TEST_USER);
do_check_eq(store.getIdentities()[TEST_USER], undefined);
// act like we're logged in to TEST_URL
store.setLoginState(TEST_URL, true, TEST_USER);
do_check_neq(store.getLoginState(TEST_URL), null);
do_check_true(store.getLoginState(TEST_URL).isLoggedIn);
do_check_eq(store.getLoginState(TEST_URL).email, TEST_USER);
// log out
store.setLoginState(TEST_URL, false, TEST_USER);
do_check_neq(store.getLoginState(TEST_URL), null);
do_check_false(store.getLoginState(TEST_URL).isLoggedIn);
// email is still set
do_check_eq(store.getLoginState(TEST_URL).email, TEST_USER);
// not logged into other site
do_check_null(store.getLoginState(TEST_URL2));
// clear login state
store.clearLoginState(TEST_URL);
do_check_null(store.getLoginState(TEST_URL));
do_check_null(store.getLoginState(TEST_URL2));
run_next_test();
}
var TESTS = [test_id_store, ];
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,90 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "IDService",
"resource://gre/modules/identity/Identity.jsm",
"IdentityService");
const WELL_KNOWN_PATH = "/.well-known/browserid";
var SERVER_PORT = 8080;
// valid IDP
function test_well_known_1() {
do_test_pending();
let server = new HttpServer();
server.registerFile(WELL_KNOWN_PATH, do_get_file("data/idp_1" + WELL_KNOWN_PATH));
server.start(SERVER_PORT);
let hostPort = "localhost:" + SERVER_PORT;
function check_well_known(aErr, aCallbackObj) {
do_check_null(aErr);
do_check_eq(aCallbackObj.domain, hostPort);
let idpParams = aCallbackObj.idpParams;
do_check_eq(idpParams['public-key'].algorithm, "RS");
do_check_eq(idpParams.authentication, "/browserid/sign_in.html");
do_check_eq(idpParams.provisioning, "/browserid/provision.html");
do_test_finished();
server.stop(run_next_test);
}
IDService._fetchWellKnownFile(hostPort, check_well_known, "http");
}
// valid domain, non-exixtent browserid file
function test_well_known_404() {
do_test_pending();
let server = new HttpServer();
// Don't register the well-known file
// Change ports to avoid HTTP caching
SERVER_PORT++;
server.start(SERVER_PORT);
let hostPort = "localhost:" + SERVER_PORT;
function check_well_known_404(aErr, aCallbackObj) {
do_check_eq("Error", aErr);
do_check_eq(undefined, aCallbackObj);
do_test_finished();
server.stop(run_next_test);
}
IDService._fetchWellKnownFile(hostPort, check_well_known_404, "http");
}
// valid domain, invalid browserid file (no "provisioning" member)
function test_well_known_invalid_1() {
do_test_pending();
let server = new HttpServer();
server.registerFile(WELL_KNOWN_PATH, do_get_file("data/idp_invalid_1" + WELL_KNOWN_PATH));
// Change ports to avoid HTTP caching
SERVER_PORT++;
server.start(SERVER_PORT);
let hostPort = "localhost:" + SERVER_PORT;
function check_well_known_invalid_1(aErr, aCallbackObj) {
// check for an error message
do_check_true(aErr && aErr.length > 0);
do_check_eq(undefined, aCallbackObj);
do_test_finished();
server.stop(run_next_test);
}
IDService._fetchWellKnownFile(hostPort, check_well_known_invalid_1, "http");
}
var TESTS = [test_well_known_1, test_well_known_404, test_well_known_invalid_1];
TESTS.forEach(add_test);
function run_test() {
run_next_test();
}

View file

@ -0,0 +1,24 @@
[DEFAULT]
head = head_identity.js
tail = tail_identity.js
skip-if = (appname != "b2g" || toolkit == 'gonk')
support-files =
data/idp_1/.well-known/browserid
data/idp_invalid_1/.well-known/browserid
# Test load modules first so syntax failures are caught early.
[test_load_modules.js]
[test_minimalidentity.js]
[test_firefox_accounts.js]
[test_identity_utils.js]
[test_log_utils.js]
[test_authentication.js]
[test_crypto_service.js]
[test_identity.js]
[test_jwcrypto.js]
[test_observer_topics.js]
[test_provisioning.js]
[test_relying_party.js]
[test_store.js]
[test_well-known.js]