invalidate a validation from a nested schema virtual mongoose - javascript

I have my parent schema defined like this:
User.js:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var PasswordSchema = require('./Password');
var UserSchema = new Schema({
name: { type: String, required: true },
password: PasswordSchema
});
mongoose.model('User', UserSchema);
My children schema defined like this:
Password.js:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var crypto = require('crypto');
var PasswordSchema = new Schema ({
_id: false,
hashedPassword: { type: String, required: true },
salt: { type: String, default: '' }
});
var passwordRegex = /^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8,24}$/;
PasswordSchema.virtual('password')
.set(function (password) {
if (passwordRegex.test(password))
{
this.invalidate('password', 'Invalid password format');
}
});
mongoose.model('Password', PasswordSchema);
module.exports = PasswordSchema;
Now I used these Models schema in my controller like this:
user.js:
require('../models/User');
var express = require('express');
var router = express.Router();
var mongoose = require('mongoose');
var User = mongoose.model('User');
var Password = mongoose.model('Password');
router.post('/register', function (req, res, next) {
var user = new User(req.body);
var password = new Password({ password: 'abcd1234' });
console.log(password.$__.validationError.errors['hashedPassword']); // Here it works I got the validation error
user.password = password;
user.password.$__.validationError = password.$__.validationError; // WORKAROUND
console.log(user.password.$__.validationError.errors['hashedPassword']); // Here it doesn't work no validation error anymore ...
user.save(function (err) {
if (err)
console.log(":(");
else
console.log(":)");
});
});
module.exports = router;
Question:
So my problem now is that no matter what password I send to my children virtual it doesn't invalidate the process. How could I invalidate the mongoose save action from a children virtual ? Is there an other better option ?
Question Updated:
In user.js why the variable password has the validation error and when I assign it to user.password I don't have the validation error anymore ? How can I correct it ?
** Update 2:**
I have found a workaround see user.js: I just assign the required property to generate validation error. But it looks really not clean is there another way?

