I have an Ldap Server running on Docker + ldapjs. This server is adding a set of records that I am trying to search for with the client.
A sample user object looks like below:
{
user: 'cn=first.last,ou=user_group,o=main',
info: {
cn: 'first.last',
email: 'first.last#mail.com'
}
}
The options would look like this:
let opts = {
scope: 'base',
attributes: ['dn', 'sn', 'cn', 'user', 'info']
};
I'm using this code in a class, so I bind in the constructor, after initializing the client:
constructor(url) {
client = ldap.createClient({
url: url
});
client.on('error', (err) => {
log.error(`${err}`);
});
client.bind(username, password, function (err) {
if (err) {
log.error(`${err}`);
}
});
log.info('Client Initialized.');
};
And my search code:
return new Promise((resolve, reject) => {
var record = {};
client.search(username, opts, function (err, res) {
res.on('searchEntry', function (entry) {
log.info(`Record Retrieved: ${JSON.stringify(entry.object)}`);
record = entry.object;
});
res.on('error', function (err) {
log.error(`Error: ${err.message}`);
});
res.on('end', function (result) {
if (err) {
reject(err);
}
else {
log.info(`Status: ${result.status}`);
resolve(record);
}
});
});
});
The issue I'm experiencing is that the code will always resolve on end when I make a search request from the client, which means that I never get a match, although it's definitely there.
I've tried:
Binding inside and outside the promise instead. No difference.
Changing the user structure and username used in client.search. No difference.
Searching for only 'cn=first'. I do get an error that it doesn't exist, which is good.
Adding a filter in options and changing the parameters there, but still no result.
I connect to the server ok, bind is ok as well, so I think I'm either doing the search wrong, or the way I have structured the users in the server is not proper.
Added screenshot showing server logs: The user added in the entry looks like it has a different name, but I changed it to match in the data.
I've found the issue, which was related to the structure I was using in my records, I've solved it using an ldapts client instead, but the same logic can be used in an ldapjs client:
Specifically:
This is a record in my ldapjs Server:
{
name: 'John Doe',
uid: 'john.doe',
dn: 'uid=john.doe, ou=users, o=server',
email: 'john.doe#email.com',
userprincipalname: 'cgi-doej',
}
This is how I search for it:
let attributes = ['cn'], filter = `(email=${email})`
const { searchEntries, searchReferences } = await this.client.search(searchDN, {
scope: 'base',
filter: filter,
attributes: attributes
});
This has solved my issues.
Related
iv been setting up a lambda instance, it grabs data from a few different services and then its meant to update a custom Cognito attribute for that user, that works correctly and i get the return response "{}" along and no errors so im assuming that means its working correctly, however when i check the users attributes its not returning anything?
i have triple checked that the app clients have read and write permissions, so i have no idea whats happening, everything, as far as i can tell, is working, just that the attribute isnt changing.
return new Promise((resolve, reject) => {
console.log("TestInside");
// setTimeout(function() {
console.log("TimeoutFunction");
var cognitoidentityserviceprovider = new AWS.CognitoIdentityServiceProvider();
var params = {
UserAttributes: [
{
Name: 'locale',
Value: UserFarms
},
],
UserPoolId: 'UserPoolID',
Username: UserName
};
console.log("Executing call");
const cognitoD = cognitoidentityserviceprovider.adminUpdateUserAttributes(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
//passon = err.message;
resolve('Error');
}
else {
// cognitodata = data;
console.log('Returned positive cognito admin update');
//UserName = data.Username;
//passon = data;
console.log(data);
resolve(data);
}
}).promise();
whats wrong, am i missing somthing really simple?
I'm developing a simple app with Node/Hapi/Mongodb, but running into a strange issue. Below is the route that handles adding/updating scores; when I send some data to this endpoint through Insomnia/Postman it works as expected. However, when this POST is coming from a different app I'm getting strange results; the value is always null for every field (again this only happens when the POST is coming from another site, but I've logged out the request payload and can see that the data is correct, just gets set to null when assigning to an object, or trying to use it a query)
server.route({
method: 'POST',
path: '/update-score',
handler: (request, h) => {
var scores = db.collection('scores');
var updateScore = new Promise((resp, rej) => {
console.log('payload ', request.payload);
scores.findOneAndUpdate({customerID: request.payload.customerID}, {$set: {customerID: request.payload.customerID, customerName: request.payload.customerName, highScore: request.payload.highScore}}, {upsert: true}, (err, res) => {
if (err) {
return rej(err);
}
else {
return resp(res);
}
})
});
return updateScore;
}
});
The console logs out the request payload correctly, but its null/undefined when the query tries to use it. I have also tried creating two objects, outside the mongo method call (like below), and after console logging these pre-defined objects out the value was null there as well; even though I can console.log the request.payload after defining these objects and the data is good.
server.route({
method: 'POST',
path: '/update-score',
handler: (request, h) => {
var scores = db.collection('scores');
var queryObj = {
customerID: request.payload.customerID
};
var updateObj = {
$set: {
customerName: request.payload.customerName,
highScore: request.payload.highScore
}
}
var updateScore = new Promise((resp, rej) => {
console.log('again ', request.payload);
scores.findOneAndUpdate(queryObj, updateObj, {upsert: true}, (err, res) => {
if (err) {
return rej(err);
}
else {
return resp(res);
}
})
});
return updateScore;
}
});
Logging the queryObj and valueObj would show the values are all null, even though I can log the request.payload and see the data correctly. Why can't I use the request.payload values anywhere?
Long story short, Insomnia/Postman sends an object as the POST body, but I was JSON encoding the POST from the app; just needed to parse that on the server!
I have a function in my Restify project that handles an HTTP GET request. After some processing it uses Sequelize to find the user entity for my current session. The User.findOne function returns a promise and depending on the result of that promise, I'm sending an HTTP response with 200 or 404.
static getMe(req, res, next) {
const userInfo = BaseController.getUserSession(req);
// hard to test this part
User.findOne({
where: {email: userInfo.email}
}).then(function(user) {
if (user) BaseController.respondWith200(res, user);
else BaseController.respondWith404(res, 'User not found.');
}, function(error) {
BaseController.respondWith404(res, error);
}).then(function() {
return next();
});
}
I've tried a few different libraries to help with testing so I'm sorry if this is a messy combination of things. This is in my beforeEach function for my tests:
const usersFixture = [
{id:2, email:'ozzy#osbourne.com', facebookId:54321, displayName: 'Ozzy Osbourne'},
{id:3, email:'zakk#wylde.com', facebookId:34521, displayName: 'Zakk Wylde'},
{id:4, email:'john#lennon.com', facebookId:12453, displayName: 'John Lennon'}
];
this.findOneSpy = sinon.spy(function(queryObj) {
return new Promise(function(resolve, reject) {
const user = usersFixture.find(function(el) { return el.email === queryObj.where.email });
if (user) resolve(user);
else resolve(null);
});
});
this.respondWith200Spy = sinon.spy(function(res, data) {});
this.respondWith400Spy = sinon.spy(function(res, error) {});
this.respondWith404Spy = sinon.spy(function(res, error) {});
this.controller = proxyquire('../../controllers/user-controller', {
'../models/user': {
findOne: this.findOneSpy
},
'./base-controller': {
respondWith200: this.respondWith200Spy,
respondWith400: this.respondWith400Spy,
respondWith404: this.respondWith404Spy
}
});
And here is what one of my tests looks like:
it('should return 200 with user data if user email matches existing user', function() {
// THIS FUNCTION IS NEVER HIT
this.respondWith200Spy = function(res, data) {
data.should.equal({id:4, email:'john#lennon.com', facebookId:12453, displayName: 'John Lennon'});
done();
};
const req = {session:{user:{email:'john#lennon.com'}}};
this.controller.getMe(req, this.res, this.nextSpy);
this.findOneSpy.should.have.been.called;
});
Since we aren't actually passing a callback to the function and the function doesn't really return anything (just does asynchronous things elsewhere), I can't figure out how to test it to make sure it's working right. Any help is appreciated.
The actual code works just fine. I'm just trying to get some quality unit testing into the project. Thanks!
I ended up finding a way to do it using proxyquire. I just re-stubbed the controller class that I am testing and made the respondWith200 callback make an assertion. Then I created a new spy for the next function that just calls done (which is passed into the test case). I verified that the code is all getting hit.
it('should return 200 with user data if user email matches existing user', function(done) {
const controller = proxyquire('../../controllers/user-controller', {
'../models/user': {
findOne: this.findOneSpy
},
'./base-controller': {
respondWith200: function(res, data) {
data.displayName.should.equal('John Lennon');
},
respondWith400: this.respondWith400Spy,
respondWith404: this.respondWith404Spy
}
});
const req = {grft_session:{user:{email:'john#lennon.com'}}};
const nextSpy = sinon.spy(function() {
done();
});
controller.getMe(req, this.res, nextSpy);
this.findOneSpy.should.have.been.called;
});
I'm working on a web app with nodejs, express, and mongodb.
In my 'main' file where I listed for API calls, I include a Users class that has methods like Users.authenticate(userObject, callback), and Users.getById(userId, callback).
Sorry for this long code snippet. It's just a snippet of my users class.
function Users (db) {
if (!db) {
return {'message': 'creating an instance of Users requires a database'}
} else {
this.db = db;
return this;
}
}
Users.prototype.authenticate = function (user, callback) {
if (!user.username) {
return {'message': 'Users.authenticate(user, callback) requires user.username'};
} else if (!user.password) {
return {'message': 'Users.authenticate(user, callback) requires user.password'};
} else if (!callback) {
return {'message': 'Users.authenticate(user, callback) requires callback(err, user)'};
}
this.db.collection('users', function (err, collection) {
if (err) {return {'message': 'could not open users collection'}};
/* query for the user argument */
collection.findOne(user, function (err, doc) {
if (!err) {
if (!doc) {
callback({'message': 'user does not exist'}, null);
} else {
callback(null, doc);
}
} else {
callback({'message': 'error finding user'}, null);
}
});
});
};
exports.Users = Users;
That's it
I pass an open DB connection to my Users class, and make a call like the following:
var server = new mongo.Server('localhost', '27017', {auto_reconnect: true});
var db = new mongo.Db('supportdash', server, {"fsync": true});
// open connection to be, init users
db.open(function (err, db) {
var users = new Users(db);
users.authenticate({"username": "admin", "password": "password"}, function (err, user) {
// do something with the error, or user object
});
});
Now for my questions
Should I be passing an open db connection, or should I be passing the info needed (localhost, port, database name) for the Users class to manage its own connection?
I tried to set up testing with jasmine-node, but I ended up with a lot of problems with async database calls. I wanted to add a user, then test that Users.authenticate was working. I used Jasmines runs() and waitsfor() async helpers, but I could not get it to work. I then ran into an issue that took me a while to debug (with a different class), and testing would have saved me a lot of time. Any advice on how I would test my classes that interact with a mongodb database?
Guys I am trying to get myself authenticated and for this I am using node.js and mongo DB.But the thing is that after registarion the user is not able to authenticate himself.Here is my snippet
app.post('/login',function(req,res){
ContactProvider.findAll(function(error, posts) {
var aut = req.body;
if (aut.user == posts.user && aut.pass == posts.pass) {
req.session.name = {name:aut.user};
res.redirect('/home');
} else {
res.send('Bad user/pass');
}
});
});
Below is my snippet for registering the user
app.post('/register',function(req, res) {
var post=req.body;
if(post.pass!=post.cpass) {
res.send("Error:Password doesnt match");
} else {
ContactProvider.save({
user: req.param('user'),
pass: req.param('pass'),
cpass: req.param('cpass'),
email: req.param('email')
}, function(error, docs) {
res.redirect('/');
});
}
});
The ContactProvider is the one below where post provider is a different file where all the mongoose things happen
var ContactProvider = require('./PostProvider').ContactProvider;
var ContactProvider= new ContactProvider();
This is the finone query in the postprovider file
ContactProvider.prototype.findone = function(name,pass, callback) {
Post.findOne({name:name},{pass:pass}, function (err, post) {
callback(null, post);
});
};
Something's seriously wrong with your code ( why you use name posts for an array of ContactProvider? ). You have to search for ContactProvider based on username and password. Something like this:
app.post('/login',function(req,res){
var aut = req.body;
ContactProvider.findOne(
{
user: aut.user,
pass: aut.pass
},
function(err, usr) {
if (error || !usr) {
res.send('Bad user/pass');
} else {
// we have a user, authenticate!
req.session.name = {name:aut.user};
res.redirect('/home');
}
}
);
});
SIDE NOTE: This is a very simple way of authenticating users, but it is not secure at all. You should read more about authentication and security in the internet. Very useful knowledge indeed.
EDIT: There's also an issue with your registration. Your data is stored in post variable, so use it on ContactProvider as well:
// some other code
ContactProvider.save({
user: post.user,
pass: post.pass,
cpass: post.cpass, // no need to store the same thing twice
email: post.email