diff options
Diffstat (limited to 'aconf/model/node.lua')
-rw-r--r-- | aconf/model/node.lua | 96 |
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', |