Here is one good example https://gist.github.com/swaj/1350041, refactor it as below
PasswordSchema.virtual('password')
.get(function(){
return this._password;
})
.set(function (password) {
this._password = password;
// invoke crypto to hash and encrypt password, then assign it to hashedPassword
this.hashedPassword = password; // this is just for test
});
PasswordSchema.path('hashedPassword').validate(function(v) {
if (v) {
if (passwordRegex.test(v)) {
this.invalidate('password', 'Invalid password format');
}
}
if (!v) {
this.validate('password', 'password required');
}
}, null);
Test codes
var user = new User({name: 'dd'});
user.password = new Password({password: 'asdfASF123444'});
user.save(function (err) {
if (err)
console.log(err);
else
console.log("save user successfully");
});
Validation error is
{ [ValidationError: User validation failed]
message: 'User validation failed',
name: 'ValidationError',
errors:
{ password:
{ [ValidatorError: Invalid password format]
properties: [Object],
message: 'Invalid password format',
name: 'ValidatorError',
kind: 'user defined',
path: 'password',
value: undefined } } }
Per invalidate source code
Document.prototype.invalidate = function (path, err, val) {
if (!this.$__.validationError) {
this.$__.validationError = new ValidationError(this);
}
// ...
We know invalidate function belong to Document.
password.$__.validationError.errors['hashedPassword']
You define the validation for PasswordSchema, not in UserSchema. so user.password.$__.validationError.errors['hashedPassword'] is not valid.
Test your code with
var user = new User({name: 'dd'});
user.password = new Password({password: 'asdfwe32113'});
user.save(function (err) {
if (err)
console.log(err);
else
console.log("save user successfully");
});
Validation will be triggered, however, with this code
`user.password = new Password({hashedPassword: 'asdfwe32113'});`
This validation is NOT triggered.
Because for virtual field, only the correct virtual name field is updated then the .set function could be called.
Also please add those codes to virtual('password'), to make sure the hashedPassword could be set correctly.
if (passwordRegex.test(password)) {
this.invalidate('password', 'Invalid password format');
}else {
this.hashedPassword = password;
}
For the second question, require('../models/User'); must be invoked before mongoose.model() to make sure the User.js is parsed firstly, and the User could be added into mongoose.model in User.js. So in user.js could find this User model from mongoose. JavaScript is an interpreted programming language, so we should tell the JS engine the file parsed order in this way.

Related

TypeError: Cannot read properties of undefined (reading '_id')

I'm working on a personal project and i really need some help
I'm having this error and i don't understand why :(
Here is my code :
//authRoutes.js
const { Router } = require('express');
const authController = require('../controllers/authController');
const { requireAuth } = require('../middleware/authMiddleware');
var Post = require('../models/post')
const router = Router();
router.use(function(req,res, next){
res.locals.user = req.user;
next();
});
router.get('/signup', authController.signup_get);
router.post('/signup', authController.signup_post);
router.get('/login', authController.login_get);
router.post('/login', authController.login_post);
router.get('/logout', authController.logout_get);
router.get("/home", function (req, res) {
res.render("home");
});
router.get("/about", function (req, res) {
res.render("about");
});
router.get("/", requireAuth, function(req,res){
Post.find({userID:req.user._id}).exec(function(err, posts){
if(err){
console.log(err);
}
res.render("posts",{posts:posts})
})
})
router.get("/add", requireAuth, function(req,res){
res.render("addpost")
})
Everything was working fine until I tried to add a new post to the database
This is the part of the code that's causing the error :
router.post("/add",requireAuth, function(req,res){
var newPost = new Post({
title:req.body.title,
content:req.body.content,
userID:req.user._id
})
newPost.save(function(err,post){
if(err){
console.log(err)
res.redirect("/posts")
}
})
})
module.exports = router;
can someone help me?
The error is because in here:
var newPost = new Post({
title:req.body.title,
content:req.body.content,
userID:req.user._id
})
you're trying to access "user._id", but there is not "_id" inside user, so check what is inside user.
Quite literally, it is what it say it is
Cannot read properties of undefined (reading '_id')
This means that there's no _id property inside req.user (the object user inside the req object).
It seems you're not sending _id (maybe your payload is using id instead of _id? Could you share your payload with us?
it seems like req.user is undefined and that is the reason of why it gives you that error. Try to find out where you get your id from! :)
Here is user.js
const mongoose = require('mongoose');
const { isEmail } = require('validator');
const bcrypt = require('bcrypt');
const userSchema = new mongoose.Schema({
email: {
type: String,
required: [true, 'Please enter an email'],
unique: true,
lowercase: true,
validate: [isEmail, 'Please enter a valid email']
},
password: {
type: String,
required: [true, 'Please enter a password'],
minlength: [6, 'Minimum password length is 6 characters'],
}
});
// fire a function before doc saved to db
userSchema.pre('save', async function(next) {
const salt = await bcrypt.genSalt();
this.password = await bcrypt.hash(this.password, salt);
next();
});
// static method to login user
userSchema.statics.login = async function(email, password) {
const user = await this.findOne({ email });
if (user) {
const auth = await bcrypt.compare(password, user.password);
if (auth) {
return user;
}
throw Error('incorrect password');
}
throw Error('incorrect email');
};
const User = mongoose.model('user', userSchema);
module.exports = User;

Getting "ReferenceError: user is not defined" even user is defined

I'm working with routes on node js. I created a user model shown below -
const mongoose = require("mongoose");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const validator = require("validator");
require("dotenv").config();
const userSchema = mongoose.Schema(
{
email: {
type: String,
required: true,
unique: true,
trim: true,
lowercase: true,
validate(value) {
if (!validator.isEmail) {
throw new Error("Invalid Email");
}
},
},
password: {
type: String,
required: true,
trim: true,
},
role: {
type: String,
enum: ["user", "admin"],
default: "user",
},
name: {
type: String,
required: true,
maxlength: 21,
},
phone: {
required: true,
type: Number,
maxlength: 12,
},
},
{ timestamps: true },
);
userSchema.pre("save", async function (next) {
if (user.isModified("password")) {
// hash the password
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(this.password, salt);
this.password = hash;
}
next();
});
const User = mongoose.model("User", userSchema);
module.exports = {
User,
};
And then I created a file containing user routes shown below -
const express = require("express");
const router = express.Router();
require("dotenv").config();
const { User } = require("../../models/userModel");
router.route("/signup").post(async (req, res) => {
// const { email, password, name, phone } = req.body;
console.log(req.body);
// try {
// // Check if user email exists
// // create user instance and hash password
// const user = new User({
// email: req.body.email,
// password: req.body.password,
// name: req.body.name,
// phone: req.body.phone,
// });
// // generate jwt token
// console.log("user is saving");
// const userDoc = await user.save();
// // send email
// // save....send token with cookie
// res
// .cookie("access-token", "jflsakjusdilfjadslfj32j43lrf")
// .status(200)
// .send(userDoc);
// } catch (error) {
// res
// .status(400)
// .json({ message: "Error while creating user", error: error });
// }
const user = new User({
name: req.body.name,
email: req.body.email,
password: req.body.password,
phone: req.body.phone,
});
user
.save()
.then((doc) => {
console.log("User saved");
res.send(doc);
})
.catch((err) => {
console.log(err);
});
});
module.exports = router;
But don't know why I'm getting this error -
ReferenceError: user is not defined
at model.<anonymous> (D:\CovidHelpers\CovidHelpers\server\models\userModel.js:46:3)
at callMiddlewareFunction (D:\CovidHelpers\CovidHelpers\node_modules\kareem\index.js:483:23)
at model.next (D:\CovidHelpers\CovidHelpers\node_modules\kareem\index.js:58:7)
at _next (D:\CovidHelpers\CovidHelpers\node_modules\kareem\index.js:107:10)
at D:\CovidHelpers\CovidHelpers\node_modules\kareem\index.js:508:38
at processTicksAndRejections (internal/process/task_queues.js:75:11)
I have just created a new project in mongodb, gave database and network access and it's connecting successfully but also getting this error
I have done this before also and it was working fine but don't know why am I getting this now :(
Any help is appreciated
save is document middleware and in document middleware functions, this refers to the document. So in your case, I believe it should be this.isModified("password") instead of user.isModified("password").
You can delete userSchema.pre() middleware and transfer the password hashing logic inside the router. Also you can simplify your router code like this:
router.route("/signup").post(async (req, res) => {
try {
const salt = await bcrypt.genSalt(10);
const hash = await bcrypt.hash(req.body.password, salt);
req.body.password = hash;
let user = await User.create(req.body)
res.status(200).json(user)
} catch (error) {
res.status(400).json({ error: error });
}
});
RECOMMENDATION:
I would recommend you to try the great Mongoose plugin called passport-local-mongoose that will do this for you out of the box, and it will also give you some nice authentication features if you are using passport for authentication.
Package: https://www.npmjs.com/package/passport-local-mongoose
You don't actually get access to the document, in the mongoose's pre('save') hook.
For your usecase, you can do the hasing before you save the user.

Insert into mongodb freezing without error

This is my database connection:
app.js
const express = require("express");
const app = express();
var { MongoClient } = require("mongodb");
MongoClient.connect("mongodb://localhost:27017", (err, client) => {
if (err) return console.log(err);
db = client.db("MyDb");
app.listen(5000, () => {
console.log("listening on 5000");
});
});
And this is my insert function:
router.post(
"/register",
[
check("email")
.notEmpty()
.withMessage("Email Field is empty"),
check("email")
.isEmail()
.withMessage("Your email is not valid")
],
function(req, res) {
const errors = validationResult(req);
if (errors.length >= 0) {
res.render("register", { errors: errors.errors });
console.log(errors.errors);
return;
}
const { name, email, password } = req.body;
const newUser = new User({
name: name,
email: email,
password: password
});
newUser.save(function(err) {
if (err) throw err;
console.log(true);
});
}
);
And this is my user model:
User.js
const mongoose = require("mongoose");
const UserSchema = mongoose.Schema({
name: { type: String, require: true },
email: { type: String, require: true, unique: true },
password: { type: String, require: true },
created_at: Date,
updated_at: Date
});
const User = mongoose.model("User", UserSchema);
module.exports = User;
There is no error in terminal or browser. When I click the "register" button, the app will freeze and there is no error message at all.
I already tested many tips concerning the database connection but couldn't solve the issue.
I find there are two order of problems in the proposed code, at least as we can read it in your question:
First, I can't find any binding between mongoose and the established mongodb connection
Second, your route handler does not seem to return any status code / content to the
caller
So, for as I see it, you can
change connection setup as follows
mongoose.connect('mongodb://localhost/test', {useNewUrlParser: true})
.then((conn, err) => {
app.listen(5000, () => {
console.log("listening on 5000");
});
});
in order to bind mongoose with MongoDb configuration
retust a status code, e.g. 201, when the new User has been saved:
newUser.save(function(err) {
console.log('Result', err)
if (err) throw err;
console.log(true);
res.send(201)
});
This way I prevent the application hanging up on receiving request...
I hope this can help you!
validationResult() "Extracts the validation errors from a request and makes them available in a Result object." https://express-validator.github.io/docs/validation-result-api.html Therfore, if you don't have any errors this object will contain no errors ( you can check with .isEmpty()), your endpoint doesn't send a response, and leaves the requestor waiting.

400 Bad Request when i register a new user in NodeJS via Mongoose using PassportJS

Am creating a registration form where user needs to enter details of his emailid(username),password,DOB,Address,phoneno. All fields are mandatory here emailid acts as an username. I have designed my Mongoose Schema
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
var User = new Schema({
username: String,
password: String,
phoneno: {
type: Number,
unique: true,
required: true
},
Address : {
type: String,
required: true
},
Dob: {
type: String,
required: true
}
},{
timestamps: true
});
User.plugin(passportLocalMongoose);
module.exports = mongoose.model('User', User);
My user router is given below
var express = require('express');
var UserDetails = express.Router();
var userSchema=require('../models/user');
var passport = require('passport');
const mongoose = require('mongoose');
var Verify = require('./verify');
UserDetails.post('/register', function(req, res, next) {
var newUser = new userSchema({
username : req.body.email,
Dob : req.body.dob,
Address : req.body.address,
phoneno : req.body.phoneno
});
userSchema.register(newUser,req.body.password, function(err, user) {
if (err) {
console.log(err);
return res.status(500).json({err: err});
}
passport.authenticate('local')(req, res, function () {
console.log(req);
console.log(res);
return res.status(200).json({status: 'Registration Successful!'});
});
});
});
This is the json object am sending via PostMan
{"email":"kannaa.in","password": "abcdef","phoneno":96930,"address":"396 SM Road","dob":"14-05-1992"}
But in the console and postman it says bad request if i try to perform the operation.However it stores the value in DB. I couldn't find out the error.
So after no one answered i found out myself what is the error about PassportJS expects the request to contain req.body.username it doesn't matter how many other datas you send it needs req.body.username from the request side. So all i had to change was rather than email i set it as username in postman and it worked like charm.
My postman Data
{"username":"kannaa.in","password": "abcdef","phoneno":96930,"address":"396 SM Road","dob":"14-05-1992"}

Cannot read property 'users' of undefined

I'm new to MongoDB and have a database of users. I'm trying it such that a user can click on a button of their favorite shoes and it updates the database. My schema is as follows:
var mongoose = require('mongoose');
var bcrypt = require('bcrypt-nodejs');
// define the schema for our user model
var userSchema = mongoose.Schema({
local : {
email : String,
password : String,
},
});
// methods ======================
// generating a hash
userSchema.methods.generateHash = function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};
// checking if password is valid
userSchema.methods.validPassword = function(password) {
return bcrypt.compareSync(password, this.local.password);
};
// create the model for users and expose it to our app
module.exports = mongoose.model('User', userSchema);
Posting DB function:
exports.addToFavs = function(req, res) {
var size_result = req.query.size;
var brandToFind = req.query.brandToFind;
req.db.users.update({email: req.user.email}, {$set: favoriteShoes[{name: brandToFind, size: size_result}]}); /*, function(error, favoriteShoes) {
if (error) return next(error);
if (!favoriteShoe) return next(new Error('Failed to save')); */
console.info('Added %s with id=%s', brandToFind, size_result);
res.redirect('/favorites');
//})
};
HTML Button:
<form action="/result" method="post">
<center><button>Save to Favorites</button></center>
</form>
Appropriate Routes:
app.post('/result', searchResults.addToFavs);
I'm really confused about how to get this to work... somehow it can't read by db table "users" (validated through the console that this is what the table is called) so trying to get it to work but it's not working...

Categories

Resources