diff --git a/client/css/style.css b/client/css/style.css index 2ec36fb2..81520904 100644 --- a/client/css/style.css +++ b/client/css/style.css @@ -1116,6 +1116,19 @@ button, line-height: 1.8; } +#settings #change-password .error, +#settings #change-password .success { + margin-bottom: 1em; +} + +#settings #change-password .error { + color: #e74c3c; +} + +#settings #change-password .success { + color: #2ecc40; +} + #form { background: #eee; border-top: 1px solid #ddd; diff --git a/client/index.html b/client/index.html index 0be10248..841a8174 100644 --- a/client/index.html +++ b/client/index.html @@ -265,6 +265,31 @@ Enable notification for all messages + <% if (!public) { %> +
+
+
+

Change password

+
+
+ + +
+
+ + +
+
+ + +
+ +
+ +
+
+
+ <% } %>

About The Lounge

diff --git a/client/js/lounge.js b/client/js/lounge.js index dafafbc7..309e4303 100644 --- a/client/js/lounge.js +++ b/client/js/lounge.js @@ -112,6 +112,31 @@ $(function() { .show(); }); + socket.on("change-password", function(data) { + var passwordForm = $("#change-password"); + if (data.error || data.success) { + var message = data.success ? data.success : data.error; + var feedback = passwordForm.find(".feedback"); + + if (data.success) { + feedback.addClass("success").removeClass("error"); + } else { + feedback.addClass("error").removeClass("success"); + } + + feedback.text(message).show(); + feedback.closest("form").one("submit", function() { + feedback.hide(); + }); + } + passwordForm + .find("input") + .val("") + .end() + .find(".btn") + .prop("disabled", false); + }); + socket.on("init", function(data) { if (data.networks.length === 0) { $("#footer").find(".connect").trigger("click"); @@ -734,7 +759,7 @@ $(function() { }); var windows = $("#windows"); - var forms = $("#sign-in, #connect"); + var forms = $("#sign-in, #connect, #change-password"); windows.on("show", "#sign-in", function() { var self = $(this); @@ -761,6 +786,8 @@ $(function() { .end(); if (form.closest(".window").attr("id") === "connect") { event = "conn"; + } else if (form.closest("div").attr("id") === "change-password") { + event = "change-password"; } var values = {}; $.each(form.serializeArray(), function(i, obj) { diff --git a/src/client.js b/src/client.js index 7b795800..60119846 100644 --- a/src/client.js +++ b/src/client.js @@ -1,7 +1,6 @@ var _ = require("lodash"); var Chan = require("./models/chan"); var crypto = require("crypto"); -var fs = require("fs"); var identd = require("./identd"); var log = require("./log"); var net = require("net"); @@ -51,14 +50,15 @@ var inputs = [ "whois" ]; -function Client(sockets, name, config) { +function Client(manager, name, config) { _.merge(this, { activeChannel: -1, config: config, id: id++, name: name, networks: [], - sockets: sockets + sockets: manager.sockets, + manager: manager }); var client = this; crypto.randomBytes(48, function(err, buf) { @@ -221,6 +221,21 @@ Client.prototype.connect = function(args) { }); }; +Client.prototype.setPassword = function(hash) { + var client = this; + client.manager.updateUser(client.name, {password:hash}); + // re-read the hash off disk to ensure we use whatever is saved. this will + // prevent situations where the password failed to save properly and so + // a restart of the server would forget the change and use the old + // password again. + var user = client.manager.readUserConfig(client.name); + if (user.password === hash) { + client.config.password = hash; + return true; + } + return false; +}; + Client.prototype.input = function(data) { var client = this; var text = data.text.trim(); @@ -353,9 +368,6 @@ Client.prototype.save = function(force) { return; } - var name = this.name; - var path = Helper.HOME + "/users/" + name + ".json"; - var networks = _.map( this.networks, function(n) { @@ -364,29 +376,6 @@ Client.prototype.save = function(force) { ); var json = {}; - fs.readFile(path, "utf-8", function(err, data) { - if (err) { - console.log(err); - return; - } - - try { - json = JSON.parse(data); - json.networks = networks; - } catch (e) { - console.log(e); - return; - } - - fs.writeFile( - path, - JSON.stringify(json, null, " "), - {mode: "0777"}, - function(err) { - if (err) { - console.log(err); - } - } - ); - }); + json.networks = networks; + client.manager.updateUser(client.name, json); }; diff --git a/src/clientManager.js b/src/clientManager.js index f006f848..2b0fbb87 100644 --- a/src/clientManager.js +++ b/src/clientManager.js @@ -29,18 +29,14 @@ ClientManager.prototype.loadUsers = function() { ClientManager.prototype.loadUser = function(name) { try { - var json = fs.readFileSync( - Helper.HOME + "/users/" + name + ".json", - "utf-8" - ); - json = JSON.parse(json); + var json = this.readUserConfig(name); } catch (e) { console.log(e); return; } if (!this.findClient(name)) { this.clients.push(new Client( - this.sockets, + this, name, json )); @@ -93,6 +89,50 @@ ClientManager.prototype.addUser = function(name, password) { return true; }; +ClientManager.prototype.updateUser = function(name, opts) { + var users = this.getUsers(); + if (users.indexOf(name) === -1) { + return false; + } + if (typeof opts === "undefined") { + return false; + } + var path = Helper.HOME + "/users/" + name + ".json"; + var user = {}; + + try { + user = this.readUserConfig(name); + _.merge(user, opts); + } catch (e) { + console.log(e); + return; + } + + fs.writeFileSync( + path, + JSON.stringify(user, null, " "), + {mode: "0777"}, + function(err) { + if (err) { + console.log(err); + } + } + ); + return true; +}; + +ClientManager.prototype.readUserConfig = function(name) { + var users = this.getUsers(); + if (users.indexOf(name) === -1) { + return false; + } + var path = Helper.HOME + "/users/" + name + ".json"; + var user = {}; + var data = fs.readFileSync(path, "utf-8"); + user = JSON.parse(data); + return user; +}; + ClientManager.prototype.removeUser = function(name) { var users = this.getUsers(); if (users.indexOf(name) === -1) { diff --git a/src/server.js b/src/server.js index baca756f..1951bbd6 100644 --- a/src/server.js +++ b/src/server.js @@ -107,6 +107,51 @@ function init(socket, client, token) { client.connect(data); } ); + if (!config.public) { + socket.on( + "change-password", + function(data) { + var old = data.old_password; + var p1 = data.new_password; + var p2 = data.verify_password; + if (typeof old === "undefined" || old === "") { + socket.emit("change-password", { + error: "Please enter your current password" + }); + return; + } + if (typeof p1 === "undefined" || p1 === "") { + socket.emit("change-password", { + error: "Please enter a new password" + }); + return; + } + if (p1 !== p2) { + socket.emit("change-password", { + error: "Both new password fields must match" + }); + return; + } + if (!bcrypt.compareSync(old || "", client.config.password)) { + socket.emit("change-password", { + error: "The current password field does not match your account password" + }); + return; + } + var salt = bcrypt.genSaltSync(8); + var hash = bcrypt.hashSync(p1, salt); + if (client.setPassword(hash)) { + socket.emit("change-password", { + success: "Successfully updated your password" + }); + return; + } + socket.emit("change-password", { + error: "Failed to update your password" + }); + } + ); + } socket.on( "open", function(data) {