From abaa02b036f4ff58ee558a4d242cc23235f1cb8b Mon Sep 17 00:00:00 2001 From: Leslie Krause Date: Sun, 8 Jul 2018 18:14:08 -0400 Subject: [PATCH] Build 04 - general code cleanup of AuthFilter class - moved datasets into separate directory of world - added two more comparison operators for rulesets - tweaked pattern matching behavior in rulesets - changed database search method to use Lua regexes - removed hard-coded file names from database methods --- README.txt | 10 ++++++++- filter.lua | 63 ++++++++++++++++++++++++++++++------------------------ init.lua | 55 +++++++++++++---------------------------------- 3 files changed, 59 insertions(+), 69 deletions(-) diff --git a/README.txt b/README.txt index 355febe..057b375 100644 --- a/README.txt +++ b/README.txt @@ -1,4 +1,4 @@ -Auth Redux Mod v2.2b +Auth Redux Mod v2.3b By Leslie Krause Auth Redux is a drop-in replacement for the builtin authentication handler of Minetest. @@ -35,6 +35,14 @@ Version 2.2b (04-Jul-2018) - fixed type-checking of try statements in rulesets - included mod.conf and description.txt files +Version 2.3b (08-Jul-2018) + - general code cleanup of AuthFilter class + - moved datasets into separate directory of world + - added two more comparison operators for rulesets + - tweaked pattern matching behavior in rulesets + - changed database search method to use Lua regexes + - removed hard-coded file names from database methods + Installation ---------------------- diff --git a/filter.lua b/filter.lua index 7e6fe69..b98e9b1 100644 --- a/filter.lua +++ b/filter.lua @@ -1,5 +1,5 @@ -------------------------------------------------------- --- Minetest :: Auth Redux Mod v2.2 (auth_rx) +-- Minetest :: Auth Redux Mod v2.3 (auth_rx) -- -- See README.txt for licensing and release notes. -- Copyright (c) 2017-2018, Leslie E. Krause @@ -22,8 +22,10 @@ FILTER_COND_FALSE = 40 FILTER_COND_TRUE = 41 FILTER_COMP_EQ = 50 FILTER_COMP_GT = 51 -FILTER_COMP_LT = 52 -FILTER_COMP_IS = 53 +FILTER_COMP_GTE = 52 +FILTER_COMP_LT = 53 +FILTER_COMP_LTE = 54 +FILTER_COMP_IS = 55 ---------------------------- -- AuthFilter class @@ -77,10 +79,10 @@ function AuthFilter( path, name ) t = vars[ var ].type v = vars[ var ].value end - elseif string.find( token, "^@[a-zA-Z._-]*$" ) then + elseif string.find( token, "^@[a-zA-Z0-9_]*%.txt$" ) then t = FILTER_TYPE_SERIES v = { } - local file = io.open( path .. "/" .. string.sub( token, 2 ), "rb" ) + local file = io.open( path .. "/filters/" .. string.sub( token, 2 ), "rb" ) if not file then return nil end @@ -91,24 +93,26 @@ function AuthFilter( path, name ) -- sanitize search phrase and convert to regexp pattern local sanitizer = { - ["["] = ""; - ["]"] = ""; - ["^"] = "%^"; - ["$"] = "%$"; - ["("] = "%("; - [")"] = "%)"; - ["%"] = "%%"; - ["."] = "%."; - ["-"] = "%-"; - ["*"] = "[a-zA-Z0-9_-]*"; - ["+"] = "[a-zA-Z0-9_-]+"; - ["?"] = "[a-zA-Z0-9_-]"; - ["#"] = "%d"; - ["~"] = "%a"; + ["["] = "", + ["]"] = "", + ["^"] = "%^", + ["$"] = "%$", + ["("] = "%(", + [")"] = "%)", + ["%"] = "%%", + ["-"] = "%-", + ["."] = "[a-z]", + ["!"] = "[A-Z]", + ["*"] = "[a-zA-Z0-9_-]*", + ["+"] = "[a-zA-Z0-9_-]+", + ["?"] = "[a-zA-Z0-9_-]", + ["#"] = "%d", + ["~"] = "%a", } + t = FILTER_TYPE_PATTERN v = minetest.decode_base64( string.sub( token, 2 ) ) - v = "^" .. string.gsub( string.upper( v ), ".", sanitizer ) .. "$" + v = "^" .. string.gsub( v, ".", sanitizer ) .. "$" elseif string.find( token, "^'.*$" ) then t = FILTER_TYPE_STRING v = minetest.decode_base64( string.sub( token, 2 ) ) @@ -235,15 +239,16 @@ function AuthFilter( path, name ) -- TODO: might want to move the redundant operand type checks out of loop? local value2 = ( comp == FILTER_COMP_IS and oper2.type == FILTER_TYPE_STRING ) and string.upper( oper2.value ) or oper2.value + local type2 = oper2.type local expr = false for i, value1 in ipairs( oper1.value ) do - if comp == FILTER_COMP_EQ and oper2.type == FILTER_TYPE_STRING then + if comp == FILTER_COMP_EQ and type2 == FILTER_TYPE_STRING then expr = ( value1 == value2 ) - elseif comp == FILTER_COMP_IS and oper2.type == FILTER_TYPE_STRING then + elseif comp == FILTER_COMP_IS and type2 == FILTER_TYPE_STRING then expr = ( string.upper( value1 ) == value2 ) - elseif comp == FILTER_COMP_IS and oper2.type == FILTER_TYPE_PATTERN then - expr = ( string.find( string.upper( value1 ), value2 ) == 1 ) + elseif comp == FILTER_COMP_IS and type2 == FILTER_TYPE_PATTERN then + expr = ( string.find( value1, value2 ) == 1 ) else return trace( "Mismatched operands in ruleset", num ) end @@ -257,7 +262,7 @@ function AuthFilter( path, name ) if #stmt ~= 4 then return trace( "Invalid 'if' or 'unless' statement in ruleset", num ) end local cond = ( { ["if"] = FILTER_COND_TRUE, ["unless"] = FILTER_COND_FALSE } )[ stmt[ 1 ] ] - local comp = ( { ["eq"] = FILTER_COMP_EQ, ["gt"] = FILTER_COMP_GT, ["lt"] = FILTER_COMP_LT, ["is"] = FILTER_COMP_IS } )[ stmt[ 3 ] ] + local comp = ( { ["eq"] = FILTER_COMP_EQ, ["gt"] = FILTER_COMP_GT, ["lt"] = FILTER_COMP_LT, ["gte"] = FILTER_COMP_GTE, ["lte"] = FILTER_COMP_LTE, ["is"] = FILTER_COMP_IS } )[ stmt[ 3 ] ] if not cond or not comp then return trace( "Unrecognized keywords in ruleset", num ) @@ -270,19 +275,21 @@ function AuthFilter( path, name ) return trace( "Unrecognized operands in ruleset", num ) end - -- FIXME: don't allow equality comparison of patterns or series - local expr if comp == FILTER_COMP_EQ and oper1.type == oper2.type and oper1.type ~= FILTER_TYPE_SERIES and oper1.type ~= FILTER_TYPE_PATTERN then expr = ( oper1.value == oper2.value ) elseif comp == FILTER_COMP_IS and oper1.type == FILTER_TYPE_STRING and oper2.type == FILTER_TYPE_STRING then expr = ( string.upper( oper1.value ) == string.upper( oper2.value ) ) elseif comp == FILTER_COMP_IS and oper1.type == FILTER_TYPE_STRING and oper2.type == FILTER_TYPE_PATTERN then - expr = ( string.find( string.upper( oper1.value ), oper2.value ) == 1 ) + expr = ( string.find( oper1.value, oper2.value ) == 1 ) elseif comp == FILTER_COMP_GT and oper1.type == FILTER_TYPE_NUMBER and oper2.type == FILTER_TYPE_NUMBER then expr = ( oper1.value > oper2.value ) elseif comp == FILTER_COMP_LT and oper1.type == FILTER_TYPE_NUMBER and oper2.type == FILTER_TYPE_NUMBER then expr = ( oper1.value < oper2.value ) + elseif comp == FILTER_COMP_GTE and oper1.type == FILTER_TYPE_NUMBER and oper2.type == FILTER_TYPE_NUMBER then + expr = ( oper1.value >= oper2.value ) + elseif comp == FILTER_COMP_LTE and oper1.type == FILTER_TYPE_NUMBER and oper2.type == FILTER_TYPE_NUMBER then + expr = ( oper1.value <= oper2.value ) else return trace( "Mismatched operands in ruleset", num ) end diff --git a/init.lua b/init.lua index 265d912..b15d45a 100644 --- a/init.lua +++ b/init.lua @@ -1,5 +1,5 @@ -------------------------------------------------------- --- Minetest :: Auth Redux Mod v2.1 (auth_rx) +-- Minetest :: Auth Redux Mod v2.3 (auth_rx) -- -- See README.txt for licensing and release notes. -- Copyright (c) 2017-2018, Leslie E. Krause @@ -112,34 +112,12 @@ end ---------------------------- local AuthDatabase = function ( path, name ) - local data, size, users, index + local data, users, index local self = { } local journal = Journal( path, name .. "x" ) -- Private methods - local find_phrase = function( source, phrase ) - -- sanitize search phrase and convert to regexp pattern - local sanitizer = - { - ["^"] = "%^"; - ["$"] = "%$"; - ["("] = "%("; - [")"] = "%)"; - ["%"] = "%%"; - ["."] = "%."; - ["["] = ""; - ["]"] = ""; - ["*"] = "%w*"; - ["+"] = "%w+"; - ["-"] = "%-"; - ["?"] = "%w"; - } - - -- parens capture only first return value of gsub - return string.find( source, ( string.gsub( phrase, ".", sanitizer ) ) ) - end - local db_update = function( meta, optime, opcode, ... ) local fields = { ... } @@ -205,9 +183,9 @@ local AuthDatabase = function ( path, name ) local db_reload = function ( ) minetest.log( "action", "Reading authentication data from disk..." ) - local file, errmsg = io.open( path .. "/auth.db", "r+b" ) + local file, errmsg = io.open( path .. "/" .. name, "r+b" ) if not file then - minetest.log( "error", "Cannot open " .. path .. "/auth.db for reading." ) + minetest.log( "error", "Cannot open " .. path .. "/" .. name .. " for reading." ) error( "Fatal exception in AuthDatabase:reload( ), aborting." ) end @@ -237,7 +215,6 @@ local AuthDatabase = function ( path, name ) approved_addrs = string.split( fields[ 9 ], "," ), assigned_privs = string.split( fields[ 10 ], "," ), } - size = size + 1 end end file:close( ) @@ -271,14 +248,13 @@ local AuthDatabase = function ( path, name ) end file:close( ) - assert( os.remove( path .. "/auth.db" ) ) - assert( os.rename( path .. "/~auth.db", path .. "/auth.db" ) ) + assert( os.remove( path .. "/" .. name ) ) + assert( os.rename( path .. "/~" .. name, path .. "/" .. name ) ) end -- Public methods self.connect = function ( ) - size = 0 data = { } users = { } @@ -296,7 +272,6 @@ local AuthDatabase = function ( path, name ) journal.reset( ) data = nil - size = nil users = nil end @@ -317,7 +292,6 @@ local AuthDatabase = function ( path, name ) assigned_privs = { }, } data[ username ] = rec - size = size + 1 journal.record_raw( TX_CREATE, username, password ) return true @@ -328,7 +302,6 @@ local AuthDatabase = function ( path, name ) if not data[ username ] or users[ username ] then return false end data[ username ] = nil - size = size - 1 journal.record_raw( TX_DELETE, username ) return true @@ -392,15 +365,19 @@ local AuthDatabase = function ( path, name ) return pairs( data ) end - self.records_match = function ( phrase ) + self.records_match = function ( pattern ) local k return function ( ) local v - local p = string.lower( phrase ) + local p = string.lower( pattern ) - k, v = next( data, k ) - if find_phrase( string.lower( k ), p ) then - return k, v + while true do + k, v = next( data, k ) + if not k then + return + elseif string.match( string.lower( k ), p ) then + return k, v + end end end end @@ -479,8 +456,6 @@ minetest.register_on_prejoinplayer( function ( player_name, player_ip ) attempts = { type = FILTER_TYPE_NUMBER, value = rec and rec.total_attempts or 0 }, } ) - -- TODO: Add optional filter logging capabilities - return filter_err end )