1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 | 1× 1× 1× 1× 16× 16× 16× 16× 16× 16× 16× 16× 16× 16× 16× 16× 9× 9× 7× 1× 1× 1× 1× 1× 1× 1× 1× 1× 1× 1× 16× 18× 18× 18× 18× 1× 1× 17× 17× 17× 17× 1× 16× 16× 16× 16× 16× 16× 1× 16× 9× 16× 16× 1× 4× 4× 2× 4× 1× 4× 1× 4× 1× 4× 2× 4× 2× 4× 1× 4× 1× 4× 10× 1× | 'use strict'; const miniquery = require('miniquery'); const regexpTpl = require('regexp-tpl'); const debug = require('debug')('reacess'); function reaccess(options) { options = options || {}; options.rightsProps = options.rightsProps || ['_rights']; options.valuesProps = options.valuesProps || []; options.errorConstructor = options.errorConstructor || Error; options.accessErrorMessage = options.accessErrorMessage || 'Unauthorized access!'; return function reaccessMiddleware(req, res, next) { let rights; let values; debug('Checking access for:"', req.path); // Getting rights rights = reaccess.getRightsFromReq(options.rightsProps, req); // Getting values to fill rights path templates values = reaccess.getValuesFromReq(options.valuesProps, req); if(reaccess.test(rights, values, req.method, req.path)) { next(); return; } next(new options.errorConstructor(options.accessErrorMessage)); // eslint-disable-line }; } // Static properties reaccess.OPTIONS = 1; reaccess.HEAD = 2; reaccess.GET = 4; reaccess.POST = 8; reaccess.PUT = 16; reaccess.PATCH = 32; reaccess.DELETE = 64; reaccess.READ_MASK = reaccess.OPTIONS | reaccess.HEAD | reaccess.GET; reaccess.WRITE_MASK = reaccess.POST | reaccess.PUT | reaccess.PATCH | reaccess.DELETE; reaccess.ALL_MASK = reaccess.READ_MASK | reaccess.WRITE_MASK; // Static methods reaccess.test = function reaccessTest(rights, values, method, path) { return rights.some((right) => { let result = false; let regExp; debug('Evaluating right:"', right); if(!('undefined' !== typeof right.methods && 'undefined' !== typeof right.path && right.methods & reaccess[method.toUpperCase()])) { debug('Method "' + method + '" do not match methods:"', right.methods); return false; } regExp = regexpTpl( values, '^' + right.path + '$', '', /(.*\/|\^):([a-z0-9_\-\.\*\@\#]+)(\/.*|\$)/ ); result = regExp && regExp.test(path); debug( 'Testing : /^' + right.path.replace('/', '\\/') + '$/"' + ' on "' + path + '" led to ' + (result ? 'SUCCESS' : 'FAILURE') ); return result; }); }; reaccess.getRightsFromReq = function reaccessGetRightsFromReq(rightsProps, req) { const rights = rightsProps.reduce((rights, prop) => { return miniquery(prop, [req]).reduce((finalRights, currentRights) => { return finalRights.concat(currentRights); }, []); }, []); debug( 'Rights properties "' + rightsProps.join(',') + '"' + ' has been resolved to:', rights ); Iif(!(rights && rights instanceof Array)) { throw new Error('The rights property must be an array.'); } return rights; }; reaccess.getValuesFromReq = function reaccessGetValuesFromReq(valuesProps, req) { const values = (valuesProps || []).reduce((values, prop) => { return values.concat(miniquery(prop, [req])); }, []); debug( 'Values properties "' + valuesProps.join(',') + '" has been resolved to:', values ); return values; }; reaccess.methodsAsStrings = function reaccessMethodsAsStrings(methods) { const strings = []; if(methods & reaccess.OPTIONS) { strings.push('OPTIONS'); } if(methods & reaccess.HEAD) { strings.push('HEAD'); } if(methods & reaccess.GET) { strings.push('GET'); } if(methods & reaccess.POST) { strings.push('POST'); } if(methods & reaccess.PUT) { strings.push('PUT'); } if(methods & reaccess.PATCH) { strings.push('PATCH'); } if(methods & reaccess.DELETE) { strings.push('DELETE'); } return strings; }; reaccess.stringsToMethods = function reaccessStringsToMethods(strings) { return strings.reduce((methods, str) => { return methods | ('number' === typeof reaccess[str.toUpperCase()] ? reaccess[str.toUpperCase()] : 0); }, 0); }; module.exports = reaccess; |