summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2020-04-15 22:12:39 +0200
committerJo-Philipp Wich <jo@mein.io>2020-04-16 13:30:35 +0200
commit125916f2f4603acf980acd074aca55d0b528f34b (patch)
tree85c17bc77e078bf4155350abc01e5c5f9b5bc496
parent06af541c376ddab7303ea4294b17e8be91458d05 (diff)
luci-base: dispatcher.lua: add support for handling menu ACL annotations
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r--modules/luci-base/luasrc/dispatcher.lua151
1 files changed, 117 insertions, 34 deletions
diff --git a/modules/luci-base/luasrc/dispatcher.lua b/modules/luci-base/luasrc/dispatcher.lua
index 118ca221a..e13204462 100644
--- a/modules/luci-base/luasrc/dispatcher.lua
+++ b/modules/luci-base/luasrc/dispatcher.lua
@@ -134,6 +134,35 @@ local function check_uci_depends(conf)
return true
end
+local function check_acl_depends(require_groups, groups)
+ if type(require_groups) == "table" and #require_groups > 0 then
+ local writable = false
+
+ for _, group in ipairs(require_groups) do
+ local read = false
+ local write = false
+ if type(groups) == "table" and type(groups[group]) == "table" then
+ for _, perm in ipairs(groups[group]) do
+ if perm == "read" then
+ read = true
+ elseif perm == "write" then
+ write = true
+ end
+ end
+ end
+ if not read and not write then
+ return nil
+ elseif write then
+ writable = true
+ end
+ end
+
+ return writable
+ end
+
+ return true
+end
+
local function check_depends(spec)
if type(spec.depends) ~= "table" then
return true
@@ -493,6 +522,7 @@ end
local function session_retrieve(sid, allowed_users)
local sdat = util.ubus("session", "get", { ubus_rpc_session = sid })
+ local sacl = util.ubus("session", "access", { ubus_rpc_session = sid })
if type(sdat) == "table" and
type(sdat.values) == "table" and
@@ -501,42 +531,38 @@ local function session_retrieve(sid, allowed_users)
util.contains(allowed_users, sdat.values.username))
then
uci:set_session_id(sid)
- return sid, sdat.values
+ return sid, sdat.values, type(sacl) == "table" and sacl or {}
end
- return nil, nil
+ return nil, nil, nil
end
-local function session_setup(user, pass, allowed_users)
- if util.contains(allowed_users, user) then
- local login = util.ubus("session", "login", {
- username = user,
- password = pass,
- timeout = tonumber(luci.config.sauth.sessiontime)
- })
-
- local rp = context.requestpath
- and table.concat(context.requestpath, "/") or ""
+local function session_setup(user, pass)
+ local login = util.ubus("session", "login", {
+ username = user,
+ password = pass,
+ timeout = tonumber(luci.config.sauth.sessiontime)
+ })
- if type(login) == "table" and
- type(login.ubus_rpc_session) == "string"
- then
- util.ubus("session", "set", {
- ubus_rpc_session = login.ubus_rpc_session,
- values = { token = sys.uniqueid(16) }
- })
+ local rp = context.requestpath
+ and table.concat(context.requestpath, "/") or ""
- io.stderr:write("luci: accepted login on /%s for %s from %s\n"
- %{ rp, user, http.getenv("REMOTE_ADDR") or "?" })
+ if type(login) == "table" and
+ type(login.ubus_rpc_session) == "string"
+ then
+ util.ubus("session", "set", {
+ ubus_rpc_session = login.ubus_rpc_session,
+ values = { token = sys.uniqueid(16) }
+ })
- return session_retrieve(login.ubus_rpc_session)
- end
+ io.stderr:write("luci: accepted login on /%s for %s from %s\n"
+ %{ rp, user or "?", http.getenv("REMOTE_ADDR") or "?" })
- io.stderr:write("luci: failed login on /%s for %s from %s\n"
- %{ rp, user, http.getenv("REMOTE_ADDR") or "?" })
+ return session_retrieve(login.ubus_rpc_session)
end
- return nil, nil
+ io.stderr:write("luci: failed login on /%s for %s from %s\n"
+ %{ rp, user or "?", http.getenv("REMOTE_ADDR") or "?" })
end
local function check_authentication(method)
@@ -635,7 +661,28 @@ local function merge_trees(node_a, node_b)
return node_a
end
-function menu_json()
+local function apply_tree_acls(node, acl)
+ if type(node.children) == "table" then
+ for _, child in pairs(node.children) do
+ apply_tree_acls(child, acl)
+ end
+ end
+
+ local perm
+ if type(node.depends) == "table" then
+ perm = check_acl_depends(node.depends.acl, acl["access-group"])
+ else
+ perm = true
+ end
+
+ if perm == nil then
+ node.satisfied = false
+ elseif perm == false then
+ node.readonly = true
+ end
+end
+
+function menu_json(acl)
local tree = context.tree or createtree()
local lua_tree = tree_to_json(tree, {
action = {
@@ -645,7 +692,13 @@ function menu_json()
})
local json_tree = createtree_json()
- return merge_trees(lua_tree, json_tree)
+ local menu_tree = merge_trees(lua_tree, json_tree)
+
+ if acl then
+ apply_tree_acls(menu_tree, acl)
+ end
+
+ return menu_tree
end
local function init_template_engine(ctx)
@@ -738,6 +791,8 @@ function dispatch(request)
local requested_path_node = {}
local requested_path_args = {}
+ local required_path_acls = {}
+
for i, s in ipairs(request) do
if type(page.children) ~= "table" or not page.children[s] then
page = nil
@@ -755,6 +810,21 @@ function dispatch(request)
suid = page.setuser or suid
sgid = page.setgroup or sgid
+ if type(page.depends) == "table" and type(page.depends.acl) == "table" then
+ for _, group in ipairs(page.depends.acl) do
+ local found = false
+ for _, item in ipairs(required_path_acls) do
+ if item == group then
+ found = true
+ break
+ end
+ end
+ if not found then
+ required_path_acls[#required_path_acls + 1] = group
+ end
+ end
+ end
+
requested_path_full[i] = s
requested_path_node[i] = s
@@ -778,16 +848,16 @@ function dispatch(request)
ctx.requested = ctx.requested or page
if type(auth) == "table" and type(auth.methods) == "table" and #auth.methods > 0 then
- local sid, sdat
+ local sid, sdat, sacl
for _, method in ipairs(auth.methods) do
- sid, sdat = check_authentication(method)
+ sid, sdat, sacl = check_authentication(method)
- if sid and sdat then
+ if sid and sdat and sacl then
break
end
end
- if not (sid and sdat) and auth.login then
+ if not (sid and sdat and sacl) and auth.login then
local user = http.getenv("HTTP_AUTH_USER")
local pass = http.getenv("HTTP_AUTH_PASS")
@@ -796,7 +866,9 @@ function dispatch(request)
pass = http.formvalue("luci_password")
end
- sid, sdat = session_setup(user, pass, { "root" })
+ if user and pass then
+ sid, sdat, sacl = session_setup(user, pass)
+ end
if not sid then
context.path = {}
@@ -815,7 +887,7 @@ function dispatch(request)
return
end
- if not sid or not sdat then
+ if not sid or not sdat or not sacl then
http.status(403, "Forbidden")
http.header("X-LuCI-Login-Required", "yes")
return
@@ -824,6 +896,17 @@ function dispatch(request)
ctx.authsession = sid
ctx.authtoken = sdat.token
ctx.authuser = sdat.username
+ ctx.authacl = sacl
+ end
+
+ if #required_path_acls > 0 then
+ local perm = check_acl_depends(required_path_acls, ctx.authacl and ctx.authacl["access-group"])
+ if perm == nil then
+ http.status(403, "Forbidden")
+ return
+ end
+
+ page.readonly = not perm
end
local action = (page and type(page.action) == "table") and page.action or {}