Node JS LDAP Auth User - node.js

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

Related

Google Cloud ETL Function To Pull IntuitAPI (Quickbooks) Data Without User Login Webpage Redirect

I have seen that there are many Intuit/Quickbooks questions on here but none of them have the answer I'm really looking for.
Is there a way to pull Quickbooks data, purely from a NodeJS cloud function in Google Cloud, without needing to literally redirect to a user login page?
I have two cloud functions, one to request authorization, and another to pull the data. I realize I have done something incorrectly, but my 1st function redirects to the 2nd function.
(1) Request Authorization Function:
var OAuthClient = require('intuit-oauth');
exports.res = {};
exports.requestAuth = (req, res) => {
exports.res = res;
try {
var oauthClient = new OAuthClient({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
environment: process.env.ENVIRONMENT,
redirectUri: process.env.FUNCTION_TWO_URI,
logging: false
});
const authUri = oauthClient.authorizeUri({ scope: [OAuthClient.scopes.Accounting, OAuthClient.scopes.OpenId], state: "testState" });
console.log("Redirecting to authUri: " + authUri);
res.redirect(authUri);
} catch (err) { exports.error("requestAuth", err); }
};
exports.error = (funcName, err) => {
console.error(funcName, err);
exports.res.status(500).send(err.message);
};
(2) Pull Data Function:
var OAuthClient = require('intuit-oauth');
const moment = require('moment');
exports.begin = (req, res) => {
try {
var oauthClient = new OAuthClient({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
environment: process.env.ENVIRONMENT,
redirectUri: process.env.FUNCTION_TWO_URI,
logging: false
});
const parseRedirect = req.url;
console.log("Creating token from parseRedirect: " + parseRedirect);
oauthClient.createToken(parseRedirect).then((authResponse) => {
console.log("Created token. Validating token: " + JSON.stringify(authResponse.getJson()));
exports.validateToken(oauthClient, authResponse.getJson(), (oauthClient) => {
console.log("Validated token. Pulling data...");
exports.pullData(oauthClient, (response) => {
console.log('SUCCESS: The API response is: ' + response);
res.status(200).send('SUCCESS: The API response is: ' + response);
});
});
}).catch((e) => {
exports.error("begin.oauthClient.createToken.catch", e);
});
} catch (err) { exports.error("begin", err); }
};
exports.validateToken = (oauthClient, token, completion) => {
try {
if (oauthClient.isAccessTokenValid()) {
oauthClient.setToken(token);
completion(oauthClient);
} else {
oauthClient.refresh().then((authResponse) => {
oauthClient.setToken(authResponse.json());
completion(oauthClient);
}).catch(function (e) {
exports.error("validateToken.oauthClient.refresh.catch", e);
});
}
} catch (err) { exports.error("validateToken", err); }
};
exports.pullData = (oauthClient, completion) => {
try {
let companyId = process.env.COMPANY_ID;
let date = moment().subtract(1, 'days').format('YYYY-MM-DD');
oauthClient.makeApiCall({
url: `/v3/company/${companyId}/reports/CustomerSales?start_date=${date}&end_date=${date}&minorversion=65`,
method: 'GET', headers: { 'Content-Type': 'application/json' }
}).then((response) => { completion(response); }).catch((e) => {
exports.error("pullData.oauthClient.makeApiCall.catch", e);
});
} catch (err) { console.error("pullData", err); }
};
exports.error = (funcName, err) => {
console.error(funcName, err);
res.status(500).send(`ERROR: ${funcName}: ` + JSON.stringify(err));
};
Upon testing this, I found that the 1st function does redirect to authUri, but then nothing happens.
I don't see any logs either.
When I go to the authUri in my browser, it just opens a log in page.
Any help is greatly appreciated!

Botpress - how to bring database values into the chat window?

