- 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
This commit is contained in:
Leslie Krause 2018-07-08 18:14:08 -04:00
parent a47562b251
commit abaa02b036
3 changed files with 59 additions and 69 deletions

View File

@ -1,4 +1,4 @@
Auth Redux Mod v2.2b Auth Redux Mod v2.3b
By Leslie Krause By Leslie Krause
Auth Redux is a drop-in replacement for the builtin authentication handler of Minetest. 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 - fixed type-checking of try statements in rulesets
- included mod.conf and description.txt files - 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 Installation
---------------------- ----------------------

View File

@ -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. -- See README.txt for licensing and release notes.
-- Copyright (c) 2017-2018, Leslie E. Krause -- Copyright (c) 2017-2018, Leslie E. Krause
@ -22,8 +22,10 @@ FILTER_COND_FALSE = 40
FILTER_COND_TRUE = 41 FILTER_COND_TRUE = 41
FILTER_COMP_EQ = 50 FILTER_COMP_EQ = 50
FILTER_COMP_GT = 51 FILTER_COMP_GT = 51
FILTER_COMP_LT = 52 FILTER_COMP_GTE = 52
FILTER_COMP_IS = 53 FILTER_COMP_LT = 53
FILTER_COMP_LTE = 54
FILTER_COMP_IS = 55
---------------------------- ----------------------------
-- AuthFilter class -- AuthFilter class
@ -77,10 +79,10 @@ function AuthFilter( path, name )
t = vars[ var ].type t = vars[ var ].type
v = vars[ var ].value v = vars[ var ].value
end end
elseif string.find( token, "^@[a-zA-Z._-]*$" ) then elseif string.find( token, "^@[a-zA-Z0-9_]*%.txt$" ) then
t = FILTER_TYPE_SERIES t = FILTER_TYPE_SERIES
v = { } 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 if not file then
return nil return nil
end end
@ -91,24 +93,26 @@ function AuthFilter( path, name )
-- sanitize search phrase and convert to regexp pattern -- sanitize search phrase and convert to regexp pattern
local sanitizer = local sanitizer =
{ {
["["] = ""; ["["] = "",
["]"] = ""; ["]"] = "",
["^"] = "%^"; ["^"] = "%^",
["$"] = "%$"; ["$"] = "%$",
["("] = "%("; ["("] = "%(",
[")"] = "%)"; [")"] = "%)",
["%"] = "%%"; ["%"] = "%%",
["."] = "%."; ["-"] = "%-",
["-"] = "%-"; ["."] = "[a-z]",
["*"] = "[a-zA-Z0-9_-]*"; ["!"] = "[A-Z]",
["+"] = "[a-zA-Z0-9_-]+"; ["*"] = "[a-zA-Z0-9_-]*",
["?"] = "[a-zA-Z0-9_-]"; ["+"] = "[a-zA-Z0-9_-]+",
["#"] = "%d"; ["?"] = "[a-zA-Z0-9_-]",
["~"] = "%a"; ["#"] = "%d",
["~"] = "%a",
} }
t = FILTER_TYPE_PATTERN t = FILTER_TYPE_PATTERN
v = minetest.decode_base64( string.sub( token, 2 ) ) 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 elseif string.find( token, "^'.*$" ) then
t = FILTER_TYPE_STRING t = FILTER_TYPE_STRING
v = minetest.decode_base64( string.sub( token, 2 ) ) 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? -- 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 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 local expr = false
for i, value1 in ipairs( oper1.value ) do 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 ) 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 ) expr = ( string.upper( value1 ) == value2 )
elseif comp == FILTER_COMP_IS and oper2.type == FILTER_TYPE_PATTERN then elseif comp == FILTER_COMP_IS and type2 == FILTER_TYPE_PATTERN then
expr = ( string.find( string.upper( value1 ), value2 ) == 1 ) expr = ( string.find( value1, value2 ) == 1 )
else else
return trace( "Mismatched operands in ruleset", num ) return trace( "Mismatched operands in ruleset", num )
end end
@ -257,7 +262,7 @@ function AuthFilter( path, name )
if #stmt ~= 4 then return trace( "Invalid 'if' or 'unless' statement in ruleset", num ) end 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 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 if not cond or not comp then
return trace( "Unrecognized keywords in ruleset", num ) return trace( "Unrecognized keywords in ruleset", num )
@ -270,19 +275,21 @@ function AuthFilter( path, name )
return trace( "Unrecognized operands in ruleset", num ) return trace( "Unrecognized operands in ruleset", num )
end end
-- FIXME: don't allow equality comparison of patterns or series
local expr 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 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 ) expr = ( oper1.value == oper2.value )
elseif comp == FILTER_COMP_IS and oper1.type == FILTER_TYPE_STRING and oper2.type == FILTER_TYPE_STRING then 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 ) ) 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 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 elseif comp == FILTER_COMP_GT and oper1.type == FILTER_TYPE_NUMBER and oper2.type == FILTER_TYPE_NUMBER then
expr = ( oper1.value > oper2.value ) expr = ( oper1.value > oper2.value )
elseif comp == FILTER_COMP_LT and oper1.type == FILTER_TYPE_NUMBER and oper2.type == FILTER_TYPE_NUMBER then elseif comp == FILTER_COMP_LT and oper1.type == FILTER_TYPE_NUMBER and oper2.type == FILTER_TYPE_NUMBER then
expr = ( oper1.value < oper2.value ) 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 else
return trace( "Mismatched operands in ruleset", num ) return trace( "Mismatched operands in ruleset", num )
end end

