node.js synchronous function call for authentication - node.js

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');
}
}

Related

Nodejs Multiple pools created on refresh

I have several DBs for which i am using connection pools in node.js. Every time i refresh page i think pools are created again. i refresh page 3 times and 3 times promises resolved. i have removed several databases just to make it little bit easier to read here.
and if i un-comment connection close line my app crashes. i can't seem to figure out why
const config = require("../config/config");
const oracledb = require("oracledb");
var crm1connPromise = new Promise((resolve, reject) => {
oracledb.createPool({
user: config.crm1.user,
password: config.crm1.password,
connectString: config.crm1.connectString,
poolAlias: config.crm1.poolAlias,
poolMin: 0,
poolMax: 10,
poolTimeout: 300
}, (error, pool) => {
if (error) {
reject(err);
}
resolve("CRM1 Promise resolved")
});
});
var query2connPromise = new Promise((resolve, reject) => {
oracledb.createPool({
user: config.query2.user,
password: config.query2.password,
connectString: config.query2.connectString,
poolAlias: config.query2.poolAlias,
poolMin: 0,
poolMax: 10,
poolTimeout: 300
}, (error, pool) => {
if (error) {
reject(err);
}
resolve("QUERY2 Promise resolved --------")
});
});
var promiseArray = [crm1connPromise, crm2connPromise, crm3connPromise, crm4connPromise, csfp1connPromise, csfp2connPromise, csfp3connPromise, csfp4connPromise, cact1connPromise, cact2connPromise, cact3connPromise, cact4connPromise, cospconnPromise, cchnconnPromise, bbaseconnPromise, bcdrconnPromise, vcdbconnPromise, crptconnPromise, query2connPromise];
function getDBConnection (dbname) {
return new Promise((resolve, reject) => {
try {
Promise.all(promiseArray).then((message) => {
console.log(message);
const pool = oracledb.getPool(dbname);
pool.getConnection( (err, connection) => {
if (err) {
reject(err);
console.log(err);
}
resolve(connection);
});
});
} catch (error) {
reject(error);
}
});
}
module.exports.query = function(dbname, sql, bind = []){
return new Promise ((resolve,reject) =>{
var conn
try {
getDBConnection(dbname).then((connection) =>{
connection.execute(sql,bind,(err,result)=>{
if (err){
reject(err);
}
resolve(result);
})
//connection.close(0);
})
} catch (error) {
reject(error);
}
})
}
you can use 'Singleton'
please google 'Singleton pattern' and examples.
like this:
dataBaseManager.js:
'use strict'
var Singleton = (function () {
var instance;
function createInstance() {
var object = new dataBaseManager();
return object;
}
return {
getInstance: function () {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
function dataBaseManager() {
this.connected = false;
this.client = null;
this.dataBase = null;
//public methods
this.connect = function () {
try {
your_database.connect({}, (err, client) => {
if (err) {
this.connected = false;
this.client = null;
this.dataBase = null;
return;
}
this.connected = true;
this.client = client;
this.dataBase = client.db();
});
} catch (error) {
}
};
this.disconnect = function () {
try {
if (this.client) {
this.client.close();
this.connected = false;
this.client = null;
this.dataBase = null;
}
} catch (error) {
}
}
}
module.exports = Singleton;
repository.js:
const dataBaseManager = require("./dataBaseManager").getInstance();
your_get_dample_data_from_data_base_func = function (data) {
dataBaseManager.dataBase
.find({})
.toArray(function (err, result) {
if (err) {
return callback(err, null);
}
callback(null, result);
});
};
index.js:
const dataBaseManager = require("./dataBaseManager").getInstance();
function connect() {
dataBaseManager.connect();
}
function disconnect() {
dataBaseManager.disconnect();
}
Look at the node-oracledb example webappawait.js which starts the pool outside the web listener code path.
async function init() {
try {
await oracledb.createPool({
user: dbConfig.user,
password: dbConfig.password,
connectString: dbConfig.connectString
});
const server = http.createServer();
server.on('error', (err) => {
console.log('HTTP server problem: ' + err);
});
server.on('request', (request, response) => {
handleRequest(request, response);
});
await server.listen(httpPort);
console.log("Server is running at http://localhost:" + httpPort);
} catch (err) {
console.error("init() error: " + err.message);
}
}
async function handleRequest(request, response) {
. . .
}

twitter.stream is not a function in node.js?

I cannot able to monitor the twitter.I followed the procedure to do the sentiment analysis (twitter) in node.js code.It verified my twitter account correctly.but it shows stream is not a function. I enclosed the code .can anyone solve this issue.Thanks in advance...
app.get('/watchTwitter', function (req, res) {
const twitter = new twitterAPI({
consumerKey: "asas",
consumerSecret: "sdcscs"
});
const accessToken = "cdccd";
const accessTokenSecret = "csdcs";
var stream;
var testTweetCount = 0;
var phrase = 'bieber';
twitter.verifyCredentials(accessToken, accessTokenSecret, params, function (error, data, response) {
if (error) {
console.log(error);
} else {
console.log(data["screen_name"]);
stream = twitter.stream('statuses/filter',
{
'track': phrase
}, function (stream) {
res.send("Monitoring Twitter for \'" + phrase + "\'... Logging Twitter traffic.");
stream.on('data', function (data)
{
testTweetCount++;
if (testTweetCount % 50 === 0)
{
console.log("Tweet #" + testTweetCount + ": " + data.text);
}
});
});
}
});
});
app.listen(8086,function()
{
console.log("port is listen on 8086");
});
You are referring Twitter npm package, but you are using node-twitter-api. See the documentations. For statuses, you need to use following method.
twitter.statuses("update", {
status: "Hello world!"
},
accessToken,
accessTokenSecret,
function(error, data, response) {
if (error) {
console.log(error);
} else {
console.log(data);
}
}
);

Nodejs socket emitting does not work

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?

How to pass changes from middleware to socket.io?

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 );
};

Node JS LDAP Auth User

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 })

Resources