зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1435929 - refactor browserid_identity.js to be less confusing and error prone. r=eoger,tcsc
MozReview-Commit-ID: IJPQv4ZvJlp --HG-- extra : rebase_source : d3052d120778dc1e8111ba76ee9df285d5483786
This commit is contained in:
Родитель
3eb90874c6
Коммит
6afd2c2ec1
|
@ -44,7 +44,6 @@ var initializeIdentityWithTokenServerResponse = function(response) {
|
|||
|
||||
// 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)) {
|
||||
|
|
|
@ -26,6 +26,7 @@ ChromeUtils.import("resource://services-common/utils.js");
|
|||
ChromeUtils.import("resource://services-crypto/utils.js");
|
||||
ChromeUtils.import("resource://services-sync/util.js");
|
||||
ChromeUtils.import("resource://services-sync/browserid_identity.js");
|
||||
ChromeUtils.import("resource://testing-common/Assert.jsm");
|
||||
ChromeUtils.import("resource://testing-common/services/common/logging.js");
|
||||
ChromeUtils.import("resource://testing-common/services/sync/fakeservices.js");
|
||||
ChromeUtils.import("resource://gre/modules/FxAccounts.jsm");
|
||||
|
@ -162,7 +163,7 @@ var makeFxAccountsInternalMock = function(config) {
|
|||
return accountState;
|
||||
},
|
||||
_getAssertion(audience) {
|
||||
return Promise.resolve("assertion");
|
||||
return Promise.resolve(config.fxaccount.user.assertion);
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -192,6 +193,8 @@ var configureFxAccountIdentity = function(authService,
|
|||
|
||||
let mockTSC = { // TokenServerClient
|
||||
async getTokenFromBrowserIDAssertion(uri, assertion) {
|
||||
Assert.equal(uri, Services.prefs.getStringPref("identity.sync.tokenserver.uri"));
|
||||
Assert.equal(assertion, config.fxaccount.user.assertion);
|
||||
config.fxaccount.token.uid = config.username;
|
||||
return config.fxaccount.token;
|
||||
},
|
||||
|
@ -220,10 +223,12 @@ var configureIdentity = async function(identityOverrides, server) {
|
|||
}
|
||||
|
||||
configureFxAccountIdentity(ns.Service.identity, config);
|
||||
await ns.Service.identity.initializeWithCurrentIdentity();
|
||||
// The token is fetched in the background, whenReadyToAuthenticate is resolved
|
||||
// when we are ready.
|
||||
await ns.Service.identity.whenReadyToAuthenticate.promise;
|
||||
// because we didn't send any FxA LOGIN notifications we must set the username.
|
||||
ns.Service.identity.username = config.username;
|
||||
// many of these tests assume all the auth stuff is setup and don't hit
|
||||
// a path which causes that auth to magically happen - so do it now.
|
||||
await ns.Service.identity._ensureValidToken();
|
||||
|
||||
// and cheat to avoid requiring each test do an explicit login - give it
|
||||
// a cluster URL.
|
||||
if (config.fxaccount.token.endpoint) {
|
||||
|
|
|
@ -28,6 +28,7 @@ const TOPICS = [
|
|||
"weave:service:sync:start",
|
||||
"weave:service:sync:finish",
|
||||
"weave:service:sync:error",
|
||||
"weave:service:start-over:finish",
|
||||
"fxaccounts:onverified",
|
||||
"fxaccounts:onlogin", // Defined in FxAccountsCommon, pulling it is expensive.
|
||||
"fxaccounts:onlogout",
|
||||
|
|
|
@ -9,7 +9,6 @@ var EXPORTED_SYMBOLS = ["BrowserIDManager", "AuthenticationError"];
|
|||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Log.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/PromiseUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/FxAccounts.jsm");
|
||||
ChromeUtils.import("resource://services-common/async.js");
|
||||
ChromeUtils.import("resource://services-common/utils.js");
|
||||
|
@ -43,6 +42,7 @@ ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js", fxAccountsCommo
|
|||
|
||||
const OBSERVER_TOPICS = [
|
||||
fxAccountsCommon.ONLOGIN_NOTIFICATION,
|
||||
fxAccountsCommon.ONVERIFIED_NOTIFICATION,
|
||||
fxAccountsCommon.ONLOGOUT_NOTIFICATION,
|
||||
fxAccountsCommon.ON_ACCOUNT_STATE_CHANGE_NOTIFICATION,
|
||||
];
|
||||
|
@ -158,11 +158,14 @@ function BrowserIDManager() {
|
|||
this._fxaService = fxAccounts;
|
||||
this._tokenServerClient = new TokenServerClient();
|
||||
this._tokenServerClient.observerPrefix = "weave:service";
|
||||
// will be a promise that resolves when we are ready to authenticate
|
||||
this.whenReadyToAuthenticate = null;
|
||||
this._log = log;
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "_username", "services.sync.username");
|
||||
|
||||
this.asyncObserver = Async.asyncObserver(this, log);
|
||||
for (let topic of OBSERVER_TOPICS) {
|
||||
Services.obs.addObserver(this.asyncObserver, topic);
|
||||
}
|
||||
};
|
||||
|
||||
this.BrowserIDManager.prototype = {
|
||||
_fxaService: null,
|
||||
|
@ -171,15 +174,6 @@ this.BrowserIDManager.prototype = {
|
|||
_token: null,
|
||||
_signedInUser: null, // the signedinuser we got from FxAccounts.
|
||||
|
||||
// null if no error, otherwise a LOGIN_FAILED_* value that indicates why
|
||||
// we failed to authenticate (but note it might not be an actual
|
||||
// authentication problem, just a transient network error or similar)
|
||||
_authFailureReason: null,
|
||||
|
||||
// it takes some time to fetch a sync key bundle, so until this flag is set,
|
||||
// we don't consider the lack of a keybundle as a failure state.
|
||||
_shouldHaveSyncKeyBundle: false,
|
||||
|
||||
hashedUID() {
|
||||
if (!this._hashedUID) {
|
||||
throw new Error("hashedUID: Don't seem to have previously seen a token");
|
||||
|
@ -196,133 +190,21 @@ this.BrowserIDManager.prototype = {
|
|||
return Utils.sha256(deviceID + uid);
|
||||
},
|
||||
|
||||
deviceID() {
|
||||
return this._signedInUser && this._signedInUser.deviceId;
|
||||
},
|
||||
|
||||
initialize() {
|
||||
for (let topic of OBSERVER_TOPICS) {
|
||||
Services.obs.addObserver(this, topic);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensure the user is logged in. Returns a promise that resolves when
|
||||
* the user is logged in, or is rejected if the login attempt has failed.
|
||||
*/
|
||||
ensureLoggedIn() {
|
||||
if (!this._shouldHaveSyncKeyBundle && this.whenReadyToAuthenticate) {
|
||||
// We are already in the process of logging in.
|
||||
return this.whenReadyToAuthenticate.promise;
|
||||
}
|
||||
|
||||
// If we are already happy then there is nothing more to do.
|
||||
if (this._syncKeyBundle) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Similarly, if we have a previous failure that implies an explicit
|
||||
// re-entering of credentials by the user is necessary we don't take any
|
||||
// further action - an observer will fire when the user does that.
|
||||
if (Weave.Status.login == LOGIN_FAILED_LOGIN_REJECTED) {
|
||||
return Promise.reject(new Error("User needs to re-authenticate"));
|
||||
}
|
||||
|
||||
// So - we've a previous auth problem and aren't currently attempting to
|
||||
// log in - so fire that off.
|
||||
this.initializeWithCurrentIdentity();
|
||||
return this.whenReadyToAuthenticate.promise;
|
||||
},
|
||||
|
||||
finalize() {
|
||||
// After this is called, we can expect Service.identity != this.
|
||||
for (let topic of OBSERVER_TOPICS) {
|
||||
Services.obs.removeObserver(this, topic);
|
||||
Services.obs.removeObserver(this.asyncObserver, topic);
|
||||
}
|
||||
this.resetCredentials();
|
||||
this._signedInUser = null;
|
||||
},
|
||||
|
||||
async initializeWithCurrentIdentity(isInitialSync = false) {
|
||||
// While this function returns a promise that resolves once we've started
|
||||
// the auth process, that process is complete when
|
||||
// this.whenReadyToAuthenticate.promise resolves.
|
||||
this._log.trace("initializeWithCurrentIdentity");
|
||||
|
||||
// Reset the world before we do anything async.
|
||||
this.whenReadyToAuthenticate = PromiseUtils.defer();
|
||||
this.whenReadyToAuthenticate.promise.catch(err => {
|
||||
this._log.error("Could not authenticate", err);
|
||||
});
|
||||
|
||||
// initializeWithCurrentIdentity() can be called after the
|
||||
// identity module was first initialized, e.g., after the
|
||||
// user completes a force authentication, so we should make
|
||||
// sure all credentials are reset before proceeding.
|
||||
this.resetCredentials();
|
||||
this._authFailureReason = null;
|
||||
|
||||
try {
|
||||
let accountData = await this._fxaService.getSignedInUser();
|
||||
if (!accountData) {
|
||||
this._log.info("initializeWithCurrentIdentity has no user logged in");
|
||||
// and we are as ready as we can ever be for auth.
|
||||
this._shouldHaveSyncKeyBundle = true;
|
||||
this.whenReadyToAuthenticate.reject("no user is logged in");
|
||||
return;
|
||||
}
|
||||
|
||||
this.username = accountData.email;
|
||||
this._updateSignedInUser(accountData);
|
||||
// The user must be verified before we can do anything at all; we kick
|
||||
// this and the rest of initialization off in the background (ie, we
|
||||
// don't return the promise)
|
||||
CommonUtils.nextTick(async () => {
|
||||
try {
|
||||
this._log.info("Waiting for user to be verified.");
|
||||
if (!accountData.verified) {
|
||||
telemetryHelper.maybeRecordLoginState(telemetryHelper.STATES.NOTVERIFIED);
|
||||
}
|
||||
accountData = await this._fxaService.whenVerified(accountData);
|
||||
this._updateSignedInUser(accountData);
|
||||
|
||||
this._log.info("Starting fetch for key bundle.");
|
||||
let token = await this._fetchTokenForUser();
|
||||
this._token = token;
|
||||
if (token) {
|
||||
// We may not have a token if the master-password is locked - but we
|
||||
// still treat this as "success" so we don't prompt for re-authentication.
|
||||
this._hashedUID = token.hashed_fxa_uid; // see _ensureValidToken for why we do this...
|
||||
}
|
||||
this._shouldHaveSyncKeyBundle = true; // and we should actually have one...
|
||||
this.whenReadyToAuthenticate.resolve();
|
||||
this._log.info("Background fetch for key bundle done");
|
||||
Weave.Status.login = LOGIN_SUCCEEDED;
|
||||
if (isInitialSync) {
|
||||
this._log.info("Doing initial sync actions");
|
||||
Svc.Prefs.set("firstSync", "resetClient");
|
||||
Services.obs.notifyObservers(null, "weave:service:setup-complete");
|
||||
CommonUtils.nextTick(Weave.Service.sync, Weave.Service);
|
||||
}
|
||||
} catch (authErr) {
|
||||
// report what failed...
|
||||
this._log.error("Background fetch for key bundle failed", authErr);
|
||||
this._shouldHaveSyncKeyBundle = true; // but we probably don't have one...
|
||||
this.whenReadyToAuthenticate.reject(authErr);
|
||||
}
|
||||
// and we are done - the fetch continues on in the background...
|
||||
});
|
||||
} catch (err) {
|
||||
this._log.error("Processing logged in account", err);
|
||||
}
|
||||
},
|
||||
|
||||
_updateSignedInUser(userData) {
|
||||
// This object should only ever be used for a single user. It is an
|
||||
// error to update the data if the user changes (but updates are still
|
||||
// necessary, as each call may add more attributes to the user).
|
||||
// We start with no user, so an initial update is always ok.
|
||||
if (this._signedInUser && this._signedInUser.email != userData.email) {
|
||||
if (this._signedInUser && this._signedInUser.uid != userData.uid) {
|
||||
throw new Error("Attempting to update to a different user.");
|
||||
}
|
||||
this._signedInUser = userData;
|
||||
|
@ -337,57 +219,62 @@ this.BrowserIDManager.prototype = {
|
|||
this._token = null;
|
||||
},
|
||||
|
||||
observe(subject, topic, data) {
|
||||
async observe(subject, topic, data) {
|
||||
this._log.debug("observed " + topic);
|
||||
switch (topic) {
|
||||
case fxAccountsCommon.ONLOGIN_NOTIFICATION: {
|
||||
this._log.info("A user has logged in");
|
||||
// If our existing Sync state is that we needed to reauth, clear that
|
||||
// state now - it will get reset back if a problem persists.
|
||||
if (Weave.Status.login == LOGIN_FAILED_LOGIN_REJECTED) {
|
||||
Weave.Status.login = LOGIN_SUCCEEDED;
|
||||
}
|
||||
// This should only happen if we've been initialized without a current
|
||||
// user - otherwise we'd have seen the LOGOUT notification and been
|
||||
// thrown away.
|
||||
// The exception is when we've initialized with a user that needs to
|
||||
// reauth with the server - in that case we will also get here, but
|
||||
// should have the same identity, and so we pass `false` into
|
||||
// initializeWithCurrentIdentity so that we won't do a full sync for our
|
||||
// first sync if we can avoid it.
|
||||
// initializeWithCurrentIdentity will throw and log if these constraints
|
||||
// aren't met (indirectly, via _updateSignedInUser()), so just go ahead
|
||||
// and do the init.
|
||||
let firstLogin = !this.username;
|
||||
this.initializeWithCurrentIdentity(firstLogin);
|
||||
this.resetCredentials();
|
||||
let accountData = await this._fxaService.getSignedInUser();
|
||||
this._updateSignedInUser(accountData);
|
||||
|
||||
if (!firstLogin) {
|
||||
// We still want to trigger these even if it isn't our first login.
|
||||
// Note that the promise returned by `initializeWithCurrentIdentity`
|
||||
// is resolved at the start of authentication, but we don't want to fire
|
||||
// this event or start the next sync until after authentication is done
|
||||
// (which is signaled by `this.whenReadyToAuthenticate.promise` resolving).
|
||||
this.whenReadyToAuthenticate.promise.then(() => {
|
||||
Services.obs.notifyObservers(null, "weave:service:setup-complete");
|
||||
return Async.promiseYield();
|
||||
}).then(() => {
|
||||
return Weave.Service.sync();
|
||||
}).catch(e => {
|
||||
this._log.warn("Failed to trigger setup complete notification", e);
|
||||
});
|
||||
if (!accountData.verified) {
|
||||
// wait for a verified notification before we kick sync off.
|
||||
this._log.info("The user is not verified");
|
||||
break;
|
||||
}
|
||||
}
|
||||
// We've been configured with an already verified user, so fall-through.
|
||||
case fxAccountsCommon.ONVERIFIED_NOTIFICATION: {
|
||||
this._log.info("The user became verified");
|
||||
|
||||
// Set the username now - that will cause Sync to know it is configured
|
||||
let accountData = await this._fxaService.getSignedInUser();
|
||||
this.username = accountData.email;
|
||||
|
||||
// And actually sync. If we've never synced before, we force a full sync.
|
||||
// If we have, then we are probably just reauthenticating so it's a normal sync.
|
||||
// We can use any pref that must be set if we've synced before.
|
||||
let isFirstSync = !Svc.Prefs.get("client.syncID", null);
|
||||
if (isFirstSync) {
|
||||
this._log.info("Doing initial sync actions");
|
||||
Svc.Prefs.set("firstSync", "resetClient");
|
||||
}
|
||||
Services.obs.notifyObservers(null, "weave:service:setup-complete");
|
||||
// There's no need to wait for sync to complete and it would deadlock
|
||||
// our AsyncObserver.
|
||||
Weave.Service.sync({why: "login"});
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case fxAccountsCommon.ONLOGOUT_NOTIFICATION:
|
||||
Async.promiseSpinningly(Weave.Service.startOver());
|
||||
Weave.Service.startOver().then(() => {
|
||||
this._log.trace("startOver completed");
|
||||
}).catch(err => {
|
||||
this._log.warn("Failed to reset sync", err);
|
||||
});
|
||||
// startOver will cause this instance to be thrown away, so there's
|
||||
// nothing else to do.
|
||||
break;
|
||||
|
||||
case fxAccountsCommon.ON_ACCOUNT_STATE_CHANGE_NOTIFICATION:
|
||||
// throw away token and fetch a new one
|
||||
// throw away token forcing us to fetch a new one later.
|
||||
this.resetCredentials();
|
||||
this._ensureValidToken().catch(err =>
|
||||
this._log.error("Error while fetching a new token", err));
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -439,7 +326,7 @@ this.BrowserIDManager.prototype = {
|
|||
* Resets/Drops all credentials we hold for the current user.
|
||||
*/
|
||||
resetCredentials() {
|
||||
this.resetSyncKeyBundle();
|
||||
this._syncKeyBundle = null;
|
||||
this._token = null;
|
||||
this._hashedUID = null;
|
||||
// The cluster URL comes from the token, so resetting it to empty will
|
||||
|
@ -447,14 +334,6 @@ this.BrowserIDManager.prototype = {
|
|||
Weave.Service.clusterURL = null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets/Drops the sync key bundle we hold for the current user.
|
||||
*/
|
||||
resetSyncKeyBundle() {
|
||||
this._syncKeyBundle = null;
|
||||
this._shouldHaveSyncKeyBundle = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pre-fetches any information that might help with migration away from this
|
||||
* identity. Called after every sync and is really just an optimization that
|
||||
|
@ -465,18 +344,11 @@ this.BrowserIDManager.prototype = {
|
|||
// nothing to do here until we decide to migrate away from FxA.
|
||||
},
|
||||
|
||||
/**
|
||||
* Return credentials hosts for this identity only.
|
||||
*/
|
||||
_getSyncCredentialsHosts() {
|
||||
return Utils.getSyncCredentialsHostsFxA();
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes Sync credentials from the password manager.
|
||||
*/
|
||||
deleteSyncCredentials() {
|
||||
for (let host of this._getSyncCredentialsHosts()) {
|
||||
for (let host of Utils.getSyncCredentialsHosts()) {
|
||||
let logins = Services.logins.findLogins({}, host, "", "");
|
||||
for (let login of logins) {
|
||||
Services.logins.removeLogin(login);
|
||||
|
@ -484,31 +356,6 @@ this.BrowserIDManager.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The current state of the auth credentials.
|
||||
*
|
||||
* This essentially validates that enough credentials are available to use
|
||||
* Sync. It doesn't check we have all the keys we need as the master-password
|
||||
* may have been locked when we tried to get them - we rely on
|
||||
* unlockAndVerifyAuthState to check that for us.
|
||||
*/
|
||||
get currentAuthState() {
|
||||
if (this._authFailureReason) {
|
||||
this._log.info("currentAuthState returning " + this._authFailureReason +
|
||||
" due to previous failure");
|
||||
return this._authFailureReason;
|
||||
}
|
||||
|
||||
// TODO: need to revisit this. Currently this isn't ready to go until
|
||||
// both the username and syncKeyBundle are both configured and having no
|
||||
// username seems to make things fail fast so that's good.
|
||||
if (!this.username) {
|
||||
return LOGIN_FAILED_NO_USERNAME;
|
||||
}
|
||||
|
||||
return STATUS_OK;
|
||||
},
|
||||
|
||||
/**
|
||||
* Verify the current auth state, unlocking the master-password if necessary.
|
||||
*
|
||||
|
@ -516,8 +363,22 @@ this.BrowserIDManager.prototype = {
|
|||
* attempting to unlock.
|
||||
*/
|
||||
async unlockAndVerifyAuthState() {
|
||||
let data = await this._fxaService.getSignedInUser();
|
||||
if (!data) {
|
||||
log.debug("unlockAndVerifyAuthState has no user");
|
||||
return LOGIN_FAILED_NO_USERNAME;
|
||||
}
|
||||
if (!data.verified) {
|
||||
// Treat not verified as if the user needs to re-auth, so the browser
|
||||
// UI reflects the state.
|
||||
log.debug("unlockAndVerifyAuthState has an unverified user");
|
||||
telemetryHelper.maybeRecordLoginState(telemetryHelper.STATES.NOTVERIFIED);
|
||||
return LOGIN_FAILED_LOGIN_REJECTED;
|
||||
}
|
||||
this._updateSignedInUser(data);
|
||||
if ((await this._fxaService.canGetKeys())) {
|
||||
log.debug("unlockAndVerifyAuthState already has (or can fetch) sync keys");
|
||||
telemetryHelper.maybeRecordLoginState(telemetryHelper.STATES.SUCCESS);
|
||||
return STATUS_OK;
|
||||
}
|
||||
// so no keys - ensure MP unlocked.
|
||||
|
@ -528,15 +389,16 @@ this.BrowserIDManager.prototype = {
|
|||
}
|
||||
// now we are unlocked we must re-fetch the user data as we may now have
|
||||
// the details that were previously locked away.
|
||||
const accountData = await this._fxaService.getSignedInUser();
|
||||
this._updateSignedInUser(accountData);
|
||||
this._updateSignedInUser(await this._fxaService.getSignedInUser());
|
||||
// If we still can't get keys it probably means the user authenticated
|
||||
// without unlocking the MP or cleared the saved logins, so we've now
|
||||
// lost them - the user will need to reauth before continuing.
|
||||
let result;
|
||||
if ((await this._fxaService.canGetKeys())) {
|
||||
telemetryHelper.maybeRecordLoginState(telemetryHelper.STATES.SUCCESS);
|
||||
result = STATUS_OK;
|
||||
} else {
|
||||
telemetryHelper.maybeRecordLoginState(telemetryHelper.STATES.REJECTED);
|
||||
result = LOGIN_FAILED_LOGIN_REJECTED;
|
||||
}
|
||||
log.debug("unlockAndVerifyAuthState re-fetched credentials and is returning", result);
|
||||
|
@ -547,7 +409,7 @@ this.BrowserIDManager.prototype = {
|
|||
* Do we have a non-null, not yet expired token for the user currently
|
||||
* signed in?
|
||||
*/
|
||||
hasValidToken() {
|
||||
_hasValidToken() {
|
||||
// If pref is set to ignore cached authentication credentials for debugging,
|
||||
// then return false to force the fetching of a new token.
|
||||
if (IGNORE_CACHED_AUTH_CREDENTIALS) {
|
||||
|
@ -578,78 +440,66 @@ this.BrowserIDManager.prototype = {
|
|||
},
|
||||
|
||||
// Refresh the sync token for our user. Returns a promise that resolves
|
||||
// with a token (which may be null in one sad edge-case), or rejects with an
|
||||
// error.
|
||||
// with a token, or rejects with an error.
|
||||
async _fetchTokenForUser() {
|
||||
// tokenServerURI is mis-named - convention is uri means nsISomething...
|
||||
let tokenServerURI = this._tokenServerUrl;
|
||||
let log = this._log;
|
||||
let client = this._tokenServerClient;
|
||||
let fxa = this._fxaService;
|
||||
let userData = this._signedInUser;
|
||||
// gotta be verified to fetch a token.
|
||||
if (!this._signedInUser.verified) {
|
||||
throw new Error("User is not verified");
|
||||
}
|
||||
|
||||
// We need keys for things to work. If we don't have them, just
|
||||
// return null for the token - sync calling unlockAndVerifyAuthState()
|
||||
// before actually syncing will setup the error states if necessary.
|
||||
if (!(await this._fxaService.canGetKeys())) {
|
||||
log.info("Unable to fetch keys (master-password locked?), so aborting token fetch");
|
||||
return Promise.resolve(null);
|
||||
this._log.info("Unable to fetch keys (master-password locked?), so aborting token fetch");
|
||||
throw new Error("Can't fetch a token as we can't get keys");
|
||||
}
|
||||
|
||||
let maybeFetchKeys = () => {
|
||||
log.info("Getting keys");
|
||||
return this._fxaService.getKeys().then(
|
||||
newUserData => {
|
||||
userData = newUserData;
|
||||
this._updateSignedInUser(userData); // throws if the user changed.
|
||||
}
|
||||
);
|
||||
};
|
||||
// Do the assertion/certificate/token dance, with a retry.
|
||||
let getToken = async () => {
|
||||
this._log.info("Getting an assertion from", this._tokenServerUrl);
|
||||
const audience = Services.io.newURI(this._tokenServerUrl).prePath;
|
||||
const assertion = await this._fxaService.getAssertion(audience);
|
||||
|
||||
let getToken = async (assertion) => {
|
||||
log.debug("Getting a token");
|
||||
let headers = {"X-Client-State": userData.kXCS};
|
||||
// Exceptions will be handled by the caller.
|
||||
const token = await client.getTokenFromBrowserIDAssertion(tokenServerURI, assertion, headers);
|
||||
log.debug("Successfully got a sync token");
|
||||
this._log.debug("Getting a token");
|
||||
const headers = {"X-Client-State": this._signedInUser.kXCS};
|
||||
const token = await this._tokenServerClient.getTokenFromBrowserIDAssertion(
|
||||
this._tokenServerUrl, assertion, headers);
|
||||
this._log.trace("Successfully got a token");
|
||||
return token;
|
||||
};
|
||||
|
||||
let getAssertion = () => {
|
||||
log.info("Getting an assertion from", tokenServerURI);
|
||||
let audience = Services.io.newURI(tokenServerURI).prePath;
|
||||
return fxa.getAssertion(audience);
|
||||
};
|
||||
let token;
|
||||
try {
|
||||
try {
|
||||
this._log.info("Getting keys");
|
||||
this._updateSignedInUser(await this._fxaService.getKeys()); // throws if the user changed.
|
||||
|
||||
// wait until the account email is verified and we know that
|
||||
// getAssertion() will return a real assertion (not null).
|
||||
return fxa.whenVerified(this._signedInUser)
|
||||
.then(() => maybeFetchKeys())
|
||||
.then(() => getAssertion())
|
||||
.then(assertion => getToken(assertion))
|
||||
.catch(err => {
|
||||
token = await getToken();
|
||||
} catch (err) {
|
||||
// If we get a 401 fetching the token it may be that our certificate
|
||||
// needs to be regenerated.
|
||||
if (!err.response || err.response.status !== 401) {
|
||||
return Promise.reject(err);
|
||||
throw err;
|
||||
}
|
||||
this._log.warn("Token server returned 401, refreshing certificate and retrying token fetch");
|
||||
await this._fxaService.invalidateCertificate();
|
||||
token = await getToken();
|
||||
}
|
||||
log.warn("Token server returned 401, refreshing certificate and retrying token fetch");
|
||||
return fxa.invalidateCertificate()
|
||||
.then(() => getAssertion())
|
||||
.then(assertion => getToken(assertion));
|
||||
})
|
||||
.then(token => {
|
||||
// TODO: Make it be only 80% of the duration, so refresh the token
|
||||
// before it actually expires. This is to avoid sync storage errors
|
||||
// otherwise, we get a nasty notification bar briefly. Bug 966568.
|
||||
// otherwise, we may briefly enter a "needs reauthentication" state.
|
||||
// (XXX - the above may no longer be true - someone should check ;)
|
||||
token.expiration = this._now() + (token.duration * 1000) * 0.80;
|
||||
if (!this._syncKeyBundle) {
|
||||
this._syncKeyBundle = BulkKeyBundle.fromHexKey(userData.kSync);
|
||||
this._syncKeyBundle = BulkKeyBundle.fromHexKey(this._signedInUser.kSync);
|
||||
}
|
||||
telemetryHelper.maybeRecordLoginState(telemetryHelper.STATES.SUCCESS);
|
||||
Weave.Status.login = LOGIN_SUCCEEDED;
|
||||
this._token = token;
|
||||
return token;
|
||||
})
|
||||
.catch(err => {
|
||||
} catch (caughtErr) {
|
||||
let err = caughtErr; // The error we will rethrow.
|
||||
// TODO: unify these errors - we need to handle errors thrown by
|
||||
// both tokenserverclient and hawkclient.
|
||||
// A tokenserver error thrown based on a bad response.
|
||||
|
@ -669,53 +519,64 @@ this.BrowserIDManager.prototype = {
|
|||
if (err instanceof AuthenticationError) {
|
||||
this._log.error("Authentication error in _fetchTokenForUser", err);
|
||||
// set it to the "fatal" LOGIN_FAILED_LOGIN_REJECTED reason.
|
||||
this._authFailureReason = LOGIN_FAILED_LOGIN_REJECTED;
|
||||
Weave.Status.login = LOGIN_FAILED_LOGIN_REJECTED;
|
||||
telemetryHelper.maybeRecordLoginState(telemetryHelper.STATES.REJECTED);
|
||||
} else {
|
||||
this._log.error("Non-authentication error in _fetchTokenForUser", err);
|
||||
// for now assume it is just a transient network related problem
|
||||
// (although sadly, it might also be a regular unhandled exception)
|
||||
this._authFailureReason = LOGIN_FAILED_NETWORK_ERROR;
|
||||
Weave.Status.login = LOGIN_FAILED_NETWORK_ERROR;
|
||||
}
|
||||
// this._authFailureReason being set to be non-null in the above if clause
|
||||
// ensures we are in the correct currentAuthState, and
|
||||
// this._shouldHaveSyncKeyBundle being true ensures everything that cares knows
|
||||
// that there is no authentication dance still under way.
|
||||
this._shouldHaveSyncKeyBundle = true;
|
||||
Weave.Status.login = this._authFailureReason;
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// Returns a promise that is resolved when we have a valid token for the
|
||||
// current user stored in this._token. When resolved, this._token is valid.
|
||||
_ensureValidToken() {
|
||||
if (this.hasValidToken()) {
|
||||
this._log.debug("_ensureValidToken already has one");
|
||||
return Promise.resolve();
|
||||
// Returns a promise that is resolved with a valid token for the current
|
||||
// user, or rejects if one can't be obtained.
|
||||
// NOTE: This does all the authentication for Sync - it both sets the
|
||||
// key bundle (ie, decryption keys) and does the token fetch. These 2
|
||||
// concepts could be decoupled, but there doesn't seem any value in that
|
||||
// currently.
|
||||
async _ensureValidToken(forceNewToken = false) {
|
||||
if (!this._signedInUser) {
|
||||
throw new Error("no user is logged in");
|
||||
}
|
||||
const notifyStateChanged =
|
||||
() => Services.obs.notifyObservers(null, "weave:service:login:change");
|
||||
if (!this._signedInUser.verified) {
|
||||
throw new Error("user is not verified");
|
||||
}
|
||||
|
||||
await this.asyncObserver.promiseObserversComplete();
|
||||
|
||||
if (!forceNewToken && this._hasValidToken()) {
|
||||
this._log.trace("_ensureValidToken already has one");
|
||||
return this._token;
|
||||
}
|
||||
|
||||
// We are going to grab a new token - re-use the same promise if we are
|
||||
// already fetching one.
|
||||
if (!this._ensureValidTokenPromise) {
|
||||
this._ensureValidTokenPromise = this.__ensureValidToken().finally(() => {
|
||||
this._ensureValidTokenPromise = null;
|
||||
});
|
||||
}
|
||||
return this._ensureValidTokenPromise;
|
||||
},
|
||||
|
||||
async __ensureValidToken() {
|
||||
// reset this._token as a safety net to reduce the possibility of us
|
||||
// repeatedly attempting to use an invalid token if _fetchTokenForUser throws.
|
||||
this._token = null;
|
||||
return this._fetchTokenForUser().then(
|
||||
token => {
|
||||
try {
|
||||
let token = await this._fetchTokenForUser();
|
||||
this._token = token;
|
||||
// we store the hashed UID from the token so that if we see a transient
|
||||
// error fetching a new token we still know the "most recent" hashed
|
||||
// UID for telemetry.
|
||||
if (token) {
|
||||
// We may not have a token if the master-password is locked.
|
||||
this._hashedUID = token.hashed_fxa_uid;
|
||||
return token;
|
||||
} finally {
|
||||
Services.obs.notifyObservers(null, "weave:service:login:change");
|
||||
}
|
||||
notifyStateChanged();
|
||||
},
|
||||
error => {
|
||||
notifyStateChanged();
|
||||
throw error;
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
getResourceAuthenticator() {
|
||||
|
@ -758,36 +619,10 @@ this.BrowserIDManager.prototype = {
|
|||
return {headers: {authorization: headerValue.field}};
|
||||
},
|
||||
|
||||
createClusterManager(service) {
|
||||
return new BrowserIDClusterManager(service);
|
||||
},
|
||||
|
||||
// Tell Sync what the login status should be if it saw a 401 fetching
|
||||
// info/collections as part of login verification (typically immediately
|
||||
// after login.)
|
||||
// In our case, it almost certainly means a transient error fetching a token
|
||||
// (and hitting this will cause us to logout, which will correctly handle an
|
||||
// authoritative login issue.)
|
||||
loginStatusFromVerification404() {
|
||||
return LOGIN_FAILED_NETWORK_ERROR;
|
||||
},
|
||||
};
|
||||
|
||||
/* An implementation of the ClusterManager for this identity
|
||||
*/
|
||||
|
||||
function BrowserIDClusterManager(service) {
|
||||
this._log = log;
|
||||
this.service = service;
|
||||
}
|
||||
|
||||
BrowserIDClusterManager.prototype = {
|
||||
get identity() {
|
||||
return this.service.identity;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determine the cluster for the current user and update state.
|
||||
* Returns true if a new cluster URL was found and it is different from
|
||||
* the existing cluster URL, false otherwise.
|
||||
*/
|
||||
async setCluster() {
|
||||
// Make sure we didn't get some unexpected response for the cluster.
|
||||
|
@ -801,12 +636,12 @@ BrowserIDClusterManager.prototype = {
|
|||
// resource.js returns to a plain-old string.
|
||||
cluster = cluster.toString();
|
||||
// Don't update stuff if we already have the right cluster
|
||||
if (cluster == this.service.clusterURL) {
|
||||
if (cluster == Weave.Service.clusterURL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this._log.debug("Setting cluster to " + cluster);
|
||||
this.service.clusterURL = cluster;
|
||||
Weave.Service.clusterURL = cluster;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
@ -814,39 +649,27 @@ BrowserIDClusterManager.prototype = {
|
|||
async _findCluster() {
|
||||
try {
|
||||
// Ensure we are ready to authenticate and have a valid token.
|
||||
await this.identity.whenReadyToAuthenticate.promise;
|
||||
// We need to handle node reassignment here. If we are being asked
|
||||
// for a clusterURL while the service already has a clusterURL, then
|
||||
// it's likely a 401 was received using the existing token - in which
|
||||
// case we just discard the existing token and fetch a new one.
|
||||
if (this.service.clusterURL) {
|
||||
log.debug("_findCluster has a pre-existing clusterURL, so discarding the current token");
|
||||
this.identity._token = null;
|
||||
let forceNewToken = false;
|
||||
if (Weave.Service.clusterURL) {
|
||||
this._log.debug("_findCluster has a pre-existing clusterURL, so fetching a new token token");
|
||||
forceNewToken = true;
|
||||
}
|
||||
await this.identity._ensureValidToken();
|
||||
|
||||
// The only reason (in theory ;) that we can end up with a null token
|
||||
// is when this._fxaService.canGetKeys() returned false. In turn, this
|
||||
// should only happen if the master-password is locked or the credentials
|
||||
// storage is screwed, and in those cases we shouldn't have started
|
||||
// syncing so shouldn't get here anyway.
|
||||
// But better safe than sorry! To keep things clearer, throw an explicit
|
||||
// exception - the message will appear in the logs and the error will be
|
||||
// treated as transient.
|
||||
if (!this.identity._token) {
|
||||
throw new Error("Can't get a cluster URL as we can't fetch keys.");
|
||||
}
|
||||
let endpoint = this.identity._token.endpoint;
|
||||
let token = await this._ensureValidToken(forceNewToken);
|
||||
let endpoint = token.endpoint;
|
||||
// For Sync 1.5 storage endpoints, we use the base endpoint verbatim.
|
||||
// However, it should end in "/" because we will extend it with
|
||||
// well known path components. So we add a "/" if it's missing.
|
||||
if (!endpoint.endsWith("/")) {
|
||||
endpoint += "/";
|
||||
}
|
||||
log.debug("_findCluster returning " + endpoint);
|
||||
this._log.debug("_findCluster returning " + endpoint);
|
||||
return endpoint;
|
||||
} catch (err) {
|
||||
log.info("Failed to fetch the cluster URL", err);
|
||||
this._log.info("Failed to fetch the cluster URL", err);
|
||||
// service.js's verifyLogin() method will attempt to fetch a cluster
|
||||
// URL when it sees a 401. If it gets null, it treats it as a "real"
|
||||
// auth error and sets Status.login to LOGIN_FAILED_LOGIN_REJECTED, which
|
||||
|
@ -865,13 +688,4 @@ BrowserIDClusterManager.prototype = {
|
|||
throw err;
|
||||
}
|
||||
},
|
||||
|
||||
getUserBaseURL() {
|
||||
// Legacy Sync and FxA Sync construct the userBaseURL differently. Legacy
|
||||
// Sync appends path components onto an empty path, and in FxA Sync the
|
||||
// token server constructs this for us in an opaque manner. Since the
|
||||
// cluster manager already sets the clusterURL on Service and also has
|
||||
// access to the current identity, we added this functionality here.
|
||||
return this.service.clusterURL;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -19,12 +19,6 @@ SYNC_API_VERSION: "1.5",
|
|||
STORAGE_VERSION: 5,
|
||||
PREFS_BRANCH: "services.sync.",
|
||||
|
||||
// Host "key" to access Weave Identity in the password manager
|
||||
PWDMGR_HOST: "chrome://weave",
|
||||
PWDMGR_PASSWORD_REALM: "Mozilla Services Password",
|
||||
PWDMGR_PASSPHRASE_REALM: "Mozilla Services Encryption Passphrase",
|
||||
PWDMGR_KEYBUNDLE_REALM: "Mozilla Services Key Bundles",
|
||||
|
||||
// Put in [] because those aren't allowed in a collection name.
|
||||
DEFAULT_KEYBUNDLE_NAME: "[default]",
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ Sync11Service.prototype = {
|
|||
storageURL: null,
|
||||
metaURL: null,
|
||||
cryptoKeyURL: null,
|
||||
// The cluster URL comes via the ClusterManager object, which in the FxA
|
||||
// The cluster URL comes via the identity object, which in the FxA
|
||||
// world is ebbedded in the token returned from the token server.
|
||||
_clusterURL: null,
|
||||
|
||||
|
@ -129,10 +129,8 @@ Sync11Service.prototype = {
|
|||
},
|
||||
|
||||
get userBaseURL() {
|
||||
if (!this._clusterManager) {
|
||||
return null;
|
||||
}
|
||||
return this._clusterManager.getUserBaseURL();
|
||||
// The user URL is the cluster URL.
|
||||
return this.clusterURL;
|
||||
},
|
||||
|
||||
_updateCachedURLs: function _updateCachedURLs() {
|
||||
|
@ -297,7 +295,6 @@ Sync11Service.prototype = {
|
|||
|
||||
this._log.info("Loading Weave " + WEAVE_VERSION);
|
||||
|
||||
this._clusterManager = this.identity.createClusterManager(this);
|
||||
this.recordManager = new RecordManager(this);
|
||||
|
||||
this.enabled = true;
|
||||
|
@ -656,22 +653,15 @@ Sync11Service.prototype = {
|
|||
},
|
||||
|
||||
async verifyLogin(allow40XRecovery = true) {
|
||||
if (!this.identity.username) {
|
||||
this._log.warn("No username in verifyLogin.");
|
||||
this.status.login = LOGIN_FAILED_NO_USERNAME;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Attaching auth credentials to a request requires access to
|
||||
// passwords, which means that Resource.get can throw MP-related
|
||||
// exceptions!
|
||||
// So we ask the identity to verify the login state after unlocking the
|
||||
// master password (ie, this call is expected to prompt for MP unlock
|
||||
// if necessary) while we still have control.
|
||||
let unlockedState = await this.identity.unlockAndVerifyAuthState();
|
||||
this._log.debug("Fetching unlocked auth state returned " + unlockedState);
|
||||
if (unlockedState != STATUS_OK) {
|
||||
this.status.login = unlockedState;
|
||||
this.status.login = await this.identity.unlockAndVerifyAuthState();
|
||||
this._log.debug("Fetching unlocked auth state returned " + this.status.login);
|
||||
if (this.status.login != STATUS_OK) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -679,7 +669,7 @@ Sync11Service.prototype = {
|
|||
// Make sure we have a cluster to verify against.
|
||||
// This is a little weird, if we don't get a node we pretend
|
||||
// to succeed, since that probably means we just don't have storage.
|
||||
if (this.clusterURL == "" && !(await this._clusterManager.setCluster())) {
|
||||
if (this.clusterURL == "" && !(await this.identity.setCluster())) {
|
||||
this.status.sync = NO_SYNC_NODE_FOUND;
|
||||
return true;
|
||||
}
|
||||
|
@ -718,16 +708,13 @@ Sync11Service.prototype = {
|
|||
|
||||
case 404:
|
||||
// Check that we're verifying with the correct cluster
|
||||
if (allow40XRecovery && (await this._clusterManager.setCluster())) {
|
||||
if (allow40XRecovery && (await this.identity.setCluster())) {
|
||||
return await this.verifyLogin(false);
|
||||
}
|
||||
|
||||
// We must have the right cluster, but the server doesn't expect us.
|
||||
// The implications of this depend on the identity being used - for
|
||||
// the legacy identity, it's an authoritatively "incorrect password",
|
||||
// (ie, LOGIN_FAILED_LOGIN_REJECTED) but for FxA it probably means
|
||||
// "transient error fetching auth token".
|
||||
this.status.login = this.identity.loginStatusFromVerification404();
|
||||
// For FxA this almost certainly means "transient error fetching token".
|
||||
this.status.login = LOGIN_FAILED_NETWORK_ERROR;
|
||||
return false;
|
||||
|
||||
default:
|
||||
|
@ -826,8 +813,8 @@ Sync11Service.prototype = {
|
|||
// possible, so let's fake for the CLIENT_NOT_CONFIGURED status for now
|
||||
// by emptying the passphrase (we still need the password).
|
||||
this._log.info("Service.startOver dropping sync key and logging out.");
|
||||
this.identity.resetSyncKeyBundle();
|
||||
this.status.login = LOGIN_FAILED_NO_PASSPHRASE;
|
||||
this.identity.resetCredentials();
|
||||
this.status.login = LOGIN_FAILED_NO_USERNAME;
|
||||
this.logout();
|
||||
Svc.Obs.notify("weave:service:start-over");
|
||||
|
||||
|
@ -850,7 +837,6 @@ Sync11Service.prototype = {
|
|||
this.identity.finalize();
|
||||
this.status.__authManager = null;
|
||||
this.identity = Status._authManager;
|
||||
this._clusterManager = this.identity.createClusterManager(this);
|
||||
Svc.Obs.notify("weave:service:start-over:finish");
|
||||
} catch (err) {
|
||||
this._log.error("startOver failed to re-initialize the identity manager", err);
|
||||
|
@ -868,22 +854,14 @@ Sync11Service.prototype = {
|
|||
throw new Error("Application is offline, login should not be called");
|
||||
}
|
||||
|
||||
this._log.info("Logging in the user.");
|
||||
// Just let any errors bubble up - they've more context than we do!
|
||||
try {
|
||||
await this.identity.ensureLoggedIn();
|
||||
} finally {
|
||||
this._checkSetup(); // _checkSetup has a side effect of setting the right state.
|
||||
}
|
||||
|
||||
this._updateCachedURLs();
|
||||
|
||||
this._log.info("User logged in successfully - verifying login.");
|
||||
if (!(await this.verifyLogin())) {
|
||||
// verifyLogin sets the failure states here.
|
||||
throw new Error(`Login failed: ${this.status.login}`);
|
||||
}
|
||||
|
||||
this._updateCachedURLs();
|
||||
|
||||
this._loggedIn = true;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -49,7 +49,7 @@ EngineSynchronizer.prototype = {
|
|||
}
|
||||
|
||||
// If we don't have a node, get one. If that fails, retry in 10 minutes.
|
||||
if (!this.service.clusterURL && !(await this.service._clusterManager.setCluster())) {
|
||||
if (!this.service.clusterURL && !(await this.service.identity.setCluster())) {
|
||||
this.service.status.sync = NO_SYNC_NODE_FOUND;
|
||||
this._log.info("No cluster URL found. Cannot sync.");
|
||||
return;
|
||||
|
|
|
@ -19,7 +19,6 @@ var Status = {
|
|||
return this.__authManager;
|
||||
}
|
||||
this.__authManager = new BrowserIDManager();
|
||||
this.__authManager.initialize();
|
||||
return this.__authManager;
|
||||
},
|
||||
|
||||
|
@ -92,13 +91,12 @@ var Status = {
|
|||
},
|
||||
|
||||
checkSetup: function checkSetup() {
|
||||
let result = this._authManager.currentAuthState;
|
||||
if (result == STATUS_OK) {
|
||||
Status.service = result;
|
||||
return result;
|
||||
if (!this._authManager.username) {
|
||||
Status.login = LOGIN_FAILED_NO_USERNAME;
|
||||
Status.service = CLIENT_NOT_CONFIGURED;
|
||||
} else if (Status.login == STATUS_OK) {
|
||||
Status.service = STATUS_OK;
|
||||
}
|
||||
|
||||
Status.login = result;
|
||||
return Status.service;
|
||||
},
|
||||
|
||||
|
|
|
@ -609,25 +609,6 @@ var Utils = {
|
|||
* reset when we drop sync credentials, etc.
|
||||
*/
|
||||
getSyncCredentialsHosts() {
|
||||
let result = new Set(this.getSyncCredentialsHostsLegacy());
|
||||
for (let host of this.getSyncCredentialsHostsFxA()) {
|
||||
result.add(host);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
/*
|
||||
* Get the "legacy" identity hosts.
|
||||
*/
|
||||
getSyncCredentialsHostsLegacy() {
|
||||
// the legacy sync host
|
||||
return new Set([PWDMGR_HOST]);
|
||||
},
|
||||
|
||||
/*
|
||||
* Get the FxA identity hosts.
|
||||
*/
|
||||
getSyncCredentialsHostsFxA() {
|
||||
let result = new Set();
|
||||
// the FxA host
|
||||
result.add(FxAccountsCommon.FXA_PWDMGR_HOST);
|
||||
|
|
|
@ -64,23 +64,22 @@ function MockFxAccounts() {
|
|||
add_test(function test_initial_state() {
|
||||
_("Verify initial state");
|
||||
Assert.ok(!globalBrowseridManager._token);
|
||||
Assert.ok(!globalBrowseridManager.hasValidToken());
|
||||
Assert.ok(!globalBrowseridManager._hasValidToken());
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
|
||||
add_task(async function test_initialializeWithCurrentIdentity() {
|
||||
_("Verify start after initializeWithCurrentIdentity");
|
||||
globalBrowseridManager.initializeWithCurrentIdentity();
|
||||
await globalBrowseridManager.whenReadyToAuthenticate.promise;
|
||||
add_task(async function test_initialialize() {
|
||||
_("Verify start after fetching token");
|
||||
await globalBrowseridManager._ensureValidToken();
|
||||
Assert.ok(!!globalBrowseridManager._token);
|
||||
Assert.ok(globalBrowseridManager.hasValidToken());
|
||||
Assert.ok(globalBrowseridManager._hasValidToken());
|
||||
Assert.deepEqual(getLoginTelemetryScalar(), {SUCCESS: 1});
|
||||
}
|
||||
);
|
||||
|
||||
add_task(async function test_initialializeWithAuthErrorAndDeletedAccount() {
|
||||
_("Verify sync unpair after initializeWithCurrentIdentity with auth error + account deleted");
|
||||
_("Verify sync state with auth error + account deleted");
|
||||
|
||||
var identityConfig = makeIdentityConfig();
|
||||
var browseridManager = new BrowserIDManager();
|
||||
|
@ -117,33 +116,16 @@ add_task(async function test_initialializeWithAuthErrorAndDeletedAccount() {
|
|||
let mockFxAClient = new AuthErrorMockFxAClient();
|
||||
browseridManager._fxaService.internal._fxAccountsClient = mockFxAClient;
|
||||
|
||||
await browseridManager.initializeWithCurrentIdentity();
|
||||
await Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
||||
await Assert.rejects(browseridManager._ensureValidToken(),
|
||||
"should reject due to an auth error");
|
||||
|
||||
Assert.ok(signCertificateCalled);
|
||||
Assert.ok(accountStatusCalled);
|
||||
Assert.ok(!browseridManager._token);
|
||||
Assert.ok(!browseridManager.hasValidToken());
|
||||
Assert.ok(!browseridManager._hasValidToken());
|
||||
Assert.deepEqual(getLoginTelemetryScalar(), {REJECTED: 1});
|
||||
});
|
||||
|
||||
add_task(async function test_initialializeWithNoKeys() {
|
||||
_("Verify start after initializeWithCurrentIdentity without kSync, kXCS, kExtSync, kExtKbHash or keyFetchToken");
|
||||
let identityConfig = makeIdentityConfig();
|
||||
delete identityConfig.fxaccount.user.kSync;
|
||||
delete identityConfig.fxaccount.user.kXCS;
|
||||
delete identityConfig.fxaccount.user.kExtSync;
|
||||
delete identityConfig.fxaccount.user.kExtKbHash;
|
||||
// there's no keyFetchToken by default, so the initialize should fail.
|
||||
configureFxAccountIdentity(globalBrowseridManager, identityConfig);
|
||||
|
||||
await globalBrowseridManager.initializeWithCurrentIdentity();
|
||||
await globalBrowseridManager.whenReadyToAuthenticate.promise;
|
||||
Assert.equal(Status.login, LOGIN_SUCCEEDED, "login succeeded even without keys");
|
||||
Assert.equal(globalBrowseridManager._token, null, "we don't have a token");
|
||||
});
|
||||
|
||||
add_task(async function test_getResourceAuthenticator() {
|
||||
_("BrowserIDManager supplies a Resource Authenticator callback which returns a Hawk header.");
|
||||
configureFxAccountIdentity(globalBrowseridManager);
|
||||
|
@ -266,34 +248,19 @@ add_task(async function test_RESTResourceAuthenticatorSkew() {
|
|||
|
||||
add_task(async function test_ensureLoggedIn() {
|
||||
configureFxAccountIdentity(globalBrowseridManager);
|
||||
await globalBrowseridManager.initializeWithCurrentIdentity();
|
||||
await globalBrowseridManager.whenReadyToAuthenticate.promise;
|
||||
await globalBrowseridManager._ensureValidToken();
|
||||
Assert.equal(Status.login, LOGIN_SUCCEEDED, "original initialize worked");
|
||||
await globalBrowseridManager.ensureLoggedIn();
|
||||
Assert.equal(Status.login, LOGIN_SUCCEEDED, "original ensureLoggedIn worked");
|
||||
Assert.ok(globalBrowseridManager._shouldHaveSyncKeyBundle,
|
||||
"_shouldHaveSyncKeyBundle should always be true after ensureLogin completes.");
|
||||
Assert.ok(globalBrowseridManager._token);
|
||||
|
||||
// arrange for no logged in user.
|
||||
let fxa = globalBrowseridManager._fxaService;
|
||||
let signedInUser = fxa.internal.currentAccountState.storageManager.accountData;
|
||||
fxa.internal.currentAccountState.storageManager.accountData = null;
|
||||
globalBrowseridManager.initializeWithCurrentIdentity();
|
||||
Assert.ok(!globalBrowseridManager._shouldHaveSyncKeyBundle,
|
||||
"_shouldHaveSyncKeyBundle should be false so we know we are testing what we think we are.");
|
||||
Status.login = LOGIN_FAILED_NO_USERNAME;
|
||||
await Assert.rejects(globalBrowseridManager.ensureLoggedIn(), "expecting rejection due to no user");
|
||||
Assert.ok(globalBrowseridManager._shouldHaveSyncKeyBundle,
|
||||
"_shouldHaveSyncKeyBundle should always be true after ensureLogin completes.");
|
||||
await Assert.rejects(globalBrowseridManager._ensureValidToken(true), "expecting rejection due to no user");
|
||||
// Restore the logged in user to what it was.
|
||||
fxa.internal.currentAccountState.storageManager.accountData = signedInUser;
|
||||
Status.login = LOGIN_FAILED_LOGIN_REJECTED;
|
||||
await Assert.rejects(globalBrowseridManager.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;
|
||||
await globalBrowseridManager.ensureLoggedIn();
|
||||
await globalBrowseridManager._ensureValidToken();
|
||||
Assert.equal(Status.login, LOGIN_SUCCEEDED, "final ensureLoggedIn worked");
|
||||
});
|
||||
|
||||
|
@ -319,7 +286,7 @@ add_task(async function test_tokenExpiration() {
|
|||
});
|
||||
Assert.ok(bimExp._token.expiration < bimExp._now());
|
||||
_("... means BrowserIDManager knows to re-fetch it on the next call.");
|
||||
Assert.ok(!bimExp.hasValidToken());
|
||||
Assert.ok(!bimExp._hasValidToken());
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -334,8 +301,7 @@ add_task(async function test_getTokenErrors() {
|
|||
});
|
||||
let browseridManager = Service.identity;
|
||||
|
||||
await browseridManager.initializeWithCurrentIdentity();
|
||||
await Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
||||
await Assert.rejects(browseridManager._ensureValidToken(),
|
||||
"should reject due to 401");
|
||||
Assert.equal(Status.login, LOGIN_FAILED_LOGIN_REJECTED, "login was rejected");
|
||||
|
||||
|
@ -350,8 +316,7 @@ add_task(async function test_getTokenErrors() {
|
|||
body: "",
|
||||
});
|
||||
browseridManager = Service.identity;
|
||||
await browseridManager.initializeWithCurrentIdentity();
|
||||
await Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
||||
await Assert.rejects(browseridManager._ensureValidToken(),
|
||||
"should reject due to non-JSON response");
|
||||
Assert.equal(Status.login, LOGIN_FAILED_NETWORK_ERROR, "login state is LOGIN_FAILED_NETWORK_ERROR");
|
||||
});
|
||||
|
@ -409,14 +374,13 @@ add_task(async function test_refreshCertificateOn401() {
|
|||
|
||||
browseridManager._tokenServerClient = mockTSC;
|
||||
|
||||
await browseridManager.initializeWithCurrentIdentity();
|
||||
await browseridManager.whenReadyToAuthenticate.promise;
|
||||
await browseridManager._ensureValidToken();
|
||||
|
||||
Assert.equal(getCertCount, 2);
|
||||
Assert.ok(didReturn401);
|
||||
Assert.ok(didReturn200);
|
||||
Assert.ok(browseridManager._token);
|
||||
Assert.ok(browseridManager.hasValidToken());
|
||||
Assert.ok(browseridManager._hasValidToken());
|
||||
});
|
||||
|
||||
|
||||
|
@ -436,8 +400,7 @@ add_task(async function test_getTokenErrorWithRetry() {
|
|||
});
|
||||
let browseridManager = Service.identity;
|
||||
|
||||
await browseridManager.initializeWithCurrentIdentity();
|
||||
await Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
||||
await Assert.rejects(browseridManager._ensureValidToken(),
|
||||
"should reject due to 503");
|
||||
|
||||
// The observer should have fired - check it got the value in the response.
|
||||
|
@ -455,8 +418,7 @@ add_task(async function test_getTokenErrorWithRetry() {
|
|||
});
|
||||
browseridManager = Service.identity;
|
||||
|
||||
await browseridManager.initializeWithCurrentIdentity();
|
||||
await Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
||||
await Assert.rejects(browseridManager._ensureValidToken(),
|
||||
"should reject due to no token in response");
|
||||
|
||||
// The observer should have fired - check it got the value in the response.
|
||||
|
@ -490,7 +452,7 @@ add_task(async function test_getKeysErrorWithBackoff() {
|
|||
});
|
||||
|
||||
let browseridManager = Service.identity;
|
||||
await Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
||||
await Assert.rejects(browseridManager._ensureValidToken(),
|
||||
"should reject due to 503");
|
||||
|
||||
// The observer should have fired - check it got the value in the response.
|
||||
|
@ -526,7 +488,7 @@ add_task(async function test_getKeysErrorWithRetry() {
|
|||
});
|
||||
|
||||
let browseridManager = Service.identity;
|
||||
await Assert.rejects(browseridManager.whenReadyToAuthenticate.promise,
|
||||
await Assert.rejects(browseridManager._ensureValidToken(),
|
||||
"should reject due to 503");
|
||||
|
||||
// The observer should have fired - check it got the value in the response.
|
||||
|
@ -658,16 +620,8 @@ add_task(async function test_getKeysMissing() {
|
|||
|
||||
browseridManager._fxaService = fxa;
|
||||
|
||||
await browseridManager.initializeWithCurrentIdentity();
|
||||
|
||||
let ex;
|
||||
try {
|
||||
await browseridManager.whenReadyToAuthenticate.promise;
|
||||
} catch (e) {
|
||||
ex = e;
|
||||
}
|
||||
|
||||
Assert.equal(ex.message, "user data missing: kSync, kXCS, kExtSync, kExtKbHash");
|
||||
await Assert.rejects(browseridManager._ensureValidToken(),
|
||||
/user data missing: kSync, kXCS, kExtSync, kExtKbHash/);
|
||||
});
|
||||
|
||||
add_task(async function test_signedInUserMissing() {
|
||||
|
@ -701,6 +655,7 @@ add_task(async function test_signedInUserMissing() {
|
|||
});
|
||||
|
||||
browseridManager._fxaService = fxa;
|
||||
browseridManager._signedInUser = await fxa.getSignedInUser();
|
||||
|
||||
let status = await browseridManager.unlockAndVerifyAuthState();
|
||||
Assert.equal(status, LOGIN_FAILED_LOGIN_REJECTED);
|
||||
|
@ -774,9 +729,8 @@ async function initializeIdentityWithHAWKResponseFactory(config, cbGetResponse)
|
|||
let fxa = new FxAccounts(internal);
|
||||
|
||||
globalBrowseridManager._fxaService = fxa;
|
||||
globalBrowseridManager._signedInUser = null;
|
||||
await globalBrowseridManager.initializeWithCurrentIdentity();
|
||||
await Assert.rejects(globalBrowseridManager.whenReadyToAuthenticate.promise,
|
||||
globalBrowseridManager._signedInUser = await fxa.getSignedInUser();
|
||||
await Assert.rejects(globalBrowseridManager._ensureValidToken(true),
|
||||
"expecting rejection due to hawk error");
|
||||
}
|
||||
|
||||
|
|
|
@ -365,7 +365,7 @@ add_task(async function test_login_syncAndReportErrors_non_network_error() {
|
|||
// when calling syncAndReportErrors
|
||||
let server = await EHTestsCommon.sync_httpd_setup();
|
||||
await EHTestsCommon.setUp(server);
|
||||
Service.identity.resetSyncKeyBundle();
|
||||
Service.identity._syncKeyBundle = null;
|
||||
|
||||
let promiseObserved = promiseOneObserver("weave:ui:login:error");
|
||||
|
||||
|
@ -418,7 +418,7 @@ add_task(async function test_login_syncAndReportErrors_prolonged_non_network_err
|
|||
// reported when calling syncAndReportErrors.
|
||||
let server = await EHTestsCommon.sync_httpd_setup();
|
||||
await EHTestsCommon.setUp(server);
|
||||
Service.identity.resetSyncKeyBundle();
|
||||
Service.identity._syncKeyBundle = null;
|
||||
|
||||
let promiseObserved = promiseOneObserver("weave:ui:login:error");
|
||||
|
||||
|
@ -543,7 +543,7 @@ add_task(async function test_login_prolonged_non_network_error() {
|
|||
// Test prolonged, non-network errors are reported
|
||||
let server = await EHTestsCommon.sync_httpd_setup();
|
||||
await EHTestsCommon.setUp(server);
|
||||
Service.identity.resetSyncKeyBundle();
|
||||
Service.identity._syncKeyBundle = null;
|
||||
|
||||
let promiseObserved = promiseOneObserver("weave:ui:login:error");
|
||||
|
||||
|
@ -630,7 +630,7 @@ add_task(async function test_login_non_network_error() {
|
|||
// Test non-network errors are reported
|
||||
let server = await EHTestsCommon.sync_httpd_setup();
|
||||
await EHTestsCommon.setUp(server);
|
||||
Service.identity.resetSyncKeyBundle();
|
||||
Service.identity._syncKeyBundle = null;
|
||||
|
||||
let promiseObserved = promiseOneObserver("weave:ui:login:error");
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@ add_task(async function setup() {
|
|||
|
||||
// Setup the FxA identity manager and cluster manager.
|
||||
Status.__authManager = Service.identity = new BrowserIDManager();
|
||||
Service._clusterManager = Service.identity.createClusterManager(Service);
|
||||
|
||||
// None of the failures in this file should result in a UI error.
|
||||
function onUIError() {
|
||||
|
@ -56,8 +55,9 @@ function prepareServer(cbAfterTokenFetch) {
|
|||
__proto__: SyncServerCallback,
|
||||
onRequest(req, resp) {
|
||||
let full = `${req.scheme}://${req.host}:${req.port}${req.path}`;
|
||||
Assert.ok(full.startsWith(config.fxaccount.token.endpoint),
|
||||
`request made to ${full}`);
|
||||
let expected = config.fxaccount.token.endpoint;
|
||||
Assert.ok(full.startsWith(expected),
|
||||
`request made to ${full}, expected ${expected}`);
|
||||
}
|
||||
};
|
||||
let server = new SyncServer(callback);
|
||||
|
@ -81,6 +81,7 @@ function prepareServer(cbAfterTokenFetch) {
|
|||
let token = config.fxaccount.token;
|
||||
token.endpoint = server.baseURI + "1.1" + trailingZeros + "/johndoe";
|
||||
token.uid = config.username;
|
||||
_(`test server saw token fetch - endpoint now ${token.endpoint}`);
|
||||
numTokenRequests += 1;
|
||||
res(token);
|
||||
if (cbAfterTokenFetch) {
|
||||
|
|
|
@ -15,11 +15,7 @@ add_task(async function test_findCluster() {
|
|||
body: "",
|
||||
});
|
||||
|
||||
await Service.identity.initializeWithCurrentIdentity();
|
||||
await Assert.rejects(Service.identity.whenReadyToAuthenticate.promise,
|
||||
"should reject due to 500");
|
||||
|
||||
await Assert.rejects(Service._clusterManager._findCluster());
|
||||
await Assert.rejects(Service.identity._findCluster());
|
||||
|
||||
_("_findCluster() returns null on authentication errors.");
|
||||
initializeIdentityWithTokenServerResponse({
|
||||
|
@ -28,11 +24,7 @@ add_task(async function test_findCluster() {
|
|||
body: "{}",
|
||||
});
|
||||
|
||||
await Service.identity.initializeWithCurrentIdentity();
|
||||
await Assert.rejects(Service.identity.whenReadyToAuthenticate.promise,
|
||||
"should reject due to 401");
|
||||
|
||||
let cluster = await Service._clusterManager._findCluster();
|
||||
let cluster = await Service.identity._findCluster();
|
||||
Assert.strictEqual(cluster, null);
|
||||
|
||||
_("_findCluster() works with correct tokenserver response.");
|
||||
|
@ -50,9 +42,7 @@ add_task(async function test_findCluster() {
|
|||
})
|
||||
});
|
||||
|
||||
await Service.identity.initializeWithCurrentIdentity();
|
||||
await Service.identity.whenReadyToAuthenticate.promise;
|
||||
cluster = await Service._clusterManager._findCluster();
|
||||
cluster = await Service.identity._findCluster();
|
||||
// The cluster manager ensures a trailing "/"
|
||||
Assert.strictEqual(cluster, endpoint + "/");
|
||||
|
||||
|
|
|
@ -121,12 +121,9 @@ add_task(async function test_ensureLoggedIn() {
|
|||
let log = Log.repository.getLogger("Test");
|
||||
Log.repository.rootLogger.addAppender(new Log.DumpAppender());
|
||||
|
||||
let identityConfig = makeIdentityConfig();
|
||||
let browseridManager = new BrowserIDManager();
|
||||
configureFxAccountIdentity(browseridManager, identityConfig);
|
||||
await browseridManager.ensureLoggedIn();
|
||||
await configureIdentity();
|
||||
|
||||
let keyBundle = browseridManager.syncKeyBundle;
|
||||
let keyBundle = Weave.Service.identity.syncKeyBundle;
|
||||
|
||||
/*
|
||||
* Build a test version of storage/crypto/keys.
|
||||
|
|
|
@ -16,13 +16,12 @@ add_task(async function test_findCluster() {
|
|||
Service.identity._ensureValidToken = () => Promise.reject(new Error("Connection refused"));
|
||||
|
||||
_("_findCluster() throws on network errors (e.g. connection refused).");
|
||||
await Assert.rejects(Service._clusterManager._findCluster());
|
||||
await Assert.rejects(Service.identity._findCluster());
|
||||
|
||||
Service.identity._ensureValidToken = () => Promise.resolve(true);
|
||||
Service.identity._token = { endpoint: "http://weave.user.node" };
|
||||
Service.identity._ensureValidToken = () => Promise.resolve({ endpoint: "http://weave.user.node" });
|
||||
|
||||
_("_findCluster() returns the user's cluster node");
|
||||
let cluster = await Service._clusterManager._findCluster();
|
||||
let cluster = await Service.identity._findCluster();
|
||||
Assert.equal(cluster, "http://weave.user.node/");
|
||||
|
||||
} finally {
|
||||
|
@ -37,19 +36,19 @@ add_task(async function test_setCluster() {
|
|||
_("Check initial state.");
|
||||
Assert.equal(Service.clusterURL, "");
|
||||
|
||||
Service._clusterManager._findCluster = () => "http://weave.user.node/";
|
||||
Service.identity._findCluster = () => "http://weave.user.node/";
|
||||
|
||||
_("Set the cluster URL.");
|
||||
Assert.ok((await Service._clusterManager.setCluster()));
|
||||
Assert.ok((await Service.identity.setCluster()));
|
||||
Assert.equal(Service.clusterURL, "http://weave.user.node/");
|
||||
|
||||
_("Setting it again won't make a difference if it's the same one.");
|
||||
Assert.ok(!(await Service._clusterManager.setCluster()));
|
||||
Assert.ok(!(await Service.identity.setCluster()));
|
||||
Assert.equal(Service.clusterURL, "http://weave.user.node/");
|
||||
|
||||
_("A 'null' response won't make a difference either.");
|
||||
Service._clusterManager._findCluster = () => null;
|
||||
Assert.ok(!(await Service._clusterManager.setCluster()));
|
||||
Service.identity._findCluster = () => null;
|
||||
Assert.ok(!(await Service.identity.setCluster()));
|
||||
Assert.equal(Service.clusterURL, "http://weave.user.node/");
|
||||
} finally {
|
||||
Svc.Prefs.resetBranch("");
|
||||
|
|
|
@ -84,7 +84,7 @@ add_task(async function test_verifyLogin() {
|
|||
_("Ensure a network error when finding the cluster sets the right Status bits.");
|
||||
Service.status.resetSync();
|
||||
Service.clusterURL = "";
|
||||
Service._clusterManager._findCluster = () => "http://localhost:12345/";
|
||||
Service.identity._findCluster = () => "http://localhost:12345/";
|
||||
Assert.equal(false, (await Service.verifyLogin()));
|
||||
Assert.equal(Service.status.service, LOGIN_FAILED);
|
||||
Assert.equal(Service.status.login, LOGIN_FAILED_NETWORK_ERROR);
|
||||
|
|
|
@ -531,6 +531,9 @@ add_task(async function test_autoconnect_mp_locked() {
|
|||
Service.identity._fxaService = new FxAccounts({
|
||||
canGetKeys() {
|
||||
return false;
|
||||
},
|
||||
getSignedInUser() {
|
||||
return origFxA.getSignedInUser();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -716,8 +719,8 @@ add_task(async function test_no_sync_node() {
|
|||
let server = sync_httpd_setup();
|
||||
await setUp(server);
|
||||
|
||||
let oldfc = Service._clusterManager._findCluster;
|
||||
Service._clusterManager._findCluster = () => null;
|
||||
let oldfc = Service.identity._findCluster;
|
||||
Service.identity._findCluster = () => null;
|
||||
Service.clusterURL = "";
|
||||
try {
|
||||
await Service.sync();
|
||||
|
@ -726,7 +729,7 @@ add_task(async function test_no_sync_node() {
|
|||
|
||||
await cleanUpAndGo(server);
|
||||
} finally {
|
||||
Service._clusterManager._findCluster = oldfc;
|
||||
Service.identity._findCluster = oldfc;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
"collection_repair.js": ["getRepairRequestor", "getAllRepairRequestors", "CollectionRepairRequestor", "getRepairResponder", "CollectionRepairResponder"],
|
||||
"collection_validator.js": ["CollectionValidator", "CollectionProblemData"],
|
||||
"Console.jsm": ["console", "ConsoleAPI"],
|
||||
"constants.js": ["WEAVE_VERSION", "SYNC_API_VERSION", "STORAGE_VERSION", "PREFS_BRANCH", "PWDMGR_HOST", "PWDMGR_PASSWORD_REALM", "PWDMGR_PASSPHRASE_REALM", "PWDMGR_KEYBUNDLE_REALM", "DEFAULT_KEYBUNDLE_NAME", "SYNC_KEY_ENCODED_LENGTH", "SYNC_KEY_DECODED_LENGTH", "NO_SYNC_NODE_INTERVAL", "MAX_ERROR_COUNT_BEFORE_BACKOFF", "MINIMUM_BACKOFF_INTERVAL", "MAXIMUM_BACKOFF_INTERVAL", "HMAC_EVENT_INTERVAL", "MASTER_PASSWORD_LOCKED_RETRY_INTERVAL", "DEFAULT_GUID_FETCH_BATCH_SIZE", "DEFAULT_DOWNLOAD_BATCH_SIZE", "SINGLE_USER_THRESHOLD", "MULTI_DEVICE_THRESHOLD", "SCORE_INCREMENT_SMALL", "SCORE_INCREMENT_MEDIUM", "SCORE_INCREMENT_XLARGE", "SCORE_UPDATE_DELAY", "IDLE_OBSERVER_BACK_DELAY", "URI_LENGTH_MAX", "MAX_HISTORY_UPLOAD", "MAX_HISTORY_DOWNLOAD", "STATUS_OK", "SYNC_FAILED", "LOGIN_FAILED", "SYNC_FAILED_PARTIAL", "CLIENT_NOT_CONFIGURED", "STATUS_DISABLED", "MASTER_PASSWORD_LOCKED", "LOGIN_SUCCEEDED", "SYNC_SUCCEEDED", "ENGINE_SUCCEEDED", "LOGIN_FAILED_NO_USERNAME", "LOGIN_FAILED_NO_PASSPHRASE", "LOGIN_FAILED_NETWORK_ERROR", "LOGIN_FAILED_SERVER_ERROR", "LOGIN_FAILED_INVALID_PASSPHRASE", "LOGIN_FAILED_LOGIN_REJECTED", "METARECORD_DOWNLOAD_FAIL", "VERSION_OUT_OF_DATE", "CREDENTIALS_CHANGED", "ABORT_SYNC_COMMAND", "NO_SYNC_NODE_FOUND", "OVER_QUOTA", "PROLONGED_SYNC_FAILURE", "SERVER_MAINTENANCE", "RESPONSE_OVER_QUOTA", "ENGINE_UPLOAD_FAIL", "ENGINE_DOWNLOAD_FAIL", "ENGINE_UNKNOWN_FAIL", "ENGINE_APPLY_FAIL", "ENGINE_BATCH_INTERRUPTED", "kSyncMasterPasswordLocked", "kSyncWeaveDisabled", "kSyncNetworkOffline", "kSyncBackoffNotMet", "kFirstSyncChoiceNotMade", "kSyncNotConfigured", "kFirefoxShuttingDown", "DEVICE_TYPE_DESKTOP", "DEVICE_TYPE_MOBILE", "SQLITE_MAX_VARIABLE_NUMBER"],
|
||||
"constants.js": ["WEAVE_VERSION", "SYNC_API_VERSION", "STORAGE_VERSION", "PREFS_BRANCH", "DEFAULT_KEYBUNDLE_NAME", "SYNC_KEY_ENCODED_LENGTH", "SYNC_KEY_DECODED_LENGTH", "NO_SYNC_NODE_INTERVAL", "MAX_ERROR_COUNT_BEFORE_BACKOFF", "MINIMUM_BACKOFF_INTERVAL", "MAXIMUM_BACKOFF_INTERVAL", "HMAC_EVENT_INTERVAL", "MASTER_PASSWORD_LOCKED_RETRY_INTERVAL", "DEFAULT_GUID_FETCH_BATCH_SIZE", "DEFAULT_DOWNLOAD_BATCH_SIZE", "SINGLE_USER_THRESHOLD", "MULTI_DEVICE_THRESHOLD", "SCORE_INCREMENT_SMALL", "SCORE_INCREMENT_MEDIUM", "SCORE_INCREMENT_XLARGE", "SCORE_UPDATE_DELAY", "IDLE_OBSERVER_BACK_DELAY", "URI_LENGTH_MAX", "MAX_HISTORY_UPLOAD", "MAX_HISTORY_DOWNLOAD", "STATUS_OK", "SYNC_FAILED", "LOGIN_FAILED", "SYNC_FAILED_PARTIAL", "CLIENT_NOT_CONFIGURED", "STATUS_DISABLED", "MASTER_PASSWORD_LOCKED", "LOGIN_SUCCEEDED", "SYNC_SUCCEEDED", "ENGINE_SUCCEEDED", "LOGIN_FAILED_NO_USERNAME", "LOGIN_FAILED_NO_PASSPHRASE", "LOGIN_FAILED_NETWORK_ERROR", "LOGIN_FAILED_SERVER_ERROR", "LOGIN_FAILED_INVALID_PASSPHRASE", "LOGIN_FAILED_LOGIN_REJECTED", "METARECORD_DOWNLOAD_FAIL", "VERSION_OUT_OF_DATE", "CREDENTIALS_CHANGED", "ABORT_SYNC_COMMAND", "NO_SYNC_NODE_FOUND", "OVER_QUOTA", "PROLONGED_SYNC_FAILURE", "SERVER_MAINTENANCE", "RESPONSE_OVER_QUOTA", "ENGINE_UPLOAD_FAIL", "ENGINE_DOWNLOAD_FAIL", "ENGINE_UNKNOWN_FAIL", "ENGINE_APPLY_FAIL", "ENGINE_BATCH_INTERRUPTED", "kSyncMasterPasswordLocked", "kSyncWeaveDisabled", "kSyncNetworkOffline", "kSyncBackoffNotMet", "kFirstSyncChoiceNotMade", "kSyncNotConfigured", "kFirefoxShuttingDown", "DEVICE_TYPE_DESKTOP", "DEVICE_TYPE_MOBILE", "SQLITE_MAX_VARIABLE_NUMBER"],
|
||||
"Constants.jsm": ["Roles", "Events", "Relations", "Filters", "States", "Prefilters"],
|
||||
"ContactDB.jsm": ["ContactDB", "DB_NAME", "STORE_NAME", "SAVED_GETALL_STORE_NAME", "REVISION_STORE", "DB_VERSION"],
|
||||
"content-server.jsm": ["init"],
|
||||
|
|
Загрузка…
Ссылка в новой задаче