NodeJS can't take my DB values for my template - node.js

I started to develop a little web site in NodeJS, with admin authentication based on https://github.com/DanialK/Simple-Authentication, it work very well.
I can create a user, login with it and see the private page (dashboard).
But I have a problem with my template (I'm using Nunjucks), when the admin come to his dashboard I just wan't to show :
logged as : 'admin_username'.
This is my code :
Model :
const UserSchema = new mongoose.Schema({
username: String,
email: String,
password: String,
salt: String,
hash: String
})
Model definition :
const User = mongoose.model('user', UserSchema)
My route :
router.route('/admin/dashboard', requiredAuthentication)
.get((req, res) => {
console.log("################ GET DASHBOARD ##################")
requiredAuthentication(req, res, function() {
User.find().then(user => {
res.render('admin/dashboard.njk', {user: user})
console.log(user)
}).catch(err => {
console.error(err)
})
})
})
So I send "user", should be used in my template :
{% extends "base/layout.njk" %}
{% block content %}
admin:dashboard page <br /><br /><br />
{% if sessionFlash.message %}
<div class="{{ sessionFlash.type }}">
{{ sessionFlash.message }}
</div>
{% endif %}
You are logged as : {{ user.username }}
{% endblock %}
With user.username I can't get the username.
With user only I get the entire document from the DB, username, email, salt, hash.
This is the result of the console.log(user) from the route :
[ { _id: 58c58ad8a5e54c00117ce85b,
username: 'test',
email: 'test',
salt: '/71BBVmr8E3b/HUz8L89IWLV7xM/vG9nvJRGzQYPw4dwR8GICr0kJtGs8dqwNzsMU7yki9aa2WM7C2NRxf/ausw+4kyiLojfugYzdrh+6obBq5HcZPZfQq+djwsTyyd+CDPJ/EmbUQyIL1yM7lRLfkhfrCIZ9P1mJZZM9fv4thw=',
hash: '��F\u0000\u000b ��a�\u001c|A˓P�N&��\u0010�5ajd�7{c �#�mQ����&��W�rW\'�+������\u0013����������N�4�y>/1��R\u001ca>���=U�u<9�T�o" \u000b�����Ʌ^�\u0004\u001f��\u0007�B`A�d���N#M$���',
__v: 0 } ]
Don't know if this is important, there is the two function used for authentication : requiredAuthentication and authenticate :
function authenticate(name, pass, fn) {
if (!module.parent) console.log('authenticating %s:%s', name, pass);
User.findOne({
username: name
},
function (err, user) {
if (user) {
if (err) return fn(new Error('cannot find user'));
hash(pass, user.salt, function (err, hash) {
if (err) return fn(err);
if (hash == user.hash) return fn(null, user);
fn(new Error('invalid password'));
});
} else {
return fn(new Error('cannot find user'));
}
});
}
function requiredAuthentication(req, res, next) {
console.log("#### ->REQUIREDAUTHENTICATION() ####")
if (req.session.user) {
console.log("#### AUTH NEXT() ####")
next();
} else {
console.log("#### AUTH DIE() ####")
req.session.sessionFlash = {
type: 'alert alert-success',
message: 'You can't access the dashboard'
}
res.redirect('/admin/account/login');
}
}
Thanks for helping me, if you wan't additional informations ask me.

Looks like that user object is actually an array with 1 item. You can tell by that leading [ in the console.log output. To fix the issue, you can either pass in user[0] to your render function, or add a for loop to your template in case you'll be grabbing multiple users later on.

Related

How to render objects in array in frontend table using ejs?

I am making a user management system where the admin can login and add user details into an array called data and render it in the form of a table.There is a front-end form for the same purpose.
The problem is, I am able to take information from the the front-end from and push it to the array but not able to render in that table.
This is my user schema:
const userSchema= new mongoose.Schema({
email: String,
password: String,
data: [{
name: String,
emailAdd: String,
phone: Number,
}]
});
This is the post route for receiving user info and storing it in the array.
app.post("/add_user",function(req,res){
const userId=req.body.id;
User.findOne({userId}, function(err, foundUser) {
if (err) {
console.log(err);
} else {
if (foundUser) {
foundUser.data.push({
name:req.body.fname,
emailAdd:req.body.emailadd,
phone:req.body.phone
});
foundUser.save(function() {
res.redirect("/database");
});
// console.log(foundUser);
}
}
});
})
This is the get route for passing the object to front end:
app.get("/database", function(req, res) {
const userId=req.body.id;
if(req.isAuthenticated()){
User.find({ _id:userId,"data": {$ne: null}}, function(err, foundUser) {
if (err) {
console.log(err);
} else {
res.render("database", { newUser: foundUser });
}
});
}else{
res.redirect("/");
}
});
There is a database.ejs page containing the table where I need to render it:
<table class="list" id="employeeList">
<thead>
<tr>
<th>Full Name</th>
<th>Email</th>
<th>Number</th>
</tr>
</thead>
<tbody>
<%newUser.forEach(function(user){ %>
<tr>
<td><%=user.data.name%></td>
<td><%=user.data.emailAdd%></td>
<td><%=user.data.phone%></td>
</tr>
<% }); %>
</tbody>
</table>
The table still remains empty even after adding the user.
(P.S: I am using passport for encryption and mongoDB as database)
.No errors are showing up hence I don't know where I am going wrong.
Any help will be appreciated!

How to stop routing to the given route if validation fails

In my signup.ejs file, I have setup a form with post request and i have written /welcome route in signup.js file, I am trying to figure out how i can stop the post request to the given route if validation fails or how i can dynamically assign a different route like an error route (localhost:3000/signup/error instead of localhost:3000/signup/welcome if validation fails) as everytime signup fails, it is routing to localhost:3000/signup/welcome which is not right. I tried setting up variables in signup.ejs file to pass it in action, however, it only routes to /welcome and it doesn't route to /error. Basically, I just want to see error endpoint or just stop the POST request if validation fails. Please suggest.
signup.ejs
<h2>Sign Up</h2>
<% var route = ''; %>
<% if (!messages.error) { %>
<% route = '/welcome' %>
<% }else{ %>
<% route = '/error' %>
<% } %>
<form action="signup<%- route %>" method="post" name="form1">
signup.js
router.post('/welcome', function(req, res, next) {
req.checkBody('name', 'Name cannot be empty').notEmpty();
req.checkBody('email', 'Please enter valid email').isEmail();
req.checkBody('password', 'Password should be more than 5').isLength({ min: 5 });
req.checkBody('password1', 'Password do not match').equals(req.body.password);
var errors = req.validationErrors();
if(!errors) {
var user = {
name: req.sanitize('name').escape().trim(),
email: req.sanitize('email').escape().trim(),
password: req.sanitize('password').escape().trim()
}
connection.query('Insert into customers set ?', user, function(err, result) {
if(err) {
req.flash('error', err);
//render to signup
res.redirect('/signup', {
title: 'Add New User',
name: user.name,
email: user.email,
password: user.password
})
}
req.flash('success', 'Successfully signed up');
res.render('welcome', {
title: 'Signed In'
});
})
}else {
var error_msg = ''
errors.forEach(function(error) {
error_msg += error.msg + '<br>'
})
req.flash('error', error_msg);
res.render('signup', {
name: req.body.name,
email: req.body.email,
password: req.body.password
})
}
});

Express and node send error messages to handlebars view

How do I output validation errors to the view for the email unique: true option? In my handlebars view I am getting errors passed from var errors = req.validationErrors(); and displaying it in the view which is working. but the email validation for uniqueness is going to user.save((err) and is just being sent to the console. The validation is working correctly, no user is created if there is a duplicate email. I'm just trying to send the error message to the view. Can I do a req.check for unique: true ?
const UserSchema = new Schema({
email: { type: String, index: { unique: true } },
password: { type: String, required: true }
});
my createUser.handlebars view
{{#if errors}}
<section class="errors">
<ul>
{{#each errors}}
<li>{{this.msg}}</li>
{{/each}}
</ul>
</section>
{{/if}}
the createUser function
module.exports.createUser =
(req, res, next) => {
let user = new User({
email: req.body.email,
password: req.body.password
});
req.check('email', 'Invalid email').isEmail();
req.check('password', "Passwords must match").isLength({min: 6}).equals(req.body.passwordConfirmation);
var errors = req.validationErrors(); // save any error messages
// check if there are any errors
if (errors) {
req.session.errors = errors;
req.session.success = false;
return res.redirect('/users/new');
} else {
req.session.success = true;
}
// save the user
user.save((err) => {
if (err) {
console.log("Error : %s ", err);
}
// set the session messages back to null and redirect
req.session.success = null;
req.session.errors = null;
res.redirect('/');
});
};
If I'm not wrong, you're using express-validator for validation. The issue is you're mixing two things: form validation and database constraints.
Uniqueness is not an intrinsic property of a data item and it cannot be checked independently of the entire set.
You can only check for uniqueness using a call to the database. If your database schema defines this attribute/column as unique, then trying to store it will throw an error (at least in SQL-based databases and MongoDB).
You can create your custom validation check like this
check('email').custom(value => {
return userController.findByEmail(value).then( user => {
if(user){
return Promise.reject('this email already exists')
}
})
})
i write this in Router and define function in Controller
async findByEmail(email){
const candidate = await User.findOne({ where: {username}})
if(candidate){
return true
}else{
return false
}
}

Display Flash Error Messages to User After Mongoose Model Save

I have a page index.js which has a form to add users, and beside it a list of users in the database.
/routes/index.js
var express = require('express');
var router = express.Router();
var User = require('../schemas/user');
router.post('/create', function(req, res, next) {
var user = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password
});
user.save(function(err) {
if (err) {
console.log('user save error ' + err.errmsg);
return res.json(err.errmsg);
}
res.redirect('/');
});
});
/* GET home page. */
router.get('/', function(req, res, next) {
User.find(function (err, users) {
if (err) {
console.log('get error ' + err);
//return res.sendStatus(500);
}
res.render(
'index',
{
userList : users
}
);
});
});
module.exports = router;
/schemas/user.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
username: { type: String, required: true, unique: true },
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
group: String,
created_at: Date,
updated_at: Date
});
var User = mongoose.model(
'User',
userSchema
);
module.exports = User;
Here is my view:
/views/index.pug
extends layout
block content
h1= title
p Welcome to #{title}
.container
.row
.col-sm
h1 Create User
form(
method='POST'
action='/create'
)
.form-group
label(for='username') Username:
input#username.form-control(
type='text',
placeholder='Enter username...',
name='username'
)
if usernameError
p.error= usernameError
.form-group
label(for='password') Password:
input#password.form-control(
type='password',
placeholder='Enter password...',
name='password'
)
if passwordError
p.error= passwordError
.form-group
label(for='email') Email:
input#email.form-control(
type='email',
placeholder='Enter email...',
name='email'
)
if emailError
p.error= emailError
button.btn.btn-primary(
type='submit',
) Submit
.col-sm
h2 User List
ul
each user in userList
li= user.username
As you can see, I have some conditionals in my index.pug file. What I want to do is if an error occurs I want to assign a message to a variable based on the error type (i.e. username already taken, or password too short) and pass that variable over to my pug view. The view will then render the message if the proper variable is set. Can somebody help me out? I'm mostly struggling with the fact that I also have to render the list of users, if I try to find users within the error catching part of the post node complains about headers already being set, i.e. If I have my router.post function like so:
router.post('/create', function(req, res, next) {
var user = new User({
username: req.body.username,
email: req.body.email,
password: req.body.password
});
user.save(function(err) {
if (err) {
console.log('user save error ' + err.errmsg);
User.find(function (err2, users) {
if (err2) {
console.log('get error ' + err2);
//return res.sendStatus(500);
}
res.render(
'index',
{
userList : users,
usernameError: err.errmsg
}
);
});
}
res.redirect('/');
});
});
Then I expect to see the usernameError message filled in my view but instead I get an error from the node server:
user save error E11000 duplicate key error collection: test.users index: username_1 dup key: { : "John" }
POST /create 302 71.995 ms - 46
Error: Can't set headers after they are sent.
username: { type: String, required: true, unique: true },
it's because unique true.
Error: Can't set headers after they are sent.
Error because you haven't return error. Whenever error occure simply return like
if (err) return next(err)
provided that you are using express centralized error handler
check last lines of your app.js.All errors from next(err) goes here
app.use(function(err, req, res, next) {
console.error( err);
..............
});

