luci-mod-rpc: more auth/login fixes, expose further libraries

The previous attempt to fix authentication broke login functionality so
rework the code once again, this time with referencing helper functions
directly via the controller scope.

Furthermore, properly expose luci.sys.wifi.getiwinfo() and luci.ip.

For getiwinfo(), the RPC wrapped function accepts one further optional
parameter specifying the operation to invoke on the iwinfo instance.
If no operation is specified, a summary object containing all info
without country and scan list is returned.

Example to obtain iwinfo summary object:
  curl --cookie sysauth=... \
       --data '{"method": "wifi.getiwinfo", "params": ["wlan0"]}' \
		"http://192.168.1.1/cgi-bin/luci/rpc/sys"

Example to obtain iwinfo scan list:
  curl --cookie sysauth=... \
       --data '{"method": "wifi.getiwinfo", "params": ["wlan0", "scanlist"]}' \
		"http://192.168.1.1/cgi-bin/luci/rpc/sys"

The exposed luci.ip class uses a similar approach to allow invoking
instance methods on cidr objects. The new(), IPv4(), IPv6() and MAC()
constructors accept two further optional arguments, with the first
specifying the instance method to invoke and the second the value to
pass to the instance method.

Example to get list of IPv4 neighbours (ARP entries):
  curl --cookie sysauth=... \
       --data '{"method": "neighbors", "params": [{"family": 4}]}' \
		"http://192.168.1.1/cgi-bin/luci/rpc/ip"

Example to add 100 hosts to a network address:
  curl --cookie sysauth=... \
       --data '{"method": "IPv4", "params": ["192.168.0.1", "255.255.255.0", "add", 1000]}' \
		"http://192.168.1.1/cgi-bin/luci/rpc/ip"

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2018-05-05 22:56:30 +02:00
parent 6b6733b11e
commit 055b621cf5

View file

@ -5,48 +5,53 @@
module("luci.controller.rpc", package.seeall)
function session_retrieve(sid, allowed_users)
local util = require "luci.util"
local sdat = util.ubus("session", "get", {
ubus_rpc_session = sid
})
if type(sdat) == "table" and
type(sdat.values) == "table" and
type(sdat.values.token) == "string" and
type(sdat.values.secret) == "string" and
type(sdat.values.username) == "string" and
util.contains(allowed_users, sdat.values.username)
then
return sid, sdat.values
end
return nil
end
function authenticator(validator, accs)
local http = require "luci.http"
local ctrl = require "luci.controller.rpc"
local auth = http.formvalue("auth", true) or http.getcookie("sysauth")
if auth then -- if authentication token was given
local sid, sdat = ctrl.session_retrieve(auth, accs)
if sdat then -- if given token is valid
return sdat.username, sid
end
http.status(403, "Forbidden")
end
end
function index()
local function session_retrieve(sid, allowed_users)
local util = require "luci.util"
local sdat = util.ubus("session", "get", {
ubus_rpc_session = sid
})
if type(sdat) == "table" and
type(sdat.values) == "table" and
type(sdat.values.token) == "string" and
type(sdat.values.secret) == "string" and
type(sdat.values.username) == "string" and
util.contains(allowed_users, sdat.values.username)
then
return sid, sdat.values
end
return nil
end
local function authenticator(validator, accs)
local http = require "luci.http"
local auth = http.formvalue("auth", true) or http.getcookie("sysauth")
if auth then -- if authentication token was given
local sid, sdat = session_retrieve(auth, accs)
if sdat then -- if given token is valid
return sdat.username, sid
end
http.status(403, "Forbidden")
end
end
local ctrl = require "luci.controller.rpc"
local rpc = node("rpc")
rpc.sysauth = "root"
rpc.sysauth_authenticator = authenticator
rpc.sysauth_authenticator = ctrl.authenticator
rpc.notemplate = true
entry({"rpc", "uci"}, call("rpc_uci"))
entry({"rpc", "fs"}, call("rpc_fs"))
entry({"rpc", "sys"}, call("rpc_sys"))
entry({"rpc", "ipkg"}, call("rpc_ipkg"))
entry({"rpc", "ip"}, call("rpc_ip"))
entry({"rpc", "auth"}, call("rpc_auth")).sysauth = false
end
@ -77,7 +82,7 @@ function rpc_auth()
}
})
local sid, sdat = session_retrieve(login.ubus_rpc_session, { user })
local sid, sdat = ctrl.session_retrieve(login.ubus_rpc_session, { user })
if sdat then
return {
sid = sid,
@ -160,13 +165,40 @@ function rpc_fs()
end
function rpc_sys()
local util = require "luci.util"
local sys = require "luci.sys"
local jsonrpc = require "luci.jsonrpc"
local http = require "luci.http"
local ltn12 = require "luci.ltn12"
local sys2 = util.clone(sys)
sys2.net = util.clone(sys.net)
sys2.wifi = util.clone(sys.wifi)
function sys2.wifi.getiwinfo(ifname, operation)
local iw = sys.wifi.getiwinfo(ifname)
if iw then
if operation then
assert(type(iwinfo[iw.type][operation]) == "function")
return iw[operation]
end
local n, f
local rv = { ifname = ifname }
for n, f in pairs(iwinfo[iw.type]) do
if type(f) == "function" and
n ~= "scanlist" and n ~= "countrylist"
then
rv[n] = iw[n]
end
end
return rv
end
return nil
end
http.prepare_content("application/json")
ltn12.pump.all(jsonrpc.handle(sys, http.source()), http.write)
ltn12.pump.all(jsonrpc.handle(sys2, http.source()), http.write)
end
function rpc_ipkg()
@ -182,3 +214,34 @@ function rpc_ipkg()
http.prepare_content("application/json")
ltn12.pump.all(jsonrpc.handle(ipkg, http.source()), http.write)
end
function rpc_ip()
if not pcall(require, "luci.ip") then
luci.http.status(404, "Not Found")
return nil
end
local util = require "luci.util"
local ip = require "luci.ip"
local jsonrpc = require "luci.jsonrpc"
local http = require "luci.http"
local ltn12 = require "luci.ltn12"
local ip2 = util.clone(ip)
local _, n
for _, n in ipairs({ "new", "IPv4", "IPv6", "MAC" }) do
ip2[n] = function(address, netmask, operation, argument)
local cidr = ip[n](address, netmask)
if cidr and operation then
assert(type(cidr[operation]) == "function")
local cidr2 = cidr[operation](cidr, argument)
return (type(cidr2) == "userdata") and cidr2:string() or cidr2
end
return (type(cidr) == "userdata") and cidr:string() or cidr
end
end
http.prepare_content("application/json")
ltn12.pump.all(jsonrpc.handle(ip2, http.source()), http.write)
end