summaryrefslogtreecommitdiffstats
path: root/services/sync/modules/userapi.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/sync/modules/userapi.js')
-rw-r--r--services/sync/modules/userapi.js224
1 files changed, 224 insertions, 0 deletions
diff --git a/services/sync/modules/userapi.js b/services/sync/modules/userapi.js
new file mode 100644
index 000000000..e906440bd
--- /dev/null
+++ b/services/sync/modules/userapi.js
@@ -0,0 +1,224 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+this.EXPORTED_SYMBOLS = [
+ "UserAPI10Client",
+];
+
+var {utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://services-common/rest.js");
+Cu.import("resource://services-common/utils.js");
+Cu.import("resource://services-sync/identity.js");
+Cu.import("resource://services-sync/util.js");
+
+/**
+ * A generic client for the user API 1.0 service.
+ *
+ * http://docs.services.mozilla.com/reg/apis.html
+ *
+ * Instances are constructed with the base URI of the service.
+ */
+this.UserAPI10Client = function UserAPI10Client(baseURI) {
+ this._log = Log.repository.getLogger("Sync.UserAPI");
+ this._log.level = Log.Level[Svc.Prefs.get("log.logger.userapi")];
+
+ this.baseURI = baseURI;
+}
+UserAPI10Client.prototype = {
+ USER_CREATE_ERROR_CODES: {
+ 2: "Incorrect or missing captcha.",
+ 4: "User exists.",
+ 6: "JSON parse failure.",
+ 7: "Missing password field.",
+ 9: "Requested password not strong enough.",
+ 12: "No email address on file.",
+ },
+
+ /**
+ * Determine whether a specified username exists.
+ *
+ * Callback receives the following arguments:
+ *
+ * (Error) Describes error that occurred or null if request was
+ * successful.
+ * (boolean) True if user exists. False if not. null if there was an error.
+ */
+ usernameExists: function usernameExists(username, cb) {
+ if (typeof(cb) != "function") {
+ throw new Error("cb must be a function.");
+ }
+
+ let url = this.baseURI + username;
+ let request = new RESTRequest(url);
+ request.get(this._onUsername.bind(this, cb, request));
+ },
+
+ /**
+ * Obtain the Weave (Sync) node for a specified user.
+ *
+ * The callback receives the following arguments:
+ *
+ * (Error) Describes error that occurred or null if request was successful.
+ * (string) Username request is for.
+ * (string) URL of user's node. If null and there is no error, no node could
+ * be assigned at the time of the request.
+ */
+ getWeaveNode: function getWeaveNode(username, password, cb) {
+ if (typeof(cb) != "function") {
+ throw new Error("cb must be a function.");
+ }
+
+ let request = this._getRequest(username, "/node/weave", password);
+ request.get(this._onWeaveNode.bind(this, cb, request));
+ },
+
+ /**
+ * Change a password for the specified user.
+ *
+ * @param username
+ * (string) The username whose password to change.
+ * @param oldPassword
+ * (string) The old, current password.
+ * @param newPassword
+ * (string) The new password to switch to.
+ */
+ changePassword: function changePassword(username, oldPassword, newPassword, cb) {
+ let request = this._getRequest(username, "/password", oldPassword);
+ request.onComplete = this._onChangePassword.bind(this, cb, request);
+ request.post(CommonUtils.encodeUTF8(newPassword));
+ },
+
+ createAccount: function createAccount(email, password, captchaChallenge,
+ captchaResponse, cb) {
+ let username = IdentityManager.prototype.usernameFromAccount(email);
+ let body = JSON.stringify({
+ "email": email,
+ "password": Utils.encodeUTF8(password),
+ "captcha-challenge": captchaChallenge,
+ "captcha-response": captchaResponse
+ });
+
+ let url = this.baseURI + username;
+ let request = new RESTRequest(url);
+
+ if (this.adminSecret) {
+ request.setHeader("X-Weave-Secret", this.adminSecret);
+ }
+
+ request.onComplete = this._onCreateAccount.bind(this, cb, request);
+ request.put(body);
+ },
+
+ _getRequest: function _getRequest(username, path, password=null) {
+ let url = this.baseURI + username + path;
+ let request = new RESTRequest(url);
+
+ if (password) {
+ let up = username + ":" + password;
+ request.setHeader("authorization", "Basic " + btoa(up));
+ }
+
+ return request;
+ },
+
+ _onUsername: function _onUsername(cb, request, error) {
+ if (error) {
+ cb(error, null);
+ return;
+ }
+
+ let body = request.response.body;
+ if (body == "0") {
+ cb(null, false);
+ return;
+ } else if (body == "1") {
+ cb(null, true);
+ return;
+ } else {
+ cb(new Error("Unknown response from server: " + body), null);
+ return;
+ }
+ },
+
+ _onWeaveNode: function _onWeaveNode(cb, request, error) {
+ if (error) {
+ cb.network = true;
+ cb(error, null);
+ return;
+ }
+
+ let response = request.response;
+
+ if (response.status == 200) {
+ let body = response.body;
+ if (body == "null") {
+ cb(null, null);
+ return;
+ }
+
+ cb(null, body);
+ return;
+ }
+
+ error = new Error("Sync node retrieval failed.");
+ switch (response.status) {
+ case 400:
+ error.denied = true;
+ break;
+ case 404:
+ error.notFound = true;
+ break;
+ default:
+ error.message = "Unexpected response code: " + response.status;
+ }
+
+ cb(error, null);
+ return;
+ },
+
+ _onChangePassword: function _onChangePassword(cb, request, error) {
+ this._log.info("Password change response received: " +
+ request.response.status);
+ if (error) {
+ cb(error);
+ return;
+ }
+
+ let response = request.response;
+ if (response.status != 200) {
+ cb(new Error("Password changed failed: " + response.body));
+ return;
+ }
+
+ cb(null);
+ },
+
+ _onCreateAccount: function _onCreateAccount(cb, request, error) {
+ let response = request.response;
+
+ this._log.info("Create account response: " + response.status + " " +
+ response.body);
+
+ if (error) {
+ cb(new Error("HTTP transport error."), null);
+ return;
+ }
+
+ if (response.status == 200) {
+ cb(null, response.body);
+ return;
+ }
+
+ error = new Error("Could not create user.");
+ error.body = response.body;
+
+ cb(error, null);
+ return;
+ },
+};
+Object.freeze(UserAPI10Client.prototype);