Failed to load user into session with

For some reason, testing my authentication, I created a user with the login 8===D, and password 123, and then passportjs says it fails to serialize that user when i try to login, however do it with any regular like username like bill#kentucky.com then it works fine and serializes the user on login. I can provide code as a example as well, but does it have do with it having strange characters(like '=')?
Also why can I serialize with just .id instead of ._id, why does that work? I use mongoDB and it always generates a _id instead.
exports.postLogin = function(req, res, next) {
// Do email and password validation for the server
passport.authenticate('local', function(err, user, info) {
if(err) return next(err);
if(!user) {
req.flash('errors', {msg: info.message});
console.log("ERROR BOYS");
}
// Passport exposes a login() function on req (also aliased as logIn()) that can be used to establish a login session
req.logIn(user, function(err) {
console.log("User: " +user + " has been logged in");
if(err) return next(err);
req.flash('success', { msg: 'Success! You are logged in'});
res.end('Success');
});
})(req, res, next);
};
/**
* GET /logout
*/
exports.getLogout = function(req, res, next) {
// Do email and password validation for the server
console.log("You have been logged out");
req.logout();
res.redirect('/');
};
/**
* POST /signup
* Create a new local account
*/
exports.postSignUp = function(req, res, next) {
var user = new User({
email: req.body.email,
password: req.body.password,
profile: {
firstName : req.body.firstName,
lastName : req.body.lastName,
section : req.body.section
}
});
User.findOne({email: req.body.email}, function(err, existingUser) {
if(existingUser) {
req.flash('errors', { msg: 'Account with that email address already exists' });
res.redirect('/sign');
}
user.save(function(err) {
if(err) return next(err);
req.logIn(user, function(err) {
if(err) return next(err);
console.log('Successfully created');
console.log('Printing user');
console.log(user);
console.log('Print our body from our request');
console.log(req.body);
res.redirect('/');
res.end();
});
});
});
};
And the serialize part:
/* Initializing passport.js */
var User = require('../models/user');
var local = require('./passport/local');
/*
* Expose
*/
module.exports = function(app, passport, config) {
// serialize sessions
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
//use the following strategies
passport.use(local);
};
Maybe it has to do with the login??::???
else {
renderedResult = (
<div>
<div className={styles['wrapper']}>
<div className={styles['container']}>
<h1 className={styles['welcomelogin']}>Welcome, If ya just signed up then go ahead and sign in</h1>
<fieldset className = {styles['loginSet']}>
<input type="text" className = {styles['form-control']} placeholder="Username" ref = "email" name = "email" />
<input type="password" className = {styles['form-control']} placeholder="Password" ref = "password" name = "password" />
<button type="submit" className={styles['login-button']} to = "dashboard" onClick={this._onLoginSubmit}>Login</button>
</fieldset>
<hr/>
<p>Need an account? <Link to="register">Signup</Link></p>
</div>
</div>
</div>
);
}}
return (
<div>
{renderedResult}
</div>
);
}
}
User Model:
/**
* Defining a User Model in mongoose
*
*/
var bcrypt = require('bcrypt-nodejs');
var mongoose = require('mongoose');
var crypto = require('crypto');
// Other oauthtypes to be added
/*
User Schema
*/
var UserSchema = new mongoose.Schema({
email: { type: String, unique: true},
password: String,
tokens: Array,
profile: {
firstName: { type: String, default: ''},
lastName: {type: String, default: ''},
gender: { type: String, default: ''},
location: { type: String, default: ''},
website: { type: String, default: ''},
picture: { type: String, default: ''},
section: { type: String, default: ''}
},
resetPasswordToken: String,
resetPasswordExpires: Date,
google: {},
isStaff : { type: Boolean, default: false}
});
/**
* Password hash middleware.
*/
UserSchema.pre('save', function(next) {
var user = this;
if (!user.isModified('password')) return next();
bcrypt.genSalt(5, 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();
});
});
});
/*
Defining our own custom document instance method
*/
UserSchema.methods = {
comparePassword: function(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
if(err) return cb(err);
cb(null, isMatch);
})
}
};
/**
* Statics
*/
UserSchema.statics = {}
module.exports = mongoose.model('User', UserSchema);
Serialize function determine what data from the user object should be stored in the session. The result of the serializeUser method is attached to the session as req.session.passport.user = {} here for instance it would be(as we provide id as key)
req.session.passport.user = {id:'xyz'}
Passport will serialize and deserialize user instances to and from the
session.
In this example, only the user ID is serialized to the session,
keeping the amount of data stored within the session small. When
subsequent requests are received, this ID is used to find the user,
which will be restored to req.user.
The serialization and deserialization logic is supplied by the
application, allowing the application to choose an appropriate
database and/or object mapper, without imposition by the
authentication layer.
http://passportjs.org/docs
Basically, you need to work around with this, transforming those =in html code or not allow these symbols. Just remember that the session is converted in strings with & and =. Like :
data=data&data2=data2

Resources