Browse Source

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
master
Leslie Krause 1 year ago
parent
commit
61d1fe67e7
4 changed files with 204 additions and 45 deletions
  1. 9
    1
      README.txt
  2. 47
    12
      filter.lua
  3. 20
    16
      init.lua
  4. 128
    16
      samples.mt

+ 9
- 1
README.txt View File

@@ -1,4 +1,4 @@
1
-Auth Redux Mod v2.6b
1
+Auth Redux Mod v2.7b
2 2
 By Leslie Krause
3 3
 
4 4
 Auth Redux is a drop-in replacement for the builtin authentication handler of Minetest.
@@ -71,6 +71,14 @@ Version 2.6b (19-Jul-2018)
71 71
   - tweaked lexer to skip comments on ruleset loading
72 72
   - added search function to AuthDatabase class
73 73
 
74
+Version 2.7b (21-Jul-2018)
75
+  - implemented time and date datatypes for rulesets
76
+  - updated code samples with latest feature-set
77
+  - added time-related functions for use by rulesets
78
+  - added time-related variables for use by rulesets
79
+  - minor formatting fixes to source code
80
+  - optimized comparison algorithm in ruleset parser
81
+
74 82
 Installation
75 83
 ----------------------
76 84
 

+ 47
- 12
filter.lua View File

@@ -1,15 +1,19 @@
1 1
 --------------------------------------------------------
2
+-- Minetest :: Auth Redux Mod v2.7 (auth_rx)
2 3
 --
3 4
 -- See README.txt for licensing and release notes.
4 5
 -- Copyright (c) 2017-2018, Leslie E. Krause
5 6
 --------------------------------------------------------
6 7
 
7 8
 FILTER_TYPE_STRING = 11
8
-FILTER_TYPE_BOOLEAN = 12
9
-FILTER_TYPE_NUMBER = 13
9
+FILTER_TYPE_NUMBER = 12
10
+FILTER_TYPE_BOOLEAN = 13
10 11
 FILTER_TYPE_PATTERN = 14
11 12
 FILTER_TYPE_SERIES = 15
13
+FILTER_TYPE_PERIOD = 16
14
+FILTER_TYPE_MOMENT = 17
15
+FILTER_TYPE_DATESPEC = 18
16
+FILTER_TYPE_TIMESPEC = 19
12 17
 FILTER_MODE_FAIL = 20
13 18
 FILTER_MODE_PASS = 21
14 19
 FILTER_BOOL_AND = 30
@@ -60,7 +64,12 @@ function AuthFilter( path, name )
60 64
 		["size"] = { type = FILTER_TYPE_NUMBER, args = { FILTER_TYPE_SERIES }, def = function ( a ) return #a end },
61 65
 		["elem"] = { type = FILTER_TYPE_STRING, args = { FILTER_TYPE_SERIES, FILTER_TYPE_NUMBER }, def = function ( a, b ) return a[ b ] or "" end },
62 66
 		["split"] = { type = FILTER_TYPE_SERIES, args = { FILTER_TYPE_STRING, FILTER_TYPE_STRING }, def = function ( a, b ) return string.split( a, b, true ) end },
63
-        }
67
+		["time"] = { type = FILTER_TYPE_TIMESPEC, args = { FILTER_TYPE_MOMENT }, def = function ( a ) return a % 86400 end },
68
+		["date"] = { type = FILTER_TYPE_DATESPEC, args = { FILTER_TYPE_MOMENT }, def = function ( a ) return math.floor( a / 86400 ) end },
69
+		["age"] = { type = FILTER_TYPE_PERIOD, args = { FILTER_TYPE_MOMENT }, def = function ( a ) return os.time( ) - a end },	-- FIXME: use global clock variable
70
+		["before"] = { type = FILTER_TYPE_MOMENT, args = { FILTER_TYPE_MOMENT, FILTER_TYPE_PERIOD }, def = function ( a, b ) return a - b end },
71
+		["after"] = { type = FILTER_TYPE_MOMENT, args = { FILTER_TYPE_MOMENT, FILTER_TYPE_PERIOD }, def = function ( a, b ) return a + b end },
72
+	}
64 73
 
65 74
 	----------------------------
66 75
 	-- private methods
@@ -169,6 +178,27 @@ function AuthFilter( path, name )
169 178
 			t = FILTER_TYPE_PATTERN
170 179
 			v = decode_base64( ref[ 1 ] )
171 180
 			v = "^" .. string.gsub( v, ".", sanitizer ) .. "$"
