diff options
| -rw-r--r-- | app/Makefile | 1 | ||||
| -rw-r--r-- | app/acf-util/acf-util.roles | 2 | ||||
| -rw-r--r-- | app/acf-util/logon-model.lua | 6 | ||||
| -rw-r--r-- | app/acf-util/password-controller.lua | 12 | ||||
| -rw-r--r-- | app/acf-util/password-listlockevents-html.lsp | 48 | ||||
| -rw-r--r-- | app/acf-util/password-model.lua | 32 | ||||
| -rw-r--r-- | app/acf-util/password-status-html.lsp | 6 | ||||
| -rw-r--r-- | app/acf-util/password.menu | 2 | ||||
| -rw-r--r-- | app/acf_www-controller.lua | 5 | ||||
| -rw-r--r-- | lib/session.lua | 70 | 
10 files changed, 147 insertions, 37 deletions
diff --git a/app/Makefile b/app/Makefile index 2b93f30..50a5821 100644 --- a/app/Makefile +++ b/app/Makefile @@ -5,6 +5,7 @@ APP_DIST= \  	acf-util/logon-controller.lua \  	acf-util/logon-model.lua \  	acf-util/password-controller.lua \ +	acf-util/password-listlockevents-html.lsp \  	acf-util/password-model.lua \  	acf-util/password-status-html.lsp \  	acf-util/password.menu \ diff --git a/app/acf-util/acf-util.roles b/app/acf-util/acf-util.roles index 926b74d..4e96271 100644 --- a/app/acf-util/acf-util.roles +++ b/app/acf-util/acf-util.roles @@ -2,4 +2,4 @@ GUEST=logon/logon,logon/logoff,logon/status,welcome/read  DEFAULT=  USER=password/editme,roles/read  EXPERT= -ADMIN=logon/logon,logon/logoff,logon/status,password/editme,password/status,password/edituser,password/newuser,password/deleteuser,roles/read,roles/getpermslist,roles/viewuserroles,roles/viewroleperms,roles/viewroles,roles/editrole,roles/deleterole,roles/newrole,welcome/read,password/status,password/edituser,password/newuser,password/deleteuser,roles/getpermslist,roles/viewuserroles,roles/viewroleperms,roles/viewroles,roles/editrole,roles/deleterole,roles/newrole,skins/update +ADMIN=logon/logon,logon/logoff,logon/status,password/editme,password/status,password/edituser,password/newuser,password/deleteuser,password/listlockevents,password/unlockuser,password/unlockip,roles/read,roles/getpermslist,roles/viewuserroles,roles/viewroleperms,roles/viewroles,roles/editrole,roles/deleterole,roles/newrole,welcome/read,password/status,password/edituser,password/newuser,password/deleteuser,roles/getpermslist,roles/viewuserroles,roles/viewroleperms,roles/viewroles,roles/editrole,roles/deleterole,roles/newrole,skins/update diff --git a/app/acf-util/logon-model.lua b/app/acf-util/logon-model.lua index 0cfba7f..3394445 100644 --- a/app/acf-util/logon-model.lua +++ b/app/acf-util/logon-model.lua @@ -45,9 +45,9 @@ end  mymodule.logon = function (self, logon)  	logon.errtxt = "Logon Attempt Failed"  	-- Check to see if we can log on this user id / ip addr -	local countevent = session.count_events(self.conf.sessiondir, logon.value.userid.value, session.hash_ip_addr(self.conf.clientip), self.conf.lockouttime, self.conf.lockouteventlimit) +	local countevent = session.count_events(self.conf.sessiondir, logon.value.userid.value, self.conf.clientip, self.conf.lockouttime, self.conf.lockouteventlimit)  	if countevent then -		session.record_event(self.conf.sessiondir, logon.value.userid.value, session.hash_ip_addr(self.conf.clientip)) +		session.record_event(self.conf.sessiondir, logon.value.userid.value, self.conf.clientip)  	end  	if false == countevent then @@ -71,7 +71,7 @@ mymodule.logon = function (self, logon)  			logon.errtxt = nil  		else  			-- We have a bad logon, log the event -			session.record_event(self.conf.sessiondir, logon.value.userid.value, session.hash_ip_addr(self.conf.clientip)) +			session.record_event(self.conf.sessiondir, logon.value.userid.value, self.conf.clientip)  		end  	end  	return logon diff --git a/app/acf-util/password-controller.lua b/app/acf-util/password-controller.lua index 264aadc..b457350 100644 --- a/app/acf-util/password-controller.lua +++ b/app/acf-util/password-controller.lua @@ -25,4 +25,16 @@ function mymodule.deleteuser(self)  	return self.handle_form(self, self.model.get_delete_user, self.model.delete_user, self.clientdata, "Delete", "Delete User", "Deleted user")  end +function mymodule.listlockevents(self) +	return self.model.list_lock_events(self, self.clientdata) +end + +function mymodule.unlockuser(self) +	return self.handle_form(self, self.model.get_unlock_user, self.model.unlock_user, self.clientdata, "Unlock", "Unlock User", "Unlocked user") +end + +function mymodule.unlockip(self) +	return self.handle_form(self, self.model.get_unlock_ip, self.model.unlock_ip, self.clientdata, "Unlock", "Unlock IP Address", "Unlocked IP address") +end +  return mymodule diff --git a/app/acf-util/password-listlockevents-html.lsp b/app/acf-util/password-listlockevents-html.lsp new file mode 100644 index 0000000..81ea7f6 --- /dev/null +++ b/app/acf-util/password-listlockevents-html.lsp @@ -0,0 +1,48 @@ +<% local view, viewlibrary, page_info, session = ...  +htmlviewfunctions = require("htmlviewfunctions") +html = require("acf.html") +%> + +<script type="text/javascript"> +	if (typeof jQuery == 'undefined') { +		document.write('<script type="text/javascript" src="<%= html.html_escape(page_info.wwwprefix) %>/js/jquery-latest.js"><\/script>'); +	} +</script> +	 +<script type="text/javascript"> +	if (typeof $.tablesorter == 'undefined') { +		document.write('<script type="text/javascript" src="<%= html.html_escape(page_info.wwwprefix) %>/js/jquery.tablesorter.js"><\/script>'); +	} +</script> +	 +<script type="text/javascript"> +	$(document).ready(function() { +		$("#list").tablesorter({ headers: {2:{sorter: 'ipAddress'}},  widgets: ['zebra']}); +	}); +</script> + + +<% htmlviewfunctions.displaycommandresults({"unlockuser", "unlockip"}, session, true) %> + +<% local header_level = htmlviewfunctions.displaysectionstart(view, page_info) %> +<table id="list" class="tablesorter"><thead> +	<tr> +		<th>User ID</th> +		<th>IP Address</th> +		<th>Time</th> +	</tr>		 +</thead><tbody> +<% for i,lock in ipairs( view.value ) do %> +	<tr> +		<td><%= html.html_escape(lock.userid) %></td> +		<td><%= html.html_escape(lock.ip) %></td> +		<td><%= format.formattime(lock.time) %></td> +	</tr> +<% end %> +</tbody></table> +<% htmlviewfunctions.displaysectionend(header_level) %> + +<% if viewlibrary and viewlibrary.dispatch_component then  +	viewlibrary.dispatch_component("unlockuser") +	viewlibrary.dispatch_component("unlockip") +end %> diff --git a/app/acf-util/password-model.lua b/app/acf-util/password-model.lua index 54faf4b..72ae416 100644 --- a/app/acf-util/password-model.lua +++ b/app/acf-util/password-model.lua @@ -2,6 +2,7 @@ local mymodule = {}  authenticator = require("authenticator")  roles = require("roles") +session = require("session")  avail_roles, avail_skins, avail_homes = nil @@ -89,6 +90,7 @@ local function get_blank_user(self)  	result.value.roles = cfe({ type="multi", value={}, label="Roles", option=avail_roles or {}, seq=3 })  	result.value.skin = cfe({ type="select", value="", label="Skin", option=avail_skins or {""}, seq=7 })  	result.value.home = cfe({ type="select", value="", label="Home", option=avail_homes or {""}, seq=6 }) +	result.value.locked = cfe({ type="boolean", value=false, label="Locked", readonly=true, seq=8 })  	return result  end @@ -96,7 +98,7 @@ end  local function get_user(self, userid)  	local result = get_blank_user(self)  	result.value.userid.key = true -	result.value.userid.value = userid +	result.value.userid.value = userid or ""  	if result.value.userid.value ~= "" then  		result.value.userid.readonly = true @@ -109,6 +111,7 @@ local function get_user(self, userid)  				if result.value[n] and n ~= "password" then result.value[n].value = v end  			end  		end +		result.value.locked.value = session.count_events(self.conf.sessiondir, result.value.userid.value)  	end  	return result @@ -204,7 +207,6 @@ function mymodule.get_users(self)  	for x,user in pairs(userlist) do  		users[#users+1] = get_user(self, user)  	end -  	return cfe({ type="group", value=users, label="User Accounts" })  end @@ -221,4 +223,30 @@ function mymodule.delete_user(self, deleteuser)  	return deleteuser  end +function mymodule.list_lock_events(self, clientdata) +	return cfe({type="structure", value=session.list_events(self.conf.sessiondir), label="Lock events"}) +end + +function mymodule.get_unlock_user(self, clientdata) +	local retval = cfe({type="group", value={}, label="Unlock user"}) +	retval.value.userid = cfe({ label="User id" }) +	return retval +end + +function mymodule.unlock_user(self, unlock) +	session.delete_events(self.conf.sessiondir, unlock.value.userid.value) +	return unlock +end + +function mymodule.get_unlock_ip(self, clientdata) +	local retval = cfe({type="group", value={}, label="Unlock IP address"}) +	retval.value.ip = cfe({ label="IP address" }) +	return retval +end + +function mymodule.unlock_ip(self, unlock) +	session.delete_events(self.conf.sessiondir, nil, unlock.value.ip.value) +	return unlock +end +  return mymodule diff --git a/app/acf-util/password-status-html.lsp b/app/acf-util/password-status-html.lsp index 551f798..8845b13 100644 --- a/app/acf-util/password-status-html.lsp +++ b/app/acf-util/password-status-html.lsp @@ -2,7 +2,7 @@  <% htmlviewfunctions = require("htmlviewfunctions") %>  <% html = require("acf.html") %> -<% htmlviewfunctions.displaycommandresults({"newuser", "edituser", "deleteuser"}, session) %> +<% htmlviewfunctions.displaycommandresults({"newuser", "edituser", "deleteuser", "unlockuser"}, session) %>  <%  local header_level = htmlviewfunctions.displaysectionstart(form, page_info) @@ -27,12 +27,16 @@ for i,user in ipairs(form.value) do  			<td style='border:none;'><b><%= html.html_escape(user.value.roles.label) %></b></td>  			<td style='border:none;'><%= html.html_escape(table.concat(user.value.roles.value, ", ")) %></td>  		</tr><tr> +			<td style='border:none;'><b><%= html.html_escape(user.value.locked.label) %></b></td> +			<td style='border:none;'><%= html.html_escape(tostring(user.value.locked.value)) %></td> +		</tr><tr>  			<td style='border:none;'><b>Option</b></td>  			<td style='border:none;'>  			<% local userid = cfe({type="hidden", value=user.value.userid.value}) %>  			<% htmlviewfunctions.displayitem(cfe({type="link", value={userid=userid, redir=redir}, label="", option="Edit", action="edituser"}), page_info, -1) %>  			<% htmlviewfunctions.displayitem(cfe({type="form", value={userid=userid}, label="", option="Delete", action="deleteuser" }), page_info, -1) %>  			<% htmlviewfunctions.displayitem(cfe({type="link", value={userid=userid}, label="", option="View Roles", action=page_info.script.."/acf-util/roles/viewuserroles"}), page_info, -1) %> +			<% if (user.value.locked.value) then htmlviewfunctions.displayitem(cfe({type="form", value={userid=userid}, label="", option="Unlock", action="unlockuser"}), page_info, -1) end %>  			</td>  		</tr>  	</tbody></table> diff --git a/app/acf-util/password.menu b/app/acf-util/password.menu index 1a079e1..be2091a 100644 --- a/app/acf-util/password.menu +++ b/app/acf-util/password.menu @@ -1,4 +1,4 @@  #CAT  		GROUP/DESC		TAB		ACTION  System		10User_management	Administration	status +System		10User_management	Lock_Events	listlockevents  System		10User_management	Edit_me		editme - diff --git a/app/acf_www-controller.lua b/app/acf_www-controller.lua index 614cd75..61dac76 100644 --- a/app/acf_www-controller.lua +++ b/app/acf_www-controller.lua @@ -277,13 +277,12 @@ mymodule.mvc.on_load = function (self, parent)  				self.clientdata.sessionid)  		if timestamp == nil then   			-- invalid session id, report event and create new one -			sessionlib.record_event(self.conf.sessiondir, nil, -				sessionlib.hash_ip_addr(self.conf.clientip)) +			sessionlib.record_event(self.conf.sessiondir, nil, self.conf.clientip)  			--self.logevent("Didn't find session")  		else  			--self.logevent("Found session")  			-- We read in a valid session, check if it's ok -			if self.sessiondata.userinfo and self.sessiondata.userinfo.userid and sessionlib.count_events(self.conf.sessiondir, self.sessiondata.userinfo.userid, sessionlib.hash_ip_addr(self.conf.clientip), self.conf.lockouttime, self.conf.lockouteventlimit) then +			if self.sessiondata.userinfo and self.sessiondata.userinfo.userid and sessionlib.count_events(self.conf.sessiondir, self.sessiondata.userinfo.userid, self.conf.clientip, self.conf.lockouttime, self.conf.lockouteventlimit) then  				--self.logevent("Bad session, erasing")  				-- Too many events on this id / ip, kill the session  				sessionlib.unlink_session(self.conf.sessiondir, self.clientdata.sessionid) diff --git a/lib/session.lua b/lib/session.lua index 34b9789..b4965ee 100644 --- a/lib/session.lua +++ b/lib/session.lua @@ -35,17 +35,17 @@ mymodule.random_hash = function (size)  end  -- FIXME: only hashes ipv4 -mymodule.hash_ip_addr = function (string) +mymodule.hash_ip_addr = function (ip)  	local str = "" -	for i in string.gmatch(string, "%d+") do +	for i in string.gmatch(ip or "", "%d+") do  		str = str .. string.format("%02x", i )  	end  	return str  end -mymodule.ip_addr_from_hash = function (string) +mymodule.ip_addr_from_hash = function (hash)  	local str = "" -	for i in string.gmatch(string, "..") do +	for i in string.gmatch(hash or "", "..") do  		str = str .. string.format("%d", "0x" .. i) .. "."  	end  	return string.sub(str, 1, string.len(str)-1) @@ -182,41 +182,45 @@ end  -- the format is lockevent.id.datetime.processid  mymodule.record_event = function( sessionpath, id_u, id_ip )  	local x = io.open (string.format ("%s/lockevent.%s.%s.%s.%s", -		 sessionpath or "/", id_u or "", id_ip or "", os.time(),  +		 sessionpath or "/", id_u or "", mymodule.hash_ip_addr(id_ip), os.time(),   		 (posix.getpid("pid")) or "" ), "w")  	io.close(x)  end --- Check how many invalid logon events --- have happened for this id in the last n minutes --- this will only effect the lockevent files -mymodule.count_events =	function (sessionpath, id_user, ipaddr, minutes, limit) -	--we need to have the counts added up? deny off any and or all +-- List invalid logon events +-- Can specify username and/or ip address to filter +mymodule.list_events =	function (sessionpath, id_user, ipaddr, minutes) +	local list = {}  	local now = os.time()  	local minutes_ago = now - ((minutes or minutes_count_events) * 60)  	local t = {}  	--give me all lockevents then we will sort through them  	local searchfor = sessionpath .. "/lockevent.*"  	local t = posix.glob(searchfor) -		 -	if t == nil or id_user == nil or ipaddr == nil then  -		return false -	else -		local count = 0 -		for a,b in pairs(t) do -			if posix.stat(b,"mtime") > minutes_ago then -				local user, ip = string.match(b, "/lockevent%.([^.]*)%.([^.]*)%.") -				if id_user == user or ipaddr == ip then -					count = count + 1 -				end + +	local ipaddrhash = mymodule.hash_ip_addr(ipaddr) +	for a,b in pairs(t) do +		if posix.stat(b,"mtime") > minutes_ago then +			local user, ip, time, pid = string.match(b, "/lockevent%.([^.]*)%.([^.]*)%.([^.]*)%.([^.]*)$") +			if user and (not id_user or id_user == user) and (not ipaddr or ipaddrhash == ip) then +				list[#list+1] = {userid=user, ip=mymodule.ip_addr_from_hash(ip), time=time, pid=pid}  			end  		end -		if count>(tonumber(limit) or limit_count_events) then -			return true -		else -			return false -		end  	end +	return list +end + +-- Check how many invalid logon events +-- have happened for this id in the last n minutes +mymodule.count_events =	function (sessionpath, id_user, ipaddr, minutes, limit) +	local locked = false +	local list = mymodule.list_events(sessionpath, id_user, ipaddr, minutes) +	if #list>(tonumber(limit) or limit_count_events) then +		locked = true +	else +		locked = false +	end +	return locked  end  -- Clear events that are older than n minutes @@ -248,4 +252,18 @@ mymodule.expired_events = function (sessionpath, minutes)   	return 0  end +mymodule.delete_events = function (sessionpath, id_user, ipaddr) +	--give me all lockevents then we will sort through them +	local searchfor = sessionpath .. "/lockevent.*" +	local t = posix.glob(searchfor) + +	local ipaddrhash = mymodule.hash_ip_addr(ipaddr) +	for a,b in pairs(t) do +		local user, ip = string.match(b, "/lockevent%.([^.]*)%.([^.]*)%.") +		if user and (not id_user or id_user == user) and (not ipaddr or ipaddrhash == ip) then +			os.remove(b) +		end +	end +end +  return mymodule  | 
