核心提示:目录目录 Luci的多级用户管理 实现方法 indexlua dispatcherlua servicectllua controllermultiUserlua modelcbimultiUserl...
目录
目录 Luci的多级用户管理 实现方法 indexlua dispatcherlua servicectllua controllermultiUserlua modelcbimultiUserlua 防止用户删除本用户操作cbilua 思路 想法
Luci的多级用户管理
Openwrt的web管理是用luci来实现的,但是luci本身不支持多级用户管理,并实现不同用户显示不同的界面。因为工作需要所以花了点时间把这个功能完成了。
实现方法
看了网上的一些文档,基本实现的都是把root改成别的用户登录,但是都没有实现多级用户的管理。根据这些文档,自己又做了些更改,主要更改的文件有:
/user/lib/lua/luci/controller/admin/index.lua /user/lib/lua/luci/dispatcher.lua /user/lib/lua/luci/controller/admin/servicectl.lua
添加的文件有:
/user/lib/lua/luci/model/cbi/multiUser.lua /user/lib/lua/luci/controller/multiUser.lua
index.lua
function index() + local fs = require "nixio.fs" + local UCI = require("luci.model.uci") + local uci = UCI.cursor() + local multiUser = { } + local root = node() if not root.target then root.target = alias("admin") @@ -14,7 +19,19 @@ function index() page.target = firstchild() page.title = _("Administration") page.order = 10 - page.sysauth = "root" + page.sysauth = { "root" } + if fs.access("/etc/config/multiUser") then + multiUser = uci:get_all("multiUser") + for k, v in pairs(multiUser) do + if type(v) == "table" then + for j, l in pairs(v) do + if j == "user" then + table.insert(page.sysauth, l) + end + end + end + end + endpage.sysauth的属性是整个页面需要验证的的用户名,当登录的用户名不存在这个表里面,那么luci将进入一个空的界面。 uci:get_all(“multiUser”)是获取/etc/config/multiUser下的配置在uci缓存里面的内容,并以表的形式放入到multiUser变量里面。
注:这个函数获是用uci方式获取的,所以表的结果并不一定等于配置文件的内容。通过uci set命令可以设置配置文件让结果不相等。所以luci界面上应该禁止删除本用户,防止出现删除后配置文件还没保存,但是已经登录不上去的情况,这个下面的代码会有说到 功能说明:根据multiUser的配置文件获取所有用户配置的用户名然后追加到page.syauth表里面,让luci的系统验证支持这些用户名。
dispatcher.lua
@@ -108,20 +108,86 @@ end function authenticator.htmlauth(validator, accs, default) local user = http.formvalue("luci_username") local pass = http.formvalue("luci_password") + local user_table = "" + local pass_table = "" + local level_table = "" + local fs = require "nixio.fs" + local UCI = require("luci.model.uci") + local uci = UCI.cursor() + local multiUser = { } + local file=io.open("/var/luci/shadow","w") + + if fs.access("/etc/config/multiUser") then + multiUser = uci:get_all("multiUser") + for k, v in pairs(multiUser) do + if type(v) == "table" then + user_table = "" + pass_table = "" + level_talbe = "" + for j, l in pairs(v) do + if j == "user" then + user_table = l + end + if j == "pwd" then + pass_table = l + end + if j == "level" then + level_table = l + end + end + if user_table == user and pass_table == pass then + if not file then + luci.util.exec("mkdir -p /var/luci/") + file = io.open("/var/luci/shadow", "w") + end + file:write(user) + file:write("\n") + file:write(level_table) + file:close() + return user + end + end + end + end - if user and validator(user, pass) then + --[[ if user and validator(user, pass) then file=io.open("/tmp/current_user","w") file:write(user) file:close() return user - end + end ]]--改变用户名密码的判断方式,如果仍想用系统的用户名密码,那么需要写一个程序在multiUser配置变化时后执行adduer命令,把用户名添加到系统即可 把当前用户写到文件里面的作用是在luci中防止本用户删除本用户的操作
if context.urltoken.stok then context.urltoken.stok = nil @@ -343,6 +408,62 @@ function dispatch(request) authen = function() return eu end end end + --[[ + context.tree.nodes.admin.nodes.status.title="" + context.tree.nodes.admin.nodes.system.title="" + context.tree.nodes.admin.nodes.services.title="" + context.tree.nodes.admin.nodes.network.title="" + ]]-- + if level == "1" then + --context.tree.nodes.admin.nodes.services.title="" + elseif level == "2" then + for j, k in pairs(context.tree.nodes.admin.nodes.network.nodes) do + if j == "wireless" + or j == "hosts" + --or j == "firewall" + or j == "diagnostics" + or j == "dhcp" + or j == "routes" + then + k.title="" + k.leaf=true + k.target="" + end + end + context.tree.nodes.admin.nodes.services.title="" + context.tree.nodes.admin.nodes.services.leaf=true + for j, k in pairs(context.tree.nodes.admin.nodes.system.nodes) do + if j == "admin" + or j == "system" + or j == "packages" + or j == "startup" + or j == "leds" + or j == "multiUser" + or j == "crontab" + or j == "fstab" + then + k.title="" + k.leaf=true + k.target="" + end + end + for j, k in pairs(context.tree.nodes.admin.nodes.status.nodes) do + if j == "dmesg" + or j == "routes" + or j == "iptables" + or j == "realtime" + or j == "syslog" + or j == "processes" + then + k.title="" + k.leaf=true + k.target="" + end + end + end隐藏页面的关键是把page.target 和page.title属性写成空,page.leaf属性官方文档上写的是防止更新的意思,我也没怎么搞明白,如果有人明白的请留言给我。 注释的内容显示了所有能显示的page的表。
servicectl.lua
module("luci.controller.admin.servicectl", package.seeall) function index() - entry({"servicectl"}, alias("servicectl", "status")).sysauth = "root" + entry({"servicectl"}, alias("servicectl", "status"))因为是多级用户管理,所以用户名不是单个。只需要顶层的表admin的sysauth属性是正确的即可。
controller/multiUser.lua
@@ -0,0 +1,12 @@ +module("luci.controller.multiUser", package.seeall) + + + +function index() + local fs = require "nixio.fs" + if fs.access("/etc/config/multiUser") then + entry({"admin", "system","multiUser"}, cbi("multiUser"), translate("Multi-leve User Configuration"), 12) + end + +end +
model/cbi/multiUser.lua
+--m = Map("multiUser", translate(" s.template里面指定了显示的格式为用户名和密码同一列的格式 s.addremove=true显示了添加和删除按钮 s.anonymous=true是隐藏了标题 m.before_save(map)这个函数需要在cbi.lua声明使用,代码如下:
Node.parse(self, ...) if self.save then + local run_hooks_return = +self:run_hooks("before_save") + if run_hooks_return ~= nil then + if run_hooks_return == false then + return + end + end
防止用户删除本用户操作:cbi.lua
@@ -1156,16 +1165,33 @@ function TypedSection.depends(self, option, value) end function TypedSection.parse(self, novld) + local fs = require "nixio.fs" + local UCI = require("luci.model.uci") + local uci = UCI.cursor() + local multiUser = { } + + if fs.access("/etc/config/multiUser") then + multiUser = uci:get_all("multiUser") + end if self.addremove then -- Remove local crval = REMOVE_PREFIX .. self.config local name = self.map:formvaluetable(crval) for k,v in pairs(name) do + local deluser = "" + local cuser = luci.util.exec("cat /tmp/luci/shadow | head -1 | tr -d '\n' 2> /dev/null") if k:sub(-2) == ".x" then k = k:sub(1, #k - 2) end + for a, b in pairs(multiUser) do + if a == k then + deluser = b.user + end + end if self:cfgvalue(k) and self:checkscope(k) then - self:remove(k) + if deluser ~= cuser then + self:remove(k) + end end end end禁止删除本用户的操作,当点击删除TypedSection类型的section的按钮时,会执行这个函数。
思路
整体的思路很简单,就是当用户登录时判断这个用户的等级,如果等级为2,那么把一些页面的3个属性值改掉。 关键点:动态的实现系统sysauth的值动态改变