181
+		elseif find_token( "^(%d+)([ydhms])$" ) then
182
+			local factor = { y = 31536000, w = 604800, d = 86400, h = 3600, m = 60, s = 1 }
183
+			t = FILTER_TYPE_PERIOD
184
+			v = tonumber( ref[ 1 ] ) * factor[ ref[ 2 ] ]
185
+		elseif find_token( "^([-+]%d+)([ydhms])$" ) then
186
+			local factor = { y = 31536000, w = 604800, d = 86400, h = 3600, m = 60, s = 1 }
187
+			local origin = string.byte( ref[ 1 ] ) == 45 and vars.clock.value or 0
188
+			t = FILTER_TYPE_MOMENT
189
+			v = origin + tonumber( ref[ 1 ] ) * factor[ ref[ 2 ] ]
190
+		elseif find_token( "^(%d?%d):(%d%d):(%d%d)$" ) or find_token( "^(%d?%d):(%d%d)$" ) then
191
+			local timespec = {
192
+				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,
193
+			}
194
+			t = FILTER_TYPE_TIMESPEC
195
+			v = os.time( timespec )
196
+		elseif find_token( "^(%d%d)%-(%d%d)%-(%d%d%d%d)$" ) then
197
+			local datespec = {
198
+				isdst = true, day = tonumber( ref[ 1 ] ), month = tonumber( ref[ 2 ] ), year = tonumber( ref[ 3 ] ), hour = 0,
199
+			}
200
+			t = FILTER_TYPE_DATESPEC
201
+			v = math.floor( os.time( datespec ) / 86400 )
172 202
 		elseif find_token( "^'([a-zA-Z0-9+/]*);$" ) then
173 203
 			t = FILTER_TYPE_STRING
174 204
 			v = decode_base64( ref[ 1 ] )
@@ -244,7 +274,8 @@ function AuthFilter( path, name )
244 274
 
245 275
 		vars[ "true" ] = { type = FILTER_TYPE_BOOLEAN, value = true }
246 276
 		vars[ "false" ] = { type = FILTER_TYPE_BOOLEAN, value = false }
247
-		vars[ "time" ] = { type = FILTER_TYPE_NUMBER, value = os.time( ) }
277
+		vars[ "clock" ] = { type = FILTER_TYPE_MOMENT, value = os.time( ) }
278
+		vars[ "epoch" ] = { type = FILTER_TYPE_MOMENT, value = 0 }
248 279
 
249 280
 		for num, line in ipairs( src ) do
250 281
 			local stmt = string.split( line, " ", false )
@@ -356,21 +387,24 @@ function AuthFilter( path, name )
356 387
 					return trace( "Unrecognized operands in ruleset", num )
357 388
 				end
358 389
 
390
+				-- only allow comparisons of appropriate and equivalent datatypes
391
+				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 }
392
+
359 393
 				local expr
360 394
 				if comp == FILTER_COMP_EQ and oper1.type == oper2.type and oper1.type ~= FILTER_TYPE_SERIES and oper1.type ~= FILTER_TYPE_PATTERN then
361 395
 					expr = ( oper1.value == oper2.value )
396
+				elseif comp == FILTER_COMP_GT and oper1.type == oper2.type and do_math[ oper2.type ] then
397
+					expr = ( oper1.value > oper2.value )
398
+				elseif comp == FILTER_COMP_GTE and oper1.type == oper2.type and do_math[ oper2.type ] then
399
+					expr = ( oper1.value >= oper2.value )
400
+				elseif comp == FILTER_COMP_LT and oper1.type == oper2.type and do_math[ oper2.type ] then
401
+					expr = ( oper1.value < oper2.value )
402
+				elseif comp == FILTER_COMP_LTE and oper1.type == oper2.type and do_math[ oper2.type ] then
403
+					expr = ( oper1.value <= oper2.value )
362 404
 				elseif comp == FILTER_COMP_IS and oper1.type == FILTER_TYPE_STRING and oper2.type == FILTER_TYPE_STRING then
363 405
 					expr = ( string.upper( oper1.value ) == string.upper( oper2.value ) )
364 406
 				elseif comp == FILTER_COMP_IS and oper1.type == FILTER_TYPE_STRING and oper2.type == FILTER_TYPE_PATTERN then
365 407
 					expr = ( string.find( oper1.value, oper2.value ) == 1 )
366
-				elseif comp == FILTER_COMP_GT and oper1.type == FILTER_TYPE_NUMBER and oper2.type == FILTER_TYPE_NUMBER then
367
-					expr = ( oper1.value > oper2.value )
368
-				elseif comp == FILTER_COMP_LT and oper1.type == FILTER_TYPE_NUMBER and oper2.type == FILTER_TYPE_NUMBER then
369
-					expr = ( oper1.value < oper2.value )
370
-				elseif comp == FILTER_COMP_GTE and oper1.type == FILTER_TYPE_NUMBER and oper2.type == FILTER_TYPE_NUMBER then
371
-					expr = ( oper1.value >= oper2.value )
372
-				elseif comp == FILTER_COMP_LTE and oper1.type == FILTER_TYPE_NUMBER and oper2.type == FILTER_TYPE_NUMBER then
373
-					expr = ( oper1.value <= oper2.value )
374 408
 				else
