forked from SmallhillCZ/express-dynacl
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
148 lines (103 loc) · 3.85 KB
/
index.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
var dynacl = (function(){
// initiate variable to store options
var options = {};
// default options to be overriden by dynacl.config
const defaultOptions = {
roles: {},
userRoles: req => req.user ? req.user.roles || [] : [],
defaultRole: "guest",
logString: (event) => `DynACL ${event.permission ? "OK" : "XX"} (action: ${event.action}${event.role ? ", role: " + event.role : ""}${Object.keys(event.params) > 0 ? ", params: " + JSON.stringify(event.params) : ""})`,
logConsole: false,
authorized: (req,res,next) => next(),
unauthorized: (req,res,next) => res.sendStatus(401)
}
// function to get user roles and evaluate permissions
async function checkCan(action,req,params){
// get user roles
var userRoles = options.userRoles(req)
// add default role
if(options.defaultRole) userRoles.push(options.defaultRole);
// default is no permission
var permission = false;
// go through all roles and check if some has permission
var roleName;
while(!!(roleName = userRoles.shift())){
// wait for the result
let result = await checkRoleCan(roleName,action,req,params);
// if permitted, save and stop going through the roles
if(result === true) {
permission = true;
break;
}
}
// log permission check
if(options.logConsole){
let logEvent = {
action:action,
permission:permission,
role:roleName,
req:req,
params:params
};
let logString = options.logString(logEvent);
if(permission) console.log(logString);
else console.error(logString);
}
// return the permission
return permission;
}
async function checkRoleCan(roleName,action,req,params){
// get the role details
let role = options.roles[roleName];
// if role does not exist user can't
if(!role) return false;
// in case we have admin role, we dont have to check anything
if(role.admin) return true;
// in case we have set permission for resource and action
if(role.can[action]){
let permission = role.can[action];
// if permission is a function, then evaluate its return value, otherwise evaluate the permission,
// both Promise and static value will be resolved
let result = await Promise.resolve(typeof permission === 'function' ? permission(req,params) : permission);
if(result) return true;
}
// in case the role inherits, we check the parent role
if(role.inherits){
for ( var i =0;i < role.inherits.length; i++){
//check the inherited role
let result = await checkRoleCan(options.roles[role.inherits[i]],action,req,params);
// terminate and return true if approved
if(result) return true;
}
}
return false;
}
// middleware factory
function dynacl(action,paramsFn){
// return middleware function for ExpressJS
return async function(req,res,next){
// if function to evaluate req to params provided, then use it
var params = paramsFn ? paramsFn(req) : {};
// evaluate permission
var result = await checkCan(action,req,params);
// if permission granted, send execution to the next middleware/route
if(result) options.authorized(req,res,next);
// if permission not granted, then end request with 401 Unauthorized
else options.unauthorized(req,res,next)
}
}
// function to configure DynACL
dynacl.config = function(userOptions){
// assign configurations
options = Object.assign({},defaultOptions,userOptions);
}
// just check the permission without using as middleware
dynacl.can = (action,req,params) => checkCan(action,req,params || {});
return dynacl;
})();
if(require.main === module){
require("./index.cli.js");
}
else{
module.exports = dynacl;
}