View File

@ -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. -- See README.txt for licensing and release notes.
-- Copyright (c) 2017-2018, Leslie E. Krause -- Copyright (c) 2017-2018, Leslie E. Krause
@ -112,34 +112,12 @@ end
---------------------------- ----------------------------
local AuthDatabase = function ( path, name ) local AuthDatabase = function ( path, name )
local data, size, users, index local data, users, index
local self = { } local self = { }
local journal = Journal( path, name .. "x" ) local journal = Journal( path, name .. "x" )
-- Private methods -- 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 db_update = function( meta, optime, opcode, ... )
local fields = { ... } local fields = { ... }
@ -205,9 +183,9 @@ local AuthDatabase = function ( path, name )
local db_reload = function ( ) local db_reload = function ( )
minetest.log( "action", "Reading authentication data from disk..." ) 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 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." ) error( "Fatal exception in AuthDatabase:reload( ), aborting." )
end end
@ -237,7 +215,6 @@ local AuthDatabase = function ( path, name )
approved_addrs = string.split( fields[ 9 ], "," ), approved_addrs = string.split( fields[ 9 ], "," ),
assigned_privs = string.split( fields[ 10 ], "," ), assigned_privs = string.split( fields[ 10 ], "," ),
} }
size = size + 1
end end
end end
file:close( ) file:close( )
@ -271,14 +248,13 @@ local AuthDatabase = function ( path, name )
end end
file:close( ) file:close( )
assert( os.remove( path .. "/auth.db" ) ) assert( os.remove( path .. "/" .. name ) )
assert( os.rename( path .. "/~auth.db", path .. "/auth.db" ) ) assert( os.rename( path .. "/~" .. name, path .. "/" .. name ) )
end end
-- Public methods -- Public methods
self.connect = function ( ) self.connect = function ( )
size = 0
data = { } data = { }
users = { } users = { }
@ -296,7 +272,6 @@ local AuthDatabase = function ( path, name )
journal.reset( ) journal.reset( )
data = nil data = nil
size = nil
users = nil users = nil
end end
@ -317,7 +292,6 @@ local AuthDatabase = function ( path, name )
assigned_privs = { }, assigned_privs = { },
} }
data[ username ] = rec data[ username ] = rec
size = size + 1
journal.record_raw( TX_CREATE, username, password ) journal.record_raw( TX_CREATE, username, password )
return true return true
@ -328,7 +302,6 @@ local AuthDatabase = function ( path, name )
if not data[ username ] or users[ username ] then return false end if not data[ username ] or users[ username ] then return false end
data[ username ] = nil data[ username ] = nil
size = size - 1
journal.record_raw( TX_DELETE, username ) journal.record_raw( TX_DELETE, username )
return true return true
@ -392,18 +365,22 @@ local AuthDatabase = function ( path, name )
return pairs( data ) return pairs( data )
end end
self.records_match = function ( phrase ) self.records_match = function ( pattern )
local k local k
return function ( ) return function ( )
local v local v
local p = string.lower( phrase ) local p = string.lower( pattern )
while true do
k, v = next( data, k ) k, v = next( data, k )
if find_phrase( string.lower( k ), p ) then if not k then
return
elseif string.match( string.lower( k ), p ) then
return k, v return k, v
end end
end end
end end
end
self.select_record = function ( username ) self.select_record = function ( username )
return data[ username ] return data[ username ]
@ -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 }, attempts = { type = FILTER_TYPE_NUMBER, value = rec and rec.total_attempts or 0 },
} ) } )
-- TODO: Add optional filter logging capabilities
return filter_err return filter_err
end ) end )