From 055b621cf5cdc15b24e78408d610a00f6869b8e0 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Sat, 5 May 2018 22:56:30 +0200 Subject: [PATCH] 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 --- modules/luci-mod-rpc/luasrc/controller/rpc.lua | 123 +++++++++++++++++++------ 1 file changed, 93 insertions(+), 30 deletions(-) diff --git a/modules/luci-mod-rpc/luasrc/controller/rpc.lua b/modules/luci-mod-rpc/luasrc/controller/rpc.lua index 571ab7db5..5df5e375a 100644 --- a/modules/luci-mod-rpc/luasrc/controller/rpc.lua +++ b/modules/luci-mod-rpc/luasrc/controller/rpc.lua @@ -5,48 +5,53 @@ module("luci.controller.rpc", package.seeall) -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 +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 - local function authenticator(validator, accs) - local http = require "luci.http" - local auth = http.formvalue("auth", true) or http.getcookie("sysauth") + return nil +end - 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") +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 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 -- 2.11.0