luci-mod-rpc: more auth/login fixes, expose further libraries
[project/luci.git] / modules / luci-mod-rpc / luasrc / controller / rpc.lua
index 129ca43..5df5e37 100644 (file)
@@ -2,37 +2,56 @@
 -- Copyright 2008 Jo-Philipp Wich <jow@openwrt.org>
 -- Licensed to the public under the Apache License 2.0.
 
-local require = require
-local pairs = pairs
-local print = print
-local pcall = pcall
-local table = table
+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
 
-module "luci.controller.rpc"
+       return nil
+end
 
-function index()
-       local function authenticator(validator, accs)
-               local auth = luci.http.formvalue("auth", true)
-               if auth then -- if authentication token was given
-                       local sdat = (luci.util.ubus("session", "get", { ubus_rpc_session = auth }) or { }).values
-                       if sdat then -- if given token is valid
-                               if sdat.user and luci.util.contains(accs, sdat.user) then
-                                       return sdat.user, auth
-                               end
-                       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
-               luci.http.status(403, "Forbidden")
+               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
 
@@ -43,39 +62,48 @@ function rpc_auth()
        local ltn12   = require "luci.ltn12"
        local util    = require "luci.util"
 
-       local loginstat
-
        local server = {}
        server.challenge = function(user, pass)
-               local sid, token, secret
-
-               require "luci.config"
-
-               if sys.user.checkpasswd(user, pass) then
-                       local sdat = util.ubus("session", "create", { timeout = luci.config.sauth.sessiontime })
+               local config = require "luci.config"
+               local login = util.ubus("session", "login", {
+                       username = user,
+                       password = pass,
+                       timeout  = tonumber(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),
+                                       secret = sys.uniqueid(16)
+                               }
+                       })
+
+                       local sid, sdat = ctrl.session_retrieve(login.ubus_rpc_session, { user })
                        if sdat then
-                               sid = sdat.ubus_rpc_session
-                               token = sys.uniqueid(16)
-                               secret = sys.uniqueid(16)
-
-                               http.header("Set-Cookie", "sysauth="..sid.."; path=/")
-                               util.ubus("session", "set", {
-                                       ubus_rpc_session = sid,
-                                       values = {
-                                               user = user,
-                                               token = token,
-                                               secret = secret
-                                       }
-                               })
+                               return {
+                                       sid = sid,
+                                       token = sdat.token,
+                                       secret = sdat.secret
+                               }
                        end
                end
 
-               return sid and {sid=sid, token=token, secret=secret}
+               return nil
        end
 
        server.login = function(...)
                local challenge = server.challenge(...)
-               return challenge and challenge.sid
+               if challenge then
+                       http.header("Set-Cookie", 'sysauth=%s; path=%s' %{
+                               challenge.sid,
+                               http.getenv("SCRIPT_NAME")
+                       })
+                       return challenge.sid
+               end
        end
 
        http.prepare_content("application/json")
@@ -137,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()
@@ -159,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