diff options
author | Ted Trask <ttrask01@yahoo.com> | 2008-04-21 20:55:44 +0000 |
---|---|---|
committer | Ted Trask <ttrask01@yahoo.com> | 2008-04-21 20:55:44 +0000 |
commit | 5e1d9734d9dc849c21e84a45913fb2d22b7dfdf0 (patch) | |
tree | 7845e677b2b36c3f8090b7e424284a28fa397713 | |
parent | ed9bf961c16e1f9d58f39ebb1afc289e5564ebfe (diff) | |
download | acf-core-5e1d9734d9dc849c21e84a45913fb2d22b7dfdf0.tar.bz2 acf-core-5e1d9734d9dc849c21e84a45913fb2d22b7dfdf0.tar.xz |
Ted's Logon/permissions changes
git-svn-id: svn://svn.alpinelinux.org/acf/core/trunk@1030 ab2d0c66-481e-0410-8bed-d214d4d58bed
-rw-r--r-- | app/Makefile | 1 | ||||
-rw-r--r-- | app/acf-util/logon-controller.lua | 42 | ||||
-rw-r--r-- | app/acf-util/logon-html.lsp | 34 | ||||
-rw-r--r-- | app/acf-util/logon-model.lua | 138 | ||||
-rw-r--r-- | app/acf-util/logon-status-html.lsp | 7 | ||||
-rwxr-xr-x | app/acf-util/password-controller.lua | 12 | ||||
-rw-r--r-- | app/acf-util/roles-controller.lua | 12 | ||||
-rw-r--r-- | app/acf-util/roles-getlist-html.lsp | 16 | ||||
-rw-r--r-- | app/acf-util/roles-model.lua | 16 | ||||
-rw-r--r-- | app/acf-util/roles-read-html.lsp | 33 | ||||
-rw-r--r-- | app/acf_www-controller.lua | 206 | ||||
-rw-r--r-- | app/template-html.lsp | 54 | ||||
-rw-r--r-- | app/welcome-controller.lua | 21 | ||||
-rw-r--r-- | app/welcome-html.lsp | 2 | ||||
-rw-r--r-- | lib/menubuilder.lua | 214 | ||||
-rw-r--r-- | lib/roles.lua | 109 | ||||
-rw-r--r-- | lib/session.lua | 95 | ||||
-rw-r--r-- | roles | 13 | ||||
-rwxr-xr-x | www/cgi-bin/mvc.lua | 83 |
19 files changed, 498 insertions, 610 deletions
diff --git a/app/Makefile b/app/Makefile index 062832d..b71b10f 100644 --- a/app/Makefile +++ b/app/Makefile @@ -5,7 +5,6 @@ APP_DIST= \ acf-util/logon-html.lsp \ acf-util/logon-model.lua \ acf-util/logon-status-html.lsp \ - acf-util/logon-logout-html.lsp\ acf-util/roles-controller.lua \ acf-util/roles-getlist-html.lsp \ acf-util/roles-model.lua \ diff --git a/app/acf-util/logon-controller.lua b/app/acf-util/logon-controller.lua index 75915fc..61b4864 100644 --- a/app/acf-util/logon-controller.lua +++ b/app/acf-util/logon-controller.lua @@ -2,30 +2,38 @@ module (..., package.seeall) ---require ("session") - +mvc = {} mvc.on_load = function(self, parent) - if (self.worker[self.conf.action] == nil ) or ( self.conf.action == "init" ) then - self.worker[self.conf.action] = list_redir(self) - end - --logit ("logon.mvc.on_load activated") - end + self.conf.default_action = "status" +end +-- Logon a new user based upon id and password in clientdata logon = function(self) - return ( {logon=self.model.logon(self, clientdata.userid, clientdata.password,clientdata.sessionid) }) + local cmdresult + if clientdata.userid and clientdata.password then + local logon = self.model:logon(clientdata, conf.clientip, conf.sessiondir, sessiondata) + -- If successful logon, redirect to status, otherwise try again + if logon then + self.conf.action = "status" + self.conf.type = "redir" + error(self.conf) + else + cmdresult = "Logon Attempt Failed" + end + end + return ({ cmdresult = cmdresult }) end +-- Log out current user and go to login screen logout = function(self) - local logout = self.model:logoff(clientdata.sessionid) - if (logout) and (logout[1]) and (logout[1]["value"]) and (string.lower(logout[1]["value"]) == "successful") then - self.conf.action = "logon" - self.conf.type = "redir" - error (self.conf) - end - - return { logout = logout } + local logout = self.model.logoff(conf.sessiondir, sessiondata) + -- We have to redirect so a new session / menu is created + self.conf.action = "logon" + self.conf.type = "redir" + error (self.conf) end +-- Report the login status status = function(self) - return( {stats= self.model:status(clientdata.sessionid) }) + return self.model.status(sessiondata) end diff --git a/app/acf-util/logon-html.lsp b/app/acf-util/logon-html.lsp index 9a930a2..c1b4500 100644 --- a/app/acf-util/logon-html.lsp +++ b/app/acf-util/logon-html.lsp @@ -1,24 +1,20 @@ <? local form = ... ?> -<h1>Logon</h1> -<? --[[ ?> -<?= html.cfe_unpack(form) ?> -<? --]] ?> +<? --[[ + io.write(html.cfe_unpack(form)) + --]] ?> -<form action="<?= form.logon.option.script .. form.logon.option.prefix .. - form.logon.option.controller .. "/" .. form.logon.option.action ?>" method="POST"> -<DL> -<? -local myform = form.logon.value -for k,v in pairs(myform) do - io.write("\t<DT") - if (#v.errtxt > 0) then io.write(" class='error'") end - io.write(">" .. v.label .. "</DT>\n") +<? if form.cmdresult then ?> +<h1>Command Result</h1> +<p class='error'> <?= form.cmdresult ?></p> +<? end ?> - io.write("\t\t<DD>" .. html.form[v.type](v) .. "\n") - if (v.descr) and (#v.descr > 0) then io.write("\t\t<P CLASS='descr'>" .. string.gsub(v.descr, "\n", "<BR>") .. "</P>\n") end - if (#v.errtxt > 0) then io.write("\t\t<P CLASS='error'>" .. string.gsub(v.errtxt, "\n", "<BR>") .. "</P>\n") end - io.write("\t\t</DD>\n") -end -?> +<h1>Logon</h1> +<form action="logon" method="POST"> +<DL> + <DT>User id</DT> + <DD><input class="text" type="text" name="userid" value=""></DD> + <DT>Password</DT> + <DD><input class="password" type="password" name="password" value=""></DD> + <DT><input class="submit" type="submit" name="Logon" value="Logon"></DD> </DL> </form> diff --git a/app/acf-util/logon-model.lua b/app/acf-util/logon-model.lua index 33ffd56..cd840f7 100644 --- a/app/acf-util/logon-model.lua +++ b/app/acf-util/logon-model.lua @@ -19,113 +19,59 @@ else auth = require ("authenticator-plaintext") end -logon = function (self, id_user, password_user,sessdata ) -local userid=cfe({ name="userid",label="User id", type="text" }) -local password=cfe({ name="password" ,label="Password", type="passwd"}) -local logon=cfe({ name="Logon", label="Logon", value="Logon", type="submit"}) -local s = "" +-- Logoff the user by deleting session data +logoff = function (sessiondir, sessiondata) + -- Unlink / delete the current session + local result = session.unlink_session(sessiondir, sessiondata.id) + -- Clear the current session data + for a,b in pairs(sessiondata) do + sessiondata[a] = nil + end -local csess = session.check_session(conf.sessiondir, sessdata) -if csess ~= "an unknown user" then -session.unlink_session(conf.sessiondir, sessdata) -for a,b in pairs(sessiondata) do -if a ~= "menu" then -sessiondata[a] = nil -end + return (result) end -sessiondata.id = session.random_hash(512) -build_menus(self) -end - -local counteven = session.count_events(conf.sessiondir, id_user, session.hash_ip_addr(ENV["REMOTE_ADDR"])) -if counteven then -userid.errtxt="Information not recognized" -return (cfe {type="form", - option={script=ENV["SCRIPT_NAME"], - prefix=self.conf.prefix, - controller=self.conf.controller, - action="logon" }, - value={userid,password,logon},testme={counteven} - }) -end +-- Log on new user if possible and set up userinfo in session +-- if we fail, we leave the session alone (don't log out) +logon = function (self, clientdata, ip_addr, sessiondir, sessiondata) + -- Check to see if we can login this user id / ip addr + local countevent = session.count_events(sessiondir, clientdata.userid, session.hash_ip_addr(ip_addr)) + if countevent then + session.record_event(sessiondir, clientdata.userid, session.hash_ip_addr(ip_addr)) + return (false) + end -session.expired_events(conf.sessiondir) - if id_user and password_user then - local password_user_md5 = fs.md5sum_string(password_user) - if auth.authenticate (self, id_user, password_user_md5) then - local t = auth.get_userinfo (self, id_user) + if clientdata.userid and clientdata.password then + local password_user_md5 = fs.md5sum_string(clientdata.password) + if auth.authenticate (self, clientdata.userid, password_user_md5) then + -- We have a successful login, change sessiondata + -- for some reason, can't call this function or it skips rest of logon + -- logout(sessiondir, sessiondata) + ---[[ so, do this instead + session.unlink_session(sessiondir, sessiondata.id) + -- Clear the current session data + for a,b in pairs(sessiondata) do + if a ~= "id" then sessiondata[a] = nil end + end + --]] sessiondata.id = session.random_hash(512) + local t = auth.get_userinfo (self, clientdata.userid) sessiondata.userinfo = t or {} - sessiondata.userinfo.perm = roles.get_roles_perm(self,auth.get_userinfo_roles(self,id_user)) - self.conf.prefix="/acf-util/" - self.conf.action="status" - self.conf.type="redir" - self.conf.controller="logon" - error(self.conf) + return (true) else - userid.errtxt = "Information not recognized" - session.record_event(conf.sessiondir, id_user, session.hash_ip_addr(ENV["REMOTE_ADDR"])) - return (cfe {type="form", - option={script=ENV["SCRIPT_NAME"], - prefix=self.conf.prefix, - controller=self.conf.controller, - action="logon" }, - value={userid,password,logon},testme={counteven} - }) + -- We have a bad login, log the event + session.record_event(sessiondir, clientdata.userid, session.hash_ip_addr(ip_addr)) end - else - return ( cfe{ type="form", - option={script=ENV["SCRIPT_NAME"], - prefix=self.conf.prefix, - controller=self.conf.controller, - action="logon" } , - value={userid,password,logon},testme={counteven} - }) end + return (false) end - - -- logged on? - -- record event and ignore the attempt - -- too many attempts for this ip? - -- record event and ignore the attempt - -- too many attempts for this user? - -- record event and ignore the attempt - -- uname/passwd invalid? - -- record event and ignore the attempt - -- All ok? - -- look up their role, issue new session - - --this goes through and will return true or false if limit reached -logoff = function (self, sessdata) - -- sessionid invalid? - -- record event, ignore the attempt - -- else - -- unlink session - -- issue new sessionid - - --made it so that we get a new sessionid then try to delete it - --need to make the whole sessiondata table go bye bye - delsess = session.unlink_session(conf.sessiondir, sessdata) - if delsess == true then - logoff = "Successful" - else - logoff = "Incomplete or Unsuccessful logoff" - end - for a,b in pairs(sessiondata) do - if a ~= "menu" then - sessiondata[a] = nil +-- Return the session id and username +status = function(sessiondata) + local name = "unknown" + if sessiondata.userinfo and sessiondata.userinfo.username then + name = sessiondata.userinfo.username end - end - sessiondata.id = session.random_hash(512) - build_menus(self) - return ( cfe{ {value=logoff,name="logoff"},{value=sessiondata,name="sessiondata"} }) -end - -status = function(self, sessdata) - sessid = sessdata - checkme = session.check_session(self.conf.sessiondir,sessdata) - return ( cfe { checkme={value=checkme,name="checkme"}, sessid={value=sessid,name="sessid" } }) + return ( { sessionid = sessiondata.id, username = name } ) end diff --git a/app/acf-util/logon-status-html.lsp b/app/acf-util/logon-status-html.lsp index 3524716..072051d 100644 --- a/app/acf-util/logon-status-html.lsp +++ b/app/acf-util/logon-status-html.lsp @@ -1,5 +1,8 @@ <? local view= ... ?> +<? --[[ + io.write(html.cfe_unpack(view)) +--]] ?> <h1>User Status </h1> <p> Below is your current Session id <p> -<?= view.stats.sessid.value ?> -<p>You are currently known to the system as <?= view.stats.checkme.value ?>.</p> +<?= view.sessionid ?> +<p>You are currently known to the system as <?= view.username ?>.</p> diff --git a/app/acf-util/password-controller.lua b/app/acf-util/password-controller.lua index 185c3e4..f891c58 100755 --- a/app/acf-util/password-controller.lua +++ b/app/acf-util/password-controller.lua @@ -1,18 +1,10 @@ module(..., package.seeall) -auth=require("authenticator-plaintext") - -local list_redir = function (self) - self.conf.action = "status" - self.conf.type = "redir" - error (self.conf) -end +local auth=require("authenticator-plaintext") mvc = {} mvc.on_load = function(self, parent) - if (self.worker[self.conf.action] == nil ) or ( self.conf.action == "init" ) then - self.worker[self.conf.action] = list_redir(self) - end + self.conf.default_action = "status" end local function admin_permission() diff --git a/app/acf-util/roles-controller.lua b/app/acf-util/roles-controller.lua index b8fa7f4..4cf1937 100644 --- a/app/acf-util/roles-controller.lua +++ b/app/acf-util/roles-controller.lua @@ -2,17 +2,9 @@ module (..., package.seeall) ---require ("session") - -mvc.on_load = function(self, parent) - if (self.worker[self.conf.action] == nil ) or ( self.conf.action == "init" ) then - self.worker[self.conf.action] = list_redir(self) - end - --logit ("logon.mvc.on_load activated") - end - read = function(self) - return( {read= self.model:read(clientdata.sessionid)}) + --return( {read= self.model:read(clientdata.sessionid)}) + return ( { userid = self.sessiondata.userinfo.userid, roles = self.sessiondata.userinfo.roles, permissions = self.sessiondata.permissions } ) end getlist = function(self) diff --git a/app/acf-util/roles-getlist-html.lsp b/app/acf-util/roles-getlist-html.lsp index 48c2aba..25d8d62 100644 --- a/app/acf-util/roles-getlist-html.lsp +++ b/app/acf-util/roles-getlist-html.lsp @@ -1,7 +1,13 @@ <? local view= ... ?> +<? --[[ + io.write(html.cfe_unpack(view)) +--]] ?> + <h1>Controller Status</h1> -<? for a,b in pairs(view.contlist.value) do -print("<b>",a,"</b>") -for k,v in pairs(b) do print(v) end -print("<br>") -end ?> +<? ---[[ +for a,b in pairs(view.contlist) do + print("<b>",a,"</b>") + for k,v in pairs(b) do print(v) end + print("<br>") +end +--]] ?> diff --git a/app/acf-util/roles-model.lua b/app/acf-util/roles-model.lua index 95f28d1..c3ce2c7 100644 --- a/app/acf-util/roles-model.lua +++ b/app/acf-util/roles-model.lua @@ -1,27 +1,17 @@ -- Roles/Group model functions -require ("session") require ("roles") module (..., package.seeall) -read = function(self,sessionid) - useid , theroles = session.check_session(conf.sessiondir,sessionid,"roles") ---we need to expand roles to give us real perm list - perm = roles.get_roles_perm(self,theroles) - return ( cfe { userid={value=useid,name="userid"},roles={ value=theroles,name="roles"}, perm={value=perm,name="perm"},{value=self.conf,name="self"},{value=sessiondata.userinfo.perm,name="perm2"} }) -end - getcont = function(self) --need to get a list of all the controllers - --t = roles.get_controllers(self,"skins") - bobo = roles.get_controllers(self) + controllers = roles.get_controllers(self) local table_m = {} - for a,b in pairs(bobo) do + for a,b in pairs(controllers) do temp = roles.get_controllers_func(self,b) table_m[b.sname] = temp end - return (cfe {value=table_m,name="mtable"}) - + return (table_m) end diff --git a/app/acf-util/roles-read-html.lsp b/app/acf-util/roles-read-html.lsp index c5ea541..ddda93a 100644 --- a/app/acf-util/roles-read-html.lsp +++ b/app/acf-util/roles-read-html.lsp @@ -1,11 +1,28 @@ <? local view= ... ?> -<h1>Role Views</h1> -<p>Roles/Permission list for <?= view.read.userid.value ?>:<p> +<? --[[ + io.write(html.cfe_unpack(view)) +--]] ?> -<p>You are valid in these role <p> -<? for a,b in pairs(view.read.roles.value) do -print("<li>",b) end ?> +<? ---[[ ?> +<H1>Roles/Permission list for <?= view.userid ?>:</H1> -<p>Your full permissions are<p> -<?= view.read.perm.value ?> -<?= html.cfe_unpack(view) ?> +<? if view.roles then ?> + <H2>You are valid in these roles</H2> + <? for a,b in pairs(view.roles) do + print("<li>",b,"</li>") + end ?> +<? end ?> +<? --]] ?> + +<? ---[[ ?> +<? if view.permissions then ?> + <H2>Your full permissions are</H2> + <? for x,cont in pairs(view.permissions) do + print("<b>",x,"</b>") + for y,act in pairs(cont) do + print(y) + end + print("<br>") + end ?> +<? end ?> +<? --]] ?> diff --git a/app/acf_www-controller.lua b/app/acf_www-controller.lua index 59c3f72..0dfbea7 100644 --- a/app/acf_www-controller.lua +++ b/app/acf_www-controller.lua @@ -15,82 +15,47 @@ require "posix" -- We use the parent exception handler in a last-case situation local parent_exception_handler -function build_menus(self) - --Build the menu +local function build_menus(self) m=require("menubuilder") roll = require ("roles") - form = require ("format") - sessiondata.menu = {} - sessiondata.menu.mainmenu = m.get_menuitems(self.conf.appdir) - sessiondata.menu.submenu = m.get_submenuitems(self.conf.appdir) - -local temp -if sessiondata.userinfo == nil then - --we are dealing with an unknown user -p = {"ANONYMOUS"} - --this will be whatever the "UNKNOWN" role is ... right now it is ANONYMOUS - --temp should be the -temp = format.string_to_table(roll.get_roles_perm(self,p),",") -else - --we don't need to figure out what permission have it is in sessiondata -temp = format.string_to_table(sessiondata.userinfo.perm,",") -end - --lets apply permissions - for a,b in pairs(sessiondata.menu.mainmenu) do - for k,v in pairs(temp) do - local control,acti = string.match(v,"(%a+):(%a+)") - if sessiondata.menu.mainmenu[a].controller == control then - if sessiondata.menu.mainmenu[a].action == acti then - sessiondata.menu.mainmenu[a].match = "yes" - break - else - sessiondata.menu.mainmenu[a].match = "no" - end - else - sessiondata.menu.mainmenu[a].match = "no" - end - if sessiondata.menu.mainmenu[a].controller == "menuhints" then - sessiondata.menu.mainmenu[a].match = "yes" - end - end + -- Build the permissions table + local roles = {} + if sessiondata.userinfo and sessiondata.userinfo.roles then + roles = sessiondata.userinfo.roles end - --also need to do the submenu tabs... this is only for appearence - --will have to make sure somewhere else they can't run them :) - for a,b in pairs(sessiondata.menu.submenu) do - for c,d in pairs(sessiondata.menu.submenu[a]) do - for k,v in pairs(temp) do - local control,acti = string.match(v,"(%a+):(%a+)") - if (a == control) then - if sessiondata.menu.submenu[a][c].action == acti then - sessiondata.menu.submenu[a][c].match = "yes" - break + local permissions = roll.get_roles_perm(self.conf.appdir,roles) + sessiondata.permissions = permissions + + --Build the menu + local cats = m.get_menuitems(self.conf.appdir) + -- now, loop through menu and remove actions without permission + -- go in reverse so we can remove entries while looping + for x = #cats,1,-1 do + local cat = cats[x] + for y = #cat.groups,1,-1 do + local group = cat.groups[y] + if nil == permissions[group.controller] then + table.remove(cat.groups, y) else - sessiondata.menu.submenu[a][c].match = "no" - end + for z = #group.tabs,1,-1 do + local tab = group.tabs[z] + if nil == permissions[group.controller][tab.action] then + table.remove(group.tabs, z) + end + end + if 0 == #group.tabs then + table.remove(cat.groups, y) + end end - end - end - end - - local temptab = {} - for a,b in pairs(sessiondata.menu.mainmenu) do - if sessiondata.menu.mainmenu[a].match ~= "no" then - temptab[#temptab +1 ] = sessiondata.menu.mainmenu[a] - end - end - - sessiondata.menu.mainmenu = temptab - local tempsub = {} - for c,d in pairs(sessiondata.menu.submenu) do - for e,f in pairs(sessiondata.menu.submenu[c]) do - if sessiondata.menu.submenu[c][e].match ~= "no" then - if not (tempsub[c]) then tempsub[c] = {} end - tempsub[c][#tempsub[c] +1] = sessiondata.menu.submenu[c][e] end - end + if 0 == #cat.groups then + table.remove(cats, x) + end end - sessiondata.menu.submenu = tempsub + sessiondata.menu = {} + sessiondata.menu.cats = cats + -- Debug: Timestamp on menu creation sessiondata.menu.timestamp = {tab="Menu_created: " .. os.date(),action="Menu_created: " .. os.date(),} end @@ -104,7 +69,8 @@ mvc.on_load = function (self, parent) self.conf.libdir = self.conf.libdir or ( self.conf.appdir .. "/lib/" ) self.conf.sessiondir = self.conf.sessiondir or "/tmp/" self.conf.appuri = "https://" .. ENV.HTTP_HOST .. ENV.SCRIPT_NAME - self.conf.default_controller = "welcome" + self.conf.default_controller = "welcome" + self.conf.default_action = "read" self.clientdata = FORM self.conf.clientip = ENV.REMOTE_ADDR @@ -115,68 +81,66 @@ mvc.on_load = function (self, parent) sessionlib=require ("session") + -- before we look at sessions, remove old sessions and events + -- this prevents us from giving a "session timeout" message, but I'm ok with that + sessionlib.expired_events(self.conf.sessiondir) + -- Load the session data + self.sessiondata = nil self.sessiondata = {} - - local tempid = "" - if self.clientdata.sessionid == nil then - self.sessiondata.id = sessionlib.random_hash(512) - tempid = self.sessiondata.id - if not (self.sessiondata.menu) then - build_menus(self) - end - - else + if nil ~= self.clientdata.sessionid then + logevent("Found session id = " .. self.clientdata.sessionid) + -- Load existing session data local timestamp - tempid = self.clientdata.sessionid timestamp, self.sessiondata = sessionlib.load_session(self.conf.sessiondir, self.clientdata.sessionid) - build_menus(self) if timestamp == nil then - self.sessiondata.id = tempid + -- invalid session id, report event and create new one sessionlib.record_event(self.conf.sessiondir, sessionlib.hash_ip_addr(self.conf.clientip)) - build_menus(self) + logevent("Didn't find session") else - - -- FIXME: This is probably wrong place to generate the menus - - local now = os.time() - local minutes_ago = now - (sessionlib.minutes_expired_events * 60) - if timestamp < minutes_ago then - sessionlib.unlink_session(self.conf.sessiondir, self.clientdata.sessionid) - sessiondata.id = sessionlib.random_hash(512) - sessionlib.count_events(self.conf.sessiondir,self.conf.userid or "", sessionlib.hash_ip_addr(self.conf.clientip),sessionlib.limit_count_events) - --[[ - FIXME --- need to write this function - if too many bad events for this ip invaidate the session - - if (timestamp is > 10 minutes old) - sessionlib.unlink.session (self.conf.sessiondir, - self.sessiondata.id) - self.sessiondata = {} - self.sessiondata.id = sessionlib.random_hash(512) - generate flash message "Inactivity logout" - end - ]]-- - sessionlib.expired_events(self.conf.sessiondir,sessionlib.minutes_expired_events) - build_menus(self) + logevent("Found session") + -- We read in a valid session, check if it's ok + if sessionlib.count_events(self.conf.sessiondir,self.conf.userid or "", sessionlib.hash_ip_addr(self.conf.clientip)) then + logevent("Bad session, erasing") + -- Too many events on this id / ip, kill the session + sessionlib.unlink_session(self.conf.sessiondir, self.clientdata.sessionid) + self.sessiondata.id = nil end end end + + if nil == self.sessiondata.id then + self.sessiondata = {} + self.sessiondata.id = sessionlib.random_hash(512) + logevent("New session = " .. self.sessiondata.id) + end + if nil == self.sessiondata.permissions or nil == self.sessiondata.menu then + logevent("Build menus") + build_menus(self) + end end +mvc.check_permission = function(self, controller, action) + logevent("Trying " .. (controller or "nil") .. ":" .. (action or "nil")) + if nil == self.sessiondata.permissions then return false end + if controller then + if nil == self.sessiondata.permissions[controller] then return false end + if action and nil == self.sessiondata.permissions[controller][action] then return false end + end + return true +end mvc.post_exec = function (self) sessionlib=require ("session") -- sessionlib.serialize("s", sessiondata)) if sessiondata.id then - sessionlib.save_session(conf.sessiondir, - sessiondata.id, sessiondata) + sessionlib.save_session(conf.sessiondir, sessiondata) end -- Close the logfile - conf.logfile:close() + conf.logfile:close() end @@ -249,15 +213,13 @@ view_resolver = function(self) local m,worker_loaded,model_loaded = self:new("alpine-baselayout/hostname") local alpineversion = self:new("alpine-baselayout/alpineversion") - -- FIXME - this is ugly, but it puts the hostname the expected - -- format if the controller doesn't load correctly - local h = {} - -- If the worker and model loaded correctly, then -- use the sub-controller + local h if worker_loaded and model_loaded then h = m.worker.read(m) else + h = {} h.hostname = { value = "unknown" } end @@ -273,18 +235,9 @@ view_resolver = function(self) skin = self.conf.skin or "" } - -- Fetch the menu's from sessiondata (filter out what's needed) - local menu = self.sessiondata.menu.mainmenu - local submenu = self.sessiondata.menu.submenu[pageinfo.controller] - ---[[ -- DEBUG: Next row's is to display when the menu was created (see function build_menus(self) in BOF) - if (submenu) then - submenu[99] = sessiondata.menu.timestamp - end ---]] return function (viewtable) local template = haserl.loadfile (template) - return template ( pageinfo, menu, submenu, viewtable, self.sessiondata ) + return template ( pageinfo, viewtable, self.sessiondata ) end end @@ -298,7 +251,10 @@ end exception_handler = function (self, message ) local html = require ("html") - mvc.post_exec (self) + pcall(function() + if sessiondata.id then logevent("Redirecting " .. sessiondata.id) end + mvc.post_exec (self) + end) -- don't want exceptions from this if type(message) == "table" then if message.type == "redir" then io.write ("Status: 302 Moved\n") @@ -315,7 +271,7 @@ exception_handler = function (self, message ) elseif message.type == "dispatch" then parent_exception_handler(self, message) end - else + else parent_exception_handler( self, message) end end diff --git a/app/template-html.lsp b/app/template-html.lsp index 473674a..cbb5c29 100644 --- a/app/template-html.lsp +++ b/app/template-html.lsp @@ -1,6 +1,5 @@ -<? local pageinfo , mainmenu, submenu, viewtable, session = ... - html=require("html") - sess=require("session") ?> +<? local pageinfo , viewtable, session = ... + html=require("html") ?> Status: 200 OK Content-Type: text/html <? if (session.id) then @@ -38,11 +37,10 @@ Content-Type: text/html <p> <? local ctlr = pageinfo.script .. "/acf-util/logon/" - sname = sess.check_session("/tmp", session.id) - if sname == "an unknown user" then - io.write ( string.format("\t\t\t\t\t\t<a href=\"%s\">Log in</a>\n", ctlr .. "logon" ) ) + if session.userinfo and session.userinfo.userid then + io.write ( string.format("\t\t\t\t\t\t<a href=\"%s\">Log out as '" .. session.userinfo.userid .. "'</a>\n", ctlr .. "logout" ) ) else - io.write ( string.format("\t\t\t\t\t\t<a href=\"%s\">Log out as '" .. sname .. "'</a>\n", ctlr .. "logout" ) ) + io.write ( string.format("\t\t\t\t\t\t<a href=\"%s\">Log in</a>\n", ctlr .. "logon" ) ) end ?> | <a href="/">home</a> | @@ -61,37 +59,25 @@ Content-Type: text/html </div> <? - -- FIXME: This needs to go in a library function somewhere (menubuilder?) + local class + local tabs io.write ( "<ul>") - - local cat, group - local class - for k,v in ipairs(mainmenu) do - if v.cat ~= cat then - if not (cat == nil) and not (cat == "") then - io.write ("\t\t\t\t\t</ul>") - end - cat = v.cat - if (cat ~= "") then -- Filter out empty categories - io.write (string.format("\n\t\t\t\t<li>%s\n\t\t\t\t\t<ul>\n", cat)) --start row - end - group = "" - end - if v.group ~= group then - group = v.group - if pageinfo.prefix == v.prefix .. "/" and - pageinfo.controller == v.controller then + for x,cat in ipairs(session.menu.cats) do + io.write (string.format("\n\t\t\t\t<li>%s\n\t\t\t\t\t<ul>\n", cat.name)) --start row + for y,group in ipairs(cat.groups) do + if pageinfo.prefix == group.prefix .. '/' and pageinfo.controller == group.controller then class="class='selected'" + tabs = group.tabs else class="" end io.write (string.format("\t\t\t\t\t\t<li %s><a href=\"%s%s/%s/%s\">%s</a></li>\n", - class,ENV.SCRIPT_NAME,v.prefix, v.controller, v.action, v.group )) + class,ENV.SCRIPT_NAME,group.prefix, group.controller, group.tabs[1].action, group.name )) end - end ?> - </ul> - </li> - </ul> + io.write ( "\t\t\t\t\t</ul>" ) + end + io.write ( "\n\t\t\t\t</li>\n\t\t\t</ul>\n") + ?> <div class="tailer"> </div> @@ -116,13 +102,13 @@ Content-Type: text/html </div> <? local class="" ?> - <? for k,v in pairs(submenu or {}) do - if submenu[k]["action"] == pageinfo.action then + <? for x,tab in pairs(tabs or {}) do + if tab.action == pageinfo.action then class="class='selected'" else class="" end - io.write (string.format('\t\t\t<a %s href="%s">%s</a>\n',class,submenu[k]["action"],submenu[k]["tab"] )) + io.write (string.format('\t\t\t<a %s href="%s">%s</a>\n',class,tab.action,tab.name )) end ?> diff --git a/app/welcome-controller.lua b/app/welcome-controller.lua index 450952c..3ff5cc5 100644 --- a/app/welcome-controller.lua +++ b/app/welcome-controller.lua @@ -1,27 +1,8 @@ -- A standin controller for testing - module (..., package.seeall) --- Cause an http redirect to our "read" action --- We use the self.conf table because it already has prefix,controller,etc --- The redir code is defined in the application error handler (acf-controller) -local list_redir = function (self) - self.conf.action = "read" - self.conf.type = "redir" - error (self.conf) -end - -mvc = {} -mvc.on_load = function(self, parent) - -- It doesn't matter what action they choose - we only support read - if ( self.conf.action ~= "read") then - list_redir(self) - end -end - - read = function (self ) - return ( { } ) + return ( {self = self} ) end diff --git a/app/welcome-html.lsp b/app/welcome-html.lsp index 7f205fe..b961a83 100644 --- a/app/welcome-html.lsp +++ b/app/welcome-html.lsp @@ -1,3 +1,5 @@ <? view = ... ?> <h1>Alpine Configuration Framework</h1> <p>Welcome.</p> + +<? io.write(html.cfe_unpack(view)) ?> diff --git a/lib/menubuilder.lua b/lib/menubuilder.lua index 1d4c974..c7ff075 100644 --- a/lib/menubuilder.lua +++ b/lib/menubuilder.lua @@ -10,157 +10,115 @@ module(..., package.seeall) -- startdir should be the app dir. local get_candidates = function (startdir) local t = {} - startdir = startdir .. "/" local fh = io.popen('find ' .. startdir .. ' -name "*.menu"') - - local start = string.gsub(startdir, "/$", "") for x in fh:lines() do - table.insert (t, (string.gsub(x, start, ""))) + t[#t + 1] = x end - return t end +-- Split string into priority and name, convert '_' to space +local parse_menu_entry = function (entry) + local name, priority + if (string.match(entry, "^%d")) then + priority, name = string.match(entry, "(%d+)(.*)") + else + name = entry + end + name = string.gsub(name, "_", " ") + return name, priority +end --- internal function for table.sort -local t_compare = function (x,y,f) - for k,v in pairs(f) do - local a = x[v] - local b = y[v] - if tonumber(a) and tonumber(b) then - a=tonumber(a) - b=tonumber(b) - end - if a < b then return true end - if a > b then return false end +-- Parse menu file entry, returning cat, group, tab, action and priorities +local parse_menu_line = function (line) + local result = nil + --skip comments and blank lines + if nil == (string.match(line, "^#") or string.match(line,"^$")) then + local item = {} + for i in string.gmatch(line, "%S+") do + item[#item + 1] = i end - return false - end - --- Returns a table of all submenu items found --- Displayorder of the tabs comes from the order in the .menu files -get_submenuitems = function (startdir) - local t = {} - local menuitems = get_menuitems(startdir) - - for k,v in pairs(menuitems) do - if (menuitems[k]["tab"] ~= "") then - if not (t[menuitems[k]["controller"]]) then t[menuitems[k]["controller"]] = {} end - table.insert (t[menuitems[k]["controller"]], {tab=menuitems[k]["tab"],action=menuitems[k]["action"]}) + if #item >= 1 then + result = {} + result.cat, result.cat_prio = parse_menu_entry(item[1]) + if (item[2]) then result.group, result.group_prio = parse_menu_entry(item[2]) end + if (item[3]) then result.tab = parse_menu_entry(item[3]) end + if (item[4]) then result.action = parse_menu_entry(item[4]) end end end + return result +end - return t +-- Function to compare priorities, missing priority moves to the front, same priority sorted alphabetically +local prio_compare = function(x,y) + if x.priority == y.priority then + if x.name < y.name then return true end + return false + end + if nil == x.priority then return true end + if nil == y.priority then return false end + if tonumber(x.priority) < tonumber(y.priority) then return true end + return false end -- returns a table of all the menu items found, sorted by priority --- Table format: prefix controller cat group tab action get_menuitems = function (startdir) - local t = {} - for k,v in pairs(get_candidates(startdir)) do - local prefix, controller = mvc.dirname(v), mvc.basename(v, ".menu") - -- open the thing, and parse the contents - local fh = io.open(startdir .. "/" .. v) - local prio = 10 - for x in fh:lines() do - local c = string.match(x, "^#") or string.match(x,"^$") - if c == nil then - local item = {} - for i in string.gmatch(x, "%S+") do - table.insert(item, i) + local cats = {} + local reversecats = {} + startdir = (string.gsub(startdir, "/$", "")) --remove trailing / + for k,filename in pairs(get_candidates(startdir)) do + local controller = mvc.basename(filename, ".menu") + local prefix = (string.gsub(mvc.dirname(filename), startdir, "")) + + -- open the menu file, and parse the contents + local handle = io.open(filename) + for x in handle:lines() do + local result = parse_menu_line(x) + if result then + for i = 1,1 do -- loop so break works + -- Add the category + if nil == reversecats[result.cat] then + table.insert ( cats, + { name=result.cat, + groups = {}, + reversegroups = {} } ) + reversecats[result.cat] = #cats + end + local cat = cats[reversecats[result.cat]] + cat.priority = cat.priority or result.cat_prio + -- Add the group + if nil == result.group then break end + if nil == cat.groups[cat.reversegroups[result.group]] then + table.insert ( cat.groups, + { name = result.group, + controller = controller, + prefix = prefix, + tabs = {} } ) + cat.reversegroups[result.group] = #cat.groups + end + local group = cat.groups[cat.reversegroups[result.group]] + group.priority = group.priority or result.group_prio + -- Add the tab + if nil == result.tab or nil == result.action then break end + local tab = { name = result.tab, action = result.action } + table.insert(group.tabs, tab) end - table.insert(t, { prefix=prefix, - controller=controller, - catprio="nan", - cat=item[1] or "", - groupprio="nan", - group=item[2] or "", - tabprio=tostring(prio), - tab=item[3] or "", - action=item[4] or "" }) - prio=prio+5 - end - end - fh:close() - end - -- Ok, we now have the raw menu table - -- now try to parse out numbers in front of any cat, group or tabs - for x in ipairs(t) do - local f = t[x] - if (string.match(f.cat, "^%d")) then - f.catprio, f.cat = string.match(f.cat, "(%d+)(.*)") - end - if (string.match(f.group, "^%d")) then - f.groupprio, f.group = string.match(f.group, "(%d+)(.*)") - end - if (string.match(f.tab, "^%d")) then - f.tabprio, f.tab = string.match(f.tab, "(%d+)(.*)") - end - end - - -- Convert underscores to spaces - for x in ipairs(t) do - t[x].cat = string.gsub(t[x].cat, "_", " ") - t[x].group = string.gsub(t[x].group, "_", " ") - t[x].tab = string.gsub(t[x].tab, "_", " ") - end - - -- Now alpha sort - table.sort(t, function(x,y) - return t_compare (x,y,{"cat", "catprio", "group", "groupprio", "tab", "tabprio"} ) - end) - - -- Fill in the priorities - local fill_prio = function (t, start, stop, col) - local prio = t[start][col] - if prio == "nan" then prio = "0" end - while start <= stop do - t[start][col] = prio - start = start + 1 - end - end - - --- Fill in the priorities --- Warning - UGLY code ahead. --- Basic rules, for each cat and group, if the prio is nan, then set it --- to the lowest value for that group or cat. - local k = 1 - while ( k <= table.maxn(t) ) do - local c = k - while ( c <= table.maxn(t) and t[c].cat == t[k].cat ) do - c=c+1 - end - c=c-1 -- back up one - we only want whats the same - fill_prio(t,k,c,"catprio") - -- from k,c is a mini table, do the same for groupprio - local g = k - while ( g <= c ) do - local h = g - while ( h <= c and t[h].group == t[g].group ) do - h=h+1 end - h=h-1 --- back up one (again) - fill_prio(t,g,h,"groupprio") - g=h+1 end - k = c + 1 + handle:close() end - - -- Now priority sort - table.sort(t, function(x,y) - return t_compare (x,y,{"catprio", "cat", "groupprio", "group", "tabprio", "tab"} ) - end) + -- Now that we have the entire menu, sort by priority + -- Categories first + table.sort(cats, prio_compare) - -- drop the priorities - they were internal - for k,v in ipairs(t) do - v.catprio = nil - v.groupprio = nil - v.tabprio = nil + -- Then groups + for x, cat in ipairs(cats) do + cat.reversegroups = nil -- don't need reverse table anymore + table.sort(cat.groups, prio_compare) end - return t + return cats end diff --git a/lib/roles.lua b/lib/roles.lua index bdaf635..806be67 100644 --- a/lib/roles.lua +++ b/lib/roles.lua @@ -7,75 +7,102 @@ require ("format") module (..., package.seeall) +-- Return a list of *controller.lua files list_controllers = function(self) -local list = {} -local f = io.popen("/usr/bin/find /usr/share/acf/ |/bin/grep \"controller.lua$\" ") - for a in f:lines() do - list[#list + 1 ] = a - end -f:close() -return list + local list = {} + local f = io.popen("/usr/bin/find /usr/share/acf/ |/bin/grep \"controller.lua$\" ") + for a in f:lines() do + list[#list + 1 ] = a + end + f:close() + return list end +-- Return information about all or specified controller files get_controllers = function(self,controller) --we get all the controllers local list = roles.list_controllers() --we need to grab the directory and name of file local temp = {} for k,v in pairs(list) do - path = string.match(v,"[/%w-]+/") - filename = string.match(v,"[^/]*.lua") - name = string.match(filename,"[^.]*") - sname = string.match(filename,"[^-]*") - temp[sname] = {path=path,filename=filename,name=name,sname=sname} + path = string.match(v,"[/%w-]+/") + filename = string.match(v,"[^/]*.lua") + name = string.match(filename,"[^.]*") + sname = string.match(filename,"[^-]*") + temp[sname] = {path=path,filename=filename,name=name,sname=sname} + end + if controller then + return temp[controller] + else + return temp end - if controller then - return temp[controller] - else - return temp - end - end +-- Find all public functions in a controller get_controllers_func = function(self,controller_info) if controller_info == nil then - return "Could not be processed" + return "Could not be processed" else package.path=package.path .. ";" .. controller_info.path .. "?.lua" temp = require (controller_info.name) temp1 = {} for a,b in pairs(temp) do - local c = string.match(a,"mvc") or string.match(a,"^_") - if c == nil then - temp1[#temp1 +1] = a + local c = string.match(a,"mvc") or string.match(a,"^_") + if c == nil then + temp1[#temp1 +1] = a + end end -end --require (controller_info.name) --we need to go through bobo and take out the mvc func and locals and -- return temp1 end end -get_roles_perm = function(self,roles) - --for now we are using the file static - --this will go through and search from the roles in sessionid to get the real - --permission list - local rolesfile = "/etc/acf/roles" - f = fs.read_file_as_array(rolesfile) - local temp = {} - for k,v in pairs(roles) do - for a,b in pairs(f) do - match = "^" .. v - c = string.match(b,match) - if c then - inval = string.match(b,"[,%w:]+$") - temp[#temp +1] = inval - end +-- returns a table of the *.roles files +-- startdir should be the app dir +local get_roles_candidates = function (startdir) + local t = {} + local fh = io.popen('find ' .. startdir .. ' -name "*.roles"') + for x in fh:lines() do + t[#t + 1] = x + end + return t +end + +-- Go through the roles files and determine the permissions for the specified roles +get_roles_perm = function(startdir,roles) + permissions = {} + + -- find all of the roles files and add in the master file + local rolesfiles = get_roles_candidates(startdir) + rolesfiles[#rolesfiles + 1] = "/etc/acf/roles" + + local reverseroles = {} + for x,role in ipairs(roles) do + reverseroles[role] = {} + end + reverseroles["ALL"] = {} -- always include ALL role + + for x,file in ipairs(rolesfiles) do + f = fs.read_file_as_array(file) + for y,line in pairs(f) do + if reverseroles[string.match(line,"^[%a]+")] then + temp = format.string_to_table(string.match(line,"[,%a:]+$"),",") + for z,perm in pairs(temp) do + local control,action = string.match(perm,"(%a+):(%a+)") + if control then + if nil == permissions[control] then + permissions[control] = {} + end + if action and nil == permissions[control][action] then + permissions[control][action] = {} + end + end + end + end end end - temp1 = format.table_to_string(temp,",") - --we now can return the first level of roles perms. What if a role is a member of a role... - return temp1 + return permissions end diff --git a/lib/session.lua b/lib/session.lua index 18a0c7b..bd2bdf6 100644 --- a/lib/session.lua +++ b/lib/session.lua @@ -91,29 +91,30 @@ function serialize (name, value, saved ) return str end -save_session = function( sessionpath, session, sessiontable) - local file = io.open(sessionpath .. "/session." .. session , "w") - if file == nil then - return nil - end - - local id = sessiontable.id +-- Save the session (unless all it contains is the id) +-- return true or false for success +save_session = function( sessionpath, sessiontable) + if nil == sessiontable or nil == sessiontable.id then return false end - -- clear the id key + -- clear the id key, don't need to store that + local id = sessiontable.id sessiontable.id = nil - -- count the keys - local count = 0 - for k,v in pairs (sessiontable) do - count = count + 1 - end + -- If the table only has an "id" field, then don't save it - if count > 1 and file then + if #sessiontable then + local file = io.open(sessionpath .. "/session." .. id , "w") + if file == nil then + sessiontable.id=id + return false + end + file:write ( "-- This is an ACF session table.\n") file:write ( "\nlocal " ) file:write ( serialize("s", sessiontable) ) file:write ( "return s\n") + file:close() end - file:close() + sessiontable.id=id return true end @@ -121,8 +122,9 @@ end -- Loads a session -- Returns a timestamp (when the session data was saved) and the session table. --- We insert a "id" field from the "session" +-- Insert the session into the "id" field load_session = function ( sessionpath, session ) + if type(session) ~= "string" then return nil, {} end local s = {} -- session can only have b64 characters in it session = string.gsub ( session or "", "[^" .. b64 .. "]", "") @@ -140,7 +142,8 @@ load_session = function ( sessionpath, session ) end end --- unlinks a session +-- Unlinks a session (deletes the session file) +-- return nil for failure, ?? for success unlink_session = function (sessionpath, session) if type(session) ~= "string" then return nil end local s = string.gsub (session, "[^" .. b64 .. "]", "") @@ -152,28 +155,6 @@ unlink_session = function (sessionpath, session) return statos end ---need to see if this is a "real"-user session or just a temp one. -check_session = function (sessionpath, session ) - if session == nil then return "an unknown user" end - local fullpath = sessionpath .. "/session." .. session - if posix.stat(fullpath) == nil then return "an unknown user" end - if type(session) ~= "string" then return nil end - local s = string.gsub (session, "[^" .. b64 .. "]", "") - if s ~= session then - return nil - end - check_size = posix.stat(fullpath,"size") - if check_size == 0 then - return "an unknown user" - else - local c = dofile(fullpath).userinfo.userid - local d = dofile(fullpath).userinfo.roles - return c,d - end - - -end - -- Record an invalid login event -- ID would typically be an ip address or username -- the format is lockevent.id.datetime.processid @@ -197,29 +178,23 @@ count_events = function (sessionpath, id_user, ipaddr) local t = posix.glob(searchfor) if t == nil or id_user == nil or ipaddr == nil then - return false - else - - local temp = {} - for a,b in pairs(t) do - if posix.stat(b,"mtime") > minutes_ago then - temp[#temp + 1] = b end - end - - local temp2 = {} - for k,v in pairs(temp) do - local c = string.match(v,id_user) or string.match(v,ipaddr) - if c ~= nil then temp2[#temp2 + 1] = v end - end - - if #temp2 > limit_count_events then - return true + return false else - return false - end - end - + local count = 0 + for a,b in pairs(t) do + if posix.stat(b,"mtime") > minutes_ago then + if string.match(b,id_user) or string.match(b,ipaddr) then + count = count + 1 + end + end + end + if count>limit_count_events then + return true + else + return false + end end +end -- Clear events that are older than n minutes expired_events = function (sessionpath) @@ -1,5 +1,10 @@ -CREATE=password:administrator,password:status,interfaces:create,dhcp:createnet -READ=welcome:read,interfaces:read,health:system,health:storage,health:proc,health:network,health:modules,opennhrp:status,opennhrp:logfile,tinydns:status,ipsectools:status,ipsectools:logfile,openntpd:status,openntpd:logfile,logfiles:status,logfiles:view,logfiles:download,syslog:status,lbu:status,dansguardian:basic,acfupdate:read,acfupdate:status,acfupdate:log,dhcp:home,dhcp:viewleases,dhcp:viewconfig,openvpn:status,openvpn:statusinfo,openvpn:logfile,shorewall:status,shorewall:logfile,snort:status,squid:basic,quagga:status,quagga:logfile,password:editme,fetchmail:status,dhcp:status,dnscache:status,dnscache:logfile,gnats:status,gnats:query,gnats:queryresult,gnats:summary,gnats:report,chrony:status,chrony:logfile, -UPDATE=skins:update,syslog:config,syslog:expert,acfupdate:update,acfupdate:diff,skins:read,interfaces:update,interfaces:config,lbu:config,lbu:commit,lbu:expert,dansguardian:general,dansguardian:advanced,dansguardian:plain,dansguardian:edit,dansguardian:category,dhcp:dep,dhcp:help,dhcp:settings,dhcp:editnet,dhcp:editspc,fetchmail:expert,ipsectools:expert,opennhrp:expert,openntpd:expert,openntpd:config,openvpn:serverconfig,openvpn:clientconfig,openvpn:peminfo,openvpn:unknownconfig,quagga:expert,shorewall:editrecords,shorewall:config,shorewall:check,shorewall:expert,shorewall:edit,snort:expert,squid:dep,squid:authentication,squid:advanced,squid:digest,squid:saccess,tinydns:expert,dnscache:expert,chrony:expert, +ALL=welcome:read,logon:logon,logon:logout,logon:status +CREATE=password:administrator,password:status +READ=password:editme,password:save +NONE=roles:read,roles:getlist + +CREATE=interfaces:create,dhcp:createnet +READ=interfaces:read,health:system,health:storage,health:proc,health:network,health:modules,opennhrp:status,opennhrp:logfile,tinydns:status,ipsectools:status,ipsectools:logfile,openntpd:status,openntpd:logfile,logfiles:status,logfiles:view,logfiles:download,syslog:status,lbu:status,acfupdate:read,acfupdate:status,acfupdate:log,dhcp:home,dhcp:viewleases,dhcp:viewconfig,openvpn:status,openvpn:statusinfo,openvpn:logfile,shorewall:status,shorewall:logfile,snort:status,squid:basic,quagga:status,quagga:logfile,fetchmail:status,dhcp:status +UPDATE=skins:update,syslog:config,syslog:expert,acfupdate:update,acfupdate:diff,skins:read,interfaces:update,lbu:config,lbu:commit,lbu:expert,dansguardian:general,dansguardian:advanced,dansguardian:plain,dansguardian:edit,dansguardian:category,dhcp:dep,dhcp:help,dhcp:settings,dhcp:editnet,dhcp:editspc,fetchmail:expert,ipsectools:expert,opennhrp:expert,openntpd:expert,openntpd:config,openvpn:serverconfig,openvpn:clientconfig,openvpn:peminfo,openvpn:unknownconfig,quagga:expert,shorewall:editrecords,shorewall:config,shorewall:check,shorewall:expert,shorewall:edit,snort:expert,squid:dep,squid:authentication,squid:advanced,squid:digest,squid:saccess,tinydns:expert,apk:loaded,apk:available,apk:delete,apk:install DELETE=interfaces:delete,logfiles:delete,dhcp:delnet -ALL=welcome:read,health:system,lbu:status,gnats:status,gnats:query,gnats:queryresult,gnats:summary,gnats:report +ALL=health:system,lbu:status diff --git a/www/cgi-bin/mvc.lua b/www/cgi-bin/mvc.lua index da80184..2b7c8b4 100755 --- a/www/cgi-bin/mvc.lua +++ b/www/cgi-bin/mvc.lua @@ -97,37 +97,86 @@ new = function (self, modname) end -- This is a sample front controller/dispatch. -dispatch = function (self, prefix, userctlr, action) +dispatch = function (self, userprefix, userctlr, useraction) local controller local success, err = xpcall ( function () - if prefix == nil then + if userprefix == nil then self.conf.prefix, self.conf.controller, self.conf.action = parse_path_info(ENV["PATH_INFO"]) else - self.conf.prefix = prefix + self.conf.prefix = userprefix self.conf.controller = userctlr or "" - self.conf.action = action or "" + self.conf.action = useraction or "" end - -- If they didn't provide a controller, and a default was specified - -- use it - if self.conf.controller == "" and self.conf.default_controller then + -- Find the proper controller/action combo + local origconf = {controller = self.conf.controller, action = self.conf.action} + local controller = nil + local action = "" + self.conf.default_controller = self.conf.default_controller or "" + if "" == self.conf.controller then self.conf.controller = self.conf.default_controller + self.conf.action = "" end + while "" ~= self.conf.controller do + -- We now know the controller / action combo, check if we're allowed to do it + local perm = true + if type(self.worker.mvc.check_permission) == "function" then + perm = self.worker.mvc.check_permission(self, self.conf.controller) + end - controller = self:new(self.conf.prefix .. self.conf.controller) - - local action = controller.conf.action + if perm then + controller = self:new(self.conf.prefix .. self.conf.controller) + end + if controller then + controller.conf.default_action = controller.conf.default_action or "" + action = controller.conf.action or "" + if "" == action then + action = controller.conf.default_action + end + while "" ~= action do + local perm = true + if type(controller.worker.mvc.check_permission) == "function" then + perm = controller.worker.mvc.check_permission(controller, self.conf.controller, action) + end + -- Because of the inheritance, normally the + -- controller.worker.action will flow up, so that all children have + -- actions of all parents. We use rawget to make sure that only + -- controller defined actions are used on dispatch + if perm and (type(rawget(controller.worker, action)) == "function") then + -- We have a valid and permissible controller / action + self.conf.action = action + controller.conf.action = action + break + end + if action ~= controller.conf.default_action then + action = controller.conf.default_action + else + action = "" + end + end + if "" ~= action then break end + end + controller = nil + self.conf.action = "" + if self.conf.controller ~= self.conf.default_controller then + self.conf.controller = self.conf.default_controller + else + self.conf.controller = "" + end + end - -- Because of the inheritance, normally the - -- controller.worker.action will flow up, so that all children have - -- actions of all parents. We sue rawget to make sure that only - -- controller defined actions are used on dispatch -- If the controller or action are missing, raise an error - if ( type(rawget(controller.worker, action)) ~= "function") then - self.conf.type = "dispatch" - error (self.conf) + if nil == controller then + origconf.type = "dispatch" + error (origconf) + end + + -- If we have different controller / action, redirect + if self.conf.controller ~= origconf.controller or self.conf.action ~= origconf.action then + self.conf.type = "redir" + error(self.conf) end -- run the (first found) pre_exec code, starting at the controller |