I have the following routers:
/* GET /users/username */
router.get("/:username", function(req, res, next) {
User.findOne({
username: req.params.username
}).exec(function(err, user) {
if (err) return next(error);
res.json(user);
});
});
/* GET /users/id */
router.get("/:id", function(req, res, next) {
User.findById(req.params.id, function(err, user) {
if (err) return next(err);
res.json(user);
});
});
In the above order, /* GET /users/id */ will return null, and if I swap the order the /* GET /users/username */ will result in this error:
Cast to ObjectId failed for value "Guest" at path "_id" for model "User"
Basically, the second middleware is being ignored. However, both are needed for different behaviors. I'm using AngularJS $resource to interact with RESTful server-side data sources.
Why can't I use both simultaneously? Why is the second middleware ignored?
In which order should these two routers be to both working?
The second middleware is ignored, because the url pattern is the same... How do you want the router make the differenciation between an 'username' or an 'id';
You've got to check if it's an id first per exemple.
router.get("/:username", function(req, res, next) {
const username = req.params.username;
if (!isNaN(parseInt(username)) { // <------ Check if it's an ID
next();
} else {
User.findOne({
username: req.params.username
}).exec(function(err, user) {
if (err) return next(error);
res.json(user);
});
}
});
/* GET /users/id */
router.get("/:id", function(req, res, next) {
User.findById(req.params.id, function(err, user) {
if (err) return next(err);
res.json(user);
});
});
It's better to route by doing:
router.route('your/route').get(aJsFile.function);
Then, instead of the exec, just write
attributes: ['id', 'username']
where: {username: username}
Or something like this.
Related
I have the problem with my routes. They work but only the one that's in the code first. The code below allows me to get a ticket by ID but not by registration number. If I put the second route above the first one it's vice versa.
What can I do to fix this?
//Get a ticket by ID
app.get('/tickets/:_id', function(req, res){
Ticket.getTicketById(req.params._id, function(err, ticket){
if(err){
throw err;
}
res.json(ticket);
});
});
//Get a ticket by registration number
app.get('/tickets/:vehRegistration', function(req, res){
Ticket.getTicketByReg(req.params.vehRegistration, function(err, ticket){
if(err){
throw err;
}
res.json(ticket);
});
});
You have written the same API path twice. :param_name can't be used to differentiate between two different paths. Try
//Get a ticket by ID
app.get('/tickets/byid/:_id', function(req, res) {
Ticket.getTicketById(req.params._id, function(err, ticket) {
if (err) {
throw err;
}
res.json(ticket);
});
});
//Get a ticket by registration number
app.get('/tickets/byreg/:vehRegistration', function(req, res) {
Ticket.getTicketByReg(req.params.vehRegistration, function(err, ticket) {
if (err) {
throw err;
}
res.json(ticket);
});
});
I've searched about this issue and all the solutions that I've found didn't actually work. I'm currently using this function to encrypt the password before storing in the database but even though the values are being changed when logging this, the password isn't stored like it was changed in the function.
UserSchema.pre('findOneAndUpdate', function(next) {
const update = this.getUpdate();
if (!_.isEmpty(update.password)) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(update.password, salt, (err, hash) => {
this.getUpdate().password = hash;
next();
})
})
}
next();
});
I've also tried to change the value of this._update.password instead but it didn't work either. I've also tried using $set or even using a post hook but none of them helped either. What am I doing wrong?
I just tested this locally with:
var result = Author.findOneAndUpdate({ _id: <some id> }, { password: '111' }).exec()
and this pre hook:
AuthorSchema.pre('findOneAndUpdate', function(next) {
this._update.password = 'BBB'
next();
});
Password was saved as BBB
Author schema has a password field which is type: String
I am on 3.6.5
In your bcrypt case you have also an extra next() without else which is messing you up ... should be:
UserSchema.pre('findOneAndUpdate', function(next) {
const update = this.getUpdate();
if (!_.isEmpty(update.password)) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(update.password, salt, (err, hash) => {
this.getUpdate().password = hash;
next();
})
})
} else {
next();
}
});
user.pre('findOneAndUpdate', function(next){
const user=this.getUpdate().$set;
if(!user.password){
next();
}
else{
bcrypt.genSalt(10, function (err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, null, function (err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
}
})
I have had many problems, when I want to get information from user model. I read some solutions, but I didnt understand.
This is my code:
* AuthController
var passport = require('passport');
module.exports = {
_config: {
actions: false,
shortcuts: false,
rest: false
},
login: function(req, res) {
passport.authenticate('local', function(err, user, info) {
if ((err) || (!user)) {
return res.send({
message: info.message,
user: user
});
}
req.logIn(user, function(err) {
if (err) res.send(err);
return res.send({
message: info.message,
user: user
});
});
})(req, res);
},
logout: function(req, res) {
req.logout();
res.redirect('/');
},
signup: function (req, res) {
var data = req.allParams();
User.create({email:data.email,password:data.password,name:data.name}).exec(function(error,user){
if(error) return res.negotiate(err);
if(!user)return res.negotiate(err);
return res.ok();
});
}
};
*view
<h1>List of my dates</h1>
<h1><%= email %></h1>
<h1><%= req.user.name %></h1>
*model
attributes: {
email: {
type: 'email',
required: true,
unique: true
},
password: {
type: 'string',
minLength: 6,
required: true
},
toJSON: function() {
var obj = this.toObject();
delete obj.password;
return obj;
}
},
beforeCreate: function(user, cb) {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(user.password, salt, function(err, hash) {
if (err) {
console.log(err);
cb(err);
} else {
user.password = hash;
cb();
}
});
});
}
};
Only works if I use res.render('view', {email: req.user.email}) but, I would like to use the user data in many views. I cant write methods with Current user params, becouse dont work.
Thanks.
It is unclear to me what your actual problem is or what the question actually is but I will try to help.
Look here:
login: function(req, res) {
passport.authenticate('local', function(err, user, info) {
if ((err) || (!user)) {
return res.send({
message: info.message,
user: user
});
}
...
})(req, res);
},
There you are adding data (locals) to the ejs and the values are message and user so in the ejs you must reference it as this, so you will use user.name and not req.user.name? I'm not sure why you're binding the (req, res) either.
It's confusing because your ejs uses the email value but I don't see it there as a local so maybe thats your problem, it must be defined?
Consider the following simple example:
// User Controller
// GET request /signin
// The signin form
signin(req, res) {
// Load the view from app/views/*
return res.view('signin', {
title: 'Sign In'
});
},
// POST request to /signin
// This was posted from the signin form
// Use io.socket.post(...) to do this from the signin form
// Can use window.location.replace('/account') on successful request
authenticate(req, res) {
// The data posted, email and password attempt
var data = req.allParams();
// Does it match?
User.findOne({
email: data.email,
// This is stupid, don't ever use plain text passwords
password: data.password
})
.exec(function(err, user) {
// Server related error?
if (err) res.serverError(err.message);
// No user was found
if (!user) res.badRequest('Username or password not found');
// Sign the user in
req.session.userId = user.id;
// User was found
res.ok();
});
},
// GET request to /account
// Displays the users information
// Can use policies to ensure that only an authenticated user may access their own account information
account(req, res) {
// If the user is not signed in
// This is an alternative to using the sails policy isLoggedIn
if (!req.session.userId) res.redirect('/signin');
// Get the users details
User.findOne({
id: req.session.userId
})
.exec(function(err, user) {
// Server related error?
if (err) res.serverError(err.message);
// No user was found
if (!user) res.redirect('/signin');
// Load the ejs file that displays the users information
return res.view('account/index', {
title: 'Account Information',
user: user
});
});
},
// Account View
<p>Email: {{user.email}}</p>
<p>Password: {{user.password}}</p>
Check this out if you want to deal with password encryption: http://node-machine.org/machinepack-passwords
And this if you want to deal with the strength tests (when the user sets the password): https://www.npmjs.com/package/owasp-password-strength-test
This is as passport seems overkill if you're only doing local authentication?
When I login using the local strategy subsequent requests having the sessionAuth policy on them fail because req.session.authenticated is undefined. I've excerpted a portion of the login function from api/services/protocols/local.js. I've inserted a comment for the code I believe is missing.
passport.validatePassword(password, function (err, res) {
if (err) {
return next(err);
}
if (!res) {
req.flash('error', 'Error.Passport.Password.Wrong');
return next(null, false);
} else {
///// Shouldn't authenticated get set true here?
///// req.session.authenticated = true;
return next(null, user);
}
});
As suggested by Alberto Souza the local strategy works if you change sessionAuth.js from:
module.exports = function(req, res, next) {
if (req.session.authenticated) {
return next();
}
return res.forbidden('You are not permitted to perform this action.');
};
to:
module.exports = function(req, res, next) {
if (req.isAuthenticated()) {
return next();
}
return res.forbidden('You are not permitted to perform this action.');
};
So the answer to my question seems to be sessionAuth.js is part of the default sails app generation and not created when you run sails generate auth and therefore is a change you need to make manually that the documentation neglects to tell you about.
I'm trying to:
Pass user's ID to a model query, that should return the user record from mongo.
Render this user object to my view so I can use its fields.
I'm not quite sure what's going wrong - the query function finds the correct user and I can console.dir to see all the fields. When I try to return it to my view with res.render I get nothing:
Here's my route:
app.get('/account', function(req, res) {
res.render('account', {title: 'Your Account', username: req.user.name, user:account.check(req.user.id) });
});
And my query function:
exports.check = function(userId) {
MongoClient.connect('mongodb://127.0.0.1:27017/test', function(err, db) {
if(err) throw err;
var collection = db.collection('test');
collection.findOne({userId : userId}, function(err, user) {
if (err) throw err;
console.log("account.check logging found user to console: ");
console.dir(user);
return user;
});
});
}
Again, this shows the proper entry
Finally my view:
<h1>Account Page</h1>
<hr>
<p>Why, Hello, there <b> {{username}} </b> </p><br/>
<p>You came from {{user.provider}}</p>
<p>{{user.lastConnected}}</p>
Go Home ~ Log Out
Any held would be most appreciated!
The MongoDB findOne function is asynchronous (it takes a callback as an argument). This means that your check function also needs to be asynchronous and take a callback as an argument (or return a promise).
Then you should call res.render() inside the callback you pass to query on success.
app.get('/account', function(req, res) {
account.check(req.user.id, function(error, user) {
if (error) {
// do something smart like res.status(500).end()
return;
}
res.render('account', {title: 'Your Account', username: req.user.name, user:user });
}
});
And the check function should be something like:
exports.check = function(userId, callback) {
MongoClient.connect('mongodb://127.0.0.1:27017/test', function(err, db) {
if(err) {
callback(err);
}
var collection = db.collection('test');
collection.findOne({userId : userId}, function(err, user) {
if(err) {
callback(err);
}
console.log("account.check logging found user to console: ");
console.dir(user);
callback(null, user);
});
});
}
Of course if you don't need to do any additional processing, you can just pass your the callback argument as the callback to collection.findOne(). I just kept it this way because it was closer to what you were doing initially.