michael@0: /* Any copyright is dedicated to the Public Domain. michael@0: http://creativecommons.org/publicdomain/zero/1.0/ */ michael@0: michael@0: Cu.import("resource://gre/modules/Log.jsm"); michael@0: Cu.import("resource://services-sync/constants.js"); michael@0: Cu.import("resource://services-sync/service.js"); michael@0: Cu.import("resource://services-sync/policies.js"); michael@0: Cu.import("resource://services-sync/util.js"); michael@0: Cu.import("resource://testing-common/services/sync/utils.js"); michael@0: michael@0: function login_handling(handler) { michael@0: return function (request, response) { michael@0: if (basic_auth_matches(request, "johndoe", "ilovejane") || michael@0: basic_auth_matches(request, "janedoe", "ilovejohn")) { michael@0: handler(request, response); michael@0: } else { michael@0: let body = "Unauthorized"; michael@0: response.setStatusLine(request.httpVersion, 401, "Unauthorized"); michael@0: response.setHeader("Content-Type", "text/plain"); michael@0: response.bodyOutputStream.write(body, body.length); michael@0: } michael@0: }; michael@0: } michael@0: michael@0: function run_test() { michael@0: let logger = Log.repository.rootLogger; michael@0: Log.repository.rootLogger.addAppender(new Log.DumpAppender()); michael@0: michael@0: run_next_test(); michael@0: } michael@0: michael@0: add_test(function test_offline() { michael@0: try { michael@0: _("The right bits are set when we're offline."); michael@0: Services.io.offline = true; michael@0: do_check_false(!!Service.login()); michael@0: do_check_eq(Service.status.login, LOGIN_FAILED_NETWORK_ERROR); michael@0: Services.io.offline = false; michael@0: } finally { michael@0: Svc.Prefs.resetBranch(""); michael@0: run_next_test(); michael@0: } michael@0: }); michael@0: michael@0: function setup() { michael@0: let janeHelper = track_collections_helper(); michael@0: let janeU = janeHelper.with_updated_collection; michael@0: let janeColls = janeHelper.collections; michael@0: let johnHelper = track_collections_helper(); michael@0: let johnU = johnHelper.with_updated_collection; michael@0: let johnColls = johnHelper.collections; michael@0: michael@0: let server = httpd_setup({ michael@0: "/1.1/johndoe/info/collections": login_handling(johnHelper.handler), michael@0: "/1.1/janedoe/info/collections": login_handling(janeHelper.handler), michael@0: michael@0: // We need these handlers because we test login, and login michael@0: // is where keys are generated or fetched. michael@0: // TODO: have Jane fetch her keys, not generate them... michael@0: "/1.1/johndoe/storage/crypto/keys": johnU("crypto", new ServerWBO("keys").handler()), michael@0: "/1.1/johndoe/storage/meta/global": johnU("meta", new ServerWBO("global").handler()), michael@0: "/1.1/janedoe/storage/crypto/keys": janeU("crypto", new ServerWBO("keys").handler()), michael@0: "/1.1/janedoe/storage/meta/global": janeU("meta", new ServerWBO("global").handler()) michael@0: }); michael@0: michael@0: Service.serverURL = server.baseURI; michael@0: return server; michael@0: } michael@0: michael@0: add_test(function test_login_logout() { michael@0: let server = setup(); michael@0: michael@0: try { michael@0: _("Force the initial state."); michael@0: ensureLegacyIdentityManager(); michael@0: Service.status.service = STATUS_OK; michael@0: do_check_eq(Service.status.service, STATUS_OK); michael@0: michael@0: _("Try logging in. It won't work because we're not configured yet."); michael@0: Service.login(); michael@0: do_check_eq(Service.status.service, CLIENT_NOT_CONFIGURED); michael@0: do_check_eq(Service.status.login, LOGIN_FAILED_NO_USERNAME); michael@0: do_check_false(Service.isLoggedIn); michael@0: michael@0: _("Try again with username and password set."); michael@0: Service.identity.account = "johndoe"; michael@0: Service.identity.basicPassword = "ilovejane"; michael@0: Service.login(); michael@0: do_check_eq(Service.status.service, CLIENT_NOT_CONFIGURED); michael@0: do_check_eq(Service.status.login, LOGIN_FAILED_NO_PASSPHRASE); michael@0: do_check_false(Service.isLoggedIn); michael@0: michael@0: _("Success if passphrase is set."); michael@0: Service.identity.syncKey = "foo"; michael@0: Service.login(); michael@0: do_check_eq(Service.status.service, STATUS_OK); michael@0: do_check_eq(Service.status.login, LOGIN_SUCCEEDED); michael@0: do_check_true(Service.isLoggedIn); michael@0: michael@0: _("We can also pass username, password and passphrase to login()."); michael@0: Service.login("janedoe", "incorrectpassword", "bar"); michael@0: setBasicCredentials("janedoe", "incorrectpassword", "bar"); michael@0: do_check_eq(Service.status.service, LOGIN_FAILED); michael@0: do_check_eq(Service.status.login, LOGIN_FAILED_LOGIN_REJECTED); michael@0: do_check_false(Service.isLoggedIn); michael@0: michael@0: _("Try again with correct password."); michael@0: Service.login("janedoe", "ilovejohn"); michael@0: do_check_eq(Service.status.service, STATUS_OK); michael@0: do_check_eq(Service.status.login, LOGIN_SUCCEEDED); michael@0: do_check_true(Service.isLoggedIn); michael@0: michael@0: _("Calling login() with parameters when the client is unconfigured sends notification."); michael@0: let notified = false; michael@0: Svc.Obs.add("weave:service:setup-complete", function() { michael@0: notified = true; michael@0: }); michael@0: setBasicCredentials(null, null, null); michael@0: Service.login("janedoe", "ilovejohn", "bar"); michael@0: do_check_true(notified); michael@0: do_check_eq(Service.status.service, STATUS_OK); michael@0: do_check_eq(Service.status.login, LOGIN_SUCCEEDED); michael@0: do_check_true(Service.isLoggedIn); michael@0: michael@0: _("Logout."); michael@0: Service.logout(); michael@0: do_check_false(Service.isLoggedIn); michael@0: michael@0: _("Logging out again won't do any harm."); michael@0: Service.logout(); michael@0: do_check_false(Service.isLoggedIn); michael@0: michael@0: } finally { michael@0: Svc.Prefs.resetBranch(""); michael@0: server.stop(run_next_test); michael@0: } michael@0: }); michael@0: michael@0: add_test(function test_login_on_sync() { michael@0: let server = setup(); michael@0: setBasicCredentials("johndoe", "ilovejane", "bar"); michael@0: michael@0: try { michael@0: _("Sync calls login."); michael@0: let oldLogin = Service.login; michael@0: let loginCalled = false; michael@0: Service.login = function() { michael@0: loginCalled = true; michael@0: Service.status.login = LOGIN_SUCCEEDED; michael@0: this._loggedIn = false; // So that sync aborts. michael@0: return true; michael@0: }; michael@0: michael@0: Service.sync(); michael@0: michael@0: do_check_true(loginCalled); michael@0: Service.login = oldLogin; michael@0: michael@0: // Stub mpLocked. michael@0: let mpLockedF = Utils.mpLocked; michael@0: let mpLocked = true; michael@0: Utils.mpLocked = function() mpLocked; michael@0: michael@0: // Stub scheduleNextSync. This gets called within checkSyncStatus if we're michael@0: // ready to sync, so use it as an indicator. michael@0: let scheduleNextSyncF = Service.scheduler.scheduleNextSync; michael@0: let scheduleCalled = false; michael@0: Service.scheduler.scheduleNextSync = function(wait) { michael@0: scheduleCalled = true; michael@0: scheduleNextSyncF.call(this, wait); michael@0: }; michael@0: michael@0: // Autoconnect still tries to connect in the background (useful behavior: michael@0: // for non-MP users and unlocked MPs, this will detect version expiry michael@0: // earlier). michael@0: // michael@0: // Consequently, non-MP users will be logged in as in the pre-Bug 543784 world, michael@0: // and checkSyncStatus reflects that by waiting for login. michael@0: // michael@0: // This process doesn't apply if your MP is still locked, so we make michael@0: // checkSyncStatus accept a locked MP in place of being logged in. michael@0: // michael@0: // This test exercises these two branches. michael@0: michael@0: _("We're ready to sync if locked."); michael@0: Service.enabled = true; michael@0: Services.io.offline = false; michael@0: Service.scheduler.checkSyncStatus(); michael@0: do_check_true(scheduleCalled); michael@0: michael@0: _("... and also if we're not locked."); michael@0: scheduleCalled = false; michael@0: mpLocked = false; michael@0: Service.scheduler.checkSyncStatus(); michael@0: do_check_true(scheduleCalled); michael@0: Service.scheduler.scheduleNextSync = scheduleNextSyncF; michael@0: michael@0: // TODO: need better tests around master password prompting. See Bug 620583. michael@0: michael@0: mpLocked = true; michael@0: michael@0: // Testing exception handling if master password dialog is canceled. michael@0: // Do this by monkeypatching. michael@0: let oldGetter = Service.identity.__lookupGetter__("syncKey"); michael@0: let oldSetter = Service.identity.__lookupSetter__("syncKey"); michael@0: _("Old passphrase function is " + oldGetter); michael@0: Service.identity.__defineGetter__("syncKey", michael@0: function() { michael@0: throw "User canceled Master Password entry"; michael@0: }); michael@0: michael@0: let oldClearSyncTriggers = Service.scheduler.clearSyncTriggers; michael@0: let oldLockedSync = Service._lockedSync; michael@0: michael@0: let cSTCalled = false; michael@0: let lockedSyncCalled = false; michael@0: michael@0: Service.scheduler.clearSyncTriggers = function() { cSTCalled = true; }; michael@0: Service._lockedSync = function() { lockedSyncCalled = true; }; michael@0: michael@0: _("If master password is canceled, login fails and we report lockage."); michael@0: do_check_false(!!Service.login()); michael@0: do_check_eq(Service.status.login, MASTER_PASSWORD_LOCKED); michael@0: do_check_eq(Service.status.service, LOGIN_FAILED); michael@0: _("Locked? " + Utils.mpLocked()); michael@0: _("checkSync reports the correct term."); michael@0: do_check_eq(Service._checkSync(), kSyncMasterPasswordLocked); michael@0: michael@0: _("Sync doesn't proceed and clears triggers if MP is still locked."); michael@0: Service.sync(); michael@0: michael@0: do_check_true(cSTCalled); michael@0: do_check_false(lockedSyncCalled); michael@0: michael@0: Service.identity.__defineGetter__("syncKey", oldGetter); michael@0: Service.identity.__defineSetter__("syncKey", oldSetter); michael@0: michael@0: // N.B., a bunch of methods are stubbed at this point. Be careful putting michael@0: // new tests after this point! michael@0: michael@0: } finally { michael@0: Svc.Prefs.resetBranch(""); michael@0: server.stop(run_next_test); michael@0: } michael@0: });