375 409
 					return trace( "Mismatched operands in ruleset", num )
376 410
 				end

+ 20
- 16
init.lua View File

@@ -1,5 +1,5 @@
1 1
 --------------------------------------------------------
2
+-- Minetest :: Auth Redux Mod v2.7 (auth_rx)
2 3
 --
3 4
 -- See README.txt for licensing and release notes.
4 5
 -- Copyright (c) 2017-2018, Leslie E. Krause
@@ -18,10 +18,10 @@ local auth_db = AuthDatabase( minetest.get_worldpath( ), "auth.db" )
18 18
 local get_minetest_config = core.setting_get	-- backwards compatibility
19 19
 
20 20
 function get_default_privs( )
21
-        local default_privs = { }
22
-        for _, p in pairs( string.split( get_minetest_config( "default_privs" ), "," ) ) do
23
-       	        table.insert( default_privs, string.trim( p ) )
24
-        end
21
+	local default_privs = { }
22
+	for _, p in pairs( string.split( get_minetest_config( "default_privs" ), "," ) ) do
23
+		table.insert( default_privs, string.trim( p ) )
24
+	end
25 25
 	return default_privs
26 26
 end
27 27
 
@@ -55,15 +55,15 @@ minetest.register_on_prejoinplayer( function ( player_name, player_ip )
55 55
 	else
56 56
 		-- prevent creation of case-insensitive duplicate accounts
57 57
 		local uname = string.lower( player_name )
58
-                for cname in auth_db.records( ) do
59
-                        if string.lower( cname ) == uname then
58
+		for cname in auth_db.records( ) do
59
+		if string.lower( cname ) == uname then
60 60
 				return string.format( "A player named %s already exists on this server.", cname )
61 61
 			end
62 62
 		end
63 63
 	end
64 64
 
65 65
 	local filter_err = auth_filter.process( {
66
-	        name = { type = FILTER_TYPE_STRING, value = player_name },
66
+		name = { type = FILTER_TYPE_STRING, value = player_name },
67 67
 		addr = { type = FILTER_TYPE_STRING, value = player_ip },
68 68
 		is_new = { type = FILTER_TYPE_BOOLEAN, value = rec == nil },
69 69
 		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 )
74 74
 		failures = { type = FILTER_TYPE_NUMBER, value = rec and rec.total_failures or 0 },
75 75
 		attempts = { type = FILTER_TYPE_NUMBER, value = rec and rec.total_attempts or 0 },
76 76
 		owner = { type = FILTER_TYPE_STRING, value = get_minetest_config( "name" ) },
77
+		uptime = { type = FILTER_TYPE_PERIOD, value = minetest.get_server_uptime( ) },
78
+		oldlogin = { type = FILTER_TYPE_MOMENT, value = rec and rec.oldlogin or 0 },
79
+		newlogin = { type = FILTER_TYPE_MOMENT, value = rec and rec.newlogin or 0 },
77 80
 	} )
78 81
 
79 82
 	return filter_err 
@@ -143,26 +146,26 @@ minetest.register_authentication_handler( {
143 146
 } )
144 147
 
145 148
 minetest.register_chatcommand( "filter", {
146
-        description = "Enable or disable ruleset-based login filtering, or reload a ruleset definition.",
147
-        privs = { server = true },
148
-        func = function( name, param )
149
-                if param == "" then
150
-                        return true, "Login filtering is currently " .. ( auth_filter.is_active( ) and "enabled" or "disabled" ) .. "."
151
-                elseif param == "disable" then
149
+	description = "Enable or disable ruleset-based login filtering, or reload a ruleset definition.",
150
+	privs = { server = true },
151
+	func = function( name, param )
152
+		if param == "" then
153
+		return true, "Login filtering is currently " .. ( auth_filter.is_active( ) and "enabled" or "disabled" ) .. "."
154
+		elseif param == "disable" then
152 155
 			auth_filter.disable( )
153 156
 			minetest.log( "action", "Login filtering disabled by " .. name .. "." )
154 157
 			return true, "Login filtering is disabled."
155
-                elseif param == "enable" then
158
+		elseif param == "enable" then
156 159
 			auth_filter.enable( )
157 160
 			minetest.log( "action", "Login filtering enabled by " .. name .. "." )
158 161
 			return true, "Login filtering is enabled."
159
-                elseif param == "reload" then
162
+		elseif param == "reload" then
160 163
 			auth_filter.refresh( )
161 164
 			return true, "Ruleset definition was loaded successfully."
162 165
 		else
163 166
 			return false, "Unknown parameter specified."
164 167
 		end
165
-        end
168
+	end
166 169
 } )
