UNPKG

expacl

Version:

Express Access Control List middleware

94 lines (93 loc) 4.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var middleware = function (opts) { var MAX_SUB_VARIANTS = 20; var route2parsed = function (route) { var splittedPart = (typeof route.path === 'string') ? route.path.split("/").filter(function (p) { return p.length > 0; }) : [route.path]; return { path: splittedPart, pathLen: splittedPart.length, roles: route.roles ? ((Array.isArray(route.roles) ? route.roles : [route.roles])) : ['*'], methods: route.methods ? (Array.isArray(route.methods) ? route.methods.map(function (m) { return m.toLowerCase(); }) : [route.methods.toLowerCase()]) : ['*'], action: route.action || opts.defaultAction || 'allow', }; }; var parsedRouteSubVariants = function (route) { var arr = [route] .concat(new Array(MAX_SUB_VARIANTS) .fill(undefined) .map(function (_, idx) { return Object.assign({}, route, { path: route.path.concat(new Array(idx + 1).fill('*')), pathLen: route.pathLen + idx + 1 }); })); return arr; }; var parseRoute = function (route) { var parsedRoute = route2parsed(route); if (route.subroutes && route.subroutes.length > 0) { if (parsedRoute.path[parsedRoute.pathLen - 1] === '*') { throw new Error('*' + " (any) path route should not have subroutes"); } var subroutes = parseRoutes(route.subroutes); return subroutes.map(function (r) { return { path: parsedRoute.path.concat(r.path), pathLen: parsedRoute.pathLen + r.pathLen, roles: r.roles, methods: r.methods, action: r.action, }; }).concat(route.transient ? [] : [parsedRoute]); } else { return route.transient ? [] : ((parsedRoute.path[parsedRoute.pathLen - 1] === '*') ? parsedRouteSubVariants(parsedRoute) : [parsedRoute]); } }; var parseRoutes = function (routes) { return routes .map(function (route) { return parseRoute(route); }) .reduce(function (acc, val) { return acc.concat(val); }, []); }; var routes = parseRoutes(opts.routes); var _middleware = function (req, res, next) { var path = (opts.resource ? opts.resource(req) : req.url).split('/').filter(function (p) { return p.length > 0; }); var pathLen = path.length; var method = req.method.toLowerCase(); var notAllowed = function () { var authenticated = opts.authenticated || (function (req) { return !!req.user; }); if (authenticated(req)) { return (opts.onNotAuthorized) ? opts.onNotAuthorized(req, res, next) : res.status(403).send("403 Not authorized"); } else { return (opts.onNotAuthenticated) ? opts.onNotAuthenticated(req, res, next) : res.status(401).send("401 Not authenticated"); } }; var notFound = function () { return (opts.onNotFound) ? opts.onNotFound(req, res, next) : res.status(404).send("404 Not found"); }; var route = routes.find(function (r) { var shallowCheck = (r.pathLen === pathLen) && (r.methods.includes('*') || r.methods.includes(method)); if (!shallowCheck) { return false; } var pathCheck = r.path.reduce(function (acc, p, idx) { return acc && ((typeof p === 'string') ? ((p === '*') || (p === path[idx])) : (p.test(path[idx]))); }, true); return pathCheck; }); if (!route) { return (opts.missingRoute != 'allow') ? notFound() : next(); } if (route.roles.includes('*')) { return next(); } var roles = (opts.roles ? opts.roles(req) : (req.user ? req.user.roles : undefined)) || []; var roleCheck = roles.findIndex(function (r) { return route.roles.includes(r); }); if (roleCheck === -1) { notAllowed(); } return next(); }; return _middleware; }; exports.middleware = middleware;