I'm trying to build a chatbot using Botpress. I'm a beginner, looking for your help. One of the requirements is to query database to answer questions. This is what I have tried so far:
dbconnect.js
var oracledb = require('oracledb');
var dbConfig = require('./dbconfig.js');
var db = function dbCall(sql, values) {
return new Promise(function(resolve, reject){
oracledb.getConnection(
{
user : dbConfig.user,
password : dbConfig.password,
connectString : dbConfig.connectString
},
function(err, connection) {
if (err) {
reject(err);
return;
}
connection.execute(
sql,
values,
{
maxRows: 1
},
function(err, result) {
if (err) {
console.error(err.message);
return;
}
resolve(result);
doRelease(connection);
}
);
});
});
}
// Note: connections should always be released when not needed
function doRelease(connection) {
connection.close(
function (err) {
if (err) {
console.error(err.message);
}
});
}
module.exports = db;
select.js
var dbConnect = require('../oracledb/dbconnect');
dbConnect('select code from table1' +
' where id=:id', {id:'value1'}).then(function (response) {
console.info(response.rows);
}).catch(function(error) {
console.info(error);
});
everything above works great, if I run select.js. How could I bring the response into the botpress chat window? I tried placing the select.js code in index.js event.reply, it doesn't work.
Thanks,
Babu.
I have resolved this by using the promise directly in the action.
return dbConnect('<SQL here>=:id', { id: Id })
.then(function(response) {
var res = response.rows
console.info(res);
const newState = { ...state, status: res}
return newState
})
.catch(function(error) {
console.info(error)
})
Note that response has the resultset.

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?

node.js synchronous function call for authentication

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

ldap query get all users in a group node.js

failing to find any info on the matter.
I need to query an active directory server with a specified group name, and to receive back all the users it contains.
Then i can iterate through those users and use their first&last name + email + phone + accountname.
Is all that possible using Node.js?
Can someone liberate me from this headache?
Using this link:
https://www.npmjs.com/package/activedirectory#getUsersForGroup
var groupName = 'Employees';
var ad = new ActiveDirectory(config);
ad.getUsersForGroup(groupName, function(err, users) {
if (err) {
console.log('ERROR: ' +JSON.stringify(err));
return;
}
if (! users) console.log('Group: ' + groupName + ' not found.');
else {
console.log(JSON.stringify(users));
}
});
The other solution posted is for ActiveDirectory, as a more general answer, you need a query which will return the "member" attribute from a group.
I was able to accomplish this using ldapjs
import ldap from 'ldapjs'
const client = ldap.createClient({ url: ['ldap://localhost:389'] })
client.on('error', (err) => { console.log(err) } )
client.bind("cn=admin,dc=example,dc=com", "password", (err) => {console.log(err)})
function logCallback(err,res) {
if(!res) {
console.log(err)
return
}
res.on('searchRequest', (searchRequest) => {
console.log('searchRequest: ', searchRequest.messageID);
});
res.on('searchEntry', (entry) => {
console.log('entry: ' + JSON.stringify(entry.object));
});
res.on('searchReference', (referral) => {
console.log('referral: ' + referral.uris.join());
});
res.on('error', (err) => {
console.error('error: ' + err.message);
});
res.on('end', (result) => {
console.log('status: ' + result?.status);
});
}
client.search("dc=example,dc=com",{filter:"(&(objectclass=groupofnames)(cn=users))",scope:"sub"},logCallback)
> searchRequest: 8
entry: {"dn":"cn=users,ou=Groups,dc=example,dc=com","controls":[],"cn":"users","description":"All Users","member":["uid=user,ou=Users,dc=example,dc=com","uid=user1,ou=Users,dc=example,dc=com","uid=user2,ou=Users,dc=example,dc=com"],"objectClass":["groupOfNames","top"]}
status: 0
The return value has a field "member" with a list of all users in the group.
exports.getUserLists = (req, res) => {
var ActiveDirectory = require('activedirectory');
var ad = new ActiveDirectory({
url: 'domainName.com',
baseDN: 'dc=domain,dc=com',
});
var opts = {
includeMembership: ['user'], // can use 'all','group','user'
baseDN: 'cn=users,cn=accounts,dc=domain,dc=com',
includeDeleted: false
};
ad.find(opts, function (err, results) {
if ((err) || (!results)) {
res.send('ERROR: ' + JSON.stringify(err));
return;
}
res.send(JSON.stringify(results))
});

Resources