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))
});
Related
I am trying to get back 'image' with username and url properties included, but it seems that the first query returns first. Apologies for my rookie knowledge.
exports.getImg = (req, res) => {
Image.find({}, '-__v').lean().exec((err, images) => {
if (err) {
res.sendStatus(400)({ 'msg': 'Something went wrong' });
}
for (let i = 0; i < images.length; i++) {
//trying to get
User.findOne({ _id: images[i].id}, (err, user) => {
images[i]['username'] = user.uname;
images[i]['url'] = req.protocol + '://' + req.get('host') + '/images/' + images[i]._id;
});
}
// console.log(images);
// res.json(images);
})
}
It is due the reason that, .find() function is asynchronous.As you are looping and also using .find() in the loop so it will not give the desrired result. You need to use async/await.
exports.getImg = async (req, res) => {
Image.find({}, '-__v').lean().exec(async(err, images) => {
if (err) {
res.sendStatus(400)({ 'msg': 'Something went wrong' });
}
for (let i = 0; i < images.length; i++) {
//trying to get
await User.findOne({ _id: images[i].id}, (err, user) => {
images[i]['username'] = user.uname;
images[i]['url'] = req.protocol + '://' + req.get('host') + '/images/' + images[i]._id;
});
}
// console.log(images);
// res.json(images);
})
}
I'm new to working with Active Directory in general. I'm trying to update the thumbnailPhoto attribute using ldapjs npm package I have my code setup to be able to update attributes in general and it works well.
I get my user like so:
const customeParser = function(entry, raw, callback){ if (raw.hasOwnProperty("thumbnailPhoto")){ entry.thumbnailPhoto = raw.thumbnailPhoto; } callback(entry) }
find(filter, cb) {
const client = ldap.createClient(this.ldapOptions)
client.on('error', err => {
console.error(err.message)
})
//Serach for users
client.bind(
this.options.dn,
this.options.password,
(err) => {
if (err) {
console.error((new Date).toUTCString(), 'Initial Bind Error', err)
client.unbind(() => {
client.destroy()
cb(err)
})
} else {
client.search(
'DC=foo,DC=local', {
scope: 'sub',
attributes: [
'distinguishedName',
'name',
'sn',
'givenName',
'mail',
'sAMAccountName',
'telephoneNumber',
'thumbnailPhoto',
// 'photoURL',
// 'profileImage',
'extensionAttribute1',
'msExchExtensionCustomAttribute1'
],
entryParser: customeParser,
filter: `${filter}`
},
(err, ee) => {
if (err) {
console.log((new Date).toUTCString(), 'SEARCH RESULT', err)
client.unbind(() => {
client.destroy()
cb(err)
})
}
ee.on('searchEntry', (entry) => {
ee.on('end', () => {
client.unbind(() => {
client.destroy()
cb(null, entry.object)
})
})
});
});
}
});
}
I call my modifyUser method from my LDAP class like so:
let changes = {
thumbnailPhoto: 'http://<ip>:<port>/img/photo.jpg'
}
ad.modifyUser(user.dn, changes, function (err, mod) {
if (err) {
console.log('ERROR: ' + JSON.stringify(err));
return;
}
if (!mod) {
console.log('Search: ' + mod + ' not found.');
} else {
console.log('MOD: ', JSON.stringify(mod));
}
})
modifyUser:
modifyUser(user, attributes, cb) {
const client = ldap.createClient(this.ldapOptions)
client.on('error', err => {
console.error(err.message)
})
//Serach for users
client.bind(
this.options.dn,
this.options.password,
(err) => {
if (err) {
console.error((new Date).toUTCString(), 'Initial Bind Error', err)
client.unbind(() => {
client.destroy()
cb(err)
})
} else {
var change = new ldap.Change({
operation: 'replace',
modification: attributes
});
console.log('CHANGE: ', attributes)
client.modify(user, change, function(err) {
if(err) console.error('ERROR: ', err);
},
(err, ee) => {
if (err) {
console.log((new Date).toUTCString(), 'SEARCH RESULT', err)
client.unbind(() => {
client.destroy()
cb(err)
})
}
ee.on('searchEntry', (entry) => {
ee.on('end', () => {
client.unbind(() => {
client.destroy()
cb(null, entry.object)
})
})
});
});
}
});
}
When I update the photo using my LDAP Tool it converts it to what i believe to be an octet or some sort of hex value. How do i mimic that in nodejs?
Am I on the right track? All other attributes get updated instantly using the code above, but the image fails to update...
Also is there a way to update multiple attributes at once? I get the error that only one attribute can be modified at a time.
Thanks in advance for your time!
I'm trying to get the data from LDAP and I'm getting it successfully but it's not written into variable so then after the code is executed I can make some checks on the data.
var server = LdapJS.createClient({
url: LdapConf.server.url,
tlsOptions: LdapConf.server.tlsOptions
});
server.bind(LdapConf.server.bindDN, LdapConf.server.bindCredentials, function(err) {
if (err) {
return done(err);
}
});
var SearchOtps = {
filter: '(uid=' + username + ')',
scope: 'one',
};
var UserSearch = server.search(LdapConf.server.searchBase, SearchOtps, function(err, res) {
res.on('searchEntry', function(entry) {
console.log('entry: ' + JSON.stringify(entry.object));
return (JSON.stringify(entry.object));
});
res.on('searchReference', function(referral) {
//console.log('referral: ' + referral.uris.join());
});
res.on('error', function(err) {
//console.error('error: ' + err.message);
});
res.on('end', function(result) {
//console.log('status: ' + result.status);
});
});
console.log(UserSearch);
I just do not know how to stop further code execution while it's waiting for the return of the LDAP search.
Server Started
undefined
You could do a function that returns it in promise.
function UserSearch(server,LdapConf, SearchOtps) {
return new Promise(function(resolve,reject) {
server.search(LdapConf.server.searchBase, SearchOtps, function(err, res) {
res.on('searchEntry', function(entry) {
console.log('entry: ' + JSON.stringify(entry.object));
resolve(JSON.stringify(entry.object)));
});
res.on('searchReference', function(referral) {
//console.log('referral: ' + referral.uris.join());
});
res.on('error', function(err) {
reject()
});
res.on('end', function(result) {
//console.log('status: ' + result.status);
});
});
}
}
UserSearch(server,LdapConf, SearchOtps)
.then(function(res) {
console.log(res)
})
Here's the code:
app.get('/vklogin', function(request, response) {
console.log('Авторизация через соц.сеть "Вконтакте"'.green);
var url_parts = url.parse(request.url, true);
var query = url_parts.query;
var data = querystring.stringify({
client_id: '4836170',
client_secret: 'cPkR53zhon0lU7TAiz9f',
code: query.code,
redirect_uri: 'http://' + request.headers.host + '/vklogin'
});
var options = {
host: 'oauth.vk.com',
port: 443,
path: '/access_token?' + data,
method: 'GET'
};
var httpsreq = https.request(options, function(response) {
response.setEncoding('utf8');
response.on('data', function(chunk) {
var chunk = JSON.parse(chunk);
pg.connect(dbconfig, function(err, client, done) {
if (err) {
return console.error('Ошибка подключения к БД',err);
}
client.query('select * from users where vk = $1', [chunk.user_id], function(err, result) {
done();
if (err) {
console.error('Ошибка получения данных',err);
} else {
if (result.rows[0]) {
console.log(result.rows[0]);
request.session.authorized = true;
request.session.userid = result.rows[0].id;
} else {
console.log('Попытка создания нового пользователя. ');
client.query("insert into users (email, vk) values ('" + chunk.email + "', " + chunk.user_id + ") returning id", function(err, result) {
done();
if (err) {
console.error('Ошибка записи данных в БД', err);
} else {
request.session.authorized = true;
request.session.userid = result.rows[0].id;
console.log('Добавлен новый пользователь # ' + result.rows[0].id);
}
});
}
}
client.end();
});
console.log("№ пользователья: " + request.session.userid);
});
});
});
httpsreq.end();
if (request.session.authorized) {
response.writeHead(301, {
Location: 'http://' + request.headers.host + '/cabinet'
});
} else {
response.writeHead(301, {
Location: 'http://' + request.headers.host
});
}
response.end();
});
That is why outside functions session is not saved? What is wrong in my code?
Inside the function, everything is fine, outside functions - undefined.
After this session, the logic must be maintained and be available everywhere, too, everywhere, or is not it?
Tried to declare a variable with the session, but it also did not work, and no error does not give, do not even know where to dig.
var sess;
app.get('/vklogin', function(request, response) {
sess = request.session;
// other code...
});
UPD:
My problem is related to the lack of understanding of the control of asynchronous processes. I can not understand how to perform the originally one - database queries, information preservation in the session, and then check the session variables and forwarding to the desired page.
If you know how to make the correct execution order for me, write the answer.
Ok, I find need async pattern. Look here: http://book.mixu.net/node/ch7.html
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 })