From 4e1b884157017b6729d2523e95385756898eac7c Mon Sep 17 00:00:00 2001
From: Jo-Philipp Wich <jo@mein.io>
Date: Tue, 11 Jul 2017 14:12:50 +0200
Subject: [PATCH] luci-base: properly handle authentication without
 authenticator

Some controller actions like the ones in "servicectl" require authentication
but are not meant to provide an authenticator because they're only invoked
by scripts.

Rework the dispatcher logic to handle this situation and only bail out if
an authenticator name other than "htmlauth" is set.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
---
 modules/luci-base/luasrc/dispatcher.lua | 97 +++++++++++++------------
 1 file changed, 50 insertions(+), 47 deletions(-)

diff --git a/modules/luci-base/luasrc/dispatcher.lua b/modules/luci-base/luasrc/dispatcher.lua
index 2b5f7b4929..1b684aa79c 100644
--- a/modules/luci-base/luasrc/dispatcher.lua
+++ b/modules/luci-base/luasrc/dispatcher.lua
@@ -174,7 +174,8 @@ local function session_retrieve(sid, allowed_users)
 	if type(sdat) == "table" and
 	   type(sdat.values) == "table" and
 	   type(sdat.values.token) == "string" and
-	   util.contains(allowed_users, sdat.values.username)
+	   (not allowed_users or
+	    util.contains(allowed_users, sdat.values.username))
 	then
 		return sid, sdat.values
 	end
@@ -182,25 +183,27 @@ local function session_retrieve(sid, allowed_users)
 	return nil, nil
 end
 
-local function session_setup(user, pass)
-	local login = util.ubus("session", "login", {
-		username = user,
-		password = pass,
-		timeout  = tonumber(luci.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) }
+local function session_setup(user, pass, allowed_users)
+	if util.contains(allowed_users, user) then
+		local login = util.ubus("session", "login", {
+			username = user,
+			password = pass,
+			timeout  = tonumber(luci.config.sauth.sessiontime)
 		})
 
-		return login.ubus_rpc_session
+		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) }
+			})
+
+			return session_retrieve(login.ubus_rpc_session)
+		end
 	end
 
-	return nil
+	return nil, nil
 end
 
 function dispatch(request)
@@ -350,6 +353,11 @@ function dispatch(request)
 		local authen = track.sysauth_authenticator
 		local _, sid, sdat, default_user, allowed_users
 
+		if type(authen) == "string" and authen ~= "htmlauth" then
+			error500("Unsupported authenticator %q configured" % authen)
+			return
+		end
+
 		if type(track.sysauth) == "table" then
 			default_user, allowed_users = nil, track.sysauth
 		else
@@ -358,44 +366,39 @@ function dispatch(request)
 
 		if type(authen) == "function" then
 			_, sid = authen(sys.user.checkpasswd, allowed_users)
-		elseif authen == "htmlauth" then
-			sid, sdat = session_retrieve(http.getcookie("sysauth"), allowed_users)
+		else
+			sid = http.getcookie("sysauth")
+		end
 
-			if not sid then
-				local user = http.getenv("HTTP_AUTH_USER")
-				local pass = http.getenv("HTTP_AUTH_PASS")
+		sid, sdat = session_retrieve(sid, allowed_users)
 
-				if user == nil and pass == nil then
-					user = http.formvalue("luci_username")
-					pass = http.formvalue("luci_password")
-				end
+		if not (sid and sdat) and authen == "htmlauth" then
+			local user = http.getenv("HTTP_AUTH_USER")
+			local pass = http.getenv("HTTP_AUTH_PASS")
 
-				if util.contains(allowed_users, user) then
-					sid, sdat = session_setup(user, pass), nil
-				end
+			if user == nil and pass == nil then
+				user = http.formvalue("luci_username")
+				pass = http.formvalue("luci_password")
+			end
 
-				if not sid then
-					require("luci.i18n")
-					require("luci.template")
-					context.path = {}
-					http.status(403, "Forbidden")
-					luci.template.render(track.sysauth_template or "sysauth", {
-						duser = default_user,
-						fuser = user
-					})
-					return
-				end
+			sid, sdat = session_setup(user, pass, allowed_users)
+
+			if not sid then
+				local tmpl = require "luci.template"
 
-				http.header("Set-Cookie", 'sysauth=%s; path=%s' %{ sid, build_url() })
-				http.redirect(build_url(unpack(ctx.requestpath)))
+				context.path = {}
+
+				http.status(403, "Forbidden")
+				tmpl.render(track.sysauth_template or "sysauth", {
+					duser = default_user,
+					fuser = user
+				})
+
+				return
 			end
-		else
-			error500("Unsupported authenticator configured")
-			return
-		end
 
-		if not sdat then
-			sid, sdat = session_retrieve(sid, allowed_users)
+			http.header("Set-Cookie", 'sysauth=%s; path=%s' %{ sid, build_url() })
+			http.redirect(build_url(unpack(ctx.requestpath)))
 		end
 
 		if not sid or not sdat then
-- 
2.30.2