naturalatlas / tilestrata

A pluggable Node.js map tile server.
Apache License 2.0
428 stars 42 forks source link

express and middleware on route. #8

Closed kozer closed 9 years ago

kozer commented 9 years ago

I have write a middleware for auth user.I want tiles not to be served if user is not authed (some of them). I am struggled to find a way to do this.If i use mapnik as it is, i know how to do this, before my route function i put my middleware and its all ok. But how i can do the same thing in tilestrata? I ve seen the reqhook, but i cant understand how to do this.

Can anyone help me? Thanks a lot.

brianreavis commented 9 years ago

Assuming your express middleware adds req.user you can use something like:

var checkauth = {
        reqhook: function(server, tile, req, res, callback) {
            if (!req.user) {
                res.writeHead(401, {'Content-Type': 'text/plain'});
                return res.end('Unauthorized user');
            }
            callback();
        }
};

Then for each TileStrata route that you want to prevent access to, add:

.use(checkauth)

If you share more about what your setup looks like in code I can try to explain more fully.

kozer commented 9 years ago

Yes but how i can add it for this particular route.Eg to get a user i have something like this

router.get('/:id', auth.isAuthenticated(), controller.show);

the auth.isAuthenticated is a middleware. How i can add it to this example?:

strata.layer('basemap')
    .route('tile.png')
        .use(disk.cache({dir: '/var/lib/tiles/basemap'}))
        .use(dependency('basemap', 'tile@2x.png'))
        .use(sharp(function(image, sharp) {
            return image.resize(256);
        }));

If i add it like this

strata.layer('basemap')
    .route('tile.png')
         .use( auth.isAuthenticated())
        .use(disk.cache({dir: '/var/lib/tiles/basemap'}))
        .use(dependency('basemap', 'tile@2x.png'))
        .use(sharp(function(image, sharp) {
            return image.resize(256);
        }));

i get an error. where i must put it?

brianreavis commented 9 years ago

TileStrata does not support express/connect middleware in its routes natively. It only includes an adapter that makes TileStrata act as middleware to allow easy integration into an existing app.

That said, what you're wanting is achievable with an adapter like so:

var expressAdapter = function(middleware) {
    return {reqhook: function(server, tile, req, res, callback) {
        middleware(req, res, callback);
    }};
};

Then use it like this:

strata.layer('basemap')
    .route('tile.png')
        .use(expressAdapter(auth.isAuthenticated()))
        .use(disk.cache({dir: '/var/lib/tiles/basemap'}))
        .use(dependency('basemap', 'tile@2x.png'))
        .use(sharp(function(image, sharp) {
            return image.resize(256);
        }));
kozer commented 9 years ago

I thought it would be something like that...Thanks a lot for your support.I ll try and report tomorrow.Thank you very much!

kozer commented 9 years ago

Well i get no results.The complete code is the following: I have an adapter:

var auth = require('./auth');

module.exports = function() {
    return {
        name: 'mapProtector',
        reqhook: function(server, tile, req, res, callback) {
              auth.isAuthenticated(req,res,callback)
        }
    };
};

and is authenticated method is this:

var tokenManager = require('./tokenManager/tokenManager');
var chain = require('chain-middleware');
var expressJwt = require('express-jwt');
var validateJwt = expressJwt({
    secret: config.secrets.session
});

function isAuthenticated() {
    return chain(function(req, res, next) {
        if (req.query && req.query.hasOwnProperty('token')) {
            req.headers.authorization = 'Bearer ' + req.query.token;
        }
        validateJwt(req, res, next);
    }
    , tokenManager.verifyToken
    , function(req, res, next) {
        User.find({
                where: {
                    id: req.user.id
                }
            })
            .then(function(user) {
                if (!user) {
                    return res.boom.unauthorized('Unauthorized');
                }
                user.removePassword();
                req.user = user;
                next();
            })
            .catch(function(err) {
                return res.boom.badImplementation();
            });
    })
}

but i get nothing. no response at all. Any ideas?

brianreavis commented 9 years ago

At a glance that all looks correct – assuming there's a module.exports = isAuthenticated in the second file that didn't get included in the copy/paste. Have you tried debugging to see how far it gets in the flow? E.g:

var auth = require('./auth');

module.exports = function() {
    return {
        name: 'mapProtector',
        reqhook: function(server, tile, req, res, callback) {
              console.log('mapProtector begin');
              auth.isAuthenticated(req,res,function(err) {
                    console.log('mapProtector end', err);
                    callback(err);
              });
        }
    };
};
kozer commented 9 years ago

It never gets in chain middleware. 'mapProtector end' never called....

brianreavis commented 9 years ago

Your isAuthenticated() function returns the middleware. Are you sure you're not meaning this?

var auth = require('./auth');
var isAuthenticated = auth.isAuthenticated();

module.exports = function() {
    return {
        name: 'mapProtector',
        reqhook: function(server, tile, req, res, callback) {
              console.log('mapProtector begin');
              isAuthenticated(req,res,function(err) {
                    console.log('mapProtector end', err);
                    callback(err);
              });
        }
    };
};
kozer commented 9 years ago

Thank god, that was it!!! THANK YOU VERY MUCH @brianreavis ! Thanks for your time and effort!

brianreavis commented 9 years ago

Awesome! Glad to hear that did it