-
Notifications
You must be signed in to change notification settings - Fork 1
/
access.js
152 lines (121 loc) · 4.27 KB
/
access.js
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
142
143
144
145
146
147
148
149
150
/**
* Module dependencies.
*/
var merge = require('utils-merge');
var wildcard = require('wildcard');
// access function
function access(options) {
var rules = [];
// access control middleware
function middleware(options) {
options = options || {};
var permissionProperty = options.permissionProperty || access.permissionProperty || 'userPermission';
return function accessMiddleware(req, res, next) {
// variables
var method = req.method;
var path = req.originalUrl.split(/[?#]/)[0];
var userPermission = req[permissionProperty];
// res.locals userCan property for things like templates.
req.userCan = function(permission) {
return !hasPermission(userPermission, permission);
}
//method: wildcard(req, def)
//path: wildcard(def, req)
//permission: wildcard(def, req)
// procedure description:
// iterate over rules, check if:
// 1. the method/path matches and if so,
// 2. check if the permission matches: yes: next(), no: call next(403)
if (!rules || rules.length === 0) return next();
var i;
for (i = 0; i < rules.length; i++) {
var rule = rules[i];
// check rule arguments:
if (!rule.length || rule.length < 3 || rule.length > 4) {
return next(new TypeError('wrong access rule definition. must have 3 or 4 arguments.'));
}
// check method (1.argument)
if (typeof rule[0] === 'string') rule[0] = rule[0].split(/[,; ]/);
if (!wildcard(method, rule[0], /[,; ]/) && rule[0] !== '*') continue; // with next rule
// path (2.argument)
if (!wildcard(rule[1], path, /\//)) continue; // with next rule
// where (4.argument) optional
if (rule.length > 3) {
return next(new TypeError('access rule `where` argument 4, is not yet implemented'));
}
// permission (3.argument)
// so method and path matched, now the permission has to match to, otherwise access is denied -> 403
return next(hasPermission(userPermission, rule[2]));
}
// no matching access definition
return next(403);
}
}
// single route middleware
function restrict(routePermission) {
var permissionProperty = access.permissionProperty || 'userPermission';
return function allowRoute(req, res, next) {
next(hasPermission(req[permissionProperty], routePermission));
}
}
// general permission query function
function hasPermission(userPermission, routePermission) {
// permission (3.argument)
// so method and path matched, now the permission has to match to, otherwise access is denied -> 403
if (routePermission === '*') return undefined; // with next rule
if (!userPermission) return 403;
if (typeof routePermission === 'string') {
routePermission = routePermission.split(/[,; ]/)
}
if (!Array.isArray(routePermission)) {
return new TypeError('wrong permission format: ' + routePermission);
}
var allow = routePermission.some(function(p) {
if (p === '*') return true;
if (userPermission[p]) return true;
if (userPermission[p.split(':')[0]]) return true;
return false;
});
if (!allow) return 403;
return undefined;
}
// adding rules function
access = function access() {
var args = [].slice.call(arguments);
if (args.length === 1 && Array.isArray(args[0])) {
var entries = args[0];
var multiple = entries.every(function(entry) {
return Array.isArray(entry);
})
if (multiple) {
// multiple rules provided via array
rules = entries;
} else {
// single rule provided via array
rules.push(args[0]);
}
} else {
// single rule provided via parameters
rules.push(args)
}
}
access.rules = rules;
access.middleware = middleware;
access.restrict = restrict;
//// when this function is called the first time: save options / or rules
//if (options) {
// if (typeof options === 'object' && !Array.isArray(options)) {
// // save options
// merge(access, options);
// } else {
// // add rules
// var args = [].slice.call(arguments);
// access.apply(null, args);
// }
//}
return access;
}
/**
* expose access
*/
module.exports = access;