Refactor authentication flow

This commit is contained in:
Pavel Djundik 2017-08-13 21:37:12 +03:00
parent d87662482b
commit 3190fd00bf
3 changed files with 169 additions and 163 deletions

View file

@ -562,7 +562,7 @@ $(function() {
}); });
sidebar.on("click", "#sign-out", function() { sidebar.on("click", "#sign-out", function() {
socket.emit("sign-out", storage.get("token")); socket.emit("sign-out");
storage.remove("token"); storage.remove("token");
if (!socket.connected) { if (!socket.connected) {

View file

@ -300,13 +300,13 @@ Client.prototype.updateSession = function(token, ip, request) {
const agent = UAParser(request.headers["user-agent"] || ""); const agent = UAParser(request.headers["user-agent"] || "");
let friendlyAgent = ""; let friendlyAgent = "";
if (agent.browser.name.length) { if (agent.browser.name) {
friendlyAgent = `${agent.browser.name} ${agent.browser.major}`; friendlyAgent = `${agent.browser.name} ${agent.browser.major}`;
} else { } else {
friendlyAgent = "Unknown browser"; friendlyAgent = "Unknown browser";
} }
if (agent.os.name.length) { if (agent.os.name) {
friendlyAgent += ` on ${agent.os.name} ${agent.os.version}`; friendlyAgent += ` on ${agent.os.name} ${agent.os.version}`;
} }

View file

@ -100,9 +100,10 @@ in ${config.public ? "public" : "private"} mode`);
sockets.on("connect", function(socket) { sockets.on("connect", function(socket) {
if (config.public) { if (config.public) {
auth.call(socket, {}); performAuthentication.call(socket, {});
} else { } else {
init(socket); socket.emit("auth", {success: true});
socket.on("auth", performAuthentication);
} }
}); });
@ -173,129 +174,157 @@ function index(req, res, next) {
res.render("index", data); res.render("index", data);
} }
function init(socket, client, generateToken) { function initializeClient(socket, client, generateToken, token) {
if (!client) { socket.emit("authorized");
socket.emit("auth", {success: true});
socket.on("auth", auth);
} else {
socket.emit("authorized");
client.ip = getClientIp(socket.request); socket.on("disconnect", function() {
client.clientDetach(socket.id);
});
client.clientAttach(socket.id);
socket.on("disconnect", function() { socket.on(
client.clientDetach(socket.id); "input",
}); function(data) {
client.clientAttach(socket.id); client.input(data);
socket.on(
"input",
function(data) {
client.input(data);
}
);
socket.on(
"more",
function(data) {
client.more(data);
}
);
socket.on(
"conn",
function(data) {
// prevent people from overriding webirc settings
data.ip = null;
data.hostname = null;
client.connect(data);
}
);
if (!Helper.config.public && !Helper.config.ldap.enable) {
socket.on(
"change-password",
function(data) {
var old = data.old_password;
var p1 = data.new_password;
var p2 = data.verify_password;
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;
}
Helper.password
.compare(old || "", client.config.password)
.then((matching) => {
if (!matching) {
socket.emit("change-password", {
error: "The current password field does not match your account password"
});
return;
}
const hash = Helper.password.hash(p1);
client.setPassword(hash, (success) => {
const obj = {};
if (success) {
obj.success = "Successfully updated your password";
} else {
obj.error = "Failed to update your password";
}
socket.emit("change-password", obj);
});
}).catch((error) => {
log.error(`Error while checking users password. Error: ${error}`);
});
}
);
} }
);
socket.on(
"more",
function(data) {
client.more(data);
}
);
socket.on(
"conn",
function(data) {
// prevent people from overriding webirc settings
data.ip = null;
data.hostname = null;
client.connect(data);
}
);
if (!Helper.config.public && !Helper.config.ldap.enable) {
socket.on( socket.on(
"open", "change-password",
function(data) { function(data) {
client.open(socket.id, data); var old = data.old_password;
} var p1 = data.new_password;
); var p2 = data.verify_password;
socket.on( if (typeof p1 === "undefined" || p1 === "") {
"sort", socket.emit("change-password", {
function(data) { error: "Please enter a new password"
client.sort(data); });
} return;
); }
socket.on( if (p1 !== p2) {
"names", socket.emit("change-password", {
function(data) { error: "Both new password fields must match"
client.names(data); });
return;
}
Helper.password
.compare(old || "", client.config.password)
.then((matching) => {
if (!matching) {
socket.emit("change-password", {
error: "The current password field does not match your account password"
});
return;
}
const hash = Helper.password.hash(p1);
client.setPassword(hash, (success) => {
const obj = {};
if (success) {
obj.success = "Successfully updated your password";
} else {
obj.error = "Failed to update your password";
}
socket.emit("change-password", obj);
});
}).catch((error) => {
log.error(`Error while checking users password. Error: ${error}`);
});
} }
); );
}
socket.on("msg:preview:toggle", function(data) { socket.on(
const networkAndChan = client.find(data.target); "open",
if (!networkAndChan) { function(data) {
return; client.open(socket.id, data);
} }
);
const message = networkAndChan.chan.findMessage(data.msgId); socket.on(
"sort",
function(data) {
client.sort(data);
}
);
if (!message) { socket.on(
return; "names",
} function(data) {
client.names(data);
}
);
const preview = message.findPreview(data.link); socket.on("msg:preview:toggle", function(data) {
const networkAndChan = client.find(data.target);
if (!networkAndChan) {
return;
}
if (preview) { const message = networkAndChan.chan.findMessage(data.msgId);
preview.shown = data.shown;
if (!message) {
return;
}
const preview = message.findPreview(data.link);
if (preview) {
preview.shown = data.shown;
}
});
socket.on("sign-out", () => {
delete client.config.sessions[token];
client.manager.updateUser(client.name, {
sessions: client.config.sessions
}, (err) => {
if (err) {
log.error("Failed to update sessions for", client.name, err);
} }
}); });
socket.on("sign-out", (token) => { socket.emit("sign-out");
delete client.config.sessions[token]; });
socket.join(client.id);
const sendInitEvent = (tokenToSend) => {
socket.emit("init", {
active: client.lastActiveChannel,
networks: client.networks,
token: tokenToSend
});
};
if (generateToken) {
client.generateToken((newToken) => {
token = newToken;
client.updateSession(token, getClientIp(socket.request), socket.request);
client.manager.updateUser(client.name, { client.manager.updateUser(client.name, {
sessions: client.config.sessions sessions: client.config.sessions
@ -305,53 +334,13 @@ function init(socket, client, generateToken) {
} }
}); });
socket.emit("sign-out"); sendInitEvent(token);
}); });
} else {
socket.join(client.id); sendInitEvent(null);
const sendInitEvent = (token) => {
socket.emit("init", {
active: client.lastActiveChannel,
networks: client.networks,
token: token
});
};
if (generateToken) {
client.generateToken((token) => {
client.updateSession(token, getClientIp(socket.request), socket.request);
client.manager.updateUser(client.name, {
sessions: client.config.sessions
}, (err) => {
if (err) {
log.error("Failed to update sessions for", client.name, err);
}
});
sendInitEvent(token);
});
} else {
sendInitEvent(null);
}
} }
} }
function reverseDnsLookup(socket, client) {
client.ip = getClientIp(socket.request);
dns.reverse(client.ip, function(err, host) {
if (!err && host.length) {
client.hostname = host[0];
} else {
client.hostname = client.ip;
}
init(socket, client);
});
}
function localAuth(client, user, password, callback) { function localAuth(client, user, password, callback) {
// If no user is found, or if the client has not provided a password, // If no user is found, or if the client has not provided a password,
// fail the authentication straight away // fail the authentication straight away
@ -408,18 +397,25 @@ function ldapAuth(client, user, password, callback) {
}); });
} }
function auth(data) { function performAuthentication(data) {
const socket = this; const socket = this;
let client; let client;
const finalInit = () => initializeClient(socket, client, !!data.remember, data.token || null);
const initClient = () => { const initClient = () => {
// If webirc is enabled and we do not know this users IP address, client.ip = getClientIp(socket.request);
// perform reverse dns lookup
if (Helper.config.webirc !== null && !client.config.ip) { // If webirc is enabled perform reverse dns lookup
reverseDnsLookup(socket, client); if (Helper.config.webirc === null) {
} else { return finalInit();
init(socket, client, data.remember === "on");
} }
reverseDnsLookup(client.ip, (hostname) => {
client.hostname = hostname;
finalInit();
});
}; };
if (Helper.config.public) { if (Helper.config.public) {
@ -470,3 +466,13 @@ function auth(data) {
localAuth(client, data.user, data.password, authCallback); localAuth(client, data.user, data.password, authCallback);
} }
} }
function reverseDnsLookup(ip, callback) {
dns.reverse(ip, (err, hostnames) => {
if (!err && hostnames.length) {
return callback(hostnames[0]);
}
callback(ip);
});
}