userapi.js 6.0 KB

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