Recently I had the opportunity to work on OpenWRT for a Network Security Product. The hardest part for getting the product finished and delivered was customizing the UI.
The default UI of OpenWRT uses LUCI Model-View-Controller Framework. In a traditionally know language like php, Java, javascript, working on MVC is always a pleasure for UI developers. I am by choice a Kernel programmer, and even I would, for once in a while case, touch upon and write some UI components. However, working on LUA based LUCI was a night mare, not only for a non professional UI guy like me, but even for veterans in UI in my team.
The primary reasons why this was the case with LUCI-LUA.
1. No documentation. ( I mean even the kernel has better documentation than LUCI-LUA)
2. The code is not written in a generally accepted and used language (Its written in Lua Language).
3. No one else has written a memoir about there tryst with LUCI-LUA.
So I decided that since recently I just finished adding a dozen custom changes to it, I should share them with readers who might stumble upon it.
The first feature which is not supported by LUCI-LUA is that it is mostly hard-coded with 1 admin user "root". Inherently it does not have multi-user support. But there are hacks lying around to make LUCI-LUA pseudo multi-user.
The reason it is pseudo multi-user is because, even though a new user "Admin" logs into the UI, the underlying user in the linux system is still "root", because the http server which serves LUCI pages is itself running as root.
Suppose you need to add a custom page for a new feature. As an example we add a new Tab under System tab which is to add External Admins configured in a RADIUS Server.
In that case take an existing Template ( like we all do while developing drivers....look into an existing similar code). Add your entry into the System.lua file and then a blank page with necessary CSS and theme automatically pops up with your Menu entry.
Then add your own lua script which does handling of all the logic and UI handling.
Code Snippets:
FIle: system.lua
entry({"admin", "system", "externaladmin"}, cbi("admin_system/externaladmin"), _("External Admin"), 97)
File: externaladmin.lua.
adminform = SimpleForm("userform1", translate("External Admin"))
adminform.reset = false
username = adminform:field(Value, "uname",translate("Username"))
serverip = adminform:field(Value, "serverip",translate("Server IP"))
key = adminform:field(Value, "key",translate("Key"), translate("Table1: External Admins maintained
in External Radius Server. Table2: Radius Server with Server IPs"))
function adminform.handle(self, state, data)
if state == FORM_VALID then
local file = io.open("/tmp/myuser.log","w+")
local usernametxt = data.uname
local serveriptxt = data.serverip
local keytxt = data.key
local serverfile
if serveriptxt and keytxt and #serveriptxt > 0 and #keytxt > 0 then
command = "sh /root/radius_adt.sh "..serveriptxt.." "..keytxt
os.execute(command)
luci.http.redirect(luci.dispatcher.build_url("admin/system/externaladmin"))
end
if usernametxt and #usernametxt > 0 then
command = "sh /root/sysauth_adt.sh add external "..usernametxt.." NULL"
--os.execute(command)
output = luci.util.exec(command)
if output ~= "" then
adminform.errmessage=output
else
luci.http.redirect(luci.dispatcher.build_url("admin/system/externaladmin"))
end
end
file:close()
-- luci.http.redirect(luci.dispatcher.build_url("admin/system/externaladmin"))
end
return true
end
The default UI of OpenWRT uses LUCI Model-View-Controller Framework. In a traditionally know language like php, Java, javascript, working on MVC is always a pleasure for UI developers. I am by choice a Kernel programmer, and even I would, for once in a while case, touch upon and write some UI components. However, working on LUA based LUCI was a night mare, not only for a non professional UI guy like me, but even for veterans in UI in my team.
The primary reasons why this was the case with LUCI-LUA.
1. No documentation. ( I mean even the kernel has better documentation than LUCI-LUA)
2. The code is not written in a generally accepted and used language (Its written in Lua Language).
3. No one else has written a memoir about there tryst with LUCI-LUA.
So I decided that since recently I just finished adding a dozen custom changes to it, I should share them with readers who might stumble upon it.
The first feature which is not supported by LUCI-LUA is that it is mostly hard-coded with 1 admin user "root". Inherently it does not have multi-user support. But there are hacks lying around to make LUCI-LUA pseudo multi-user.
The reason it is pseudo multi-user is because, even though a new user "Admin" logs into the UI, the underlying user in the linux system is still "root", because the http server which serves LUCI pages is itself running as root.
Suppose you need to add a custom page for a new feature. As an example we add a new Tab under System tab which is to add External Admins configured in a RADIUS Server.
In that case take an existing Template ( like we all do while developing drivers....look into an existing similar code). Add your entry into the System.lua file and then a blank page with necessary CSS and theme automatically pops up with your Menu entry.
Then add your own lua script which does handling of all the logic and UI handling.
Code Snippets:
FIle: system.lua
entry({"admin", "system", "externaladmin"}, cbi("admin_system/externaladmin"), _("External Admin"), 97)
File: externaladmin.lua.
adminform = SimpleForm("userform1", translate("External Admin"))
adminform.reset = false
username = adminform:field(Value, "uname",translate("Username"))
serverip = adminform:field(Value, "serverip",translate("Server IP"))
key = adminform:field(Value, "key",translate("Key"), translate("Table1: External Admins maintained
in External Radius Server. Table2: Radius Server with Server IPs"))
function adminform.handle(self, state, data)
if state == FORM_VALID then
local file = io.open("/tmp/myuser.log","w+")
local usernametxt = data.uname
local serveriptxt = data.serverip
local keytxt = data.key
local serverfile
if serveriptxt and keytxt and #serveriptxt > 0 and #keytxt > 0 then
command = "sh /root/radius_adt.sh "..serveriptxt.." "..keytxt
os.execute(command)
luci.http.redirect(luci.dispatcher.build_url("admin/system/externaladmin"))
end
if usernametxt and #usernametxt > 0 then
command = "sh /root/sysauth_adt.sh add external "..usernametxt.." NULL"
--os.execute(command)
output = luci.util.exec(command)
if output ~= "" then
adminform.errmessage=output
else
luci.http.redirect(luci.dispatcher.build_url("admin/system/externaladmin"))
end
end
file:close()
-- luci.http.redirect(luci.dispatcher.build_url("admin/system/externaladmin"))
end
return true
end
--[[ This section adds table to display External Admins ]]--
tbluser = adminform:section(Table, userdata)
tblcoluser = tbluser:option(DummyValue, "coluser", translate("User Name"))
tblcoldel = tbluser:option(Button, "deluser", translate("Delete"))
tblcoldel.render = function(self, section, scope)
if userdata[section].enabled then
self.title = translate("Delete")
self.inputstyle = "delete"
end
Button.render(self, section, scope)
end
tblcoldel.write = function(self, section)
local userdel = userdata[section].coluser
command = "sh /root/sysauth_adt.sh remove external "..userdel.." NULL"
--command = "deluser "..userdel
os.execute(command)
luci.http.redirect(luci.dispatcher.build_url("admin/system/externaladmin"))
end
Rest of the template remains same. Much of my LUCI LUA experience has been constantly looking at existing code and putting debug prints to understand the flow.
If you want to add a Start up script UI element which allows you to DO a ENABLE/START/STOP/RESTART of your script, then just write a init script.
#!/bin/sh /etc/rc.common
# script to Run DHCP snoop module using previous state
# Copyright (C) 2015 Nevis Networks
# Author: Gadre Nayan Anand Version: 1.0
# This is a init script to enable, start, stop, restart DHCP snoop Feature.
# For More details on commands involved read User Manual.
START=58
STOP=58
FILE1=/etc/config/dhcp_sis
FILE2=/etc/config/trusted_interfaces
start()
{
local p_m
local u_b
while IFS="=" read -r key value;
do
case "$key" in
"preventive_mode") p_m=$value ;;
"use_bridge") u_b=$value;;
esac
done < "$FILE1"
echo preventive_mode = $p_m use_bridge = $u_b
echo "starting DHCP SNOOP IP SPOOF"
/usr/sbin/insmod /root/dhcp_snoop_IP_spoof.ko preventive_mode=$p_m use_bridge=$u_b
while read -r key;
do
echo "a-$key" > /sys/kernel/dhcp/trusted_interfaces
done < "$FILE2"
}
stop()
{
echo "stopping DHCP SNOOP IP SPOOF"
/usr/sbin/rmmod dhcp_snoop_IP_spoof
}
If you want to add a Start up script UI element which allows you to DO a ENABLE/START/STOP/RESTART of your script, then just write a init script.
#!/bin/sh /etc/rc.common
# script to Run DHCP snoop module using previous state
# Copyright (C) 2015 Nevis Networks
# Author: Gadre Nayan Anand Version: 1.0
# This is a init script to enable, start, stop, restart DHCP snoop Feature.
# For More details on commands involved read User Manual.
START=58
STOP=58
FILE1=/etc/config/dhcp_sis
FILE2=/etc/config/trusted_interfaces
start()
{
local p_m
local u_b
while IFS="=" read -r key value;
do
case "$key" in
"preventive_mode") p_m=$value ;;
"use_bridge") u_b=$value;;
esac
done < "$FILE1"
echo preventive_mode = $p_m use_bridge = $u_b
echo "starting DHCP SNOOP IP SPOOF"
/usr/sbin/insmod /root/dhcp_snoop_IP_spoof.ko preventive_mode=$p_m use_bridge=$u_b
while read -r key;
do
echo "a-$key" > /sys/kernel/dhcp/trusted_interfaces
done < "$FILE2"
}
stop()
{
echo "stopping DHCP SNOOP IP SPOOF"
/usr/sbin/rmmod dhcp_snoop_IP_spoof
}
And LUCI framework is really pathetically rigid, especially with the GUI perspective. CSS and other aspects are very hard to change....atleast this is what I felt.
No comments:
Post a Comment