|
1 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 |
|
5 "use strict"; |
|
6 |
|
7 this.EXPORTED_SYMBOLS = [ |
|
8 "UserAPI10Client", |
|
9 ]; |
|
10 |
|
11 const {utils: Cu} = Components; |
|
12 |
|
13 Cu.import("resource://gre/modules/Log.jsm"); |
|
14 Cu.import("resource://services-common/rest.js"); |
|
15 Cu.import("resource://services-common/utils.js"); |
|
16 Cu.import("resource://services-sync/identity.js"); |
|
17 Cu.import("resource://services-sync/util.js"); |
|
18 |
|
19 /** |
|
20 * A generic client for the user API 1.0 service. |
|
21 * |
|
22 * http://docs.services.mozilla.com/reg/apis.html |
|
23 * |
|
24 * Instances are constructed with the base URI of the service. |
|
25 */ |
|
26 this.UserAPI10Client = function UserAPI10Client(baseURI) { |
|
27 this._log = Log.repository.getLogger("Sync.UserAPI"); |
|
28 this._log.level = Log.Level[Svc.Prefs.get("log.logger.userapi")]; |
|
29 |
|
30 this.baseURI = baseURI; |
|
31 } |
|
32 UserAPI10Client.prototype = { |
|
33 USER_CREATE_ERROR_CODES: { |
|
34 2: "Incorrect or missing captcha.", |
|
35 4: "User exists.", |
|
36 6: "JSON parse failure.", |
|
37 7: "Missing password field.", |
|
38 9: "Requested password not strong enough.", |
|
39 12: "No email address on file.", |
|
40 }, |
|
41 |
|
42 /** |
|
43 * Determine whether a specified username exists. |
|
44 * |
|
45 * Callback receives the following arguments: |
|
46 * |
|
47 * (Error) Describes error that occurred or null if request was |
|
48 * successful. |
|
49 * (boolean) True if user exists. False if not. null if there was an error. |
|
50 */ |
|
51 usernameExists: function usernameExists(username, cb) { |
|
52 if (typeof(cb) != "function") { |
|
53 throw new Error("cb must be a function."); |
|
54 } |
|
55 |
|
56 let url = this.baseURI + username; |
|
57 let request = new RESTRequest(url); |
|
58 request.get(this._onUsername.bind(this, cb, request)); |
|
59 }, |
|
60 |
|
61 /** |
|
62 * Obtain the Weave (Sync) node for a specified user. |
|
63 * |
|
64 * The callback receives the following arguments: |
|
65 * |
|
66 * (Error) Describes error that occurred or null if request was successful. |
|
67 * (string) Username request is for. |
|
68 * (string) URL of user's node. If null and there is no error, no node could |
|
69 * be assigned at the time of the request. |
|
70 */ |
|
71 getWeaveNode: function getWeaveNode(username, password, cb) { |
|
72 if (typeof(cb) != "function") { |
|
73 throw new Error("cb must be a function."); |
|
74 } |
|
75 |
|
76 let request = this._getRequest(username, "/node/weave", password); |
|
77 request.get(this._onWeaveNode.bind(this, cb, request)); |
|
78 }, |
|
79 |
|
80 /** |
|
81 * Change a password for the specified user. |
|
82 * |
|
83 * @param username |
|
84 * (string) The username whose password to change. |
|
85 * @param oldPassword |
|
86 * (string) The old, current password. |
|
87 * @param newPassword |
|
88 * (string) The new password to switch to. |
|
89 */ |
|
90 changePassword: function changePassword(username, oldPassword, newPassword, cb) { |
|
91 let request = this._getRequest(username, "/password", oldPassword); |
|
92 request.onComplete = this._onChangePassword.bind(this, cb, request); |
|
93 request.post(CommonUtils.encodeUTF8(newPassword)); |
|
94 }, |
|
95 |
|
96 createAccount: function createAccount(email, password, captchaChallenge, |
|
97 captchaResponse, cb) { |
|
98 let username = IdentityManager.prototype.usernameFromAccount(email); |
|
99 let body = JSON.stringify({ |
|
100 "email": email, |
|
101 "password": Utils.encodeUTF8(password), |
|
102 "captcha-challenge": captchaChallenge, |
|
103 "captcha-response": captchaResponse |
|
104 }); |
|
105 |
|
106 let url = this.baseURI + username; |
|
107 let request = new RESTRequest(url); |
|
108 |
|
109 if (this.adminSecret) { |
|
110 request.setHeader("X-Weave-Secret", this.adminSecret); |
|
111 } |
|
112 |
|
113 request.onComplete = this._onCreateAccount.bind(this, cb, request); |
|
114 request.put(body); |
|
115 }, |
|
116 |
|
117 _getRequest: function _getRequest(username, path, password=null) { |
|
118 let url = this.baseURI + username + path; |
|
119 let request = new RESTRequest(url); |
|
120 |
|
121 if (password) { |
|
122 let up = username + ":" + password; |
|
123 request.setHeader("authorization", "Basic " + btoa(up)); |
|
124 } |
|
125 |
|
126 return request; |
|
127 }, |
|
128 |
|
129 _onUsername: function _onUsername(cb, request, error) { |
|
130 if (error) { |
|
131 cb(error, null); |
|
132 return; |
|
133 } |
|
134 |
|
135 let body = request.response.body; |
|
136 if (body == "0") { |
|
137 cb(null, false); |
|
138 return; |
|
139 } else if (body == "1") { |
|
140 cb(null, true); |
|
141 return; |
|
142 } else { |
|
143 cb(new Error("Unknown response from server: " + body), null); |
|
144 return; |
|
145 } |
|
146 }, |
|
147 |
|
148 _onWeaveNode: function _onWeaveNode(cb, request, error) { |
|
149 if (error) { |
|
150 cb.network = true; |
|
151 cb(error, null); |
|
152 return; |
|
153 } |
|
154 |
|
155 let response = request.response; |
|
156 |
|
157 if (response.status == 200) { |
|
158 let body = response.body; |
|
159 if (body == "null") { |
|
160 cb(null, null); |
|
161 return; |
|
162 } |
|
163 |
|
164 cb(null, body); |
|
165 return; |
|
166 } |
|
167 |
|
168 let error = new Error("Sync node retrieval failed."); |
|
169 switch (response.status) { |
|
170 case 400: |
|
171 error.denied = true; |
|
172 break; |
|
173 case 404: |
|
174 error.notFound = true; |
|
175 break; |
|
176 default: |
|
177 error.message = "Unexpected response code: " + response.status; |
|
178 } |
|
179 |
|
180 cb(error, null); |
|
181 return; |
|
182 }, |
|
183 |
|
184 _onChangePassword: function _onChangePassword(cb, request, error) { |
|
185 this._log.info("Password change response received: " + |
|
186 request.response.status); |
|
187 if (error) { |
|
188 cb(error); |
|
189 return; |
|
190 } |
|
191 |
|
192 let response = request.response; |
|
193 if (response.status != 200) { |
|
194 cb(new Error("Password changed failed: " + response.body)); |
|
195 return; |
|
196 } |
|
197 |
|
198 cb(null); |
|
199 }, |
|
200 |
|
201 _onCreateAccount: function _onCreateAccount(cb, request, error) { |
|
202 let response = request.response; |
|
203 |
|
204 this._log.info("Create account response: " + response.status + " " + |
|
205 response.body); |
|
206 |
|
207 if (error) { |
|
208 cb(new Error("HTTP transport error."), null); |
|
209 return; |
|
210 } |
|
211 |
|
212 if (response.status == 200) { |
|
213 cb(null, response.body); |
|
214 return; |
|
215 } |
|
216 |
|
217 let error = new Error("Could not create user."); |
|
218 error.body = response.body; |
|
219 |
|
220 cb(error, null); |
|
221 return; |
|
222 }, |
|
223 }; |
|
224 Object.freeze(UserAPI10Client.prototype); |