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