1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/services/sync/modules-testing/utils.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,260 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +"use strict"; 1.9 + 1.10 +this.EXPORTED_SYMBOLS = [ 1.11 + "btoa", // It comes from a module import. 1.12 + "encryptPayload", 1.13 + "ensureLegacyIdentityManager", 1.14 + "setBasicCredentials", 1.15 + "makeIdentityConfig", 1.16 + "configureFxAccountIdentity", 1.17 + "configureIdentity", 1.18 + "SyncTestingInfrastructure", 1.19 + "waitForZeroTimer", 1.20 + "Promise", // from a module import 1.21 + "add_identity_test", 1.22 +]; 1.23 + 1.24 +const {utils: Cu} = Components; 1.25 + 1.26 +Cu.import("resource://services-sync/status.js"); 1.27 +Cu.import("resource://services-sync/identity.js"); 1.28 +Cu.import("resource://services-common/utils.js"); 1.29 +Cu.import("resource://services-crypto/utils.js"); 1.30 +Cu.import("resource://services-sync/util.js"); 1.31 +Cu.import("resource://services-sync/browserid_identity.js"); 1.32 +Cu.import("resource://testing-common/services-common/logging.js"); 1.33 +Cu.import("resource://testing-common/services/sync/fakeservices.js"); 1.34 +Cu.import("resource://gre/modules/FxAccounts.jsm"); 1.35 +Cu.import("resource://gre/modules/FxAccountsCommon.js"); 1.36 +Cu.import("resource://gre/modules/Promise.jsm"); 1.37 + 1.38 +/** 1.39 + * First wait >100ms (nsITimers can take up to that much time to fire, so 1.40 + * we can account for the timer in delayedAutoconnect) and then two event 1.41 + * loop ticks (to account for the Utils.nextTick() in autoConnect). 1.42 + */ 1.43 +this.waitForZeroTimer = function waitForZeroTimer(callback) { 1.44 + let ticks = 2; 1.45 + function wait() { 1.46 + if (ticks) { 1.47 + ticks -= 1; 1.48 + CommonUtils.nextTick(wait); 1.49 + return; 1.50 + } 1.51 + callback(); 1.52 + } 1.53 + CommonUtils.namedTimer(wait, 150, {}, "timer"); 1.54 +} 1.55 + 1.56 +/** 1.57 + * Ensure Sync is configured with the "legacy" identity provider. 1.58 + */ 1.59 +this.ensureLegacyIdentityManager = function() { 1.60 + let ns = {}; 1.61 + Cu.import("resource://services-sync/service.js", ns); 1.62 + 1.63 + Status.__authManager = ns.Service.identity = new IdentityManager(); 1.64 + ns.Service._clusterManager = ns.Service.identity.createClusterManager(ns.Service); 1.65 +} 1.66 + 1.67 +this.setBasicCredentials = 1.68 + function setBasicCredentials(username, password, syncKey) { 1.69 + let ns = {}; 1.70 + Cu.import("resource://services-sync/service.js", ns); 1.71 + 1.72 + let auth = ns.Service.identity; 1.73 + auth.username = username; 1.74 + auth.basicPassword = password; 1.75 + auth.syncKey = syncKey; 1.76 +} 1.77 + 1.78 +// Return an identity configuration suitable for testing with our identity 1.79 +// providers. |overrides| can specify overrides for any default values. 1.80 +this.makeIdentityConfig = function(overrides) { 1.81 + // first setup the defaults. 1.82 + let result = { 1.83 + // Username used in both fxaccount and sync identity configs. 1.84 + username: "foo", 1.85 + // fxaccount specific credentials. 1.86 + fxaccount: { 1.87 + user: { 1.88 + assertion: 'assertion', 1.89 + email: 'email', 1.90 + kA: 'kA', 1.91 + kB: 'kB', 1.92 + sessionToken: 'sessionToken', 1.93 + uid: 'user_uid', 1.94 + verified: true, 1.95 + }, 1.96 + token: { 1.97 + endpoint: Svc.Prefs.get("tokenServerURI"), 1.98 + duration: 300, 1.99 + id: "id", 1.100 + key: "key", 1.101 + // uid will be set to the username. 1.102 + } 1.103 + }, 1.104 + sync: { 1.105 + // username will come from the top-level username 1.106 + password: "whatever", 1.107 + syncKey: "abcdeabcdeabcdeabcdeabcdea", 1.108 + } 1.109 + }; 1.110 + 1.111 + // Now handle any specified overrides. 1.112 + if (overrides) { 1.113 + if (overrides.username) { 1.114 + result.username = overrides.username; 1.115 + } 1.116 + if (overrides.sync) { 1.117 + // TODO: allow just some attributes to be specified 1.118 + result.sync = overrides.sync; 1.119 + } 1.120 + if (overrides.fxaccount) { 1.121 + // TODO: allow just some attributes to be specified 1.122 + result.fxaccount = overrides.fxaccount; 1.123 + } 1.124 + } 1.125 + return result; 1.126 +} 1.127 + 1.128 +// Configure an instance of an FxAccount identity provider with the specified 1.129 +// config (or the default config if not specified). 1.130 +this.configureFxAccountIdentity = function(authService, 1.131 + config = makeIdentityConfig()) { 1.132 + let MockInternal = {}; 1.133 + let fxa = new FxAccounts(MockInternal); 1.134 + 1.135 + // until we get better test infrastructure for bid_identity, we set the 1.136 + // signedin user's "email" to the username, simply as many tests rely on this. 1.137 + config.fxaccount.user.email = config.username; 1.138 + fxa.internal.currentAccountState.signedInUser = { 1.139 + version: DATA_FORMAT_VERSION, 1.140 + accountData: config.fxaccount.user 1.141 + }; 1.142 + fxa.internal.currentAccountState.getCertificate = function(data, keyPair, mustBeValidUntil) { 1.143 + this.cert = { 1.144 + validUntil: fxa.internal.now() + CERT_LIFETIME, 1.145 + cert: "certificate", 1.146 + }; 1.147 + return Promise.resolve(this.cert.cert); 1.148 + }; 1.149 + 1.150 + let mockTSC = { // TokenServerClient 1.151 + getTokenFromBrowserIDAssertion: function(uri, assertion, cb) { 1.152 + config.fxaccount.token.uid = config.username; 1.153 + cb(null, config.fxaccount.token); 1.154 + }, 1.155 + }; 1.156 + authService._fxaService = fxa; 1.157 + authService._tokenServerClient = mockTSC; 1.158 + // Set the "account" of the browserId manager to be the "email" of the 1.159 + // logged in user of the mockFXA service. 1.160 + authService._signedInUser = fxa.internal.currentAccountState.signedInUser.accountData; 1.161 + authService._account = config.fxaccount.user.email; 1.162 +} 1.163 + 1.164 +this.configureIdentity = function(identityOverrides) { 1.165 + let config = makeIdentityConfig(identityOverrides); 1.166 + let ns = {}; 1.167 + Cu.import("resource://services-sync/service.js", ns); 1.168 + 1.169 + if (ns.Service.identity instanceof BrowserIDManager) { 1.170 + // do the FxAccounts thang... 1.171 + configureFxAccountIdentity(ns.Service.identity, config); 1.172 + return ns.Service.identity.initializeWithCurrentIdentity().then(() => { 1.173 + // need to wait until this identity manager is readyToAuthenticate. 1.174 + return ns.Service.identity.whenReadyToAuthenticate.promise; 1.175 + }); 1.176 + } 1.177 + // old style identity provider. 1.178 + setBasicCredentials(config.username, config.sync.password, config.sync.syncKey); 1.179 + let deferred = Promise.defer(); 1.180 + deferred.resolve(); 1.181 + return deferred.promise; 1.182 +} 1.183 + 1.184 +this.SyncTestingInfrastructure = function (server, username, password, syncKey) { 1.185 + let ns = {}; 1.186 + Cu.import("resource://services-sync/service.js", ns); 1.187 + 1.188 + ensureLegacyIdentityManager(); 1.189 + let config = makeIdentityConfig(); 1.190 + // XXX - hacks for the sync identity provider. 1.191 + if (username) 1.192 + config.username = username; 1.193 + if (password) 1.194 + config.sync.password = password; 1.195 + if (syncKey) 1.196 + config.sync.syncKey = syncKey; 1.197 + let cb = Async.makeSpinningCallback(); 1.198 + configureIdentity(config).then(cb, cb); 1.199 + cb.wait(); 1.200 + 1.201 + let i = server.identity; 1.202 + let uri = i.primaryScheme + "://" + i.primaryHost + ":" + 1.203 + i.primaryPort + "/"; 1.204 + 1.205 + ns.Service.serverURL = uri; 1.206 + ns.Service.clusterURL = uri; 1.207 + 1.208 + this.logStats = initTestLogging(); 1.209 + this.fakeFilesystem = new FakeFilesystemService({}); 1.210 + this.fakeGUIDService = new FakeGUIDService(); 1.211 + this.fakeCryptoService = new FakeCryptoService(); 1.212 +} 1.213 + 1.214 +/** 1.215 + * Turn WBO cleartext into fake "encrypted" payload as it goes over the wire. 1.216 + */ 1.217 +this.encryptPayload = function encryptPayload(cleartext) { 1.218 + if (typeof cleartext == "object") { 1.219 + cleartext = JSON.stringify(cleartext); 1.220 + } 1.221 + 1.222 + return { 1.223 + ciphertext: cleartext, // ciphertext == cleartext with fake crypto 1.224 + IV: "irrelevant", 1.225 + hmac: fakeSHA256HMAC(cleartext, CryptoUtils.makeHMACKey("")), 1.226 + }; 1.227 +} 1.228 + 1.229 +// This helper can be used instead of 'add_test' or 'add_task' to run the 1.230 +// specified test function twice - once with the old-style sync identity 1.231 +// manager and once with the new-style BrowserID identity manager, to ensure 1.232 +// it works in both cases. 1.233 +// 1.234 +// * The test itself should be passed as 'test' - ie, test code will generally 1.235 +// pass |this|. 1.236 +// * The test function is a regular test function - although note that it must 1.237 +// be a generator - async operations should yield them, and run_next_test 1.238 +// mustn't be called. 1.239 +this.add_identity_test = function(test, testFunction) { 1.240 + function note(what) { 1.241 + let msg = "running test " + testFunction.name + " with " + what + " identity manager"; 1.242 + test._log("test_info", 1.243 + {_message: "TEST-INFO | | " + msg + "\n"}); 1.244 + } 1.245 + let ns = {}; 1.246 + Cu.import("resource://services-sync/service.js", ns); 1.247 + // one task for the "old" identity manager. 1.248 + test.add_task(function() { 1.249 + note("sync"); 1.250 + let oldIdentity = Status._authManager; 1.251 + ensureLegacyIdentityManager(); 1.252 + yield testFunction(); 1.253 + Status.__authManager = ns.Service.identity = oldIdentity; 1.254 + }); 1.255 + // another task for the FxAccounts identity manager. 1.256 + test.add_task(function() { 1.257 + note("FxAccounts"); 1.258 + let oldIdentity = Status._authManager; 1.259 + Status.__authManager = ns.Service.identity = new BrowserIDManager(); 1.260 + yield testFunction(); 1.261 + Status.__authManager = ns.Service.identity = oldIdentity; 1.262 + }); 1.263 +}