luci-base: generalize post security token handling
authorJo-Philipp Wich <jow@openwrt.org>
Tue, 20 Oct 2015 18:58:30 +0000 (20:58 +0200)
committerJo-Philipp Wich <jow@openwrt.org>
Tue, 20 Oct 2015 19:04:46 +0000 (21:04 +0200)
* Add a generic helper function to check need for post / csrf token validation
* Remove custom token verification in cbi targets
* Support requiring post security depending on specific submit parameters,
  usable through post_on() action

Signed-off-by: Jo-Philipp Wich <jow@openwrt.org>
modules/luci-base/luasrc/dispatcher.lua

index 5d9d1b0..df562dd 100644 (file)
@@ -174,6 +174,32 @@ function httpdispatch(request, prefix)
        --context._disable_memtrace()
 end
 
+local function require_post_security(target)
+       if type(target) == "table" then
+               if type(target.post) == "table" then
+                       local param_name, required_val, request_val
+
+                       for param_name, required_val in pairs(target.post) do
+                               request_val = http.formvalue(param_name)
+
+                               if (type(required_val) == "string" and
+                                   request_val ~= required_val) or
+                                  (required_val == true and
+                                   (request_val == nil or request_val == ""))
+                               then
+                                       return false
+                               end
+                       end
+
+                       return true
+               end
+
+               return (target.post == true)
+       end
+
+       return false
+end
+
 function dispatch(request)
        --context._disable_memtrace = require "luci.debug".trap_memtrace("l")
        local ctx = context
@@ -381,7 +407,7 @@ function dispatch(request)
                end
        end
 
-       if c and type(c.target) == "table" and c.target.post == true then
+       if c and require_post_security(c.target) then
                if http.getenv("REQUEST_METHOD") ~= "POST" then
                        http.status(405, "Method Not Allowed")
                        http.header("Allow", "POST")
@@ -720,16 +746,20 @@ function call(name, ...)
        return {type = "call", argv = {...}, name = name, target = _call}
 end
 
-function post(name, ...)
+function post_on(params, name, ...)
        return {
                type = "call",
-               post = true,
+               post = params,
                argv = { ... },
                name = name,
                target = _call
        }
 end
 
+function post(...)
+       return post_on(true, ...)
+end
+
 
 local _template = function(self, ...)
        require "luci.template".render(self.view)
@@ -744,15 +774,6 @@ local function _cbi(self, ...)
        local cbi = require "luci.cbi"
        local tpl = require "luci.template"
        local http = require "luci.http"
-       local disp = require "luci.dispatcher"
-
-       if http.formvalue("cbi.submit") == "1" and
-          http.formvalue("token") ~= disp.context.urltoken.stok
-       then
-               http.status(403, "Forbidden")
-               luci.template.render("csrftoken")
-               return
-       end
 
        local config = self.config or {}
        local maps = cbi.load(self.model, ...)
@@ -850,7 +871,13 @@ local function _cbi(self, ...)
 end
 
 function cbi(model, config)
-       return {type = "cbi", config = config, model = model, target = _cbi}
+       return {
+               type = "cbi",
+               post = { ["cbi.submit"] = "1" },
+               config = config,
+               model = model,
+               target = _cbi
+       }
 end
 
 
@@ -870,15 +897,6 @@ local function _form(self, ...)
        local cbi = require "luci.cbi"
        local tpl = require "luci.template"
        local http = require "luci.http"
-       local disp = require "luci.dispatcher"
-
-       if http.formvalue("cbi.submit") == "1" and
-          http.formvalue("token") ~= disp.context.urltoken.stok
-       then
-               http.status(403, "Forbidden")
-               luci.template.render("csrftoken")
-               return
-       end
 
        local maps = luci.cbi.load(self.model, ...)
        local state = nil
@@ -899,7 +917,12 @@ local function _form(self, ...)
 end
 
 function form(model)
-       return {type = "cbi", model = model, target = _form}
+       return {
+               type = "cbi",
+               post = { ["cbi.submit"] = "1" },
+               model = model,
+               target = _form
+       }
 end
 
 translate = i18n.translate