summaryrefslogtreecommitdiffstats
path: root/aconf/model/node.lua
diff options
context:
space:
mode:
Diffstat (limited to 'aconf/model/node.lua')
-rw-r--r--aconf/model/node.lua96
1 files changed, 68 insertions, 28 deletions
diff --git a/aconf/model/node.lua b/aconf/model/node.lua
index 92f3a1f..50edac6 100644
--- a/aconf/model/node.lua
+++ b/aconf/model/node.lua
@@ -40,6 +40,7 @@ function M.BoundMember:init(parent, name, field)
field,
{
txn=pmt.txn,
+ privileged=pmt.privileged,
parent=parent,
path=pth.join(pmt.path, name),
addr=pth.to_absolute(addr, pmt.addr)
@@ -66,13 +67,18 @@ function M.TreeNode:init(context, params)
mt.name = pth.name(mt.path)
mt.__eq = equal_tns
+ if not (mt.txn and mt.txn.user) then mt.privileged = true end
+ mt.escalate = mt.privileged and self or mt.class(
+ setdefaults({privileged=true}, context), params
+ )
+
function mt.get(k, options) return mt.load(k, options) end
function mt.fetch(path, create)
if type(path) == 'string' then
if pth.is_absolute(path) and mt.path > '/' then
assert(not create)
- return mt.txn:fetch(path)
+ return mt.txn:fetch(path, mt.privileged)
end
path = pth.split(path)
end
@@ -111,6 +117,26 @@ function M.TreeNode:init(context, params)
return getmetatable(next).fetch(path, create)
end
+ function mt.has_permission(permission)
+ if mt.privileged then return true end
+
+ local p = permission..mt.path
+ if getmetatable(mt.escalate).fetch('/auth/permissions')[p] then
+ return mt.txn.user:check_permission(p)
+ end
+
+ if ({create=true, delete=true})[permission] then
+ permission = 'modify'
+ end
+ return getmetatable(mt.parent).has_permission(permission)
+ end
+
+ function mt.check_permission(permission)
+ if not mt.has_permission(permission) then
+ raise('forbidden', permission..mt.path)
+ end
+ end
+
function mt.removable() end
function mt.value_removable(v)
@@ -118,13 +144,15 @@ function M.TreeNode:init(context, params)
end
local function key_removable(k)
+ if not mt.removing_permitted() then return false end
+
local res = mt.value_removable(mt.load(k, {dereference=false}))
if res == nil then return params.editable end
return res
end
- function mt.check_removable(k, v)
- if v == nil and not key_removable(k) then
+ function mt.check_removable(k)
+ if not key_removable(k) then
raise(pth.join(mt.path, k), 'Cannot be deleted')
end
end
@@ -155,18 +183,6 @@ function M.TreeNode:init(context, params)
function mt.__index(t, k) return mt.get(k, {private=true}) end
function mt.__newindex(t, k, v) mt.save(k, v) end
- function mt.has_permission(user, permission)
- local p = permission..mt.path
- if mt.fetch('/auth/permissions')[p] then
- return user:check_permission(p)
- end
-
- if ({create=true, delete=true})[permission] then
- permission = 'modify'
- end
- return M.has_permission(mt.parent, user, permission)
- end
-
mt.txn.validable[mt.path] = mt.addr
end
@@ -175,7 +191,8 @@ function M.TreeNode:search_refs(path)
if #path == 0 then return {} end
- local mt = getmetatable(self)
+ local mt = getmetatable(getmetatable(self).escalate)
+
local name = path[1]
table.remove(path, 1)
@@ -224,13 +241,15 @@ function M.Collection:init(context, params)
function mt.load(k, options) return mt.member(k):load(options) end
+ function mt.removing_permitted() return mt.has_permission('delete') end
+
if not mt.txn then return end
function mt.init_meta(meta)
update(
meta,
{
- editable=params.editable,
+ editable=params.editable and mt.has_permission('create'),
members=field:meta(),
required=params.required,
['ui-member']=params.ui_member or meta['ui-name']:gsub('s$', ''),
@@ -260,13 +279,26 @@ function M.Collection:init(context, params)
validate(mt.parent)
end
end
-
+
function mt.save(k, v)
- if not params.editable then
- raise(mt.path, 'Collection is not editable')
- end
+ local delete = v == nil
+ local old = mt.load(k, {dereference=false})
+
+ if old == nil then
+ if delete then return end
+ if not params.editable then
+ raise(mt.path, 'Collection is not editable')
+ end
+ mt.check_permission('create')
+
+ elseif delete then mt.check_removable(k)
+
+ elseif type(old) == 'table' then
+ mt.check_removable(k)
+ mt.check_permission('create')
+
+ else mt.check_permission('modify') end
- mt.check_removable(k, v)
if params.key then
local kf = M.BoundMember(self, k, params.key)
@@ -287,26 +319,32 @@ function M.List:init(context, params)
super(self, M.List):init(context, setdefaults(params, {dtype='list'}))
local mt = getmetatable(self)
+ local tmt = getmetatable(mt.escalate)
+
+ mt._save = mt.save
- local save = mt.save
function mt.save(k, v)
assert(type(k) == 'number')
if v == nil then
+ mt.check_removable(k)
local len = #mt.members()
while k < len do
- save(k, mt.load(k + 1, {dereference=false}))
+ tmt._save(k, tmt.load(k + 1, {dereference=false}))
k = k + 1
end
- end
- save(k, v)
+ tmt._save(len)
+ else mt._save(k, v) end
end
function mt.insert(v, i)
assert(v ~= nil)
+ mt.check_permission('create')
local len = #mt.members()
if not i then i = len + 1 end
- for j = len,i,-1 do save(j + 1, mt.load(j, {dereference=false})) end
- save(i, v)
+ for j = len,i,-1 do
+ tmt._save(j + 1, tmt.load(j, {dereference=false}))
+ end
+ tmt._save(i, v)
end
end
@@ -336,7 +374,9 @@ end
for _, mf in ipairs{
'addr',
+ 'check_permission',
'contains',
+ 'escalate',
'fetch',
'has_permission',
'insert',