mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-29 10:37:33 +09:00
Remove various FxA tests
This commit is contained in:
parent
1b9b6e7e75
commit
ccf32a6b79
14 changed files with 7 additions and 1522 deletions
|
|
@ -1,58 +0,0 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = [
|
|
||||||
"initializeIdentityWithTokenServerResponse",
|
|
||||||
];
|
|
||||||
|
|
||||||
var {utils: Cu} = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Log.jsm");
|
|
||||||
Cu.import("resource://services-sync/main.js");
|
|
||||||
Cu.import("resource://services-sync/browserid_identity.js");
|
|
||||||
Cu.import("resource://services-common/tokenserverclient.js");
|
|
||||||
Cu.import("resource://testing-common/services/common/logging.js");
|
|
||||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
|
||||||
|
|
||||||
// Create a new browserid_identity object and initialize it with a
|
|
||||||
// mocked TokenServerClient which always receives the specified response.
|
|
||||||
this.initializeIdentityWithTokenServerResponse = function(response) {
|
|
||||||
// First create a mock "request" object that well' hack into the token server.
|
|
||||||
// A log for it
|
|
||||||
let requestLog = Log.repository.getLogger("testing.mock-rest");
|
|
||||||
if (!requestLog.appenders.length) { // might as well see what it says :)
|
|
||||||
requestLog.addAppender(new Log.DumpAppender());
|
|
||||||
requestLog.level = Log.Level.Trace;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A mock request object.
|
|
||||||
function MockRESTRequest(url) {};
|
|
||||||
MockRESTRequest.prototype = {
|
|
||||||
_log: requestLog,
|
|
||||||
setHeader: function() {},
|
|
||||||
get: function(callback) {
|
|
||||||
this.response = response;
|
|
||||||
callback.call(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The mocked TokenServer client which will get the response.
|
|
||||||
function MockTSC() { }
|
|
||||||
MockTSC.prototype = new TokenServerClient();
|
|
||||||
MockTSC.prototype.constructor = MockTSC;
|
|
||||||
MockTSC.prototype.newRESTRequest = function(url) {
|
|
||||||
return new MockRESTRequest(url);
|
|
||||||
}
|
|
||||||
// Arrange for the same observerPrefix as browserid_identity uses.
|
|
||||||
MockTSC.prototype.observerPrefix = "weave:service";
|
|
||||||
|
|
||||||
// tie it all together.
|
|
||||||
Weave.Status.__authManager = Weave.Service.identity = new BrowserIDManager();
|
|
||||||
Weave.Service._clusterManager = Weave.Service.identity.createClusterManager(Weave.Service);
|
|
||||||
let browseridManager = Weave.Service.identity;
|
|
||||||
// a sanity check
|
|
||||||
if (!(browseridManager instanceof BrowserIDManager)) {
|
|
||||||
throw new Error("sync isn't configured for browserid_identity");
|
|
||||||
}
|
|
||||||
let mockTSC = new MockTSC()
|
|
||||||
configureFxAccountIdentity(browseridManager);
|
|
||||||
browseridManager._tokenServerClient = mockTSC;
|
|
||||||
}
|
|
||||||
|
|
@ -28,8 +28,6 @@ Cu.import("resource://services-sync/util.js");
|
||||||
Cu.import("resource://services-sync/browserid_identity.js");
|
Cu.import("resource://services-sync/browserid_identity.js");
|
||||||
Cu.import("resource://testing-common/services/common/logging.js");
|
Cu.import("resource://testing-common/services/common/logging.js");
|
||||||
Cu.import("resource://testing-common/services/sync/fakeservices.js");
|
Cu.import("resource://testing-common/services/sync/fakeservices.js");
|
||||||
Cu.import("resource://gre/modules/FxAccounts.jsm");
|
|
||||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
|
||||||
Cu.import("resource://gre/modules/Promise.jsm");
|
Cu.import("resource://gre/modules/Promise.jsm");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -77,27 +75,8 @@ this.setBasicCredentials =
|
||||||
this.makeIdentityConfig = function(overrides) {
|
this.makeIdentityConfig = function(overrides) {
|
||||||
// first setup the defaults.
|
// first setup the defaults.
|
||||||
let result = {
|
let result = {
|
||||||
// Username used in both fxaccount and sync identity configs.
|
// Username used in sync identity config.
|
||||||
username: "foo",
|
username: "foo",
|
||||||
// fxaccount specific credentials.
|
|
||||||
fxaccount: {
|
|
||||||
user: {
|
|
||||||
assertion: 'assertion',
|
|
||||||
email: 'email',
|
|
||||||
kA: 'kA',
|
|
||||||
kB: 'kB',
|
|
||||||
sessionToken: 'sessionToken',
|
|
||||||
uid: 'user_uid',
|
|
||||||
verified: true,
|
|
||||||
},
|
|
||||||
token: {
|
|
||||||
endpoint: Svc.Prefs.get("tokenServerURI"),
|
|
||||||
duration: 300,
|
|
||||||
id: "id",
|
|
||||||
key: "key",
|
|
||||||
// uid will be set to the username.
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sync: {
|
sync: {
|
||||||
// username will come from the top-level username
|
// username will come from the top-level username
|
||||||
password: "whatever",
|
password: "whatever",
|
||||||
|
|
@ -114,64 +93,15 @@ this.makeIdentityConfig = function(overrides) {
|
||||||
// TODO: allow just some attributes to be specified
|
// TODO: allow just some attributes to be specified
|
||||||
result.sync = overrides.sync;
|
result.sync = overrides.sync;
|
||||||
}
|
}
|
||||||
if (overrides.fxaccount) {
|
|
||||||
// TODO: allow just some attributes to be specified
|
|
||||||
result.fxaccount = overrides.fxaccount;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure an instance of an FxAccount identity provider with the specified
|
|
||||||
// config (or the default config if not specified).
|
|
||||||
this.configureFxAccountIdentity = function(authService,
|
|
||||||
config = makeIdentityConfig()) {
|
|
||||||
let MockInternal = {};
|
|
||||||
let fxa = new FxAccounts(MockInternal);
|
|
||||||
|
|
||||||
// until we get better test infrastructure for bid_identity, we set the
|
|
||||||
// signedin user's "email" to the username, simply as many tests rely on this.
|
|
||||||
config.fxaccount.user.email = config.username;
|
|
||||||
fxa.internal.currentAccountState.signedInUser = {
|
|
||||||
version: DATA_FORMAT_VERSION,
|
|
||||||
accountData: config.fxaccount.user
|
|
||||||
};
|
|
||||||
fxa.internal.currentAccountState.getCertificate = function(data, keyPair, mustBeValidUntil) {
|
|
||||||
this.cert = {
|
|
||||||
validUntil: fxa.internal.now() + CERT_LIFETIME,
|
|
||||||
cert: "certificate",
|
|
||||||
};
|
|
||||||
return Promise.resolve(this.cert.cert);
|
|
||||||
};
|
|
||||||
|
|
||||||
let mockTSC = { // TokenServerClient
|
|
||||||
getTokenFromBrowserIDAssertion: function(uri, assertion, cb) {
|
|
||||||
config.fxaccount.token.uid = config.username;
|
|
||||||
cb(null, config.fxaccount.token);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
authService._fxaService = fxa;
|
|
||||||
authService._tokenServerClient = mockTSC;
|
|
||||||
// Set the "account" of the browserId manager to be the "email" of the
|
|
||||||
// logged in user of the mockFXA service.
|
|
||||||
authService._signedInUser = fxa.internal.currentAccountState.signedInUser.accountData;
|
|
||||||
authService._account = config.fxaccount.user.email;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.configureIdentity = function(identityOverrides) {
|
this.configureIdentity = function(identityOverrides) {
|
||||||
let config = makeIdentityConfig(identityOverrides);
|
let config = makeIdentityConfig(identityOverrides);
|
||||||
let ns = {};
|
let ns = {};
|
||||||
Cu.import("resource://services-sync/service.js", ns);
|
Cu.import("resource://services-sync/service.js", ns);
|
||||||
|
|
||||||
if (ns.Service.identity instanceof BrowserIDManager) {
|
|
||||||
// do the FxAccounts thang...
|
|
||||||
configureFxAccountIdentity(ns.Service.identity, config);
|
|
||||||
return ns.Service.identity.initializeWithCurrentIdentity().then(() => {
|
|
||||||
// need to wait until this identity manager is readyToAuthenticate.
|
|
||||||
return ns.Service.identity.whenReadyToAuthenticate.promise;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// old style identity provider.
|
|
||||||
setBasicCredentials(config.username, config.sync.password, config.sync.syncKey);
|
setBasicCredentials(config.username, config.sync.password, config.sync.syncKey);
|
||||||
let deferred = Promise.defer();
|
let deferred = Promise.defer();
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
|
|
@ -184,7 +114,6 @@ this.SyncTestingInfrastructure = function (server, username, password, syncKey)
|
||||||
|
|
||||||
ensureLegacyIdentityManager();
|
ensureLegacyIdentityManager();
|
||||||
let config = makeIdentityConfig();
|
let config = makeIdentityConfig();
|
||||||
// XXX - hacks for the sync identity provider.
|
|
||||||
if (username)
|
if (username)
|
||||||
config.username = username;
|
config.username = username;
|
||||||
if (password)
|
if (password)
|
||||||
|
|
@ -223,10 +152,10 @@ this.encryptPayload = function encryptPayload(cleartext) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This helper can be used instead of 'add_test' or 'add_task' to run the
|
// This helper was used instead of 'add_test' or 'add_task' to run the
|
||||||
// specified test function twice - once with the old-style sync identity
|
// specified test function twice - once with the old-style sync identity
|
||||||
// manager and once with the new-style BrowserID identity manager, to ensure
|
// manager and once with the new-style BrowserID identity manager, to ensure
|
||||||
// it works in both cases.
|
// it worked in both cases. Currently it's equal to just one. XXX: cleanup?
|
||||||
//
|
//
|
||||||
// * The test itself should be passed as 'test' - ie, test code will generally
|
// * The test itself should be passed as 'test' - ie, test code will generally
|
||||||
// pass |this|.
|
// pass |this|.
|
||||||
|
|
@ -248,12 +177,4 @@ this.add_identity_test = function(test, testFunction) {
|
||||||
yield testFunction();
|
yield testFunction();
|
||||||
Status.__authManager = ns.Service.identity = oldIdentity;
|
Status.__authManager = ns.Service.identity = oldIdentity;
|
||||||
});
|
});
|
||||||
// another task for the FxAccounts identity manager.
|
|
||||||
test.add_task(function() {
|
|
||||||
note("FxAccounts");
|
|
||||||
let oldIdentity = Status._authManager;
|
|
||||||
Status.__authManager = ns.Service.identity = new BrowserIDManager();
|
|
||||||
yield testFunction();
|
|
||||||
Status.__authManager = ns.Service.identity = oldIdentity;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,6 @@ EXTRA_JS_MODULES['services-sync'].stages += [
|
||||||
|
|
||||||
TESTING_JS_MODULES.services.sync += [
|
TESTING_JS_MODULES.services.sync += [
|
||||||
'modules-testing/fakeservices.js',
|
'modules-testing/fakeservices.js',
|
||||||
'modules-testing/fxa_utils.js',
|
|
||||||
'modules-testing/rotaryengine.js',
|
'modules-testing/rotaryengine.js',
|
||||||
'modules-testing/utils.js',
|
'modules-testing/utils.js',
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,682 +0,0 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
|
||||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/FxAccounts.jsm");
|
|
||||||
Cu.import("resource://services-sync/browserid_identity.js");
|
|
||||||
Cu.import("resource://services-sync/rest.js");
|
|
||||||
Cu.import("resource://services-sync/util.js");
|
|
||||||
Cu.import("resource://services-common/utils.js");
|
|
||||||
Cu.import("resource://services-crypto/utils.js");
|
|
||||||
Cu.import("resource://testing-common/services/sync/utils.js");
|
|
||||||
Cu.import("resource://testing-common/services/sync/fxa_utils.js");
|
|
||||||
Cu.import("resource://services-common/hawkclient.js");
|
|
||||||
Cu.import("resource://gre/modules/FxAccounts.jsm");
|
|
||||||
Cu.import("resource://gre/modules/FxAccountsClient.jsm");
|
|
||||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
|
||||||
Cu.import("resource://services-sync/service.js");
|
|
||||||
Cu.import("resource://services-sync/status.js");
|
|
||||||
Cu.import("resource://services-sync/constants.js");
|
|
||||||
|
|
||||||
const SECOND_MS = 1000;
|
|
||||||
const MINUTE_MS = SECOND_MS * 60;
|
|
||||||
const HOUR_MS = MINUTE_MS * 60;
|
|
||||||
|
|
||||||
let identityConfig = makeIdentityConfig();
|
|
||||||
let browseridManager = new BrowserIDManager();
|
|
||||||
configureFxAccountIdentity(browseridManager, identityConfig);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mock client clock and skew vs server in FxAccounts signed-in user module and
|
|
||||||
* API client. browserid_identity.js queries these values to construct HAWK
|
|
||||||
* headers. We will use this to test clock skew compensation in these headers
|
|
||||||
* below.
|
|
||||||
*/
|
|
||||||
let MockFxAccountsClient = function() {
|
|
||||||
FxAccountsClient.apply(this);
|
|
||||||
};
|
|
||||||
MockFxAccountsClient.prototype = {
|
|
||||||
__proto__: FxAccountsClient.prototype
|
|
||||||
};
|
|
||||||
|
|
||||||
function MockFxAccounts() {
|
|
||||||
let fxa = new FxAccounts({
|
|
||||||
_now_is: Date.now(),
|
|
||||||
|
|
||||||
now: function () {
|
|
||||||
return this._now_is;
|
|
||||||
},
|
|
||||||
|
|
||||||
fxAccountsClient: new MockFxAccountsClient()
|
|
||||||
});
|
|
||||||
fxa.internal.currentAccountState.getCertificate = function(data, keyPair, mustBeValidUntil) {
|
|
||||||
this.cert = {
|
|
||||||
validUntil: fxa.internal.now() + CERT_LIFETIME,
|
|
||||||
cert: "certificate",
|
|
||||||
};
|
|
||||||
return Promise.resolve(this.cert.cert);
|
|
||||||
};
|
|
||||||
return fxa;
|
|
||||||
}
|
|
||||||
|
|
||||||
function run_test() {
|
|
||||||
initTestLogging("Trace");
|
|
||||||
Log.repository.getLogger("Sync.Identity").level = Log.Level.Trace;
|
|
||||||
Log.repository.getLogger("Sync.BrowserIDManager").level = Log.Level.Trace;
|
|
||||||
run_next_test();
|
|
||||||
};
|
|
||||||
|
|
||||||
add_test(function test_initial_state() {
|
|
||||||
_("Verify initial state");
|
|
||||||
do_check_false(!!browseridManager._token);
|
|
||||||
do_check_false(browseridManager.hasValidToken());
|
|
||||||
run_next_test();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
add_task(function test_initialializeWithCurrentIdentity() {
|
|
||||||
_("Verify start after initializeWithCurrentIdentity");
|
|
||||||
browseridManager.initializeWithCurrentIdentity();
|
|
||||||
yield browseridManager.whenReadyToAuthenticate.promise;
|
|
||||||
do_check_true(!!browseridManager._token);
|
|
||||||
do_check_true(browseridManager.hasValidToken());
|
|
||||||
do_check_eq(browseridManager.account, identityConfig.fxaccount.user.email);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
add_task(function test_initialializeWithNoKeys() {
|
|
||||||
_("Verify start after initializeWithCurrentIdentity without kA, kB or keyFetchToken");
|
|
||||||
let identityConfig = makeIdentityConfig();
|
|
||||||
delete identityConfig.fxaccount.user.kA;
|
|
||||||
delete identityConfig.fxaccount.user.kB;
|
|
||||||
// there's no keyFetchToken by default, so the initialize should fail.
|
|
||||||
configureFxAccountIdentity(browseridManager, identityConfig);
|
|
||||||
|
|
||||||
yield browseridManager.initializeWithCurrentIdentity();
|
|
||||||
yield browseridManager.whenReadyToAuthenticate.promise;
|
|
||||||
do_check_eq(Status.login, LOGIN_SUCCEEDED, "login succeeded even without keys");
|
|
||||||
do_check_false(browseridManager._canFetchKeys(), "_canFetchKeys reflects lack of keys");
|
|
||||||
do_check_eq(browseridManager._token, null, "we don't have a token");
|
|
||||||
});
|
|
||||||
|
|
||||||
add_test(function test_getResourceAuthenticator() {
|
|
||||||
_("BrowserIDManager supplies a Resource Authenticator callback which returns a Hawk header.");
|
|
||||||
configureFxAccountIdentity(browseridManager);
|
|
||||||
let authenticator = browseridManager.getResourceAuthenticator();
|
|
||||||
do_check_true(!!authenticator);
|
|
||||||
let req = {uri: CommonUtils.makeURI(
|
|
||||||
"https://example.net/somewhere/over/the/rainbow"),
|
|
||||||
method: 'GET'};
|
|
||||||
let output = authenticator(req, 'GET');
|
|
||||||
do_check_true('headers' in output);
|
|
||||||
do_check_true('authorization' in output.headers);
|
|
||||||
do_check_true(output.headers.authorization.startsWith('Hawk'));
|
|
||||||
_("Expected internal state after successful call.");
|
|
||||||
do_check_eq(browseridManager._token.uid, identityConfig.fxaccount.token.uid);
|
|
||||||
run_next_test();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
add_test(function test_getRESTRequestAuthenticator() {
|
|
||||||
_("BrowserIDManager supplies a REST Request Authenticator callback which sets a Hawk header on a request object.");
|
|
||||||
let request = new SyncStorageRequest(
|
|
||||||
"https://example.net/somewhere/over/the/rainbow");
|
|
||||||
let authenticator = browseridManager.getRESTRequestAuthenticator();
|
|
||||||
do_check_true(!!authenticator);
|
|
||||||
let output = authenticator(request, 'GET');
|
|
||||||
do_check_eq(request.uri, output.uri);
|
|
||||||
do_check_true(output._headers.authorization.startsWith('Hawk'));
|
|
||||||
do_check_true(output._headers.authorization.includes('nonce'));
|
|
||||||
do_check_true(browseridManager.hasValidToken());
|
|
||||||
run_next_test();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
add_test(function test_resourceAuthenticatorSkew() {
|
|
||||||
_("BrowserIDManager Resource Authenticator compensates for clock skew in Hawk header.");
|
|
||||||
|
|
||||||
// Clock is skewed 12 hours into the future
|
|
||||||
// We pick a date in the past so we don't risk concealing bugs in code that
|
|
||||||
// uses new Date() instead of our given date.
|
|
||||||
let now = new Date("Fri Apr 09 2004 00:00:00 GMT-0700").valueOf() + 12 * HOUR_MS;
|
|
||||||
let browseridManager = new BrowserIDManager();
|
|
||||||
let hawkClient = new HawkClient("https://example.net/v1", "/foo");
|
|
||||||
|
|
||||||
// mock fxa hawk client skew
|
|
||||||
hawkClient.now = function() {
|
|
||||||
dump("mocked client now: " + now + '\n');
|
|
||||||
return now;
|
|
||||||
}
|
|
||||||
// Imagine there's already been one fxa request and the hawk client has
|
|
||||||
// already detected skew vs the fxa auth server.
|
|
||||||
let localtimeOffsetMsec = -1 * 12 * HOUR_MS;
|
|
||||||
hawkClient._localtimeOffsetMsec = localtimeOffsetMsec;
|
|
||||||
|
|
||||||
let fxaClient = new MockFxAccountsClient();
|
|
||||||
fxaClient.hawk = hawkClient;
|
|
||||||
|
|
||||||
// Sanity check
|
|
||||||
do_check_eq(hawkClient.now(), now);
|
|
||||||
do_check_eq(hawkClient.localtimeOffsetMsec, localtimeOffsetMsec);
|
|
||||||
|
|
||||||
// Properly picked up by the client
|
|
||||||
do_check_eq(fxaClient.now(), now);
|
|
||||||
do_check_eq(fxaClient.localtimeOffsetMsec, localtimeOffsetMsec);
|
|
||||||
|
|
||||||
let fxa = new MockFxAccounts();
|
|
||||||
fxa.internal._now_is = now;
|
|
||||||
fxa.internal.fxAccountsClient = fxaClient;
|
|
||||||
|
|
||||||
// Picked up by the signed-in user module
|
|
||||||
do_check_eq(fxa.internal.now(), now);
|
|
||||||
do_check_eq(fxa.internal.localtimeOffsetMsec, localtimeOffsetMsec);
|
|
||||||
|
|
||||||
do_check_eq(fxa.now(), now);
|
|
||||||
do_check_eq(fxa.localtimeOffsetMsec, localtimeOffsetMsec);
|
|
||||||
|
|
||||||
// Mocks within mocks...
|
|
||||||
configureFxAccountIdentity(browseridManager, identityConfig);
|
|
||||||
|
|
||||||
// Ensure the new FxAccounts mock has a signed-in user.
|
|
||||||
fxa.internal.currentAccountState.signedInUser = browseridManager._fxaService.internal.currentAccountState.signedInUser;
|
|
||||||
|
|
||||||
browseridManager._fxaService = fxa;
|
|
||||||
|
|
||||||
do_check_eq(browseridManager._fxaService.internal.now(), now);
|
|
||||||
do_check_eq(browseridManager._fxaService.internal.localtimeOffsetMsec,
|
|
||||||
localtimeOffsetMsec);
|
|
||||||
|
|
||||||
do_check_eq(browseridManager._fxaService.now(), now);
|
|
||||||
do_check_eq(browseridManager._fxaService.localtimeOffsetMsec,
|
|
||||||
localtimeOffsetMsec);
|
|
||||||
|
|
||||||
let request = new SyncStorageRequest("https://example.net/i/like/pie/");
|
|
||||||
let authenticator = browseridManager.getResourceAuthenticator();
|
|
||||||
let output = authenticator(request, 'GET');
|
|
||||||
dump("output" + JSON.stringify(output));
|
|
||||||
let authHeader = output.headers.authorization;
|
|
||||||
do_check_true(authHeader.startsWith('Hawk'));
|
|
||||||
|
|
||||||
// Skew correction is applied in the header and we're within the two-minute
|
|
||||||
// window.
|
|
||||||
do_check_eq(getTimestamp(authHeader), now - 12 * HOUR_MS);
|
|
||||||
do_check_true(
|
|
||||||
(getTimestampDelta(authHeader, now) - 12 * HOUR_MS) < 2 * MINUTE_MS);
|
|
||||||
|
|
||||||
run_next_test();
|
|
||||||
});
|
|
||||||
|
|
||||||
add_test(function test_RESTResourceAuthenticatorSkew() {
|
|
||||||
_("BrowserIDManager REST Resource Authenticator compensates for clock skew in Hawk header.");
|
|
||||||
|
|
||||||
// Clock is skewed 12 hours into the future from our arbitary date
|
|
||||||
let now = new Date("Fri Apr 09 2004 00:00:00 GMT-0700").valueOf() + 12 * HOUR_MS;
|
|
||||||
let browseridManager = new BrowserIDManager();
|
|
||||||
let hawkClient = new HawkClient("https://example.net/v1", "/foo");
|
|
||||||
|
|
||||||
// mock fxa hawk client skew
|
|
||||||
hawkClient.now = function() {
|
|
||||||
return now;
|
|
||||||
}
|
|
||||||
// Imagine there's already been one fxa request and the hawk client has
|
|
||||||
// already detected skew vs the fxa auth server.
|
|
||||||
hawkClient._localtimeOffsetMsec = -1 * 12 * HOUR_MS;
|
|
||||||
|
|
||||||
let fxaClient = new MockFxAccountsClient();
|
|
||||||
fxaClient.hawk = hawkClient;
|
|
||||||
let fxa = new MockFxAccounts();
|
|
||||||
fxa.internal._now_is = now;
|
|
||||||
fxa.internal.fxAccountsClient = fxaClient;
|
|
||||||
|
|
||||||
configureFxAccountIdentity(browseridManager, identityConfig);
|
|
||||||
|
|
||||||
// Ensure the new FxAccounts mock has a signed-in user.
|
|
||||||
fxa.internal.currentAccountState.signedInUser = browseridManager._fxaService.internal.currentAccountState.signedInUser;
|
|
||||||
|
|
||||||
browseridManager._fxaService = fxa;
|
|
||||||
|
|
||||||
do_check_eq(browseridManager._fxaService.internal.now(), now);
|
|
||||||
|
|
||||||
let request = new SyncStorageRequest("https://example.net/i/like/pie/");
|
|
||||||
let authenticator = browseridManager.getResourceAuthenticator();
|
|
||||||
let output = authenticator(request, 'GET');
|
|
||||||
dump("output" + JSON.stringify(output));
|
|
||||||
let authHeader = output.headers.authorization;
|
|
||||||
do_check_true(authHeader.startsWith('Hawk'));
|
|
||||||
|
|
||||||
// Skew correction is applied in the header and we're within the two-minute
|
|
||||||
// window.
|
|
||||||
do_check_eq(getTimestamp(authHeader), now - 12 * HOUR_MS);
|
|
||||||
do_check_true(
|
|
||||||
(getTimestampDelta(authHeader, now) - 12 * HOUR_MS) < 2 * MINUTE_MS);
|
|
||||||
|
|
||||||
run_next_test();
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(function test_ensureLoggedIn() {
|
|
||||||
configureFxAccountIdentity(browseridManager);
|
|
||||||
yield browseridManager.initializeWithCurrentIdentity();
|
|
||||||
yield browseridManager.whenReadyToAuthenticate.promise;
|
|
||||||
Assert.equal(Status.login, LOGIN_SUCCEEDED, "original initialize worked");
|
|
||||||
yield browseridManager.ensureLoggedIn();
|
|
||||||
Assert.equal(Status.login, LOGIN_SUCCEEDED, "original ensureLoggedIn worked");
|
|
||||||
Assert.ok(browseridManager._shouldHaveSyncKeyBundle,
|
|
||||||
"_shouldHaveSyncKeyBundle should always be true after ensureLogin completes.");
|
|
||||||
|
|
||||||
// arrange for no logged in user.
|
|
||||||
let fxa = browseridManager._fxaService
|
|
||||||
let signedInUser = fxa.internal.currentAccountState.signedInUser;
|
|
||||||
fxa.internal.currentAccountState.signedInUser = null;
|
|
||||||
browseridManager.initializeWithCurrentIdentity();
|
|
||||||
Assert.ok(!browseridManager._shouldHaveSyncKeyBundle,
|
|
||||||
"_shouldHaveSyncKeyBundle should be false so we know we are testing what we think we are.");
|
|
||||||
Status.login = LOGIN_FAILED_NO_USERNAME;
|
|
||||||
yield Assert.rejects(browseridManager.ensureLoggedIn(), "expecting rejection due to no user");
|
|
||||||
Assert.ok(browseridManager._shouldHaveSyncKeyBundle,
|
|
||||||
"_shouldHaveSyncKeyBundle should always be true after ensureLogin completes.");
|
|
||||||
fxa.internal.currentAccountState.signedInUser = signedInUser;
|
|
||||||
Status.login = LOGIN_FAILED_LOGIN_REJECTED;
|
|
||||||
yield Assert.rejects(browseridManager.ensureLoggedIn(),
|
|
||||||
"LOGIN_FAILED_LOGIN_REJECTED should have caused immediate rejection");
|
|
||||||
Assert.equal(Status.login, LOGIN_FAILED_LOGIN_REJECTED,
|
|
||||||
"status should remain LOGIN_FAILED_LOGIN_REJECTED");
|
|
||||||
Status.login = LOGIN_FAILED_NETWORK_ERROR;
|
|
||||||
yield browseridManager.ensureLoggedIn();
|
|
||||||
Assert.equal(Status.login, LOGIN_SUCCEEDED, "final ensureLoggedIn worked");
|
|
||||||
});
|
|
||||||
|
|
||||||
add_test(function test_tokenExpiration() {
|
|
||||||
_("BrowserIDManager notices token expiration:");
|
|
||||||
let bimExp = new BrowserIDManager();
|
|
||||||
configureFxAccountIdentity(bimExp, identityConfig);
|
|
||||||
|
|
||||||
let authenticator = bimExp.getResourceAuthenticator();
|
|
||||||
do_check_true(!!authenticator);
|
|
||||||
let req = {uri: CommonUtils.makeURI(
|
|
||||||
"https://example.net/somewhere/over/the/rainbow"),
|
|
||||||
method: 'GET'};
|
|
||||||
authenticator(req, 'GET');
|
|
||||||
|
|
||||||
// Mock the clock.
|
|
||||||
_("Forcing the token to expire ...");
|
|
||||||
Object.defineProperty(bimExp, "_now", {
|
|
||||||
value: function customNow() {
|
|
||||||
return (Date.now() + 3000001);
|
|
||||||
},
|
|
||||||
writable: true,
|
|
||||||
});
|
|
||||||
do_check_true(bimExp._token.expiration < bimExp._now());
|
|
||||||
_("... means BrowserIDManager knows to re-fetch it on the next call.");
|
|
||||||
do_check_false(bimExp.hasValidToken());
|
|
||||||
run_next_test();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
add_test(function test_sha256() {
|
|
||||||
// Test vectors from http://www.bichlmeier.info/sha256test.html
|
|
||||||
let vectors = [
|
|
||||||
["",
|
|
||||||
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"],
|
|
||||||
["abc",
|
|
||||||
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"],
|
|
||||||
["message digest",
|
|
||||||
"f7846f55cf23e14eebeab5b4e1550cad5b509e3348fbc4efa3a1413d393cb650"],
|
|
||||||
["secure hash algorithm",
|
|
||||||
"f30ceb2bb2829e79e4ca9753d35a8ecc00262d164cc077080295381cbd643f0d"],
|
|
||||||
["SHA256 is considered to be safe",
|
|
||||||
"6819d915c73f4d1e77e4e1b52d1fa0f9cf9beaead3939f15874bd988e2a23630"],
|
|
||||||
["abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
|
|
||||||
"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"],
|
|
||||||
["For this sample, this 63-byte string will be used as input data",
|
|
||||||
"f08a78cbbaee082b052ae0708f32fa1e50c5c421aa772ba5dbb406a2ea6be342"],
|
|
||||||
["This is exactly 64 bytes long, not counting the terminating byte",
|
|
||||||
"ab64eff7e88e2e46165e29f2bce41826bd4c7b3552f6b382a9e7d3af47c245f8"]
|
|
||||||
];
|
|
||||||
let bidUser = new BrowserIDManager();
|
|
||||||
for (let [input,output] of vectors) {
|
|
||||||
do_check_eq(CommonUtils.bytesAsHex(bidUser._sha256(input)), output);
|
|
||||||
}
|
|
||||||
run_next_test();
|
|
||||||
});
|
|
||||||
|
|
||||||
add_test(function test_computeXClientStateHeader() {
|
|
||||||
let kBhex = "fd5c747806c07ce0b9d69dcfea144663e630b65ec4963596a22f24910d7dd15d";
|
|
||||||
let kB = CommonUtils.hexToBytes(kBhex);
|
|
||||||
|
|
||||||
let bidUser = new BrowserIDManager();
|
|
||||||
let header = bidUser._computeXClientState(kB);
|
|
||||||
|
|
||||||
do_check_eq(header, "6ae94683571c7a7c54dab4700aa3995f");
|
|
||||||
run_next_test();
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(function test_getTokenErrors() {
|
|
||||||
_("BrowserIDManager correctly handles various failures to get a token.");
|
|
||||||
|
|
||||||
_("Arrange for a 401 - Sync should reflect an auth error.");
|
|
||||||
initializeIdentityWithTokenServerResponse({
|
|
||||||
status: 401,
|
|
||||||
headers: {"content-type": "application/json"},
|
|
||||||
body: JSON.stringify({}),
|
|
||||||
});
|
|
||||||
let browseridManager = Service.identity;
|
|
||||||
|
|
||||||
yield browseridManager.initializeWithCurrentIdentity();
|
|
||||||
yield Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
|
||||||
"should reject due to 401");
|
|
||||||
Assert.equal(Status.login, LOGIN_FAILED_LOGIN_REJECTED, "login was rejected");
|
|
||||||
|
|
||||||
// XXX - other interesting responses to return?
|
|
||||||
|
|
||||||
// And for good measure, some totally "unexpected" errors - we generally
|
|
||||||
// assume these problems are going to magically go away at some point.
|
|
||||||
_("Arrange for an empty body with a 200 response - should reflect a network error.");
|
|
||||||
initializeIdentityWithTokenServerResponse({
|
|
||||||
status: 200,
|
|
||||||
headers: [],
|
|
||||||
body: "",
|
|
||||||
});
|
|
||||||
browseridManager = Service.identity;
|
|
||||||
yield browseridManager.initializeWithCurrentIdentity();
|
|
||||||
yield Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
|
||||||
"should reject due to non-JSON response");
|
|
||||||
Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "login state is LOGIN_FAILED_NETWORK_ERROR");
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(function test_getTokenErrorWithRetry() {
|
|
||||||
_("tokenserver sends an observer notification on various backoff headers.");
|
|
||||||
|
|
||||||
// Set Sync's backoffInterval to zero - after we simulated the backoff header
|
|
||||||
// it should reflect the value we sent.
|
|
||||||
Status.backoffInterval = 0;
|
|
||||||
_("Arrange for a 503 with a Retry-After header.");
|
|
||||||
initializeIdentityWithTokenServerResponse({
|
|
||||||
status: 503,
|
|
||||||
headers: {"content-type": "application/json",
|
|
||||||
"retry-after": "100"},
|
|
||||||
body: JSON.stringify({}),
|
|
||||||
});
|
|
||||||
let browseridManager = Service.identity;
|
|
||||||
|
|
||||||
yield browseridManager.initializeWithCurrentIdentity();
|
|
||||||
yield Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
|
||||||
"should reject due to 503");
|
|
||||||
|
|
||||||
// The observer should have fired - check it got the value in the response.
|
|
||||||
Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "login was rejected");
|
|
||||||
// Sync will have the value in ms with some slop - so check it is at least that.
|
|
||||||
Assert.ok(Status.backoffInterval >= 100000);
|
|
||||||
|
|
||||||
_("Arrange for a 200 with an X-Backoff header.");
|
|
||||||
Status.backoffInterval = 0;
|
|
||||||
initializeIdentityWithTokenServerResponse({
|
|
||||||
status: 503,
|
|
||||||
headers: {"content-type": "application/json",
|
|
||||||
"x-backoff": "200"},
|
|
||||||
body: JSON.stringify({}),
|
|
||||||
});
|
|
||||||
browseridManager = Service.identity;
|
|
||||||
|
|
||||||
yield browseridManager.initializeWithCurrentIdentity();
|
|
||||||
yield Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
|
||||||
"should reject due to no token in response");
|
|
||||||
|
|
||||||
// The observer should have fired - check it got the value in the response.
|
|
||||||
Assert.ok(Status.backoffInterval >= 200000);
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(function test_getKeysErrorWithBackoff() {
|
|
||||||
_("Auth server (via hawk) sends an observer notification on backoff headers.");
|
|
||||||
|
|
||||||
// Set Sync's backoffInterval to zero - after we simulated the backoff header
|
|
||||||
// it should reflect the value we sent.
|
|
||||||
Status.backoffInterval = 0;
|
|
||||||
_("Arrange for a 503 with a X-Backoff header.");
|
|
||||||
|
|
||||||
let config = makeIdentityConfig();
|
|
||||||
// We want no kA or kB so we attempt to fetch them.
|
|
||||||
delete config.fxaccount.user.kA;
|
|
||||||
delete config.fxaccount.user.kB;
|
|
||||||
config.fxaccount.user.keyFetchToken = "keyfetchtoken";
|
|
||||||
yield initializeIdentityWithHAWKResponseFactory(config, function(method, data, uri) {
|
|
||||||
Assert.equal(method, "get");
|
|
||||||
Assert.equal(uri, "http://mockedserver:9999/account/keys")
|
|
||||||
return {
|
|
||||||
status: 503,
|
|
||||||
headers: {"content-type": "application/json",
|
|
||||||
"x-backoff": "100"},
|
|
||||||
body: "{}",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let browseridManager = Service.identity;
|
|
||||||
yield Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
|
||||||
"should reject due to 503");
|
|
||||||
|
|
||||||
// The observer should have fired - check it got the value in the response.
|
|
||||||
Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "login was rejected");
|
|
||||||
// Sync will have the value in ms with some slop - so check it is at least that.
|
|
||||||
Assert.ok(Status.backoffInterval >= 100000);
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(function test_getKeysErrorWithRetry() {
|
|
||||||
_("Auth server (via hawk) sends an observer notification on retry headers.");
|
|
||||||
|
|
||||||
// Set Sync's backoffInterval to zero - after we simulated the backoff header
|
|
||||||
// it should reflect the value we sent.
|
|
||||||
Status.backoffInterval = 0;
|
|
||||||
_("Arrange for a 503 with a Retry-After header.");
|
|
||||||
|
|
||||||
let config = makeIdentityConfig();
|
|
||||||
// We want no kA or kB so we attempt to fetch them.
|
|
||||||
delete config.fxaccount.user.kA;
|
|
||||||
delete config.fxaccount.user.kB;
|
|
||||||
config.fxaccount.user.keyFetchToken = "keyfetchtoken";
|
|
||||||
yield initializeIdentityWithHAWKResponseFactory(config, function(method, data, uri) {
|
|
||||||
Assert.equal(method, "get");
|
|
||||||
Assert.equal(uri, "http://mockedserver:9999/account/keys")
|
|
||||||
return {
|
|
||||||
status: 503,
|
|
||||||
headers: {"content-type": "application/json",
|
|
||||||
"retry-after": "100"},
|
|
||||||
body: "{}",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let browseridManager = Service.identity;
|
|
||||||
yield Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
|
||||||
"should reject due to 503");
|
|
||||||
|
|
||||||
// The observer should have fired - check it got the value in the response.
|
|
||||||
Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "login was rejected");
|
|
||||||
// Sync will have the value in ms with some slop - so check it is at least that.
|
|
||||||
Assert.ok(Status.backoffInterval >= 100000);
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(function test_getHAWKErrors() {
|
|
||||||
_("BrowserIDManager correctly handles various HAWK failures.");
|
|
||||||
|
|
||||||
_("Arrange for a 401 - Sync should reflect an auth error.");
|
|
||||||
let config = makeIdentityConfig();
|
|
||||||
yield initializeIdentityWithHAWKResponseFactory(config, function(method, data, uri) {
|
|
||||||
Assert.equal(method, "post");
|
|
||||||
Assert.equal(uri, "http://mockedserver:9999/certificate/sign")
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
headers: {"content-type": "application/json"},
|
|
||||||
body: JSON.stringify({}),
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Assert.equal(Status.login, LOGIN_FAILED_LOGIN_REJECTED, "login was rejected");
|
|
||||||
|
|
||||||
// XXX - other interesting responses to return?
|
|
||||||
|
|
||||||
// And for good measure, some totally "unexpected" errors - we generally
|
|
||||||
// assume these problems are going to magically go away at some point.
|
|
||||||
_("Arrange for an empty body with a 200 response - should reflect a network error.");
|
|
||||||
yield initializeIdentityWithHAWKResponseFactory(config, function(method, data, uri) {
|
|
||||||
Assert.equal(method, "post");
|
|
||||||
Assert.equal(uri, "http://mockedserver:9999/certificate/sign")
|
|
||||||
return {
|
|
||||||
status: 200,
|
|
||||||
headers: [],
|
|
||||||
body: "",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "login state is LOGIN_FAILED_NETWORK_ERROR");
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(function test_getGetKeysFailing401() {
|
|
||||||
_("BrowserIDManager correctly handles 401 responses fetching keys.");
|
|
||||||
|
|
||||||
_("Arrange for a 401 - Sync should reflect an auth error.");
|
|
||||||
let config = makeIdentityConfig();
|
|
||||||
// We want no kA or kB so we attempt to fetch them.
|
|
||||||
delete config.fxaccount.user.kA;
|
|
||||||
delete config.fxaccount.user.kB;
|
|
||||||
config.fxaccount.user.keyFetchToken = "keyfetchtoken";
|
|
||||||
yield initializeIdentityWithHAWKResponseFactory(config, function(method, data, uri) {
|
|
||||||
Assert.equal(method, "get");
|
|
||||||
Assert.equal(uri, "http://mockedserver:9999/account/keys")
|
|
||||||
return {
|
|
||||||
status: 401,
|
|
||||||
headers: {"content-type": "application/json"},
|
|
||||||
body: "{}",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Assert.equal(Status.login, LOGIN_FAILED_LOGIN_REJECTED, "login was rejected");
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(function test_getGetKeysFailing503() {
|
|
||||||
_("BrowserIDManager correctly handles 5XX responses fetching keys.");
|
|
||||||
|
|
||||||
_("Arrange for a 503 - Sync should reflect a network error.");
|
|
||||||
let config = makeIdentityConfig();
|
|
||||||
// We want no kA or kB so we attempt to fetch them.
|
|
||||||
delete config.fxaccount.user.kA;
|
|
||||||
delete config.fxaccount.user.kB;
|
|
||||||
config.fxaccount.user.keyFetchToken = "keyfetchtoken";
|
|
||||||
yield initializeIdentityWithHAWKResponseFactory(config, function(method, data, uri) {
|
|
||||||
Assert.equal(method, "get");
|
|
||||||
Assert.equal(uri, "http://mockedserver:9999/account/keys")
|
|
||||||
return {
|
|
||||||
status: 503,
|
|
||||||
headers: {"content-type": "application/json"},
|
|
||||||
body: "{}",
|
|
||||||
}
|
|
||||||
});
|
|
||||||
Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "state reflects network error");
|
|
||||||
});
|
|
||||||
|
|
||||||
add_task(function test_getKeysMissing() {
|
|
||||||
_("BrowserIDManager correctly handles getKeys succeeding but not returning keys.");
|
|
||||||
|
|
||||||
let browseridManager = new BrowserIDManager();
|
|
||||||
let identityConfig = makeIdentityConfig();
|
|
||||||
// our mock identity config already has kA and kB - remove them or we never
|
|
||||||
// try and fetch them.
|
|
||||||
delete identityConfig.fxaccount.user.kA;
|
|
||||||
delete identityConfig.fxaccount.user.kB;
|
|
||||||
identityConfig.fxaccount.user.keyFetchToken = 'keyFetchToken';
|
|
||||||
|
|
||||||
configureFxAccountIdentity(browseridManager, identityConfig);
|
|
||||||
|
|
||||||
// Mock a fxAccounts object that returns no keys
|
|
||||||
let fxa = new FxAccounts({
|
|
||||||
fetchAndUnwrapKeys: function () {
|
|
||||||
return Promise.resolve({});
|
|
||||||
},
|
|
||||||
fxAccountsClient: new MockFxAccountsClient()
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add a mock to the currentAccountState object.
|
|
||||||
fxa.internal.currentAccountState.getCertificate = function(data, keyPair, mustBeValidUntil) {
|
|
||||||
this.cert = {
|
|
||||||
validUntil: fxa.internal.now() + CERT_LIFETIME,
|
|
||||||
cert: "certificate",
|
|
||||||
};
|
|
||||||
return Promise.resolve(this.cert.cert);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ensure the new FxAccounts mock has a signed-in user.
|
|
||||||
fxa.internal.currentAccountState.signedInUser = browseridManager._fxaService.internal.currentAccountState.signedInUser;
|
|
||||||
|
|
||||||
browseridManager._fxaService = fxa;
|
|
||||||
|
|
||||||
yield browseridManager.initializeWithCurrentIdentity();
|
|
||||||
|
|
||||||
let ex;
|
|
||||||
try {
|
|
||||||
yield browseridManager.whenReadyToAuthenticate.promise;
|
|
||||||
} catch (e) {
|
|
||||||
ex = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
Assert.ok(ex.message.indexOf("missing kA or kB") >= 0);
|
|
||||||
});
|
|
||||||
|
|
||||||
// End of tests
|
|
||||||
// Utility functions follow
|
|
||||||
|
|
||||||
// Create a new browserid_identity object and initialize it with a
|
|
||||||
// hawk mock that simulates HTTP responses.
|
|
||||||
// The callback function will be called each time the mocked hawk server wants
|
|
||||||
// to make a request. The result of the callback should be the mock response
|
|
||||||
// object that will be returned to hawk.
|
|
||||||
// A token server mock will be used that doesn't hit a server, so we move
|
|
||||||
// directly to a hawk request.
|
|
||||||
function* initializeIdentityWithHAWKResponseFactory(config, cbGetResponse) {
|
|
||||||
// A mock request object.
|
|
||||||
function MockRESTRequest(uri, credentials, extra) {
|
|
||||||
this._uri = uri;
|
|
||||||
this._credentials = credentials;
|
|
||||||
this._extra = extra;
|
|
||||||
};
|
|
||||||
MockRESTRequest.prototype = {
|
|
||||||
setHeader: function() {},
|
|
||||||
post: function(data, callback) {
|
|
||||||
this.response = cbGetResponse("post", data, this._uri, this._credentials, this._extra);
|
|
||||||
callback.call(this);
|
|
||||||
},
|
|
||||||
get: function(callback) {
|
|
||||||
this.response = cbGetResponse("get", null, this._uri, this._credentials, this._extra);
|
|
||||||
callback.call(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The hawk client.
|
|
||||||
function MockedHawkClient() {}
|
|
||||||
MockedHawkClient.prototype = new HawkClient("http://mockedserver:9999");
|
|
||||||
MockedHawkClient.prototype.constructor = MockedHawkClient;
|
|
||||||
MockedHawkClient.prototype.newHAWKAuthenticatedRESTRequest = function(uri, credentials, extra) {
|
|
||||||
return new MockRESTRequest(uri, credentials, extra);
|
|
||||||
}
|
|
||||||
// Arrange for the same observerPrefix as FxAccountsClient uses
|
|
||||||
MockedHawkClient.prototype.observerPrefix = "FxA:hawk";
|
|
||||||
|
|
||||||
// tie it all together - configureFxAccountIdentity isn't useful here :(
|
|
||||||
let fxaClient = new MockFxAccountsClient();
|
|
||||||
fxaClient.hawk = new MockedHawkClient();
|
|
||||||
let internal = {
|
|
||||||
fxAccountsClient: fxaClient,
|
|
||||||
}
|
|
||||||
let fxa = new FxAccounts(internal);
|
|
||||||
fxa.internal.currentAccountState.signedInUser = {
|
|
||||||
accountData: config.fxaccount.user,
|
|
||||||
};
|
|
||||||
|
|
||||||
browseridManager._fxaService = fxa;
|
|
||||||
browseridManager._signedInUser = null;
|
|
||||||
yield browseridManager.initializeWithCurrentIdentity();
|
|
||||||
yield Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
|
||||||
"expecting rejection due to hawk error");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getTimestamp(hawkAuthHeader) {
|
|
||||||
return parseInt(/ts="(\d+)"/.exec(hawkAuthHeader)[1], 10) * SECOND_MS;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTimestampDelta(hawkAuthHeader, now=Date.now()) {
|
|
||||||
return Math.abs(getTimestamp(hawkAuthHeader) - now);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -37,7 +37,6 @@ const testingModules = [
|
||||||
"fakeservices.js",
|
"fakeservices.js",
|
||||||
"rotaryengine.js",
|
"rotaryengine.js",
|
||||||
"utils.js",
|
"utils.js",
|
||||||
"fxa_utils.js",
|
|
||||||
];
|
];
|
||||||
|
|
||||||
function run_test() {
|
function run_test() {
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ skip-if = os == "win" || os == "android"
|
||||||
[test_syncstoragerequest.js]
|
[test_syncstoragerequest.js]
|
||||||
|
|
||||||
# Generic Sync types.
|
# Generic Sync types.
|
||||||
[test_browserid_identity.js]
|
|
||||||
[test_collection_inc_get.js]
|
[test_collection_inc_get.js]
|
||||||
[test_collections_recovery.js]
|
[test_collections_recovery.js]
|
||||||
[test_identity_manager.js]
|
[test_identity_manager.js]
|
||||||
|
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = [
|
|
||||||
"Authentication",
|
|
||||||
];
|
|
||||||
|
|
||||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/FxAccounts.jsm");
|
|
||||||
Cu.import("resource://gre/modules/FxAccountsClient.jsm");
|
|
||||||
Cu.import("resource://services-common/async.js");
|
|
||||||
Cu.import("resource://services-sync/main.js");
|
|
||||||
Cu.import("resource://tps/logger.jsm");
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper object for Firefox Accounts authentication
|
|
||||||
*/
|
|
||||||
var Authentication = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if an user has been logged in
|
|
||||||
*/
|
|
||||||
get isLoggedIn() {
|
|
||||||
return !!this.getSignedInUser();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to retrieve the currently signed in user
|
|
||||||
*
|
|
||||||
* @returns Information about the currently signed in user
|
|
||||||
*/
|
|
||||||
getSignedInUser: function getSignedInUser() {
|
|
||||||
let cb = Async.makeSpinningCallback();
|
|
||||||
|
|
||||||
fxAccounts.getSignedInUser().then(user => {
|
|
||||||
cb(null, user);
|
|
||||||
}, error => {
|
|
||||||
cb(error);
|
|
||||||
})
|
|
||||||
|
|
||||||
try {
|
|
||||||
return cb.wait();
|
|
||||||
} catch (error) {
|
|
||||||
Logger.logError("getSignedInUser() failed with: " + JSON.stringify(error));
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper to synchronize the login of a user
|
|
||||||
*
|
|
||||||
* @param account
|
|
||||||
* Account information of the user to login
|
|
||||||
* @param account.username
|
|
||||||
* The username for the account (utf8)
|
|
||||||
* @param account.password
|
|
||||||
* The user's password
|
|
||||||
*/
|
|
||||||
signIn: function signIn(account) {
|
|
||||||
let cb = Async.makeSpinningCallback();
|
|
||||||
|
|
||||||
Logger.AssertTrue(account["username"], "Username has been found");
|
|
||||||
Logger.AssertTrue(account["password"], "Password has been found");
|
|
||||||
|
|
||||||
Logger.logInfo("Login user: " + account["username"] + '\n');
|
|
||||||
|
|
||||||
let client = new FxAccountsClient();
|
|
||||||
client.signIn(account["username"], account["password"], true).then(credentials => {
|
|
||||||
return fxAccounts.setSignedInUser(credentials);
|
|
||||||
}).then(() => {
|
|
||||||
cb(null, true);
|
|
||||||
}, error => {
|
|
||||||
cb(error, false);
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
cb.wait();
|
|
||||||
|
|
||||||
if (Weave.Status.login !== Weave.LOGIN_SUCCEEDED) {
|
|
||||||
Logger.logInfo("Logging into Weave.");
|
|
||||||
Weave.Service.login();
|
|
||||||
Logger.AssertEqual(Weave.Status.login, Weave.LOGIN_SUCCEEDED,
|
|
||||||
"Weave logged in");
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (error) {
|
|
||||||
throw new Error("signIn() failed with: " + error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
@ -202,7 +202,7 @@ this.LoginHelper = {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
// newURI will throw for some values e.g. chrome://FirefoxAccounts
|
// newURI will throw for some values
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -406,7 +406,7 @@ this.LoginHelper = {
|
||||||
try {
|
try {
|
||||||
preferredOriginScheme = Services.io.newURI(preferredOrigin, null, null).scheme;
|
preferredOriginScheme = Services.io.newURI(preferredOrigin, null, null).scheme;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
// Handle strings that aren't valid URIs e.g. chrome://FirefoxAccounts
|
// Handle strings that aren't valid URIs
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -457,7 +457,7 @@ this.LoginHelper = {
|
||||||
|
|
||||||
return loginURI.scheme == preferredOriginScheme;
|
return loginURI.scheme == preferredOriginScheme;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
// Some URLs aren't valid nsIURI (e.g. chrome://FirefoxAccounts)
|
// Some URLs aren't valid nsIURI
|
||||||
log.debug("dedupeLogins/shouldReplaceExisting: Error comparing schemes:",
|
log.debug("dedupeLogins/shouldReplaceExisting: Error comparing schemes:",
|
||||||
existingLogin.hostname, login.hostname,
|
existingLogin.hostname, login.hostname,
|
||||||
"preferredOrigin:", preferredOrigin, ex);
|
"preferredOrigin:", preferredOrigin, ex);
|
||||||
|
|
|
||||||
|
|
@ -471,7 +471,7 @@ LoginManagerStorage_mozStorage.prototype = {
|
||||||
params["http" + field] = "http://" + valueURI.hostPort;
|
params["http" + field] = "http://" + valueURI.hostPort;
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
// newURI will throw for some values (e.g. chrome://FirefoxAccounts)
|
// newURI will throw for some values
|
||||||
// but those URLs wouldn't support upgrades anyways.
|
// but those URLs wouldn't support upgrades anyways.
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,313 +0,0 @@
|
||||||
/* 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/. */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = ["FirefoxAccounts"];
|
|
||||||
|
|
||||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/Log.jsm");
|
|
||||||
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, "objectCopy",
|
|
||||||
"resource://gre/modules/identity/IdentityUtils.jsm");
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "makeMessageObject",
|
|
||||||
"resource://gre/modules/identity/IdentityUtils.jsm");
|
|
||||||
|
|
||||||
// loglevel preference should be one of: "FATAL", "ERROR", "WARN", "INFO",
|
|
||||||
// "CONFIG", "DEBUG", "TRACE" or "ALL". We will be logging error messages by
|
|
||||||
// default.
|
|
||||||
const PREF_LOG_LEVEL = "identity.fxaccounts.loglevel";
|
|
||||||
try {
|
|
||||||
this.LOG_LEVEL =
|
|
||||||
Services.prefs.getPrefType(PREF_LOG_LEVEL) == Ci.nsIPrefBranch.PREF_STRING
|
|
||||||
&& Services.prefs.getCharPref(PREF_LOG_LEVEL);
|
|
||||||
} catch (e) {
|
|
||||||
this.LOG_LEVEL = Log.Level.Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
var log = Log.repository.getLogger("Identity.FxAccounts");
|
|
||||||
log.level = LOG_LEVEL;
|
|
||||||
log.addAppender(new Log.ConsoleAppender(new Log.BasicFormatter()));
|
|
||||||
|
|
||||||
log.warn("The FxAccountsManager has been removed.");
|
|
||||||
var FxAccountsManager = null;
|
|
||||||
var ONVERIFIED_NOTIFICATION = null;
|
|
||||||
var ONLOGIN_NOTIFICATION = null;
|
|
||||||
var ONLOGOUT_NOTIFICATION = null;
|
|
||||||
|
|
||||||
function FxAccountsService() {
|
|
||||||
Services.obs.addObserver(this, "quit-application-granted", false);
|
|
||||||
if (ONVERIFIED_NOTIFICATION) {
|
|
||||||
Services.obs.addObserver(this, ONVERIFIED_NOTIFICATION, false);
|
|
||||||
Services.obs.addObserver(this, ONLOGIN_NOTIFICATION, false);
|
|
||||||
Services.obs.addObserver(this, ONLOGOUT_NOTIFICATION, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maintain interface parity with Identity.jsm and MinimalIdentity.jsm
|
|
||||||
this.RP = this;
|
|
||||||
|
|
||||||
this._rpFlows = new Map();
|
|
||||||
|
|
||||||
// Enable us to mock FxAccountsManager service in testing
|
|
||||||
this.fxAccountsManager = FxAccountsManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
FxAccountsService.prototype = {
|
|
||||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports, Ci.nsIObserver]),
|
|
||||||
|
|
||||||
observe: function observe(aSubject, aTopic, aData) {
|
|
||||||
switch (aTopic) {
|
|
||||||
case null:
|
|
||||||
// Guard against matching null ON*_NOTIFICATION
|
|
||||||
break;
|
|
||||||
case ONVERIFIED_NOTIFICATION:
|
|
||||||
log.debug("Received " + ONVERIFIED_NOTIFICATION + "; firing request()s");
|
|
||||||
for (let [rpId,] of this._rpFlows) {
|
|
||||||
this.request(rpId);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ONLOGIN_NOTIFICATION:
|
|
||||||
log.debug("Received " + ONLOGIN_NOTIFICATION + "; doLogin()s fired");
|
|
||||||
for (let [rpId,] of this._rpFlows) {
|
|
||||||
this.request(rpId);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ONLOGOUT_NOTIFICATION:
|
|
||||||
log.debug("Received " + ONLOGOUT_NOTIFICATION + "; doLogout()s fired");
|
|
||||||
for (let [rpId,] of this._rpFlows) {
|
|
||||||
this.doLogout(rpId);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "quit-application-granted":
|
|
||||||
Services.obs.removeObserver(this, "quit-application-granted");
|
|
||||||
if (ONVERIFIED_NOTIFICATION) {
|
|
||||||
Services.obs.removeObserver(this, ONVERIFIED_NOTIFICATION);
|
|
||||||
Services.obs.removeObserver(this, ONLOGIN_NOTIFICATION);
|
|
||||||
Services.obs.removeObserver(this, ONLOGOUT_NOTIFICATION);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
cleanupRPRequest: function(aRp) {
|
|
||||||
aRp.pendingRequest = false;
|
|
||||||
this._rpFlows.set(aRp.id, aRp);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register a listener for a given windowID as a result of a call to
|
|
||||||
* navigator.id.watch().
|
|
||||||
*
|
|
||||||
* @param aRPCaller
|
|
||||||
* (Object) an object that represents the caller document, and
|
|
||||||
* is expected to have properties:
|
|
||||||
* - id (unique, e.g. uuid)
|
|
||||||
* - origin (string)
|
|
||||||
*
|
|
||||||
* and a bunch of callbacks
|
|
||||||
* - doReady()
|
|
||||||
* - doLogin()
|
|
||||||
* - doLogout()
|
|
||||||
* - doError()
|
|
||||||
* - doCancel()
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
watch: function watch(aRpCaller) {
|
|
||||||
this._rpFlows.set(aRpCaller.id, aRpCaller);
|
|
||||||
log.debug("watch: " + aRpCaller.id);
|
|
||||||
log.debug("Current rp flows: " + this._rpFlows.size);
|
|
||||||
|
|
||||||
// Log the user in, if possible, and then call ready().
|
|
||||||
let runnable = {
|
|
||||||
run: () => {
|
|
||||||
this.fxAccountsManager.getAssertion(aRpCaller.audience,
|
|
||||||
aRpCaller.principal,
|
|
||||||
{ silent:true }).then(
|
|
||||||
data => {
|
|
||||||
if (data) {
|
|
||||||
this.doLogin(aRpCaller.id, data);
|
|
||||||
} else {
|
|
||||||
this.doLogout(aRpCaller.id);
|
|
||||||
}
|
|
||||||
this.doReady(aRpCaller.id);
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
log.error("get silent assertion failed: " + JSON.stringify(error));
|
|
||||||
this.doError(aRpCaller.id, error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Services.tm.currentThread.dispatch(runnable,
|
|
||||||
Ci.nsIThread.DISPATCH_NORMAL);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete the flow when the screen is unloaded
|
|
||||||
*/
|
|
||||||
unwatch: function(aRpCallerId, aTargetMM) {
|
|
||||||
log.debug("unwatching: " + aRpCallerId);
|
|
||||||
this._rpFlows.delete(aRpCallerId);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
aOptions = aOptions || {};
|
|
||||||
let rp = this._rpFlows.get(aRPId);
|
|
||||||
if (!rp) {
|
|
||||||
log.error("request() called before watch()");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We check if we already have a pending request for this RP and in that
|
|
||||||
// case we just bail out. We don't want duplicated onlogin or oncancel
|
|
||||||
// events.
|
|
||||||
if (rp.pendingRequest) {
|
|
||||||
log.debug("request() already called");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, we set the RP flow with the pending request flag.
|
|
||||||
rp.pendingRequest = true;
|
|
||||||
this._rpFlows.set(rp.id, rp);
|
|
||||||
|
|
||||||
let options = makeMessageObject(rp);
|
|
||||||
objectCopy(aOptions, options);
|
|
||||||
|
|
||||||
log.debug("get assertion for " + rp.audience);
|
|
||||||
|
|
||||||
this.fxAccountsManager.getAssertion(rp.audience, rp.principal, options)
|
|
||||||
.then(
|
|
||||||
data => {
|
|
||||||
log.debug("got assertion for " + rp.audience + ": " + data);
|
|
||||||
this.doLogin(aRPId, data);
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
log.debug("get assertion failed: " + JSON.stringify(error));
|
|
||||||
// Cancellation is passed through an error channel; here we reroute.
|
|
||||||
if ((error.error && (error.error.details == "DIALOG_CLOSED_BY_USER")) ||
|
|
||||||
(error.details == "DIALOG_CLOSED_BY_USER")) {
|
|
||||||
return this.doCancel(aRPId);
|
|
||||||
}
|
|
||||||
this.doError(aRPId, error);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
() => {
|
|
||||||
this.cleanupRPRequest(rp);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.catch(
|
|
||||||
() => {
|
|
||||||
this.cleanupRPRequest(rp);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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) {
|
|
||||||
// XXX Bug 945363 - Resolve the SSO story for FXA and implement
|
|
||||||
// logout accordingly.
|
|
||||||
//
|
|
||||||
// For now, it makes no sense to logout from a specific RP in
|
|
||||||
// Firefox Accounts, so just directly call the logout callback.
|
|
||||||
if (!this._rpFlows.has(aRpCallerId)) {
|
|
||||||
log.error("logout() called before watch()");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call logout() on the next tick
|
|
||||||
let runnable = {
|
|
||||||
run: () => {
|
|
||||||
this.fxAccountsManager.signOut().then(() => {
|
|
||||||
this.doLogout(aRpCallerId);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
Services.tm.currentThread.dispatch(runnable,
|
|
||||||
Ci.nsIThread.DISPATCH_NORMAL);
|
|
||||||
},
|
|
||||||
|
|
||||||
childProcessShutdown: function childProcessShutdown(messageManager) {
|
|
||||||
for (let [key,] of this._rpFlows) {
|
|
||||||
if (this._rpFlows.get(key)._mm === messageManager) {
|
|
||||||
this._rpFlows.delete(key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
doLogin: function doLogin(aRpCallerId, aAssertion) {
|
|
||||||
let rp = this._rpFlows.get(aRpCallerId);
|
|
||||||
if (!rp) {
|
|
||||||
log.warn("doLogin found no rp to go with callerId " + aRpCallerId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rp.doLogin(aAssertion);
|
|
||||||
},
|
|
||||||
|
|
||||||
doLogout: function doLogout(aRpCallerId) {
|
|
||||||
let rp = this._rpFlows.get(aRpCallerId);
|
|
||||||
if (!rp) {
|
|
||||||
log.warn("doLogout found no rp to go with callerId " + aRpCallerId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rp.doLogout();
|
|
||||||
},
|
|
||||||
|
|
||||||
doReady: function doReady(aRpCallerId) {
|
|
||||||
let rp = this._rpFlows.get(aRpCallerId);
|
|
||||||
if (!rp) {
|
|
||||||
log.warn("doReady found no rp to go with callerId " + aRpCallerId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rp.doReady();
|
|
||||||
},
|
|
||||||
|
|
||||||
doCancel: function doCancel(aRpCallerId) {
|
|
||||||
let rp = this._rpFlows.get(aRpCallerId);
|
|
||||||
if (!rp) {
|
|
||||||
log.warn("doCancel found no rp to go with callerId " + aRpCallerId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rp.doCancel();
|
|
||||||
},
|
|
||||||
|
|
||||||
doError: function doError(aRpCallerId, aError) {
|
|
||||||
let rp = this._rpFlows.get(aRpCallerId);
|
|
||||||
if (!rp) {
|
|
||||||
log.warn("doError found no rp to go with callerId " + aRpCallerId);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rp.doError(aError);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
this.FirefoxAccounts = new FxAccountsService();
|
|
||||||
|
|
||||||
|
|
@ -29,8 +29,4 @@ EXTRA_JS_MODULES.identity += [
|
||||||
'Sandbox.jsm',
|
'Sandbox.jsm',
|
||||||
]
|
]
|
||||||
|
|
||||||
EXTRA_PP_JS_MODULES.identity += [
|
|
||||||
'FirefoxAccounts.jsm',
|
|
||||||
]
|
|
||||||
|
|
||||||
FINAL_LIBRARY = 'xul'
|
FINAL_LIBRARY = 'xul'
|
||||||
|
|
|
||||||
|
|
@ -1,270 +0,0 @@
|
||||||
/* 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();
|
|
||||||
}
|
|
||||||
|
|
@ -9,7 +9,6 @@ support-files =
|
||||||
# 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_minimalidentity.js]
|
[test_minimalidentity.js]
|
||||||
[test_firefox_accounts.js]
|
|
||||||
|
|
||||||
[test_identity_utils.js]
|
[test_identity_utils.js]
|
||||||
[test_log_utils.js]
|
[test_log_utils.js]
|
||||||
|
|
|
||||||
|
|
@ -82,15 +82,6 @@
|
||||||
"forms.jsm": ["FormData"],
|
"forms.jsm": ["FormData"],
|
||||||
"frame.js": ["Collector", "Runner", "events", "runTestFile", "log", "timers", "persisted", "shutdownApplication"],
|
"frame.js": ["Collector", "Runner", "events", "runTestFile", "log", "timers", "persisted", "shutdownApplication"],
|
||||||
"FrameScriptManager.jsm": ["getNewLoaderID"],
|
"FrameScriptManager.jsm": ["getNewLoaderID"],
|
||||||
"fxa_utils.js": ["initializeIdentityWithTokenServerResponse"],
|
|
||||||
"fxaccounts.jsm": ["Authentication"],
|
|
||||||
"FxAccounts.jsm": ["fxAccounts", "FxAccounts"],
|
|
||||||
"FxAccountsOAuthGrantClient.jsm": ["FxAccountsOAuthGrantClient", "FxAccountsOAuthGrantClientError"],
|
|
||||||
"FxAccountsProfileClient.jsm": ["FxAccountsProfileClient", "FxAccountsProfileClientError"],
|
|
||||||
"FxAccountsPush.js": ["FxAccountsPushService"],
|
|
||||||
"FxAccountsStorage.jsm": ["FxAccountsStorageManagerCanStoreField", "FxAccountsStorageManager"],
|
|
||||||
"FxAccountsWebChannel.jsm": ["EnsureFxAccountsWebChannel"],
|
|
||||||
"FxaMigrator.jsm": ["fxaMigrator"],
|
|
||||||
"gDevTools.jsm": ["gDevTools", "gDevToolsBrowser"],
|
"gDevTools.jsm": ["gDevTools", "gDevToolsBrowser"],
|
||||||
"gDevTools.jsm": ["gDevTools", "gDevToolsBrowser"],
|
"gDevTools.jsm": ["gDevTools", "gDevToolsBrowser"],
|
||||||
"Geometry.jsm": ["Point", "Rect"],
|
"Geometry.jsm": ["Point", "Rect"],
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue