Node.js socket.io handling users - javascript

So at my server app when I get a socket request I send data based on what user is requesting the information...
server.on('connection', function(socket) {
socket.on('login', function(data) {
connection.query('SELECT * FROM users WHERE name = ? AND password = ? LIMIT 1', [data.name, data.password], function(err, result) {
if(result.length === 0)
{
socket.emit('login-failed');
}
else
{
if(users.check(result[0].name))
{
result[0].socket = socket.id;
users.add(result[0]);
console.log(colors.yellow('User: ' + result[0].name + ' connected.'));
socket.emit('login-success');
}
}
});
});
So when a user logs in I save some information like username, password, socketid.
var userList = [];
exports.add = function(data)
{
userList.push(data);
}
exports.check = function(data)
{
for(var i = 0; i < userList.length; i++)
{
if(userList[i].name === data)
{
return false;
}
}
return true;
}
exports.remove = function(id)
{
for(var i = 0; i < userList.length; i++)
{
if(userList[i].socket === id)
{
var index = userList.indexOf(i);
userList.splice(index, 1);
return true;
}
}
return false;
}
So as you can see Im saving users data that I will access later for example on here
socket.on('test', function(data) {
if(users.remove(socket.id)) console.log('Yes');
});
But the only way I have to identify a socket is by using its socket.id, now my question is if I can really trust socketid or if I should use another 'system' to actually manage my users logic.
I cant really figure any other system to actually check what socket is calling my server

Related

managing sessions on login nodejs

I am trying to manage user sessions in nodejs. I have built a dashboard where people will be able to manage their products for inventory and such. I basically have it running right now where a user logs in, and it stores there username in a global variable, and then userAuth gets set to true. Obviously in a prod env this would not work, so I am trying to manage each session. the user should log on, and they should have their own session, and all their database creds should be pulled from my master table, and then used for that specific session. multiple users should be able to use this and edit their products and inventory at the same time. I have tried express-session, but no luck, I'm doing something wrong but not sure where to start really. here's my login code:
//LOGIN FUNCTIONALITY
app.post("/login", (req, res) => {
//defining variables for users username & password inputs
const inputUsername = req.body.inputUsername;
const inputPassword = req.body.inputPassword;
//functionality to query db by username
var userLogin = "select * from login where USERNAME = ?";
ibmdb.open(ibmdbconnMaster, function (err, conn) {
if (err) return console.log(err);
conn.query(userLogin, [inputUsername], function (err, rows) {
if (err) {
console.log(err);
}
//if the query returns results that are > 0
if (rows.length > 0) {
var pass = "";
userSessionId = req.body.sessionID
var sessUsername = userUsername
//loop for getting those values that correspond with the username of the user
for (var i = 0; i < rows.length; i++) {
userUsername = rows[i]["USERNAME"];
pass = rows[i]["PASSWORD"];
firstName = rows[i]["FN"];
lastName = rows[i]["LN"];
company = rows[i]["COMPANY"];
ibmdbconnDash = rows[i]["DBCONNSTRINGDASH"];
ibmdbconnBlog = rows[i]["DBCONNSTRINGBLOG"];
mailerStatus = rows[i]["MAILERSTATUS"];
//these will be more secure when time comes
cloudinaryName = rows[i]["CLOUDINARYNAME"];
cloudinaryKey = rows[i]["CLOUDINARYKEY"];
cloudinarySecret = rows[i]["CLOUDINARYSECRET"];
}
//comparing user input password to hashed db password
bcrypt.compare(inputPassword, pass, function (err, result) {
console.log("result is " + result);
//if the result of the compare is true, then redirect to the index function
if (result == true) {
console.log("login works");
userAuth = "true"
res.redirect("/index");
} else {
//if compare returns false, re-render login page
userAuth = "false";
res.render("login.ejs");
alert("Incorrect username or password. Please try again");
}
});
//if the entire query returns rows < 1 (username and password don't match, then re-render login page)
} else {
userAuth = "false";
res.render("login.ejs");
alert("Incorrect username or password. Please try again");
}
conn.close(function () {
console.log("closed the function /login");
});
});
});
});
global variables
var userAuth = ""
var userName = "";
var firstName = "";
var lastName = "";
var company = "";
var password = "";
var ibmdbconnMaster =
"db2 conn string";
var ibmdbconnDash = "";
var ibmdbconnBlog = "";
var userUsername = "";
var mailerStatus = "";
var cloudinaryName = "";
var cloudinaryKey = "";
var cloudinarySecret = "";
I have tried implementing sessions using express-sessions, the code I had set up for that was the standard code from their site:
app.use(session({
secret: "sec",
resave: false,
uninitialized: true,
}))
main index / landing page (dashboard) function
//DEFINING GLOBAL VARIABLES FOR AUTH
var sessionID = "";
var numOfOrders = "";
var numOfUsersM = "";
var userAuth = ""
var userName = "";
var firstName = "";
var lastName = "";
var company = "";
var password = "";
var ibmdbconnMaster =
"db conn string";
var ibmdbconnDash = "";
var ibmdbconnBlog = "";
var userUsername = "";
var mailerStatus = "";
var cloudinaryName = "";
var cloudinaryKey = "";
var cloudinarySecret = "";
//manage sessions
app.use(session({
secret: 'secret-key',
resave: true,
saveUninitialized: true,
}))
//rendering login page
app.get("/login", (req, res) => {
res.render("login.ejs");
});
/
//LOGIN FUNCTIONALITY
app.post("/login", (req, res) => {
// console.log("sessionsid is: " + req.body.sessionID)
// sessionID = req.body.sessionID
//defining variables for users username & password inputs
const inputUsername = req.body.inputUsername;
const inputPassword = req.body.inputPassword;
//functionality to query db by username
var userLogin = "select * from login where USERNAME = ?";
ibmdb.open(ibmdbconnMaster, function (err, conn) {
if (err) return console.log(err);
conn.query(userLogin, [inputUsername], function (err, rows) {
if (err) {
console.log(err);
}
//if the query returns results that are > 0
if (rows.length > 0) {
var pass = "";
//var userUsername = ""
userSessionId = req.body.sessionID
var sessUsername = userUsername
//loop for getting those values that correspond with the username of the user
for (var i = 0; i < rows.length; i++) {
var userUsername1 = rows[i]["USERNAME"];
pass = rows[i]["PASSWORD"];
firstName = rows[i]["FN"];
lastName = rows[i]["LN"];
company = rows[i]["COMPANY"];
ibmdbconnDash = rows[i]["DBCONNSTRINGDASH"];
ibmdbconnBlog = rows[i]["DBCONNSTRINGBLOG"];
mailerStatus = rows[i]["MAILERSTATUS"];
cloudinaryName = rows[i]["CLOUDINARYNAME"];
cloudinaryKey = rows[i]["CLOUDINARYKEY"];
cloudinarySecret = rows[i]["CLOUDINARYSECRET"];
}
//comparing user input password to hashed db password
bcrypt.compare(inputPassword, pass, function (err, result) {
console.log("result is " + result);
//if the result of the compare is true, then redirect to the index function
if (result == true) {
console.log("login works");
var userAuth1 = "true"
//successful login
req.session.user = {
userUsername1,
userAuth1
}
console.log("rquu1 " + req.session.user.userUsername1)
res.redirect("/index");
} else {
//if compare returns false, re-render login page
userAuth1 = "false";
res.render("login.ejs");
alert("Incorrect username or password. Please try again");
}
});
//if the entire query returns rows < 1 (username and password don't match, then re-render login page)
} else {
userAuth = "false";
res.render("login.ejs");
alert("Incorrect username or password. Please try again");
}
conn.close(function () {
console.log("closed the function /login");
});
});
});
});
//function for logout page
app.get("/logout", (req, res) => {
userAuth = "false";
res.render("login.ejs");
});
//RENDERING INDEX PAGE WITH INFORMATION ABOUT PRODUCTS AND ANALYTICS
app.get("/index", (req, res) => {
// if (userAuth == "true") {
if (req.session.user) {
console.log(req.session.user)
console.log("username is: " + userName);
pageName = "/index";
numOfOrdersFun(req, res, numOfOrders)
//end of location manager
//initializing counter
var counterTest2 = "select * from VISITORS";
ibmdb.open(ibmdbconnDash, function (err, conn) {
if (err) return console.log(err);
conn.query(counterTest2, function (err, rows) {
if (err) {
console.log(err);
}
for (var i = 0; i < rows.length; i++) {
var dbCountCurrent = rows[i]["NUM"];
}
console.log("currentCount " + dbCountCurrent);
conn.close(function () {
console.log("closed the function /login");
});
//showing information for products
var showingDBINFO = "SELECT * FROM PRODUCTS";
ibmdb.open(ibmdbconnDash, function (err, conn) {
if (err) return console.log(err);
conn.query(showingDBINFO, function (err, rows) {
if (err) {
console.log(err);
}
//rendering page with all users information, products, and data from login. also a redirect from the login info.
res.render("index", {
page_title: "index",
data: rows,
userName: userName,
FN: firstName,
LN: lastName,
CO: company,
dbcc: dbCountCurrent,
numOfOrders: numOfOrders,
mailerStatus: mailerStatus,
});
conn.close(function () {
console.log("closed the function /index);
});
});
});
});
});
} else {
req.session.user.userAuth1 == "false"
res.render("login.ejs");
}
});
but now im confused on how to manage each session individually when their are so many global variables I have that are needed for each session, and would users be able to use the app simultaneously?
thanks for the help!
When using express-session you can use the req.session object and store your preferred data. In your concrete example you could set all the information about the user you need later in your code to req.session.user.
Tiny example:
//successful login
req.session.user = {
userName,
firstName
}
If you need to access any information about the user later, just use req.session.user.userName for instance.
This data is stored server-side and is also available in new requests.
Please also note that the secret shouldn't be the default, instead use a strong & generated password nobody knows.

Create multiple user login system with personal messages

I want to create a multi user login system with private messages.
I created the theory in JavaScript (just for sketching out the theory and functionalities), and I wonder if I am on the right track.
Of course I will change it to a backend language later on with all the validations, this is purely for sketching.
// User database simulation
var users = [];
var defaultUser = {
'rights': 1, /* 0 - 3: 0 is banned, 1 is default, 2 is moderator, 3 is admin */
'activated': false,
'createdAt': null,
'updatedAt': null,
'username': null,
'userId': null,
'email': null,
'pass': null, /* will be encrypted */
'profile': {
'sex': null,
'age': null,
'avatar': null,
'updatedAt': null,
},
'messages': {
'inbox': [],
'outbox': [],
'trash': [],
'drafts': []
}
};
var defaultMessage = {
'id': null,
'date': null,
'from': null,
'to': null,
'message': null
};
var userManagement = {
'register': function(username, email, pass){
var user = $.extend({}, defaultUser);
user.username = username;
user.email = email;
user.pass = pass;
user.userId = username + '_' + Math.floor(Date.now() / 1000);
// If everything is valid, register:
// User database insert simulation
users.push(user);
console.log('Registered', user);
},
'login': function(username, pass) {
// User database query simulation
for(var i = 0, l = users.length; i < l; i++) {
var user = users[i];
if(user.username === username) {
if(user.pass === pass) {
console.log('Logged in', user);
} else {
console.log('Pass incorrect');
}
} else {
console.log('User not found');
}
}
},
'forgotUsername': function(email) {
// User database query simulation
for(var i = 0, l = users.length; i < l; i++) {
var user = users[i];
if(user.email === email) {
console.log('username ['+ user.username +'] send to ' + user.email);
} else {
console.log('User not found');
}
}
},
'forgotPass': function(username) {
// User database query simulation
for(var i = 0, l = users.length; i < l; i++) {
var user = users[i];
if(user.username === username) {
console.log('pass from user ['+ user.username +'] send to ' + user.email);
} else {
console.log('User not found');
}
}
},
'getUserById': function(userId){
var key;
for(var i = 0, l = users.length; i < l; i++) {
var user = users[i];
if(user.userId === userId) {
return user;
}
}
return null;
},
'getUserByUsername': function(username){
for(var i = 0, l = users.length; i < l; i++) {
var user = users[i];
if(user.username === username) {
return user;
}
}
return null;
}
/* TODO: updateProfile, activate */
}
var message = {
'send': function(fromUserId, toUserId, msg){
var sender = userManagement.getUserById(fromUserId);
var receiver = userManagement.getUserById(toUserId);
var message = $.extend({}, defaultMessage);
message.id = fromUserId + '_' + Math.floor(Date.now() / 1000);
message.from = sender.username;
message.fromUserId = fromUserId;
message.to = receiver.username
message.toUserId = toUserId;
message.message = msg;
message.date = new Date();
sender.messages.outbox.push(message);
receiver.messages.inbox.push(message);
}
/* TODO: delete, move etc. */
}
userManagement.register('barry', 'barry#test.nl', 'bcf2ibc');
userManagement.register('john', 'john#test.nl', 'bahjscb');
userManagement.login('test', 'blabla'); // fail
userManagement.login('barry', 'blabla'); // fail
userManagement.login('barry', 'bcf2ibc'); // success
userManagement.login('John', 'bahjscb'); // success
//userManagement.forgotPass('barry');
var barry = userManagement.getUserByUsername('barry');
var john = userManagement.getUserByUsername('John');
message.send(barry.userId, john.userId, 'My test message.');
message.send(barry.userId, john.userId, 'You received my previous message?');
message.send(john.userId, barry.userId, 'Yes I did received them.');
console.log(users);
JS Fiddle: https://jsfiddle.net/vmjs1n9n/12/
They way I setup the private message per user, is that a good thing to do? I would appreciate advice on the rest as well!
It's a start, if your primary intent is to facilitate private messages, then yes, private message per user is a good thing to do.
My first thoughts, you know you are re-inventing the wheel right? If I was given this as a business requirement I would integrate with an existing messaging service or protocol rather than have to deal with the long term management of this kind of data. Even authentication, in this day you should be attempting to implement some kind of open authentication standard, like OAuth, again to reduce the amount effort you need to spend to get this off the ground and keep it running long term.
I normally wouldn't put message data physically into the sender's outbox and then into the receivers inbox as well, primarily because your data storage would be double, but I guess like email, routing copies of the original message would make management of the messages really simple, whilst making it hard to accidentally give one user access to another's messages.
Here because you are prototyping, it's hard to provide decent comments because you have already alluded to the fact that you will do things differently in the backend so I don't want to second guess where you have already decided to go with this. For a simple system like this the UI should be really lite, with the logic in the backend, the specific logic is where I would like to provide future comments and insights.

Respond to request after all data is inserted to database

I am trying to implement multiple data insertion on one call and trigger response only after all data is inserted. This is how I am currently doing it:
create: function(req, res) {
var response = {};
var num = req.body.num;
var ret = 0;
for (var i = 0; i < num; i++) {
var db = new user();
db.enabled = false;
db.save(function(err){
if(err) {
// Handle error
} else {
ret++;
// Do something
}
});
}
response = {"status" : 200, "message" : "It's working: " + ret};
res.json(response);
}
The problem with this approach is that all the callbacks for save will be triggered after res.json(response) which is wrong because sometimes I would also like to inform user how much data was saved. User will always receive the following response:
It's working: 0
Because ret variable is always 0. It's getting increased only after response is already triggered. What am I doing wrong?
EDIT:
Code after Will's suggestion:
var Q = require('q');
create: function(req, res) {
var response = {};
var num = req.body.num;
var ret = 0;
var tasks = [];
for (var i = 0; i < num; i++) {
var db = new user();
db.enabled = false;
tasks.push(db.save());
}
Q.all(tasks).then(
function(results) {
response = {"status" : 200, "message" : "It's working!"};
},
function(err) {
response = {"status" : 500, "message" : "Not working!" };
);
res.json(response);
}
for (var i = 0; i < num; i++) {
var db = new user();
db.enabled = false;
db.save(function(err){
if(err) {
// Handle error
} else {
ret++;
if(ret == num){
response = {"status" : 200, "message" : "It's working: " + ret};
res.json(response);
}
}
});
}

Javascript for loop wait for callback

I have this function:
function tryStartLocalTrendsFetch(woeid) {
var userIds = Object.keys(twitClientsMap);
var isStarted = false;
for (var i = 0; i < userIds.length; i++) {
var userId = userIds[i];
var twitClientData = twitClientsMap[userId];
var isWoeidMatch = (woeid === twitClientData.woeid);
if (isWoeidMatch) {
startLocalTrendsFetch(woeid, twitClientData, function (err, data) {
if (err) {
// Couldn't start local trends fetch for userId: and woeid:
isStarted = false;
} else {
isStarted = true;
}
});
// This will not obviously work because startLocalTrendsFetch method is async and will execute immediately
if (isStarted) {
break;
}
}
}
console.log("No users are fetching woeid: " + woeid);
}
The gist of this method is that I want the line if (isStarted) { break; } to work. The reason is that if it's started it should not continue the loop and try to start another one.
I'm doing this in NodeJS.
try to use a recursive definition instead
function tryStartLocalTrendsFetch(woeid) {
var userIds = Object.keys(twitClientsMap);
recursiveDefinition (userIds, woeid);
}
function recursiveDefinition (userIds, woeid, userIndex)
var userId = userIds[userIndex = userIndex || 0];
var twitClientData = twitClientsMap[userId];
var isWoeidMatch = (woeid === twitClientData.woeid);
if (isWoeidMatch && userIndex<userIds.length) {
startLocalTrendsFetch(woeid, twitClientData, function (err, data) {
if (err) {
recursiveDefinition(userIds, woeid, userIndex + 1)
} else {
console.log("No users are fetching woeid: " + woeid);
}
});
} else {
console.log("No users are fetching woeid: " + woeid);
}
}
You may also use async (npm install async):
var async = require('async');
async.forEach(row, function(col, callback){
// Do your magic here
callback(); // indicates the end of loop - exit out of loop
}, function(err){
if(err) throw err;
});
More material to help you out: Node.js - Using the async lib - async.foreach with object

How to handle error of http call when site is down

I would like to replace the if(body.toString().indexOf("404") !== 0) block with some generic error handling code but I can't seem to see where it throws an error when the target host is down. So far, this is the only hacky method I've managed to put together that works.
app.get('/', function(req, res){
var sites = ["foo.com", "bar.com"];
var returnObj = [];
var index = 0;
getSites(index);
// Recursively add data from each site listed in "sites" array
function getSites(index) {
if(index < sites.length) {
var url = sites[index];
var _req = http.get({host: url}, function(_res) {
var bodyChunks = [];
_res.on('data', function(chunk) {
bodyChunks.push(chunk);
}).on('end', function() {
var body = Buffer.concat(bodyChunks);
if(body.toString().indexOf("404") !== 0) {
returnObj.push(JSON.parse(body));
}
getSites(++index);
});
});
_req.on('error', function(e) {
console.log('ERROR: ' + e.message);
});
} else {
res.json(returnObj);
res.end();
}
}
});
You can check the status code of the response.
if(_req.statusCode === 200) {
//Response okay.
}
Here's a list of the status codes.

Categories

Resources