test_service_login.js (6925B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 const { Service } = ChromeUtils.importESModule( 5 "resource://services-sync/service.sys.mjs" 6 ); 7 8 Log.repository.rootLogger.addAppender(new Log.DumpAppender()); 9 10 function login_handling(handler) { 11 return function (request, response) { 12 if (has_hawk_header(request)) { 13 handler(request, response); 14 } else { 15 let body = "Unauthorized"; 16 response.setStatusLine(request.httpVersion, 401, "Unauthorized"); 17 response.setHeader("Content-Type", "text/plain"); 18 response.bodyOutputStream.write(body, body.length); 19 } 20 }; 21 } 22 23 add_task(async function test_offline() { 24 try { 25 _("The right bits are set when we're offline."); 26 Services.io.offline = true; 27 Assert.ok(!(await Service.login())); 28 Assert.equal(Service.status.login, LOGIN_FAILED_NETWORK_ERROR); 29 Services.io.offline = false; 30 } finally { 31 for (const pref of Svc.PrefBranch.getChildList("")) { 32 Svc.PrefBranch.clearUserPref(pref); 33 } 34 } 35 }); 36 37 function setup() { 38 let janeHelper = track_collections_helper(); 39 let janeU = janeHelper.with_updated_collection; 40 let johnHelper = track_collections_helper(); 41 let johnU = johnHelper.with_updated_collection; 42 43 let server = httpd_setup({ 44 "/1.1/johndoe/info/collections": login_handling(johnHelper.handler), 45 "/1.1/janedoe/info/collections": login_handling(janeHelper.handler), 46 47 // We need these handlers because we test login, and login 48 // is where keys are generated or fetched. 49 // TODO: have Jane fetch her keys, not generate them... 50 "/1.1/johndoe/storage/crypto/keys": johnU( 51 "crypto", 52 new ServerWBO("keys").handler() 53 ), 54 "/1.1/johndoe/storage/meta/global": johnU( 55 "meta", 56 new ServerWBO("global").handler() 57 ), 58 "/1.1/janedoe/storage/crypto/keys": janeU( 59 "crypto", 60 new ServerWBO("keys").handler() 61 ), 62 "/1.1/janedoe/storage/meta/global": janeU( 63 "meta", 64 new ServerWBO("global").handler() 65 ), 66 }); 67 68 return server; 69 } 70 71 add_task(async function test_not_logged_in() { 72 let server = setup(); 73 try { 74 await Service.login(); 75 Assert.ok(!Service.isLoggedIn, "no user configured, so can't be logged in"); 76 Assert.equal(Service._checkSync(), kSyncNotConfigured); 77 } finally { 78 for (const pref of Svc.PrefBranch.getChildList("")) { 79 Svc.PrefBranch.clearUserPref(pref); 80 } 81 await promiseStopServer(server); 82 } 83 }); 84 85 add_task(async function test_login_logout() { 86 enableValidationPrefs(); 87 88 let server = setup(); 89 90 try { 91 _("Force the initial state."); 92 Service.status.service = STATUS_OK; 93 Assert.equal(Service.status.service, STATUS_OK); 94 95 _("Try logging in. It won't work because we're not configured yet."); 96 await Service.login(); 97 Assert.equal(Service.status.service, CLIENT_NOT_CONFIGURED); 98 Assert.equal(Service.status.login, LOGIN_FAILED_NO_USERNAME); 99 Assert.ok(!Service.isLoggedIn); 100 101 _("Try again with a configured account"); 102 await configureIdentity({ username: "johndoe" }, server); 103 await Service.login(); 104 Assert.equal(Service.status.service, STATUS_OK); 105 Assert.equal(Service.status.login, LOGIN_SUCCEEDED); 106 Assert.ok(Service.isLoggedIn); 107 108 _("Logout."); 109 Service.logout(); 110 Assert.ok(!Service.isLoggedIn); 111 112 _("Logging out again won't do any harm."); 113 Service.logout(); 114 Assert.ok(!Service.isLoggedIn); 115 } finally { 116 for (const pref of Svc.PrefBranch.getChildList("")) { 117 Svc.PrefBranch.clearUserPref(pref); 118 } 119 await promiseStopServer(server); 120 } 121 }); 122 123 add_task(async function test_login_on_sync() { 124 enableValidationPrefs(); 125 126 let server = setup(); 127 await configureIdentity({ username: "johndoe" }, server); 128 129 try { 130 _("Sync calls login."); 131 let oldLogin = Service.login; 132 let loginCalled = false; 133 Service.login = async function () { 134 loginCalled = true; 135 Service.status.login = LOGIN_SUCCEEDED; 136 this._loggedIn = false; // So that sync aborts. 137 return true; 138 }; 139 140 await Service.sync(); 141 142 Assert.ok(loginCalled); 143 Service.login = oldLogin; 144 145 // Stub mpLocked. 146 let mpLocked = true; 147 Utils.mpLocked = () => mpLocked; 148 149 // Stub scheduleNextSync. This gets called within checkSyncStatus if we're 150 // ready to sync, so use it as an indicator. 151 let scheduleNextSyncF = Service.scheduler.scheduleNextSync; 152 let scheduleCalled = false; 153 Service.scheduler.scheduleNextSync = function (wait) { 154 scheduleCalled = true; 155 scheduleNextSyncF.call(this, wait); 156 }; 157 158 // Autoconnect still tries to connect in the background (useful behavior: 159 // for non-MP users and unlocked MPs, this will detect version expiry 160 // earlier). 161 // 162 // Consequently, non-MP users will be logged in as in the pre-Bug 543784 world, 163 // and checkSyncStatus reflects that by waiting for login. 164 // 165 // This process doesn't apply if your MP is still locked, so we make 166 // checkSyncStatus accept a locked MP in place of being logged in. 167 // 168 // This test exercises these two branches. 169 170 _("We're ready to sync if locked."); 171 Service.enabled = true; 172 Services.io.offline = false; 173 Service.scheduler.checkSyncStatus(); 174 Assert.ok(scheduleCalled); 175 176 _("... and also if we're not locked."); 177 scheduleCalled = false; 178 mpLocked = false; 179 Service.scheduler.checkSyncStatus(); 180 Assert.ok(scheduleCalled); 181 Service.scheduler.scheduleNextSync = scheduleNextSyncF; 182 183 // TODO: need better tests around master password prompting. See Bug 620583. 184 185 mpLocked = true; 186 187 // Testing exception handling if master password dialog is canceled. 188 // Do this by monkeypatching. 189 Service.identity.unlockAndVerifyAuthState = () => 190 Promise.resolve(MASTER_PASSWORD_LOCKED); 191 192 let cSTCalled = false; 193 let lockedSyncCalled = false; 194 195 Service.scheduler.clearSyncTriggers = function () { 196 cSTCalled = true; 197 }; 198 Service._lockedSync = async function () { 199 lockedSyncCalled = true; 200 }; 201 202 _("If master password is canceled, login fails and we report lockage."); 203 Assert.ok(!(await Service.login())); 204 Assert.equal(Service.status.login, MASTER_PASSWORD_LOCKED); 205 Assert.equal(Service.status.service, LOGIN_FAILED); 206 _("Locked? " + Utils.mpLocked()); 207 _("checkSync reports the correct term."); 208 Assert.equal(Service._checkSync(), kSyncMasterPasswordLocked); 209 210 _("Sync doesn't proceed and clears triggers if MP is still locked."); 211 await Service.sync(); 212 213 Assert.ok(cSTCalled); 214 Assert.ok(!lockedSyncCalled); 215 216 // N.B., a bunch of methods are stubbed at this point. Be careful putting 217 // new tests after this point! 218 } finally { 219 for (const pref of Svc.PrefBranch.getChildList("")) { 220 Svc.PrefBranch.clearUserPref(pref); 221 } 222 await promiseStopServer(server); 223 } 224 });