UNPKG

bauhausjs

Version:
278 lines (238 loc) 9.64 kB
var debug = require('debug')('bauhaus:page'), Page = require('./model/page'), contentMiddleware = require('../content/middleware'), securityMiddleware = require('../security/middleware'); var middleware = module.exports = {}; function PageNotFoundError (route) { this.value = route; this.message = "Page was not found" } /** * Middleware that loads page from database an add it to req.bauhaus.page */ middleware.loadPage = function loadPage (req, res, next) { if (!req.bauhaus) req.bauhaus = {}; var route = req.path; var pathArray = route.split("/"); var lastPositionIndex = pathArray.length-1; var lastPosition = pathArray[lastPositionIndex]; var alternativeRoute = route.replace(lastPosition, ":id"); Page.findOne({ 'route': route }, "title label isSecure roles _type _model", function (err, page) { if (err || page === null){ // page not found. Try to find with parameter Page.findOne({ 'route': alternativeRoute }, "title label isSecure roles _type _model", function (err, page) { if (err || page === null){ return next(new PageNotFoundError(route)); // page not found 404 } else { req.params.id = lastPosition; // foward last position as parameter id req.bauhaus.page = page; debug('Loaded "' + page.title + '" (' + page._id + ') for route ' + route); next(); } }); } else{ req.bauhaus.page = page; debug('Loaded "' + page.title + '" (' + page._id + ') for route ' + route); next(); } }); }; middleware.checkAccess = function (req, res, next) { if (!req.bauhaus || !req.bauhaus.page) return next(); if (req.bauhaus.page.isSecure !== true) { // page is not secured, let request pass return next(); } if (!req.session.user) { debug("request page is secured, but no user is authorized"); res.status(401) return next("Unauthorized"); } if (req.bauhaus.page.roles && req.bauhaus.page.roles.length > 0) { debug("Page accessible only with any of the roles", req.bauhaus.page.roles); if (!req.session.user.roleIds || req.session.user.roleIds.length === 0) { res.status(403); return next("Forbidden"); } var userRoles = req.session.user.roleIds; for (var r in req.bauhaus.page.roles) { var pageRoleId = req.bauhaus.page.roles[r].toString(); if (userRoles.indexOf(pageRoleId) !== -1) { debug("User has role, let pass", pageRoleId); return next(); } } debug("User did not match any of the roles, reject"); res.status(403); return next("Forbidden"); } else { // no roles defined and user authorized -> let pass return next(); } }; /** * Returns middleware function which adds page type * matching loaded page to req.bauhaus.pageType * @param {Array} pageTypes Array of pageTypes, initialize with service page.types * @return {Function} Middleware */ middleware.loadPageType = function (pageTypes) { return function loadPageType (req, res, next) { if (!req.bauhaus || !req.bauhaus.page) return next(); var type = req.bauhaus.page._type; req.bauhaus.pageType = pageTypes[type]; debug('Loaded page type "' + type + '"'); next(); }; }; middleware.loadNavigation = function (req, res, next) { req.bauhaus.navigation = {}; var query = { parentId: null }; // Method recursively iterates over items and adds `isActive` and // `hasActiveChild` fields function parseNavItem (item) { if (item.route === req.path) { item.isActive = true; } // replace object by array structure var childArray = []; for (var child in item.children) { var subItem = parseNavItem( item.children[ child ] ); if (subItem.isActive || subItem.hasActiveChildren) { item.hasActiveChildren = true; } // check if user can access item if (userHasAccess(subItem)) { childArray.push(subItem); } } // sort by _w childArray.sort(function (a, b) { return a._w - b._w }); item.children = childArray; return item; } // Returns true if user is allowed to see page function userHasAccess (item) { if (typeof item.isSecure !== 'boolean' || item.isSecure === false) { // page has no security settings or false, let user pass return true; } else { // page is secured, check if user has access var pageRoles = item.roles; debug('pageRoles', item.title, pageRoles) if (Array.isArray(pageRoles) && pageRoles.length === 0) { // page is secured, but requires no specific role if (req.session.user) { // user is authenticated, let pass return true; } else { // user is not authenticated, reject return false } } if (!req.session.user || !req.session.user.roleIds) { // user session does not exist, skip return false; } // make sure roleIds are strings (on first request they are objects) var userRoles = []; var sessionRoleIds = req.session.user.roleIds; for (var r in sessionRoleIds) { userRoles.push(sessionRoleIds[r].toString()); } // compare required roles and user roles for (var r in pageRoles) { var pageRoleId = pageRoles[r].toString(); if (userRoles.indexOf(pageRoleId) !== -1) { // user has role, let pass return true; } } } // all pass rules failed, reject user return false; } Page.findOne(query, function (err, doc) { doc.getTree({ condition: { public: true }, fields: { route: 1, title: 1, label: 1, isSecure: 1, roles: 1, _w: 1 } }, { fields: { route: 1, title: 1, label: 1, path: 1, id: 1, parentId: 1, isSecure: 1, roles: 1, _w: 1 }, condition: { public: true } }, function (err, tree) { req.bauhaus.navigation.main = []; for (var id in tree) { if (tree[ id ].parentId === null) { for (var child in tree[ id ].children) { if (userHasAccess(tree[ id ].children[ child ])) { req.bauhaus.navigation.main.push( parseNavItem( tree[ id ].children[ child ] ) ); } } } } // sort by _w req.bauhaus.navigation.main.sort(function (a, b) { return a._w - b._w }); debug('Loaded navigation'); next(); }); }); }; /** * Middleware which concats rendered content to slots, according to page type definition * and exposes them as object at req.bauhaus.slots with slotname as key and html string as value */ middleware.renderSlots = function renderSlots (req, res, next) { if (!req.bauhaus.pageType.slots) return next(); req.bauhaus.slots = {}; var slotNameMap = []; req.bauhaus.pageType.slots.forEach(function (element, index) { slotNameMap[index] = element.name; }); // Order content by slot and position var renderedSlots = {}; if (!req.bauhaus.content) return next(); req.bauhaus.content.data.forEach(function (content, index) { var slot = content.meta.slot ? content.meta.slot : 0; var position = content.meta.position ? content.meta.position : 0; if (renderedSlots[ slot ] === undefined) renderedSlots[ slot ] = []; renderedSlots[ slot ][ position ] = req.bauhaus.content.rendered[index]; }); // Join each slot to single string slotNameMap.forEach(function (name, index) { req.bauhaus.slots[ name ] = renderedSlots[ index ].join("\n"); debug('Rendered slot "' + name + '"'); }); next(); }; /** * Middleware which renders page. It uses the template as defined in req.pageType.template. * The template receives the req.bauhaus object. */ middleware.renderPage = function renderPage (req, res, next) { var template = req.bauhaus.pageType.template; var data = req.bauhaus; debug('Render and send page'); res.render(template, data); }; /** * Middleware error handler which is added to end of render stack and resolves error which occured * during rendering (e.g. page not found) and contiues on the express middleware stack without error. */ middleware.errorHandler = function errorHandler (err, req, res, next) { debug('Error occured', err); next(); }; middleware.renderStack = function (pageTypes, contentTypes) { return [ middleware.loadPage, middleware.checkAccess, middleware.loadPageType(pageTypes), middleware.loadNavigation, contentMiddleware.loadContent(contentTypes), contentMiddleware.renderContent(contentTypes), middleware.renderSlots, securityMiddleware.addUserToRender, middleware.renderPage, middleware.errorHandler ]; };