i've got a problem using node-formidable (https://github.com/felixge/node-formidable) with expressjs: connect-multipart is now deprecated (http://www.senchalabs.org/connect/multipart.html).
I'm trying to use node-formidable to directly parse my uploaded files but can't make it works.
Urlencoded forms are working well but not multipart. I'm not sure but i think that it comes from the connect-csrf:
Update: it works well when i remove the csrf middleware.
Error: Forbidden
at Object.exports.error (/srv/www/mysite.com/nodejs/myapp/node_modules/express/node_modules/connect/lib/utils.js:63:13)
at createToken (/srv/www/mysite.com/nodejs/myapp/node_modules/express/node_modules/connect/lib/middleware/csrf.js:82:55)
at Object.handle (/srv/www/mysite.com/nodejs/myapp/node_modules/express/node_modules/connect/lib/middleware/csrf.js:48:24)
at next (/srv/www/mysite.com/nodejs/myapp/node_modules/express/node_modules/connect/lib/proto.js:193:15)
at next (/srv/www/mysite.com/nodejs/myapp/node_modules/express/node_modules/connect/lib/middleware/session.js:315:9)
at /srv/www/mysite.com/nodejs/myapp/node_modules/express/node_modules/connect/lib/middleware/session.js:339:9
at /srv/www/mysite.com/nodejs/myapp/node_modules/connect-redis/lib/connect-redis.js:101:14
at try_callback (/srv/www/mysite.com/nodejs/myapp/node_modules/connect-redis/node_modules/redis/index.js:581:9)
at RedisClient.return_reply (/srv/www/mysite.com/nodejs/myapp/node_modules/connect-redis/node_modules/redis/index.js:671:13)
at ReplyParser.<anonymous> (/srv/www/mysite.com/nodejs/myapp/node_modules/connect-redis/node_modules/redis/index.js:313:14)
What can i do? Here is my code:
// Body parser
app.use(express.urlencoded());
app.use(function(req, res, next) {
if (req.is('multipart/form-data') && req.method == "POST") {
var form = new formidable.IncomingForm();
form.uploadDir = "mytmpfolder";
form.parse(req, function(err, fields, files) {
req.files = files;
});
}
next();
});
// Cookie parser
app.use(express.cookieParser());
// Session
app.use(express.session({
key: 'secure_session',
store: new redisStore,
secret: 'secret',
proxy: true,
cookie: {
secure: true,
maxAge: null
}
}));
// CSRF
app.use(express.csrf());
app.use(function(req, res, next){
res.locals.token = req.csrfToken();
next();
});
I found a way to finally make it works:
// Body parser
app.use(function(req, res, next) {
if (req.method == "POST") {
var form = new formidable.IncomingForm();
var fieldsObj = {};
var filesObj = {};
form.uploadDir = "/srv/www/mysite.com/nodejs/myapp/static/uploads";
form.on('field', function(field, value) {
fieldsObj[field] = value;
});
form.on('file', function(field, file) {
filesObj[field] = file;
});
form.on('end', function() {
req.body = fieldsObj;
req.files = filesObj;
next();
});
form.parse(req);
}
else {
next();
}
});
Related
On my express js app, I have set a session cookie, however even after logging out, my cookie is not removed from the browser. Hence clicking signing in, I can log back in without valid authentication.
Here is my app.js:
'use strict';
var auth = require('http-auth');
var bodyParser = require('body-parser');
var config = require('./test/lib/utils/config');
var cookieParser = require('cookie-parser');
var express = require('express');
var mysql = require('mysql2');
var passport = require('passport');
var path = require('path');
var session = require('express-session');
var favicon = require('serve-favicon');
var Auth = require('./lib/auth');
var Utils = require('./lib/utils');
var admin = require('./routes/admin');
var committee = require('./routes/committee');
var index = require('./routes/index');
var logout = require('./routes/logout');
var professor = require('./routes/professor');
var roles = require('./routes/roles');
var creds = config.credentials.database;
var connection = mysql.createConnection(creds);
connection.connect();
var authentication = new Auth(connection);
var utils = new Utils(connection);
var basic = auth.basic({
realm: 'Welcome to My App',
file: path.resolve(__dirname, '.private', '.htpasswd')
}, function(username, password, cb) {
utils.getMemberId(username, function(err, id) {
if (err) return cb(err);
utils.isLoggedIn(id, function(err, isLoggedIn) {
if (err) return cb(err);
if (!isLoggedIn) {
authentication.logIn(id, function(err) {
if (err) return cb(err);
return cb(id);
});
} else {
return cb(id);
}
});
});
});
var app = express();
passport.use(auth.passport(basic));
// Setup strategy.
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
app.use(favicon(path.join(__dirname, 'public', 'image', 'favicon.ico')));
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({ name: 'sid', rolling: true, secret: 'keyboard cat', cookie: { maxAge: 30000, httpOnly: true }}));
app.use(passport.initialize());
app.use(passport.session());
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
app.use('/', index);
app.use('/roles', [passport.authenticate('http', {session: true}), setUserId,
setUserRoles, setUserFirstName, setUserFullName], roles);
app.use('/roles/admin', admin);
app.use('/roles/committee', committee);
app.use('/roles/professor', professor);
app.use('/logout', performLogout, logout);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
// error handler
app.use(function(err, req, res) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
function setUserId(req, res, next) {
console.log(JSON.stringify(req.session));
utils.getMemberId(req.session.passport.user, function(err, id) {
if (err) next(err);
req.session.passport.id = id;
next();
});
}
function setUserRoles(req, res, next) {
utils.getRoles(req.session.passport.id, function(err, roles) {
if (err) next(err);
req.session.passport.roles = roles;
next();
});
}
function setUserFullName(req, res, next) {
utils.getMemberFullName(req.session.passport.id, function(err, fname) {
if (err) next(err);
req.session.passport.fullname = fname;
next();
});
}
function setUserFirstName(req, res, next) {
utils.getMemberFirstName(req.session.passport.id, function(err, fname) {
if (err) next(err);
req.session.passport.fname = fname;
next();
});
}
function performLogout(req, res, next) {
authentication.logOut(req.session.passport.id, function(err) {
if (err) next(err);
next();
});
}
module.exports = app;
Here is my logout.js router:
'use strict';
var express = require('express');
var router = express.Router();
router.get('*', function(req, res) {
console.log('Session before logging out: ' + JSON.stringify(req.session));
req.session.destroy(function() {
res.clearCookie('connect.sid', { name: 'sid', rolling: true, secret: 'keyboard cat', cookie: { maxAge: 30000, httpOnly: true }});
res.redirect('/');
console.log('Session after logging out: ' + JSON.stringify(req.session));
});
});
module.exports = router;
I am unsure of what I am doing wrong to not clear the session cookie properly. Any help with guiding the right away will be appreciated.
I'm trying to develop a small API using express. Just want to have 2 views, which, in my case, means 2 html files. One accesing as default with "/" and the other with "/lessons", so we got 2 get controls plus another one which handles every other get input.
*Both files are in the "views" folder and their extension is: *.html
I have no problem accessing the "app.get("/lessons", function..." in fact I know I can acces to that because a simple "console.log(..)" command. The problem is that I got the next error when trying to render:
[TypeError: this.engine is not a function].
Could you help me? I can't understand where is the problem or what I'm doing wrong. I believe it's in the rendering function and has something to do with its configuration and the lessons.html file because index.html has no problem using the same approach.
var express = require('express');
var app = express();
var mod = require('./module');
app.use(express.static('public'));
app.use(express.static('views'));
var port = process.env.PORT || 8080;
app.listen(port, function() {
console.log('Node.js listening on port ' + port);
});
app.get("/", function(req, res) {
console.log("Passed through /");
res.render('index.html');
});
app.get("/lessons", function(req, res) {
console.log("passed through lessons");
res.render('lessons.html', function(err, html) {
if(err) {
console.log(err);
}
res.send(html);
});
//I have tried to to use just: res.render('lessons.html');
});
app.get("*", function(req, res) {
var usageReq = false;
var urlPassed = req.url;
urlPassed = urlPassed.substring(1, urlPassed.length); //remove first "/"
var expected = mod.seeIfExpected(urlPassed); //returns url(if url) or num(if number) or na(if it doesn't match any)
mod.processInfo(expected, urlPassed, function(answer) {
if (answer.found == false && answer.jsonRes == true && answer.info != "inserted") {
res.json({
"error": answer.info
});
} else {
if (answer.jsonRes == true) {
res.json({
"long_url": answer.url,
"short_url": answer.id
});
} else { // go to url
var newUrl = "https://" + answer.url;
res.redirect(newUrl);
}
}
});
});
I have installed Jasmine CLI globally using npm install -g jasmine
I'm trying to test multiple http requests at once using test suite below, multiple calls per each requests were sent (seeing output of console.log() but nothing returned so the test was failure, please guide me is this possible to do so ? and how to do this ?
index.js
var app = require('express')();
var request = require('request');
app.get('/', function(req, res) {
console.log('GET /');
res.status(200);
res.send('Hello World');
});
app.listen(3000);
spec/multipleRequestSpec.js
var request = require('request');
var async = require('async');
describe('express application', function() {
var baseUrl = 'http://localhost:3000';
var statusCode = [0, 0];
var b = ['', ''];
beforeEach(function(done) {
async.parallel([
function() {
request.get(baseUrl, function(err, res, body) {
statusCode[0] = res.statusCode;
b[0] = body;
})
}
,
function() {
request.post(baseUrl, function(err, res, body) {
statusCode[1] = res.statusCode;
b[1] = body;
})
}
], done());
});
it('should return 200', function() {
expect(statusCode[0]).toBe(200);
});
it('should return hello world', function() {
expect(b[0]).toEqual('Hello World');
});
it('should return error 404', function() {
expect(statusCode[1]).toBe(404);
});
});
Edited
When testing only one request I place done() inside the request() it works just fine, but I quite confuse where to place done() when using async.pararell()
spec/requestSpec.js
var request = require('request');
describe('expresss application', function() {
var baseUrl = 'http://localhost:3000';
var statusCode = 0;
beforeEach(function(done) {
request.get(baseUrl, function(err, res, body) {
statusCode = res.statusCode;
done();
});
});
it('should return 200', function() {
expect(statusCode).toBe(200);
});
});
In describe block you initiate variable body. And you use it in it blocks. But in request.get and in request.post you have callback function with parameter body which is in use instead of your describe body variable.
Change beforeEach to:
beforeEach(function(done) {
async.parallel([
function(callback) {
request.get(baseUrl, function(err, res, reqBody) {
statusCode[0] = res.statusCode;
body[0] = reqBody;
callback();
})
}
,
function(callback) {
request.post(baseUrl, function(err, res, reqBody) {
statusCode[1] = res.statusCode;
body[1] = reqBody;
callback();
})
}
], done);
});
I think that you should also check err param in request callbacks. Because there may be errors which fails/pass your tests.
For api endpoints tests it is more easy to use superagent or supertest instead of request.
I'm using koa and passport trying to implement middleware to prevent access to URIs when not authenticated.
var koa = require('koa');
var session = require('koa-generic-session');
var bodyParser = require('koa-bodyparser');
var koaRouter = require('koa-router');
var passport = require('koa-passport');
var views = require('co-views');
var render = views('.', { map: { html: 'swig' }});
var localStrategy = require('passport-local').Strategy;
var app = koa();
var router = koaRouter();
app.keys = ['secret'];
app.use(session());
app.use(bodyParser());
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
passport.use(new localStrategy(function(username, password, done) {
if (username === 'user1' && password === 'password2') {
done(null, { userId: 99, userName: 'redBallons' });
} else {
done(null, false);
}
}));
router.get('/login', function *(next) {
this.body = yield render('index.html');
});
router.post('/login', passport.authenticate('local', {
successRedirect: '/secretBankAccount',
failureRedirect: '/login'
}));
router.get('*', function *(next) {
if (! this.isAuthenticated()) {
console.log('not authenticated');
this.redirect('/login');
} else {
console.log('authenticated');
yield next;
}
});
router.get('/secretBankAccount', function *(next) {
this.body = '2 dollars';
});
app.use(router.routes());
app.listen(8080);
however, I can never get to my secretBankAccount. I can enter the correct user and password and can see the authenicated message, but the yield next in router.get('*') does not pass me through to the next routing function
When using koa-router it is expected that only one route is hit. So when you hit the '*' route it won't hit another route even if you yield next.
So you should replace the universal route with your own authentication middleware:
app.use(function*(next) {
if (this.isAuthenticated()) {
yield next
} else {
this.redirect('/login')
}
});
The authentication middleware will force you to do your routing with two routing objects instead of one. This is so you can distinguish between public and secured routes. So something like:
var public = new koaRouter();
public.get('/login', function *(next) {
this.body = yield render('index.html');
});
public.post('/login', passport.authenticate('local', {
successRedirect: '/secretBankAccount',
failureRedirect: '/login'
}));
app.use(public.routes());
app.use(function*(next) {
if (this.isAuthenticated()) {
yield next;
} else {
this.redirect('/login');
}
})
var secured = new koaRouter();
secured.get('/secretBankAccount', function *(next) {
this.body = '2 dollars';
});
app.use(secured.routes());
In the above example a request will first hit the public routing middleware. Then if it doesn't match the current request with a public route it will move onto the authentication middleware. If isAuthenticated() is false a redirect will occur. If isAuthenticated() is true it'll move onto the secured routing.
This approach is based on the koa-passport-example project which was created by the author of koa-passport.
Answer from peadar-doyle is the way to do this but needs updating to avoid the warning: koa deprecated Support for generators will be removed in v3.
Here's the updated version. I'm sending 401 instead of redirecting:
// all requests must now be authenticated
app.use(async (ctx, next) => {
if (ctx.isAuthenticated()) {
await next();
} else {
ctx.body = "access denied";
ctx.status = 401;
}
})
I have a Loopback model on which I am logging requests to keen.io using the afterRemote hook. http://docs.strongloop.com/display/public/LB/Remote+hooks#Remotehooks-ctx.result
I am also using the response-time package to add the response time header to the response. https://github.com/expressjs/response-time
This is working fine, expect I cannot figure out how to get to the X-Response-Time header in the response in order to log it to keen.io.
Can I get to the response headers in any way below?
module.exports = function(Studio) {
var isStatic = true;
var isNotStatic = false;
Studio.disableRemoteMethod('deleteById', isStatic); // DELETE /Studios/{id}
Studio.disableRemoteMethod('create', isStatic); // POST /Studios
Studio.disableRemoteMethod('upsert', isStatic); // PUT /Studios
Studio.disableRemoteMethod('updateAll', isStatic); // POST /Studios/update
Studio.disableRemoteMethod('updateAttributes', isNotStatic); // PUT /Studios/{id}
Studio.disableRemoteMethod('__create__ListenNps', isNotStatic);
Studio.disableRemoteMethod('__delete__ListenNps', isNotStatic);
Studio.disableRemoteMethod('__destroyById__ListenNps', isNotStatic);
Studio.disableRemoteMethod('__updateById__ListenNps', isNotStatic);
Studio.afterRemote('*', function(ctx, affectedModelInstance, next) {
var Keen = require('keen-js');
var client = new Keen({
projectId: "myid",
writeKey: "mykey"
});
var queryEvent = {
ip: ctx.req.ip,
baseUrl: ctx.req.baseUrl,
url: ctx.req.url,
route: ctx.req.route,
query: ctx.req.query,
method: ctx.methodString,
// response: ctx.result.???, What can I do here to get to the response headers? Specifically X-Response-Time
keen: {
timestamp: new Date().toISOString()
}
};
client.addEvent("queries", queryEvent, function(err, res) {
if (err) {
console.log(err)
} else {
console.log(res)
}
});
next();
});
};
Try to use ctx.res.getHeader('X-Response-Time') method
or
listen the res.on('finish') event.