Run multiple functions based on a SINGLE form submission (method="post") using Node-express - html

I am looking to perform multiple actions upon receiving HTML(or EJS) form content using the POST method. I am using Node express, mongoose & mongoDB. Each of the below POST responses work individually but i am unsure how to proceed in updating multiple databases based on ONE SINGLE form submission.
// insert into passport db
app.post('/signup', passport.authenticate('local-signup',
{
successRedirect : '/index', // redirect to the secure profile section
failureRedirect : '/signup', // redirect back to the signup page if there is an error
failureFlash : true // allow flash messages
}));
//insert into my database here
[the content of in the second function is unimportant as that is working fine and has been stripped down for simplification.]
app.post('/signup', function( req, res )
{
new UserDB(
{
user_id : req.body.content,
first_name : req.body.fname,
}).save( function( err, mySite, count )
{
res.redirect( '/index' );
});
});
I have tried redirecting but the form content is not accessible after the redirect so only the first function stores the data (ie. only 1 database is filled).
How would i run both functions within
app.post('/signup',.....
{
...
});
?
Thanks in advance!

You can do this by making one function the callback of the other. This is easy because each function maintains the same Connect middleware signature, function(req, res, next), where req and res are the request and response objects created and manipulated by the application, and next is the next function to call at the end of the current function's execution.
According to the official documentation, passport.authenticate() is a normal piece of middleware. All you need to do is specify the middleware you want to be called next. Express queues middleware functions in the order in which you pass them into app.post. You can do something like this:
app.post('/signup', passport.authenticate('local-signup', {
failureRedirect : '/signup',
failureFlash : true
}),
function(req, res) {
new UserDB({
user_id : req.body.content,
first_name : req.body.fname,
}).save(function(err, mySite, count) {
res.redirect('/index');
});
});
Middleware is an extremely powerful feature of the Express framework and possibly the single most important one to master. This guide would be a great next step if you want to learn more.

Related

/favicon.ico after login in Node JS

I have created a login/register system using express (passport) on my website and I am saving the originalUrl before the user was redirected to the login page but every time after the login, the user is redirected to /favicon.ico instead of the saved Url. Could someone tell me what is the cause of the issue?
My app.use():
app.use((req, res, next) => {
if (!['/login'].includes(req.originalUrl)) {
req.session.returnTo = req.originalUrl;
}
res.locals.currentUser = req.user;
res.locals.success = req.flash('success');
res.locals.error = req.flash('error');
next();
})
My /login get and post request:
app.get('/login', (req, res) => {
res.render('login');
})
app.post('/login', passport.authenticate('local', { failureFlash: true, failureRedirect: '/login' }), (req, res) => {
const redirectUrl = req.session.returnTo || '/';
console.log(redirectUrl);
delete req.session.returnTo;
res.redirect(redirectUrl);
})
You have a race condition in your session between two incoming requests.
Your app.use() middleware is going to see the /favicon.ico request and will overwrite the req.session.returnTo value that your login route may have just set. If these two requests come in one immediately after the other (which is likely when a browser first visits your site), then the /favicon.ico route will mess up the session state you just tried to set with the /login route.
I can't tell what that middleware is trying to do, but it looks like it's very capable of overwriting stuff in the session that other requests are in the middle of using. Redirects after login are much, much safer to do by putting the eventual redirect URL in the query parameter. Then it is stateless on the server and isn't subject to these types of race conditions when there is more than one incoming request to the server form the same user.
FYI, you could also fairly easily prevent this particular problem (though not other potential race conditions) by just putting this route handler before your middleware:
// put this before your middleware
app.get("/favicon.ico", (req, res) => {
res.sendStatus(404);
// or instead of a 404, send an actual favicon.ico file
// just don't let routing continue to your middleware
});
This would keep your middleware from running at all when /favicon.ico is requested and thus prevent that specific place that a race condition with your session data is caused.

How to get around previously declared json body-parser in Nodebb?

