My nodejs app is working well neither sockets server. Its connecting with user side but I can't handle any events.
var app = require("express")();
var http = require("http").createServer(app);
var io = require("socket.io")(http);
http.listen(appSettings.RUNNING_PORT, function () {
globals.debug('Server is running on port: ' + appSettings.RUNNING_PORT, 'success');
});
io.set('authorization', function (handshakeData, accept) {
if (handshakeData && handshakeData.headers && handshakeData.headers.referer) {
var domain = handshakeData.headers.referer.replace('http://', '').replace('https://', '').split(/[/?#]/)[0];
if ('**' == domain) {
accept(null, true);
} else {
globals.debug('Bad site authentication data, game will be disabled.', 'danger');
return accept('Bad site authentication data, game will be disabled.', false);
}
} else {
accept('Failed transaction', false);
}
io.use(function (sock, next) {
var handshakeData = sock.request;
var userToken = handshakeData._query.key;
users.prepare(io, []);
users.defineUser(userToken, sock.id, next);
// start the game
game.prepare(io, []);
game.startPolling();
});
});
so I'm connecting here and calling for some methods. users.defineUser(userToken, sock.id, next);
this.defineUser = function (token, sockID, next) {
if (token) {
Promise.try(function () {
return db('users')
.where('key', '=', token)
.orderBy('id', 'desc')
.limit(1);
}).then(function (result) {
if (result && result.length > 0) {
self.io.to(sockID).emit('connect-success', {
message: 'Successfully connected',
data: {
name: result[0].name
}
});
globals.debug('User '+ result[0].name +' connected as ' + sockID, 'success');
next('Successfully connected', true);
} else {
self.io.to(sockID).emit('global-error', {
message: 'Only connected users are available to play'
});
next(false, false);
}
}).catch(function (e) {
if (e.code) {
self.io.to(sockID).emit('global-error', {
message: 'There was a problem with our database, please try later. ' + e.code
});
next(false, false);
}
});
} else {
self.io.to(sockID).emit('global-error', {
message: 'Only connected users are available to play'
});
next(false, false);
}
};
so I can see debug for "User Sandra connected as /#Q82F5WCvLZvgy65YAAAB" but when I try to send anything to user it does not work.
Even in frontend I can see that app were connected with user by calling socket.on('connection) method. So where the problem could be?
Related
I am trying to make a proxy server in Node.js which can block URLs based off of a Regex expression and also has basic authentication, to be used as a proxy with IOS devices.
Here is my current code:
let http = require('http');
let net = require('net');
let httpProxy = require('http-proxy');
let url = require('url');
let fs = require('fs');
let auth = require('basic-auth');
const port = process.env.PORT || 8080;
let proxy = httpProxy.createServer();
let blacklist = [/.*example\.com$/];
function inBlacklist(url) {
return blacklist.some(function (blacklistItem) {
return blacklistItem.test(url);
});
}
let logStream = fs.createWriteStream('log.txt', { flags: 'a' });
console.log("Booting Up | Port", port);
let allowedCredentials = [
'root:root'
]
function check (username, password) {
if (username && password) {
if(allowedCredentials.indexOf(`${username}:${password}`) > -1){
return true;
} else return false;
} else {
return false;
}
};
var server = http.createServer(function (req, res) {
var credentials = auth(req);
if (!credentials || !check(credentials.name, credentials.pass)) {
res.statusCode = 401
res.setHeader('WWW-Authenticate', 'Basic realm="Please log in using your given username and password."')
return res.end('Access denied');
} else {
res.end('Access granted')
}
var serverUrl;
try { serverUrl = url.parse(req.url); }
catch { console.log(0); return; }
var shouldBlock = false;
try { shouldBlock = inBlacklist(serverUrl.hostname); } catch {
console.log(1); return;
}
logStream.write(`CONNECT1: ${req.url}\nHOSTNAME: ${serverUrl.hostname}\nshouldBlock: ${shouldBlock}\nreqURL: ${req.url}\n---------\n`);
if (shouldBlock) {
try {
return proxy.web(req, res, { target: '0.0.0.0' }, function () {
res.writeHead(404);
res.end('Not found.');
});
} catch { console.log(3); return; }
}
try { proxy.web(req, res, { target: req.url, secure: false }, function (e) { console.log("Proxy.web Accept Error"); res.writeHead(404); res.end('Not found. (P.W. Accept Error)'); }); } catch { console.log(2); }
}).listen(port, '0.0.0.0', () => {
console.log("Listening at: http://example.com:" + port);
});
server.on('listening', () => {
console.log(`Server is listening on port ${server.address().port}`);
});
server.on('close', function () {
logStream.write('server closed\n');
logStream.end();
});
server.on('error', function (err) {
logStream.write(`ERROR: ${err}\n---------\n`);
console.log('Server error:', err);
});
server.on('connect', function (req, socket) {
var reqport = req.url.split(':')[1];
var serverUrl;
if (reqport == 443) {
try { serverUrl = url.parse("https://" + req.url); }
catch (err) { console.log(err); return; }
} else if (reqport == 80) {
try { serverUrl = url.parse("http://" + req.url); }
catch (err) { console.log(err); return; }
} else return;
var shouldBlock = false;
try { shouldBlock = inBlacklist(serverUrl.hostname); } catch {
console.log(1); return;
}
logStream.write(`CONNECT2: ${req.url}\nHOSTNAME: ${serverUrl.hostname}\nshouldBlock: ${shouldBlock}\nreqURL: ${req.url}\n---------\n`);
if (shouldBlock) {
try {
socket.write('HTTP/1.1 404 Not Found\r\n' +
'\r\nNot found.');
}
catch { console.log("Caught error"); }
return socket.end();
}
var srvSocket = net.connect(serverUrl.port, serverUrl.hostname, function () {
try {
socket.write('HTTP/1.1 200 Connection Established\r\n' +
'Proxy-agent: Node-Proxy\r\n' +
'\r\n');
} catch {
logStream.write(`ERROR WRITING\n---------\n`);
}
srvSocket.pipe(socket).on('error', (err) => {});
socket.pipe(srvSocket).on('error', (err) => {});
}).on('error', (err) => err);
});
Is there any better way to do this? I am getting frequent errors with the srvSocket socket disconnecting.
Most importantly, the authentication doesn't work. For some devices, no authentication is required in the IOS settings, while on others authentication is required but doesn't matter what username and password are inputted.
Why does this happen?
i am completely newbie in node.js, and trying learn how it actually works. I know that by default all node.js function calls are asynchronous. Now i need LDAP authentication in my application where i need wait for the server response to check whether the user credentials are right or wrong.The ldap part is working fine but i am not sure on how to return data from a function call in synchronous way. below is the part of my code.
router.js
var express = require('express');
var router = express.Router();
var tools = require('./authenticateUser');
router.post('/authenticateUser', function(req, res) {
// In the below line i am calling the method which
// should return the userDN (a string)
tools.searchUser(req.body.user, req.body.passwd);
res.render('home.jade');
});
authenticateUser.js
module.exports = {
searchUser : function (username, password) {
adminDN = *************;
adminPassword = '*********';
baseDN = '***';
var ldap = require('ldapjs');
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
var adminClient = ldap.createClient({
url: '*******'
});
var opts = {
filter: '(&(objectClass=userProxyFull)(sAMAccountName=' + username + '))',
scope: 'sub',
attribute: ['sAMAccountName']
};
console.log('--- going to try to connect user ---');
try {
adminClient.bind(adminDN, adminPassword, function (error) {
if (error) {
console.log(error.message);
adminClient.unbind(function (error) {
if (error) {
console.log(error.message);
} else {
console.log('adminClient disconnected');
}
});
} else {
// Searching Client ID in LDS
adminClient.search(baseDN, opts, function (error, search) {
console.log('Searching.....' + userDN);
search.on('searchEntry', function (entry) {
if (entry.object) {
// Here i need to return the object back
//to the router.js from where i call in a synchronous way
adminClient.unbind(function (error) {
if (error) {
console.log(error.message);
}
});
}
});
search.on('error', function (error) {
console.error('error: ' + error.message);
});
});
}
});
} catch (error) {
console.log(error);
adminClient.unbind(function (error) {
if (error) {
console.log(error.message);
} else {
console.log('client disconnected');
}
});
} finally {
adminClient.unbind(function (error) {
if (error) {
console.log(error.message);
} else {
console.log('client disconnected');
}
});
}
},
};
You have to pass res.render('home.jade') as a function(the callback) to your searchUser function.
It should look like
tools.searchUser(req.body.user,
req.body.password,
res}
)
searchUser function
searchUser : function (username, password,res) {
...
finally(){
res.render('home.jade');
}
}
I found example of using socket.IO 1.* width Express 4. Here is a link
Everyting works perfectly. But there is a code:
io.use(function(socket, next) {
try {
var data = socket.handshake || socket.request;
if (! data.headers.cookie) {
return next(new Error('Missing cookie headers'));
}
console.log('cookie header ( %s )', JSON.stringify(data.headers.cookie));
var cookies = cookie.parse(data.headers.cookie);
console.log('cookies parsed ( %s )', JSON.stringify(cookies));
if (! cookies[COOKIE_NAME]) {
return next(new Error('Missing cookie ' + COOKIE_NAME));
}
var sid = cookieParser.signedCookie(cookies[COOKIE_NAME], COOKIE_SECRET);
if (! sid) {
return next(new Error('Cookie signature is not valid'));
}
console.log('session ID ( %s )', sid);
data.sid = sid;
sessionStore.get(sid, function(err, session) {
if (err) return next(err);
if (! session) return next(new Error('session not found'));
data.session = session;
next();
});
} catch (err) {
console.error(err.stack);
next(new Error('Internal server error'));
}
});
So if there is an error, it passes to next. But where goes this next? How to handle this error without try, catch. I mean handle where this next is receiving like in express:
// something here
// And this is callback function which accepts next with err
functin(err, anythingHere){
if err
throw err;
else
// some code here
}
It goes to the client.
https://socket.io/docs/server-api/#socket-use-fn
Errors passed to middleware callbacks are sent as special error packets to clients.
io.on('connection', (socket) => {
socket.use((packet, next) => {
if (packet.doge === true) return next();
next(new Error('Not a doge error'));
});
});
Note that any middlewares following it aren't invoked.
On the client-side you can handle then like so:
socket.on('error', function(err){
// do something with err
});
Sharing another example for convenience
Node Version : 12.13.1
Express : 4.xx.x
Socket IO: 2.1.1
Socket IO client: 2.x.x
io.use((socket, next) => {
if (
process.env.NODE_ENV == "production" &&
socket.handshake.headers.origin != undefined &&
socket.handshake.headers.origin == config.SOCKET_ORIGIN
) {
return next();
} else {
// return next();
return next(new Error("Invalid Origin"));
}
})
.use(async (socket, next) => {
if (socket.handshake.query.token != undefined) {
const verificationResponse = await hlpr.JWTVerification(
socket.handshake.query.token
);
if (verificationResponse.err != null) {
next(new Error("Unauthorized"));
} else {
return next();
}
} else {
next(new Error("Unauthorized"));
}
})
.use(async (socket, next) => {
console.log("***** socket ******");
console.log("***** next ******");
console.log("***** payload Middlewar Check ******");
return next();
})
.on("connection", socket => {
console.log("User Connected Successfully");
});
Client side code
const socket = io.connect('ws://xXx.xXx.x.xXx:xxxx', {
// resource: 'nodejs',
transport: true,
query: {token: "xxxXXXxxXXXXXxxxXXXXxxxxXXXXXXXX"}
});
socket.on('connect', () => {
console.log("*******");
console.log("Successfull Hand Shake");
console.log("*******");
}).on('error', (err) => {
console.log("************ Error ************")
console.log("************ Error ************")
console.log(err)
console.log("************ Error ************")
// Show the toaster with the error
// Try re-connect
// close the socket connection
});
I am using node.js with socket.io to push real time notifications to users. However, currently I am just sending back a query result done in my socket.io code and sending it back to the client but I need to let socket know about the changes that occur and to either update with the changes or re-query the db to check for the new number and send that to the client.
For example if a user gets a friend request then the notification count will change and I want socket.io to push the new notification count number to the user.
here is my socket.io code in my app.js file:
io.on('connection', function(socket) {
var sessionID = socket.handshake.sessionID,
session = new connect.middleware.session.Session({ sessionStore: sessionStore }, socket.handshake.session)
console.log('socket: new ' + sessionID)
socket.broadcast.emit('arpNewConn', session.passport.user)
var intervalID = setInterval(function() {
socket.handshake.session.reload(function() {
socket.handshake.session.touch().save()
})
socket.emit('pulse', { heartbeat: new Date().toString(), timestamp: new Date().getTime() })
}, 300 * 1000)
socket.on('disconnect', function() {
console.log('socket: dump ' + sessionID)
socket.broadcast.emit('arpLostConn', session.passport.user)
clearInterval(intervalID)
})
socket.emit('entrance', {message: 'Message works'});
dbnotif.findOne(userID, function (err, user) {
if(err) throw err;
notify = user.notifications;
socket.emit('notify', {notific: notify});
});
});
Here is the client side:
div#CheckSocket
script(src='http://localhost:3000/socket.io/socket.io.js')
script.
$(document).ready(function () {
console.log('socket');
var socket = io.connect('http://localhost:3000/');
console.log('entered1');
socket.on('entrance', function (data) {
console.log('entered');
console.log(data.message);
});
socket.on('notify', function (data) {
console.log('noting');
console.log(data.notific);
if(data.notific !== 0)
$('.notifications').html(data.notific);
});
socket.on('reconnecting', function(data) {
setStatus('reconnecting');
console.log('entered2');
});
function setStatus(msg) {
console.log('connection status: ' + msg);
console.log('entered5');
}
});
Here is the example of adding a friend in the route file:
exports.addContactPost = function(req, res, err) {
async.waterfall([
function(callback) {
var success;
var newFriend = new Friend ({
userId: req.signedCookies.userid,
friend_id: mongoose.Types.ObjectId(req.body.otherUser),
friend_status: 1
});
newFriend.save(function(err){
if(err) {
console.log(err);
} else {
console.log("saved it");
success = true;
}
});
callback(null, success)
},
function(success, callback) {
//if(success === true) {
var success2;
var newFriend2 = new Friend ({
userId: mongoose.Types.ObjectId(req.body.otherUser),
friend_id: req.signedCookies.userid,
friend_status: 2
});
newFriend2.save(function(err){
if(err) {
res.send("request not received");
} else {
success2 = true;
}
});
callback(null, success2);
//} else {
// res.send("error with request sent");
//}
},
function(success2, callback) {
console.log('callback3');
//if(success2 === true) {
var success3;
Notification.findOneAndUpdate({userId: mongoose.Types.ObjectId(req.body.otherUser)}, {
$inc: {notifications: 1}
}, function(err, notify) {
if(err) {
res.send(err);
} else {
console.log(notify);
if(notify.added_notifications === true) {
// enable mail and include general u have got a new request... do not include name because not storing it
}
}
success3 = true;
callback(null, success3);
}],
function(err, results) {
res.json({response: true});
console.log("Add successful");
});
};
Notes: dbnotif is a model being called by mongoose,
userID is a global variable available to the file
I helped him solve this question offline, but we ended up using an EventEmitter as a proxy.
// main.js
var EventEmitter = require('events').EventEmitter;
var emitter = new EventEmitter();
Then add it to each request as middleware:
// elsewhere in main.js
app.use(function(req, res, next) {
req.emitter = emitter;
next();
});
Then in external routes file:
// routes.js
exports.addContactPost = function(req, res, err) {
req.emitter.emit( 'some-key', whatever, data, you, want );
};
I am creating a login authentication page, where a user would input there active directory username and password and using NodeJS I would check to see if it's valid, but I keep getting
[Error: LDAP Error Bad search filter]
or
[Error: Search returned != 1 results]
When I'm trying to search for the username and password, my code is below:
I'm using: https://github.com/jeremycx/node-LDAP, let's say that the user entered a username of hhill
var ldap = require('LDAP');
var ldapServer = new ldap({ uri: 'ldap://batman.lan', version: 3});
ldapServer.open(function(error) {
if(error) {
throw new Error('Cant not connect');
} else {
console.log('---- connected to ldap ----');
username = '(cn='+username+')';
ldapServer.findandbind({
base: 'ou=users,ou=compton,dc=batman,dc=lan',
filter: username,
password: password
}, function(error, data) {
if(error){
console.log(error);
} else {
console.log('---- verified user ----');
}
});
}
});
Does anyone have any suggestions on what I'm doing wrong?
UPDATE
Here is the solution I came up with if anyone ever needs it, with the help of the answer below
var username = request.param('username');
var password = request.param('password');
var ldap = require('ldapjs');
ldap.Attribute.settings.guid_format = ldap.GUID_FORMAT_B;
var client = ldap.createClient({
url: 'ldap://batman.com/cn='+username+', ou=users, ou=compton, dc=batman, dc=com',
timeout: 5000,
connectTimeout: 10000
});
var opts = {
filter: '(&(objectclass=user)(samaccountname='+username+'))',
scope: 'sub',
attributes: ['objectGUID']
};
console.log('--- going to try to connect user ---');
try {
client.bind(username, password, function (error) {
if(error){
console.log(error.message);
client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
} else {
console.log('connected');
client.search('ou=users, ou=compton, dc=batman, dc=com', opts, function(error, search) {
console.log('Searching.....');
search.on('searchEntry', function(entry) {
if(entry.object){
console.log('entry: %j ' + JSON.stringify(entry.object));
}
});
search.on('error', function(error) {
console.error('error: ' + error.message);
});
client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
});
}
});
} catch(error){
console.log(error);
client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
}
In this case, you need ldapClient rather than ldapServer, this is the example code from the official doc:
var ldap = require('ldapjs');
ldap.Attribute.settings.guid_format = ldap.GUID_FORMAT_B;
var client = ldap.createClient({
url: 'ldap://127.0.0.1/CN=test,OU=Development,DC=Home'
});
var opts = {
filter: '(objectclass=user)',
scope: 'sub',
attributes: ['objectGUID']
};
client.bind('username', 'password', function (err) {
client.search('CN=test,OU=Development,DC=Home', opts, function (err, search) {
search.on('searchEntry', function (entry) {
var user = entry.object;
console.log(user.objectGUID);
});
});
});
#Sukh Thank you for posting your UPDATE solution; however, there is a problem with the code you posted in your UPDATE. While it works for simple cases, with larger queries, you will find you are unbinding before the results have been output. The solution for me was to move your unbinds into the search.on functions.
Here is an edit of your UPDATE:
var ldap = require('ldapjs');
ldap.Attribute.settings.guid_format = ldap.GUID_FORMAT_B;
var client = ldap.createClient({
url: 'ldap://batman.com/cn='+username+', ou=users, ou=compton, dc=batman, dc=com',
timeout: 5000,
connectTimeout: 10000
});
var opts = {
filter: '(&(objectclass=user)(samaccountname='+username+'))',
scope: 'sub',
//attributes: ['objectGUID']
// This attribute list is what broke your solution
attributes: ['objectGUID','sAMAccountName','cn','mail','manager','memberOf']
};
console.log('--- going to try to connect user ---');
try {
client.bind(username, password, function (error) {
if(error){
console.log(error.message);
client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
} else {
console.log('connected');
client.search('ou=users, ou=compton, dc=batman, dc=com', opts, function(error, search) {
console.log('Searching.....');
search.on('searchEntry', function(entry) {
if(entry.object){
console.log('entry: %j ' + JSON.stringify(entry.object));
}
client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
});
search.on('error', function(error) {
console.error('error: ' + error.message);
client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
});
// don't do this here
//client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
});
}
});
} catch(error){
console.log(error);
client.unbind(function(error) {if(error){console.log(error.message);} else{console.log('client disconnected');}});
}
At least this is what I discovered when using your solution with Active Directory searches. memberOf returns A LOT of entries in my use case and the unbinds were being done prematurely, so I was getting the following error:
error: 1__ldap://my.domain.com/,OU=Employees,OU=Accounts,DC=my,DC=domain,DC=com closed
client disconnected
Suggestions
1.Don't use ldapauth-fork (Huge hanging issue, if we hit multiple requests then after some time library gets unresponsive and doesn't return anything.)
2.Don't use passport-ldapauth (Internally calls ldapauth-fork)
We can use ldapjs, which has easy implementation and is based on event driven approach.
Below nodejs code explain complete solution for ldap auth and search.
JS code
const ldap = require('ldapjs');
let client
// unbind after completion of process
function closeConnection() {
console.log('closeConnection')
client.unbind(err => {
console.log('unbind error', err)
});
}
function search() {
const searchOptions = {
filter: '(uid=yourSearchText)', // search text
scope: 'sub'
};
return new Promise((resolve, reject) => {
client.search('ou=consultants,' + 'ou="Your OU",ou=yourOu,dc=yourDc,dc=com', searchOptions, (err, res) => {
res.on('searchEntry', entry => {
console.log('searchEntry', entry.object);
resolve(entry.object)
});
res.on('searchReference', referral => {
console.log('referral: ' + referral.uris.join());
resolve(referral.uris.join())
});
res.on('error', err => {
console.error('search error: ' + err.message);
reject(err)
});
res.on('end', result => {
console.log('If not found', result);
reject({ message:'User not found'})
});
});
})
}
function authenticate() {
const server = 'ldap server ip';
client = ldap.createClient({
url: `ldap://${server}`
});
return new Promise((resolve, reject) => {
client.bind('cn=yourcn,dc=yourdc,dc=com', 'sortedSolutions', err => {
if (err) {
reject(err)
}
resolve('Authenticated successfully')
});
})
}
function start(req, res) {
let searchResponseData
authenticate()
.then(authenticateResponse => {
console.log('authenticateResponse', authenticateResponse)
return search()
})
.then(searchResponse => {
console.log('searchResponsesearchResponse', searchResponse)
searchResponseData = searchResponse
return closeConnection()
})
.then(closeConnectionResponse => {
console.log('ldap connection closed', closeConnectionResponse)
res.status(200).send(searchResponseData)
})
.catch(error => {
console.log('catch error', error)
res.status(400).send(error)
})
}
module.exports.start = start
// We can use same code with no authentication, Just pass '' to bind function client.bind('', '', err => { //same as above })