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
This commit is contained in:
parent
a47562b251
commit
abaa02b036
10
README.txt
10
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
|
||||
----------------------
|
||||
|
||||
|
63
filter.lua
63
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
|
||||
|
55
init.lua
55
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 )
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user