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
|
local array = require "util.array";
local it = require "util.iterators";
local new_short_id = require "util.id".short;
local role_methods = {};
local role_mt = {
__index = role_methods;
__name = "role";
__add = nil;
};
local function is_role(o)
local mt = getmetatable(o);
return mt == role_mt;
end
local function _new_may(permissions, inherited_mays)
local n_inherited = inherited_mays and #inherited_mays;
return function (role, action, context)
-- Note: 'role' may be a descendent role, not only the one we're attached to
local policy = permissions[action];
if policy ~= nil then
return policy;
end
if n_inherited then
for i = 1, n_inherited do
policy = inherited_mays[i](role, action, context);
if policy ~= nil then
return policy;
end
end
end
return nil;
end
end
local permissions_key = {};
-- {
-- Required:
-- name = "My fancy role";
--
-- Optional:
-- inherits = { role_obj... }
-- default = true
-- priority = 100
-- permissions = {
-- ["foo"] = true; -- allow
-- ["bar"] = false; -- deny
-- }
-- }
local function new(base_config, overrides)
local config = setmetatable(overrides or {}, { __index = base_config });
local permissions = {};
local inherited_mays;
if config.inherits then
inherited_mays = array.pluck(config.inherits, "may");
end
local new_role = {
id = new_short_id();
name = config.name;
description = config.description;
default = config.default;
priority = config.priority;
may = _new_may(permissions, inherited_mays);
inherits = config.inherits;
[permissions_key] = permissions;
};
local desired_permissions = config.permissions or config[permissions_key];
for k, v in pairs(desired_permissions or {}) do
permissions[k] = v;
end
return setmetatable(new_role, role_mt);
end
function role_methods:clone(overrides)
return new(self, overrides);
end
function role_methods:set_permission(permission_name, policy, overwrite)
local permissions = self[permissions_key];
if overwrite ~= true and permissions[permission_name] ~= nil and permissions[permission_name] ~= policy then
return false, "policy-already-exists";
end
permissions[permission_name] = policy;
return true;
end
function role_methods:policies()
local policy_iterator, s, v = it.join(pairs(self[permissions_key]));
if self.inherits then
for _, inherited_role in ipairs(self.inherits) do
policy_iterator:append(inherited_role:policies());
end
end
return policy_iterator, s, v;
end
function role_mt.__tostring(self)
return ("role<[%s] %s>"):format(self.id or "nil", self.name or "[no name]");
end
function role_mt.__pairs(self)
return it.filter(permissions_key, next, self);
end
return {
is_role = is_role;
new = new;
};
|