Build 10
- implemented time and date datatypes for rulesets - updated code samples with latest feature-set - added time-related functions for use by rulesets - added time-related variables for use by rulesets - minor formatting fixes to source code - optimized comparison algorithm in ruleset parser
This commit is contained in:
parent
a0799fe6a4
commit
61d1fe67e7
10
README.txt
10
README.txt
@ -1,4 +1,4 @@
|
||||
Auth Redux Mod v2.6b
|
||||
Auth Redux Mod v2.7b
|
||||
By Leslie Krause
|
||||
|
||||
Auth Redux is a drop-in replacement for the builtin authentication handler of Minetest.
|
||||
@ -71,6 +71,14 @@ Version 2.6b (19-Jul-2018)
|
||||
- tweaked lexer to skip comments on ruleset loading
|
||||
- added search function to AuthDatabase class
|
||||
|
||||
Version 2.7b (21-Jul-2018)
|
||||
- implemented time and date datatypes for rulesets
|
||||
- updated code samples with latest feature-set
|
||||
- added time-related functions for use by rulesets
|
||||
- added time-related variables for use by rulesets
|
||||
- minor formatting fixes to source code
|
||||
- optimized comparison algorithm in ruleset parser
|
||||
|
||||
Installation
|
||||
----------------------
|
||||
|
||||
|
60
filter.lua
60
filter.lua
@ -1,15 +1,19 @@
|
||||
--------------------------------------------------------
|
||||
-- Minetest :: Auth Redux Mod v2.6 (auth_rx)
|
||||
-- Minetest :: Auth Redux Mod v2.7 (auth_rx)
|
||||
--
|
||||
-- See README.txt for licensing and release notes.
|
||||
-- Copyright (c) 2017-2018, Leslie E. Krause
|
||||
--------------------------------------------------------
|
||||
|
||||
FILTER_TYPE_STRING = 11
|
||||
FILTER_TYPE_BOOLEAN = 12
|
||||
FILTER_TYPE_NUMBER = 13
|
||||
FILTER_TYPE_NUMBER = 12
|
||||
FILTER_TYPE_BOOLEAN = 13
|
||||
FILTER_TYPE_PATTERN = 14
|
||||
FILTER_TYPE_SERIES = 15
|
||||
FILTER_TYPE_PERIOD = 16
|
||||
FILTER_TYPE_MOMENT = 17
|
||||
FILTER_TYPE_DATESPEC = 18
|
||||
FILTER_TYPE_TIMESPEC = 19
|
||||
FILTER_MODE_FAIL = 20
|
||||
FILTER_MODE_PASS = 21
|
||||
FILTER_BOOL_AND = 30
|
||||
@ -60,7 +64,12 @@ function AuthFilter( path, name )
|
||||
["size"] = { type = FILTER_TYPE_NUMBER, args = { FILTER_TYPE_SERIES }, def = function ( a ) return #a end },
|
||||
["elem"] = { type = FILTER_TYPE_STRING, args = { FILTER_TYPE_SERIES, FILTER_TYPE_NUMBER }, def = function ( a, b ) return a[ b ] or "" end },
|
||||
["split"] = { type = FILTER_TYPE_SERIES, args = { FILTER_TYPE_STRING, FILTER_TYPE_STRING }, def = function ( a, b ) return string.split( a, b, true ) end },
|
||||
}
|
||||
["time"] = { type = FILTER_TYPE_TIMESPEC, args = { FILTER_TYPE_MOMENT }, def = function ( a ) return a % 86400 end },
|
||||
["date"] = { type = FILTER_TYPE_DATESPEC, args = { FILTER_TYPE_MOMENT }, def = function ( a ) return math.floor( a / 86400 ) end },
|
||||
["age"] = { type = FILTER_TYPE_PERIOD, args = { FILTER_TYPE_MOMENT }, def = function ( a ) return os.time( ) - a end }, -- FIXME: use global clock variable
|
||||
["before"] = { type = FILTER_TYPE_MOMENT, args = { FILTER_TYPE_MOMENT, FILTER_TYPE_PERIOD }, def = function ( a, b ) return a - b end },
|
||||
["after"] = { type = FILTER_TYPE_MOMENT, args = { FILTER_TYPE_MOMENT, FILTER_TYPE_PERIOD }, def = function ( a, b ) return a + b end },
|
||||
}
|
||||
|
||||
----------------------------
|
||||
-- private methods
|
||||
@ -169,6 +178,27 @@ function AuthFilter( path, name )
|
||||
t = FILTER_TYPE_PATTERN
|
||||
v = decode_base64( ref[ 1 ] )
|
||||
v = "^" .. string.gsub( v, ".", sanitizer ) .. "$"
|
||||
elseif find_token( "^(%d+)([ydhms])$" ) then
|
||||
local factor = { y = 31536000, w = 604800, d = 86400, h = 3600, m = 60, s = 1 }
|
||||
t = FILTER_TYPE_PERIOD
|
||||
v = tonumber( ref[ 1 ] ) * factor[ ref[ 2 ] ]
|
||||
elseif find_token( "^([-+]%d+)([ydhms])$" ) then
|
||||
local factor = { y = 31536000, w = 604800, d = 86400, h = 3600, m = 60, s = 1 }
|
||||
local origin = string.byte( ref[ 1 ] ) == 45 and vars.clock.value or 0
|
||||
t = FILTER_TYPE_MOMENT
|
||||
v = origin + tonumber( ref[ 1 ] ) * factor[ ref[ 2 ] ]
|
||||
elseif find_token( "^(%d?%d):(%d%d):(%d%d)$" ) or find_token( "^(%d?%d):(%d%d)$" ) then
|
||||
local timespec = {
|
||||
isdst = true, day = 1, month = 1, year = 1970, hour = tonumber( ref[ 1 ] ), min = tonumber( ref[ 2 ] ), sec = ref[ 3 ] and tonumber( ref[ 3 ] ) or 0,
|
||||
}
|
||||
t = FILTER_TYPE_TIMESPEC
|
||||
v = os.time( timespec )
|
||||
elseif find_token( "^(%d%d)%-(%d%d)%-(%d%d%d%d)$" ) then
|
||||
local datespec = {
|
||||
isdst = true, day = tonumber( ref[ 1 ] ), month = tonumber( ref[ 2 ] ), year = tonumber( ref[ 3 ] ), hour = 0,
|
||||
}
|
||||
t = FILTER_TYPE_DATESPEC
|
||||
v = math.floor( os.time( datespec ) / 86400 )
|
||||
elseif find_token( "^'([a-zA-Z0-9+/]*);$" ) then
|
||||
t = FILTER_TYPE_STRING
|
||||
v = decode_base64( ref[ 1 ] )
|
||||
@ -244,7 +274,8 @@ function AuthFilter( path, name )
|
||||
|
||||
vars[ "true" ] = { type = FILTER_TYPE_BOOLEAN, value = true }
|
||||
vars[ "false" ] = { type = FILTER_TYPE_BOOLEAN, value = false }
|
||||
vars[ "time" ] = { type = FILTER_TYPE_NUMBER, value = os.time( ) }
|
||||
vars[ "clock" ] = { type = FILTER_TYPE_MOMENT, value = os.time( ) }
|
||||
vars[ "epoch" ] = { type = FILTER_TYPE_MOMENT, value = 0 }
|
||||
|
||||
for num, line in ipairs( src ) do
|
||||
local stmt = string.split( line, " ", false )
|
||||
@ -356,21 +387,24 @@ function AuthFilter( path, name )
|
||||
return trace( "Unrecognized operands in ruleset", num )
|
||||
end
|
||||
|
||||
-- only allow comparisons of appropriate and equivalent datatypes
|
||||
local do_math = { [FILTER_TYPE_STRING] = false, [FILTER_TYPE_NUMBER] = true, [FILTER_TYPE_PERIOD] = true, [FILTER_TYPE_MOMENT] = true, [FILTER_TYPE_DATESPEC] = true, [FILTER_TYPE_TIMESPEC] = true }
|
||||
|
||||
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_GT and oper1.type == oper2.type and do_math[ oper2.type ] then
|
||||
expr = ( oper1.value > oper2.value )
|
||||
elseif comp == FILTER_COMP_GTE and oper1.type == oper2.type and do_math[ oper2.type ] then
|
||||
expr = ( oper1.value >= oper2.value )
|
||||
elseif comp == FILTER_COMP_LT and oper1.type == oper2.type and do_math[ oper2.type ] then
|
||||
expr = ( oper1.value < oper2.value )
|
||||
elseif comp == FILTER_COMP_LTE and oper1.type == oper2.type and do_math[ oper2.type ] 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( 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
|
||||
|
37
init.lua
37
init.lua
@ -1,5 +1,5 @@
|
||||
--------------------------------------------------------
|
||||
-- Minetest :: Auth Redux Mod v2.6 (auth_rx)
|
||||
-- Minetest :: Auth Redux Mod v2.7 (auth_rx)
|
||||
--
|
||||
-- See README.txt for licensing and release notes.
|
||||
-- Copyright (c) 2017-2018, Leslie E. Krause
|
||||
@ -18,10 +18,10 @@ local auth_db = AuthDatabase( minetest.get_worldpath( ), "auth.db" )
|
||||
local get_minetest_config = core.setting_get -- backwards compatibility
|
||||
|
||||
function get_default_privs( )
|
||||
local default_privs = { }
|
||||
for _, p in pairs( string.split( get_minetest_config( "default_privs" ), "," ) ) do
|
||||
table.insert( default_privs, string.trim( p ) )
|
||||
end
|
||||
local default_privs = { }
|
||||
for _, p in pairs( string.split( get_minetest_config( "default_privs" ), "," ) ) do
|
||||
table.insert( default_privs, string.trim( p ) )
|
||||
end
|
||||
return default_privs
|
||||
end
|
||||
|
||||
@ -55,15 +55,15 @@ minetest.register_on_prejoinplayer( function ( player_name, player_ip )
|
||||
else
|
||||
-- prevent creation of case-insensitive duplicate accounts
|
||||
local uname = string.lower( player_name )
|
||||
for cname in auth_db.records( ) do
|
||||
if string.lower( cname ) == uname then
|
||||
for cname in auth_db.records( ) do
|
||||
if string.lower( cname ) == uname then
|
||||
return string.format( "A player named %s already exists on this server.", cname )
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local filter_err = auth_filter.process( {
|
||||
name = { type = FILTER_TYPE_STRING, value = player_name },
|
||||
name = { type = FILTER_TYPE_STRING, value = player_name },
|
||||
addr = { type = FILTER_TYPE_STRING, value = player_ip },
|
||||
is_new = { type = FILTER_TYPE_BOOLEAN, value = rec == nil },
|
||||
privs_list = { type = FILTER_TYPE_SERIES, value = rec and rec.assigned_privs or { } },
|
||||
@ -74,6 +74,9 @@ minetest.register_on_prejoinplayer( function ( player_name, player_ip )
|
||||
failures = { type = FILTER_TYPE_NUMBER, value = rec and rec.total_failures or 0 },
|
||||
attempts = { type = FILTER_TYPE_NUMBER, value = rec and rec.total_attempts or 0 },
|
||||
owner = { type = FILTER_TYPE_STRING, value = get_minetest_config( "name" ) },
|
||||
uptime = { type = FILTER_TYPE_PERIOD, value = minetest.get_server_uptime( ) },
|
||||
oldlogin = { type = FILTER_TYPE_MOMENT, value = rec and rec.oldlogin or 0 },
|
||||
newlogin = { type = FILTER_TYPE_MOMENT, value = rec and rec.newlogin or 0 },
|
||||
} )
|
||||
|
||||
return filter_err
|
||||
@ -143,26 +146,26 @@ minetest.register_authentication_handler( {
|
||||
} )
|
||||
|
||||
minetest.register_chatcommand( "filter", {
|
||||
description = "Enable or disable ruleset-based login filtering, or reload a ruleset definition.",
|
||||
privs = { server = true },
|
||||
func = function( name, param )
|
||||
if param == "" then
|
||||
return true, "Login filtering is currently " .. ( auth_filter.is_active( ) and "enabled" or "disabled" ) .. "."
|
||||
elseif param == "disable" then
|
||||
description = "Enable or disable ruleset-based login filtering, or reload a ruleset definition.",
|
||||
privs = { server = true },
|
||||
func = function( name, param )
|
||||
if param == "" then
|
||||
return true, "Login filtering is currently " .. ( auth_filter.is_active( ) and "enabled" or "disabled" ) .. "."
|
||||
elseif param == "disable" then
|
||||
auth_filter.disable( )
|
||||
minetest.log( "action", "Login filtering disabled by " .. name .. "." )
|
||||
return true, "Login filtering is disabled."
|
||||
elseif param == "enable" then
|
||||
elseif param == "enable" then
|
||||
auth_filter.enable( )
|
||||
minetest.log( "action", "Login filtering enabled by " .. name .. "." )
|
||||
return true, "Login filtering is enabled."
|
||||
elseif param == "reload" then
|
||||
elseif param == "reload" then
|
||||
auth_filter.refresh( )
|
||||
return true, "Ruleset definition was loaded successfully."
|
||||
else
|
||||
return false, "Unknown parameter specified."
|
||||
end
|
||||
end
|
||||
end
|
||||
} )
|
||||
|
||||
auth_db.connect( )
|
||||
|
144
samples.mt
144
samples.mt
@ -1,16 +1,3 @@
|
||||
#####################################################################
|
||||
#
|
||||
# disallow new players whenever server is overloaded
|
||||
#
|
||||
#####################################################################
|
||||
|
||||
try "There are too many players online right now."
|
||||
|
||||
fail all
|
||||
if $is_new eq $true
|
||||
if $cur_users gt 20
|
||||
continue
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# only allow administrator access (by username or IP address)
|
||||
@ -23,6 +10,8 @@ if $addr eq "172.16.100.2"
|
||||
if $name eq "admin"
|
||||
continue
|
||||
|
||||
fail now
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# block a range of IP addresses using wildcards
|
||||
@ -41,7 +30,7 @@ pass now
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# only allow access from whitelisted users
|
||||
# only allow access from whitelisted players
|
||||
#
|
||||
#####################################################################
|
||||
|
||||
@ -56,7 +45,7 @@ fall now
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# never allow access from blacklisted users
|
||||
# never allow access from blacklisted players
|
||||
#
|
||||
#####################################################################
|
||||
|
||||
@ -69,10 +58,133 @@ pass now
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# notify users that the server is unavailable right now
|
||||
# notify players that the server is unavailable right now
|
||||
#
|
||||
#####################################################################
|
||||
|
||||
try "The server is temporarily offline for maintenance."
|
||||
|
||||
fail now
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# disallow players with all uppercase names
|
||||
#
|
||||
#####################################################################
|
||||
|
||||
try "Sorry, we do not accept all uppercase player names."
|
||||
|
||||
fail all
|
||||
$name eq uc($name)
|
||||
continue
|
||||
|
||||
pass now
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# disallow players with very short or very long names
|
||||
#
|
||||
#####################################################################
|
||||
|
||||
try "Sorry, this player name is too long or too short."
|
||||
|
||||
fail any
|
||||
$name->len() gt 20
|
||||
$name->len() lt 3
|
||||
continue
|
||||
|
||||
pass now
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# disallow users that appear to be bots or guests
|
||||
#
|
||||
#####################################################################
|
||||
|
||||
try "Sorry, we do not accept autogenerated player names."
|
||||
|
||||
fail any
|
||||
if $name is /;*;*##/
|
||||
if $name is /;*;*###/
|
||||
if $name is /Player#/
|
||||
if $name is /Player##/
|
||||
if $name is /Guest#/
|
||||
if $name is /Guest##/
|
||||
continue
|
||||
|
||||
pass now
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# disallow new players when the server is near capacity
|
||||
#
|
||||
#####################################################################
|
||||
|
||||
try "There are too many players online right now."
|
||||
|
||||
fail all
|
||||
$is_new eq $true
|
||||
$cur_users gte $max_users->mul(0.8)
|
||||
continue
|
||||
|
||||
pass now
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# prevent players from joining with a reserved name
|
||||
#
|
||||
#####################################################################
|
||||
|
||||
try "Sorry, this acccount has been permanently restricted."
|
||||
|
||||
fail all
|
||||
$is_new eq $true
|
||||
when ("moderator","server","client","owner","player","system","operator","minetest") is $name
|
||||
continue
|
||||
|
||||
pass now
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# disallow players that have been inactive for 90 days
|
||||
#
|
||||
#####################################################################
|
||||
|
||||
try "Sorry, this acccount has been disabled for inactivity."
|
||||
|
||||
fail all
|
||||
if $is_new eq $false
|
||||
if age($newlogin) gt 90d
|
||||
continue
|
||||
|
||||
pass now
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# disallow new players during the weekends
|
||||
#
|
||||
#####################################################################
|
||||
|
||||
try "Sorry, we are not accepting new players at this time."
|
||||
|
||||
fail now
|
||||
if $is_new eq $true
|
||||
when ("Sat","Sun") eq $clock->day()
|
||||
continue
|
||||
|
||||
pass now
|
||||
|
||||
#####################################################################
|
||||
#
|
||||
# prevent players from spam-logging the server
|
||||
#
|
||||
#####################################################################
|
||||
|
||||
try "You are doing that too much. Please wait awhile."
|
||||
|
||||
fail all
|
||||
if $is_new eq $false
|
||||
if age($newlogin) lt 15s
|
||||
continue
|
||||
|
||||
pass now
|
||||
|
Loading…
x
Reference in New Issue
Block a user