var debug = require('debug')('crowi:lib:middlewares'); var md5 = require('md5'); exports.csrfKeyGenerator = function(crowi, app) { return function(req, res, next) { var csrfKey = (req.session && req.session.id) || 'anon'; if (req.csrfToken === null) { req.csrfToken = crowi.getTokens().create(csrfKey); } next(); } } exports.loginChecker = function(crowi, app) { return function(req, res, next) { var User = crowi.model('User'); // session に user object が入ってる if (req.session.user && '_id' in req.session.user) { User.findById(req.session.user._id, function(err, userData) { if (err) { next(); } else { req.user = req.session.user = userData; res.locals.user = req.user; next(); } }); } else { req.user = req.session.user = false; res.locals.user = req.user; next(); } }; }; exports.loginCheckerForPassport = function(crowi, app) { return function(req, res, next) { if (req.user == null) { req.user = false; } res.locals.user = req.user; next(); }; }; exports.csrfVerify = function(crowi, app) { return function(req, res, next) { var token = req.body._csrf || req.query._csrf || null; var csrfKey = (req.session && req.session.id) || 'anon'; debug('req.skipCsrfVerify', req.skipCsrfVerify); if (req.skipCsrfVerify) { debug('csrf verify skipped'); return next(); } if (crowi.getTokens().verify(csrfKey, token)) { debug('csrf successfully verified'); return next(); } debug('csrf verification failed. return 403', csrfKey, token); return res.sendStatus(403); }; }; exports.swigFunctions = function(crowi, app) { return function(req, res, next) { require('../util/swigFunctions')(crowi, app, req, res.locals); next(); }; }; exports.swigFilters = function(app, swig) { // define a function for Gravatar const generateGravatarSrc = function(user) { const email = user.email || ''; const hash = md5(email.trim().toLowerCase()); return `https://gravatar.com/avatar/${hash}`; }; // define a function for uploaded picture const getUploadedPictureSrc = function(user) { if (user.image) { return user.image; } else { return '/images/userpicture.png'; } }; return function(req, res, next) { swig.setFilter('path2name', function(string) { var name = string.replace(/(\/)$/, ''); if (name.match(/.+\/([^/]+\/\d{4}\/\d{2}\/\d{2})$/)) { // /.../hoge/YYYY/MM/DD 形式のページ return name.replace(/.+\/([^/]+\/\d{4}\/\d{2}\/\d{2})$/, '$1'); } if (name.match(/.+\/([^/]+\/\d{4}\/\d{2})$/)) { // /.../hoge/YYYY/MM 形式のページ return name.replace(/.+\/([^/]+\/\d{4}\/\d{2})$/, '$1'); } if (name.match(/.+\/([^/]+\/\d{4})$/)) { // /.../hoge/YYYY 形式のページ return name.replace(/.+\/([^/]+\/\d{4})$/, '$1'); } return name.replace(/.+\/(.+)?$/, '$1'); // ページの末尾を拾う }); swig.setFilter('normalizeDateInPath', function(path) { var patterns = [ [/20(\d{2})(\d{2})(\d{2})(.+)/g, '20$1/$2/$3/$4'], [/20(\d{2})(\d{2})(\d{2})/g, '20$1/$2/$3'], [/20(\d{2})(\d{2})(.+)/g, '20$1/$2/$3'], [/20(\d{2})(\d{2})/g, '20$1/$2'], [/20(\d{2})_(\d{1,2})_(\d{1,2})_?(.+)/g, '20$1/$2/$3/$4'], [/20(\d{2})_(\d{1,2})_(\d{1,2})/g, '20$1/$2/$3'], [/20(\d{2})_(\d{1,2})_?(.+)/g, '20$1/$2/$3'], [/20(\d{2})_(\d{1,2})/g, '20$1/$2'], ]; for (var i = 0; i < patterns.length ; i++) { var mat = patterns[i][0]; var rep = patterns[i][1]; if (path.match(mat)) { return path.replace(mat, rep); } } return path; }); swig.setFilter('datetz', function(input, format) { // timezone var swigFilters = require('swig-templates/lib/filters'); return swigFilters.date(input, format, app.get('tzoffset')); }); swig.setFilter('nl2br', function(string) { return string .replace(/\n/g, '
'); }); swig.setFilter('removeLastSlash', function(string) { if (string == '/') { return string; } return string.substr(0, string.length - 1); }); swig.setFilter('presentation', function(string) { // 手抜き return string .replace(/[\n]+#/g, '\n\n\n#') .replace(/\s(https?.+(jpe?g|png|gif))\s/, '\n\n\n![]($1)\n\n\n'); }); swig.setFilter('gravatar', generateGravatarSrc); swig.setFilter('uploadedpicture', getUploadedPictureSrc); swig.setFilter('picture', function(user) { if (!user) { return '/images/userpicture.png'; } if (user.isGravatarEnabled === true) { return generateGravatarSrc(user); } else { return getUploadedPictureSrc(user); } }); next(); }; }; exports.adminRequired = function() { return function(req, res, next) { if (req.user && '_id' in req.user) { if (req.user.admin) { next(); return; } return res.redirect('/'); } return res.redirect('/login'); }; }; /** * require login handler * * @param {any} crowi * @param {any} app * @param {boolean} isStrictly whethere strictly restricted (default true) */ exports.loginRequired = function(crowi, app, isStrictly = true) { return function(req, res, next) { var User = crowi.model('User') // when the route is not strictly restricted if (!isStrictly) { var config = req.config; var Config = crowi.model('Config'); // when allowed to read if (Config.isGuesstAllowedToRead(config)) { return next(); } } if (req.user && '_id' in req.user) { if (req.user.status === User.STATUS_ACTIVE) { // Active の人だけ先に進める return next(); } else if (req.user.status === User.STATUS_REGISTERED) { return res.redirect('/login/error/registered'); } else if (req.user.status === User.STATUS_SUSPENDED) { return res.redirect('/login/error/suspended'); } else if (req.user.status === User.STATUS_INVITED) { return res.redirect('/login/invited'); } } // is api path var path = req.path || ''; if (path.match(/^\/_api\/.+$/)) { return res.sendStatus(403); } req.session.jumpTo = req.originalUrl; return res.redirect('/login'); }; }; exports.accessTokenParser = function(crowi, app) { return function(req, res, next) { // TODO: comply HTTP header of RFC6750 / Authorization: Bearer var accessToken = req.query.access_token || req.body.access_token || null; if (!accessToken) { return next(); } var User = crowi.model('User') debug('accessToken is', accessToken); User.findUserByApiToken(accessToken) .then(function(userData) { req.user = userData; req.skipCsrfVerify = true; debug('Access token parsed: skipCsrfVerify'); next(); }).catch(function(err) { next(); }); }; }; // this is for Installer exports.applicationNotInstalled = function() { return function(req, res, next) { var config = req.config; if (Object.keys(config.crowi).length !== 1) { req.flash('errorMessage', 'Application already installed.'); return res.redirect('admin'); // admin以外はadminRequiredで'/'にリダイレクトされる } return next(); }; }; exports.checkSearchIndicesGenerated = function(crowi, app) { return function(req, res, next) { const searcher = crowi.getSearcher(); // build index if (searcher) { searcher.buildIndex() .then((data) => { if (!data.errors) { debug('Index created.'); } return searcher.addAllPages(); }) .catch((error) => { if (error.message != null && error.message.match(/index_already_exists_exception/)) { debug('Creating index is failed: ', error.message); } else { console.log(`Error while building index of Elasticsearch': ${error.message}`); } }); } return next(); }; } exports.applicationInstalled = function() { return function(req, res, next) { var config = req.config; if (Object.keys(config.crowi).length === 1) { // app:url is set by process return res.redirect('/installer'); } return next(); }; }; exports.awsEnabled = function() { return function (req, res, next) { var config = req.config; if (config.crowi['aws:region'] !== '' && config.crowi['aws:bucket'] !== '' && config.crowi['aws:accessKeyId'] !== '' && config.crowi['aws:secretAccessKey'] !== '') { req.flash('globalError', 'AWS settings required to use this function. Please ask the administrator.'); return res.redirect('/'); } return next(); }; };