167 170
 
168 171
 auth_db.connect( )

+ 128
- 16
samples.mt View File

@@ -1,16 +1,3 @@
1
-#####################################################################
2
-#
3
-# disallow new players whenever server is overloaded
4
-#
5
-#####################################################################
6
-
7
-try "There are too many players online right now."
8
-
9
-fail all
10
-if $is_new eq $true
11
-if $cur_users gt 20
12
-continue
13
-
14 1
 #####################################################################
15 2
 #
16 3
 # only allow administrator access (by username or IP address)
@@ -23,6 +10,8 @@ if $addr eq "172.16.100.2"
23 10
 if $name eq "admin"
24 11
 continue
25 12
 
13
+fail now
14
+
26 15
 #####################################################################
27 16
 #
28 17
 # block a range of IP addresses using wildcards
@@ -41,7 +30,7 @@ pass now
41 30
 
42 31
 #####################################################################
43 32
 #
44
-# only allow access from whitelisted users
33
+# only allow access from whitelisted players
45 34
 #
46 35
 #####################################################################
47 36
 
@@ -56,7 +45,7 @@ fall now
56 45
 
57 46
 #####################################################################
58 47
 #
59
-# never allow access from blacklisted users
48
+# never allow access from blacklisted players
60 49
 #
61 50
 #####################################################################
62 51
 
@@ -69,10 +58,133 @@ pass now
69 58
 
70 59
 #####################################################################
71 60
 #
72
-# notify users that the server is unavailable right now
61
+# notify players that the server is unavailable right now
73 62
 #
74 63
 #####################################################################
75 64
 
76 65
 try "The server is temporarily offline for maintenance."
77 66
 
78 67
 fail now
68
+
69
+#####################################################################
70
+#
71
+# disallow players with all uppercase names
72
+#
73
+#####################################################################
74
+
75
+try "Sorry, we do not accept all uppercase player names."
76
+
77
+fail all
78
+$name eq uc($name)
79
+continue
80
+
81
+pass now
82
+
83
+#####################################################################
84
+#
85
+# disallow players with very short or very long names
86
+#
87
+#####################################################################
88
+
89
+try "Sorry, this player name is too long or too short."
90
+
91
+fail any
92
+$name->len() gt 20
93
+$name->len() lt 3
94
+continue
95
+
96
+pass now
97
+
98
+#####################################################################
99
+#
100
+# disallow users that appear to be bots or guests
101
+#
102
+#####################################################################
103
+
104
+try "Sorry, we do not accept autogenerated player names."
105
+
106
+fail any
107
+if $name is /;*;*##/
108
+if $name is /;*;*###/
109
+if $name is /Player#/
110
+if $name is /Player##/
111
+if $name is /Guest#/
112
+if $name is /Guest##/
113
+continue
114
+
115
+pass now
116
+
117
+#####################################################################
118
+#
119
+# disallow new players when the server is near capacity
120
+#
121
+#####################################################################
122
+
123
+try "There are too many players online right now."
124
+
125
+fail all
126
+$is_new eq $true
127
+$cur_users gte $max_users->mul(0.8)
128
+continue
129
+
130
+pass now
131
+
132
+#####################################################################
133
+#
134
+# prevent players from joining with a reserved name
135
+#
136
+#####################################################################
137
+
138
+try "Sorry, this acccount has been permanently restricted."
139
+
140
+fail all
141
+$is_new eq $true
142
+when ("moderator","server","client","owner","player","system","operator","minetest") is $name
143
+continue
144
+
145
+pass now
146
+
147
+#####################################################################
148
+#
149
+# disallow players that have been inactive for 90 days
150
+#
151
+#####################################################################
152
+
153
+try "Sorry, this acccount has been disabled for inactivity."
154
+
155
+fail all
156
+if $is_new eq $false
157
+if age($newlogin) gt 90d
158
+continue
159
+
160
+pass now
161
+
162
+#####################################################################
163
+#
164
+# disallow new players during the weekends
165
+#
166
+#####################################################################
167
+
168
+try "Sorry, we are not accepting new players at this time."
169
+
170
+fail now
171
+if $is_new eq $true
172
+when ("Sat","Sun") eq $clock->day()
173
+continue
174
+
175
+pass now
176
+
177
+#####################################################################
178
+#
179
+# prevent players from spam-logging the server
180
+#
181
+#####################################################################
182
+
183
+try "You are doing that too much. Please wait awhile."
184
+
185
+fail all
186
+if $is_new eq $false
187
+if age($newlogin) lt 15s
188
+continue
189
+
190
+pass now

Loading…
Cancel
Save