I am writing a private plugin for nodebb (open forum software). In the nodebb's webserver.js file there is a line that seems to be hogging all incoming json data.
app.use(bodyParser.json(jsonOpts));
I am trying to convert all incoming json data for one of my end-points into raw data. However the challenge is I cannot remove or modify the line above.
The following code works ONLY if I temporarily remove the line above.
var rawBodySaver = function (req, res, buf, encoding) {
if (buf && buf.length) {
req.rawBody = buf.toString(encoding || 'utf8');
}
}
app.use(bodyParser.json({ verify: rawBodySaver }));
However as soon as I put the app.use(bodyParser.json(jsonOpts)); middleware back into the webserver.js file it stops working. So it seems like body-parser only processes the first parser that matches the incoming data type and then skips all the rest?
How can I get around that? I could not find any information in their official documentation.
Any help is greatly appreciated.
** Update **
The problem I am trying to solve is to correctly handle an incoming stripe webhook event. In the official stripe documentation they suggested I do the following:
// Match the raw body to content type application/json
app.post('/webhook', bodyParser.raw({type: 'application/json'}),
(request, response) => {
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig,
endpointSecret);
} catch (err) {
return response.status(400).send(Webhook Error:
${err.message});
}
Both methods, the original at the top of this post and the official stripe recommended way, construct the stripe event correctly but only if I remove the middleware in webserver. So my understanding now is that you cannot have multiple middleware to handle the same incoming data. I don't have much wiggle room when it comes to the first middleware except for being able to modify the argument (jsonOpts) that is being passed to it and comes from a .json file. I tried adding a verify field but I couldn't figure out how to add a function as its value. I hope this makes sense and sorry for not stating what problem I am trying to solve initially.
The only solution I can find without modifying the NodeBB code is to insert your middleware in a convenient hook (that will be later than you want) and then hack into the layer list in the app router to move that middleware earlier in the app layer list to get it in front of the things you want to be in front of.
This is a hack so if Express changes their internal implementation at some future time, then this could break. But, if they ever changed this part of the implementation, it would likely only be in a major revision (as in Express 4 ==> Express 5) and you could just adapt the code to fit the new scheme or perhaps NodeBB will have given you an appropriate hook by then.
The basic concept is as follows:
Get the router you need to modify. It appears it's the app router you want for NodeBB.
Insert your middleware/route as you normally would to allow Express to do all the normal setup for your middleware/route and insert it in the internal Layer list in the app router.
Then, reach into the list, take it off the end of the list (where it was just added) and insert it earlier in the list.
Figure out where to put it earlier in the list. You probably don't want it at the very start of the list because that would put it after some helpful system middleware that makes things like query parameter parsing work. So, the code looks for the first middleware that has a name we don't recognize from the built-in names we know and insert it right after that.
Here's the code for a function to insert your middleware.
function getAppRouter(app) {
// History:
// Express 4.x throws when accessing app.router and the router is on app._router
// But, the router is lazy initialized with app.lazyrouter()
// Express 5.x again supports app.router
// And, it handles the lazy construction of the router for you
let router;
try {
router = app.router; // Works for Express 5.x, Express 4.x will throw when accessing
} catch(e) {}
if (!router) {
// Express 4.x
if (typeof app.lazyrouter === "function") {
// make sure router has been created
app.lazyrouter();
}
router = app._router;
}
if (!router) {
throw new Error("Couldn't find app router");
}
return router;
}
// insert a method on the app router near the front of the list
function insertAppMethod(app, method, path, fn) {
let router = getAppRouter(app);
let stack = router.stack;
// allow function to be called with no path
// as insertAppMethod(app, metod, fn);
if (typeof path === "function") {
fn = path;
path = null;
}
// add the handler to the end of the list
if (path) {
app[method](path, fn);
} else {
app[method](fn);
}
// now remove it from the stack
let layerObj = stack.pop();
// now insert it near the front of the stack,
// but after a couple pre-built middleware's installed by Express itself
let skips = new Set(["query", "expressInit"]);
for (let i = 0; i < stack.length; i++) {
if (!skips.has(stack[i].name)) {
// insert it here before this item
stack.splice(i, 0, layerObj);
break;
}
}
}
You would then use this to insert your method like this from any NodeBB hook that provides you the app object sometime during startup. It will create your /webhook route handler and then insert it earlier in the layer list (before the other body-parser middleware).
let rawMiddleware = bodyParser.raw({type: 'application/json'});
insertAppMethod(app, 'post', '/webhook', (request, response, next) => {
rawMiddleware(request, response, (err) => {
if (err) {
next(err);
return;
}
const sig = request.headers['stripe-signature'];
let event;
try {
event = stripe.webhooks.constructEvent(request.body, sig, endpointSecret);
// you need to either call next() or send a response here
} catch (err) {
return response.status(400).send(`Webhook Error: ${err.message}`);
}
});
});
The bodyParser.json() middleware does the following:
Check the response type of an incoming request to see if it is application/json.
If it is that type, then read the body from the incoming stream to get all the data from the stream.
When it has all the data from the stream, parse it as JSON and put the result into req.body so follow-on request handlers can access the already-read and already-parsed data there.
Because it reads the data from the stream, there is no longer any more data in the stream. Unless it saves the raw data somewhere (I haven't looked to see if it does), then the original RAW data is gone - it's been read from the stream already. This is why you can't have multiple different middleware all trying to process the same request body. Whichever one goes first reads the data from the incoming stream and then the original data is no longer there in the stream.
To help you find a solution, we need to know what end-problem you're really trying to solve? You will not be able to have two middlewares both looking for the same content-type and both reading the request body. You could replace bodyParser.json() that does both what it does now and does something else for your purpose in the same middleware, but not in separate middleware.

how can I hide the data that my controller returns making a CRUD in Laravel & vue.js?

I´m making a CRUD in laravel with vue.js & axios. I did create the complete CRUD, but using routes that return the data from my controller, but if I put the route that returns the data, obviously all people can access to the data that returns the controller.
I´m using laravel 5.5, vue.js & axios. if I put the route in the browser, for example: localhost/tasks all the data that I passed in my controller it shows.
Routes:
Route::get('/', function () {
return view('dashboard');
});
Route::resource('tasks', 'TaskController',['except' => 'show', 'create', 'edit']);
Controller:
public function index()
{
$tasks = Task::orderBy('id', 'DESC')->get();
return $tasks;
}
app.js
data: {
keeps: [],
newKeep: '',
fillKeep: {'id': '', 'keep': ''},
errors: []
},
methods: {
getKeeps: function()
{
//este es el nombre de las rutas (index)
var urlKeeps = 'tasks';
axios.get(urlKeeps).then(response =>{
this.keeps = response.data
});
},
// Metodo de editar
editKeep: function(keep){
this.fillKeep.id = keep.id;
this.fillKeep.keep = keep.keep;
$('#edit').modal('show');
},
The principal dashboard is in the '/' route, the controller returns the data from my controller, the route is "/tasks" and the other methods, but when any other user put in the browser "www.myweb.com/tasks" it returns all the data from my controller, ¿How can I avoid that problem?
If the Tasks model has multiple users' data you should be restricting the tasks route to return only the current logged in user's data. This is the same whether its accessed via Ajax or directly in the browser window.
Restrict that route with the auth middleware and scope the tasks Eloquent query to return that user's data.
e.g.
$tasks = Task::where('user_id', \Auth::user()->id)->orderBy('id', 'DESC')->get();
Then it doesn't matter if the user visits the URL directly and sees their own data.
Well if you want to restrict all from entering this path you could simply put a middleware that if the requested url is this then return to home.
Here you can find all middleware documentation:
https://laravel.com/docs/5.8/middleware
About the condition you could simply put in your user table a column that its name for example tasks_access and you put it nullable() so that the middleware will do an if statement then if this column is null then return redirect home and basically it will be always null.
In migration the user migration
$table->boolean('tasks_access')->nullable();
then you can either make a middleware or you can do simply in your controller
if (!#Auth::user()->tasks_access){
return redirect('/user');
so that your controller will be like this
public function index()
{
if (!#Auth::user()->tasks_access){
return redirect('/user');
}
$tasks = Task::orderBy('id', 'DESC')->get();
return $tasks;
}
so that based on your user table the tasks_access column the user tasks access is null by default so that it will always return redirect the user to the /user route or you could change it to be /home or whatever you want
Note : I answered based on your question but I don't know why you want to block all users from seeing data and the data in the first place should be shared with users because data without users is useless

Using passport.js and plain SQL query to authenticate

I'm working on a project and I have the following issue.
I want to implement logic for user login with Passport API but I'm having difficulties of understanding how it works, especially the way I want to implement it (with plain SQL queries). I have gone thru several tutorials which explain how this can be done ,but the problem is that in them it is shown only with ORMs, and I do not want it that way. I've wrote a few thousand lines of code so far ,but without success which were deleted after this of course and this is the reason I haven't provided any code below.
I'm using MySQL and Express as frameworks to build the website. If you have any brief or advanced idea of how things can happen I will be happy to hear from you.
Thanks in advance !
Passport can be quite confusing at times, I'll give that to you! I'm assuming based on your question that you want to use the "local" login strategy and not offer something like Google or GitHub Single Sign On. I'll also assume you want to use "Sessions" (cookies) rather than something like JWT.
To do this you'll need to first configure passport with your express app up front. This requires you to initialise passport and a session store (you can use MySQL if you like, or something like Redis).
Then you need to configure your "strategy" which in our cases is the local strategy.
I'll run you through an example with some code that shows how this can be done. I'll shove this all into one code snippet but you may wish to break this out into several files.
Snippet you can clone:
https://gist.github.com/BlueHatbRit/5d07d3f98d41d536a776b74fcb843174
Mirrored here for answer longevity:
const express = require('express');
const session = require('express-session');
const bodyParser = require('body-parser');
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
// Create the express app
const app = express();
// Initialise express-session module effectively deals with serilaising some
// form of ID in a cookie which is secured with the given secret. In this case
// express then remembers this ID in memory. When this cookie is handed
// back to your server, express-session takes that ID and matches it up to
// the data it has stored against that ID in memory. Remember, in production
// you will most probably want to hook this up to some sort of data store,
// either Redis, MySQL, etc...
app.use(session({ secret: "cats" }));
// We need some body parser setup to use Passport with express
// you can checkout the body parser and passport docs to find out why
app.use(bodyParser.urlencoded({ extended: false }));
// Now we initialise passport
app.use(passport.initialize());
// Now setup the session strategy, this happens after the express-session
// initialisation as that must run on a request first. Once we have the data
// from express-session (remember, it converted from a session ID given to
// the user via a cookie, back into the data we stored against the ID) we can
// then pull our any additional information.
app.use(passport.session());
passport.serializeUser(function(user, done) {
// This happens at the end of a request, it recieves the
// req.user object, and you can then choose what to serialise
// into the session (returning the user a new cookie with a
// session ID).
// In most cases you'll want to store as little data as possible
// so just a user.id might be fine.
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
// Assume we stored the user ID in the session in the above
// function call, we can now access it.
// You can now take "id" and pass it into your database and
// get back whatever you want in regards to the user. This may
// just be a small representation of the user, or the entire
// record.
// You can use either SQL or an ORM here. The important bit is
// that you call the "done" callback with whatever object you
// want to represent the user.
User.findById(id, function(err, user) {
// In your main request handlers, you will then call `req.user`
// and get back whatever you passed into the callback.
done(err, user);
});
});
// Now we setup the main "login" route, this will do the first round
// of authentication. It will take a username and password, will check
// those credentails and will then decide whether or not to log the user in.
passport.use(new LocalStrategy(function(username, password, done) {
// Run your SQL here to find the user by their username
// Then check their password is correct
// If something fails then call the "done" callback with a descriptive error
// otherwise call "done" with no error, and pass it the "user" object. This will
// be assigned to req.user which will then later be put through our serialize
// function above.
// In this case I'm using an ORM, but you can use something to execute raw SQL
// if you like.
User.findOne({ username: username }, function(err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
// This is a made up function here, you'll need to create this and fill it out
// if you're using SQL you will probably have a function called "validPassword"
// (not assigned to a user object) where you will then pass in the hashed password
// from your database, and the password they provided you (the password string in this
// case).
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
// We have a user and the passwords match so we can return the user object!
return done(null, user);
}
});
// Now we need to mount our configured strategy to an endpoint
app.post('/login', function(req, res, next) {
passport.authenticate('local', {
successRedirect: '/dashboard', // The user logged in fine, redirect them do the dashboard
failureRedirect: '/login', // The login failed, send them back to the login page
// It is possible to use "connect-flash" here to send back the reason but that's outside of the scope of this
});
});
// Now we'll create some middleware to ensure a user is logged in when trying to access
// a protected endpoint
function protected(req, res, next) {
// req.user will only exist if they've been authenticated
if (!req.user) {
return next(new Error('nice try, but you are not logged in!');
}
return next();
}
app.get('/private-things', protected, function(req, res, next) {
// This code will only be accessible if someone goes to /private-things and
// has a valid session!
console.log(the user is logged in!);
console.log(req.user);
res.sendStatus(200);
});
A warning, I have not run this code. All the code is there though you might spot a few syntax errors and will need to write the SQL to match up to your database.

Customize Loopback response after save

I have a loopback 2.x app, in which I have a model Conversation and a model Message, with a relationship "Conversation has many messages". I want to customize the response for POST conversations/:id/messages with a json response different than the default, say {status: 'success'}. I tried to use remote hook for the method __create__messages, but it did not work:
Conversation.afterRemote('__create__messages', function(ctx, next) {
ctx.result.data = {
success: 'yes'
};
next();
});
This still returns the default response. How can I return a custom json for a remote method? I have seen examples only for all models, or for all methods: multiple models, multiple methods
Maybe you can try a version of following code below. Also, I think you are meaning to to manipulate data before the method finishes, not after. If you wait, the response will already be created, preventing your intended goal. Let me know if this works (replace with methods that will work for your use case).
Conversation.observe('before save', function(context, next) {
var instance = context.instance || context.data;
if (!instance) return next();
// Your code here
next();
});