Remove various FxA tests

This commit is contained in:
Moonchild 2019-04-18 15:49:19 +00:00 • committed by Roy Tam
commit ccf32a6b79
14 changed files with 7 additions and 1522 deletions

View file

@ -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;
}

View file

@ -28,8 +28,6 @@ Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/browserid_identity.js");
Cu.import("resource://testing-common/services/common/logging.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");
/**
@ -77,27 +75,8 @@ this.setBasicCredentials =
this.makeIdentityConfig = function(overrides) {
// first setup the defaults.
let result = {
// Username used in both fxaccount and sync identity configs.
// Username used in sync identity config.
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: {
// username will come from the top-level username
password: "whatever",
@ -114,64 +93,15 @@ this.makeIdentityConfig = function(overrides) {
// TODO: allow just some attributes to be specified
result.sync = overrides.sync;
}
if (overrides.fxaccount) {
// TODO: allow just some attributes to be specified
result.fxaccount = overrides.fxaccount;
}
}
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) {
let config = makeIdentityConfig(identityOverrides);
let 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);
let deferred = Promise.defer();
deferred.resolve();
@ -184,7 +114,6 @@ this.SyncTestingInfrastructure = function (server, username, password, syncKey)
ensureLegacyIdentityManager();
let config = makeIdentityConfig();
// XXX - hacks for the sync identity provider.
if (username)
config.username = username;
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
// 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
// pass |this|.
@ -248,12 +177,4 @@ this.add_identity_test = function(test, testFunction) {
yield testFunction();
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;
});
}

View file

@ -54,7 +54,6 @@ EXTRA_JS_MODULES['services-sync'].stages += [
TESTING_JS_MODULES.services.sync += [
'modules-testing/fakeservices.js',
'modules-testing/fxa_utils.js',
'modules-testing/rotaryengine.js',
'modules-testing/utils.js',
]

View file

@ -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);
}

View file

@ -37,7 +37,6 @@ const testingModules = [
"fakeservices.js",
"rotaryengine.js",
"utils.js",
"fxa_utils.js",
];
function run_test() {

View file

@ -50,7 +50,6 @@ skip-if = os == "win" || os == "android"
[test_syncstoragerequest.js]
# Generic Sync types.
[test_browserid_identity.js]
[test_collection_inc_get.js]
[test_collections_recovery.js]
[test_identity_manager.js]

View file

@ -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);
}
}
};