mckaygerhard 7aad294b99 auth_rx - correct handling of permissions when user is global admin
* there are two king of administrators: those that have many
  of the privilegies and the user that are pointed in config file,
  auth_rx never initialized this from zero cos always assumed
  a starting point as migration, so never need or really detect
  who are the admin of the server, cos after migration the admin
  already has previous privilegies from plain db file.
* override all the possible handlers with right detection of the
  admin user from the config file, on admin detected, no matter
  what .. give it all the (on fly) privilegies currently on server
* still a privilegied user at this commit can erase the admin
  or change the password if have also enought privilegies
* WARNING: this commit will depends of the previusly commit
  as of 2aef6adf19 for right
  admin name detection.
* wrongly invoked register_on_authplayer over 5.0+ engines
* fix engine detection for 5.4 due callbacks handler
2023-06-16 00:00:55 -04:00

207 lines
7.8 KiB
Lua

--------------------------------------------------------
-- Minetest :: Auth Redux Mod v2.13 (auth_rx)
--
-- See README.txt for licensing and release notes.
-- Copyright (c) 2017-2018, Leslie E. Krause
--------------------------------------------------------
local world_path = minetest.get_worldpath( )
local mod_name = minetest.get_current_modname() or "auth_rx"
local mod_path = minetest.get_modpath( mod_name )
dofile( mod_path .. "/helpers.lua" )
dofile( mod_path .. "/filter.lua" )
dofile( mod_path .. "/db.lua" )
dofile( mod_path .. "/watchdog.lua" )
local __commands = dofile( mod_path .. "/commands.lua" )
AuthInitFile( world_path, "auth.db" )
-----------------------------------------------------
-- Registered Authentication Handler
-----------------------------------------------------
local auth_filter = AuthFilter( world_path, "greenlist.mt" )
local auth_db = AuthDatabase( world_path, "auth.db" )
local auth_watchdog = AuthWatchdog( )
local is_50 = minetest.has_feature("object_use_texture_alpha")
local is_54 = minetest.has_feature("direct_velocity_on_players")
if is_50 then
if minetest.register_on_auth_fail then
minetest.register_on_auth_fail( function ( player_name, player_ip )
auth_db.on_login_failure( player_name, player_ip )
auth_watchdog.on_failure( convert_ipv4( player_ip ) )
end )
end
end
if is_54 then
if minetest.register_on_authplayer then
minetest.register_on_authplayer( function ( player_name, player_ip, is_success )
if is_success then
return
end
auth_db.on_login_failure( player_name, player_ip )
auth_watchdog.on_failure( convert_ipv4( player_ip ) )
end )
end
end
minetest.register_on_prejoinplayer( function ( player_name, player_ip )
local rec = auth_db.select_record( player_name )
local meta = auth_watchdog.get_metadata( convert_ipv4( player_ip ) )
if rec then
auth_db.on_login_attempt( 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
return string.format( "A player named %s already exists on this server.", cname )
end
end
end
local res = auth_filter.is_enabled and auth_filter.process( {
name = { type = FILTER_TYPE_STRING, value = player_name },
addr = { type = FILTER_TYPE_ADDRESS, value = convert_ipv4( player_ip ) },
is_new = { type = FILTER_TYPE_BOOLEAN, value = rec == nil },
privs_list = { type = FILTER_TYPE_SERIES, value = rec and rec.assigned_privs or { } },
users_list = { type = FILTER_TYPE_SERIES, value = auth_db.search( true ) },
cur_users = { type = FILTER_TYPE_NUMBER, value = #auth_db.search( true ) },
max_users = { type = FILTER_TYPE_NUMBER, value = get_minetest_config( "max_users" ) },
lifetime = { type = FILTER_TYPE_PERIOD, value = rec and rec.lifetime or 0 },
sessions = { type = FILTER_TYPE_NUMBER, value = rec and rec.total_sessions or 0 },
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 },
ip_names_list = { type = FILTER_TYPE_SERIES, value = meta.previous_names or { } },
ip_prelogin = { type = FILTER_TYPE_MOMENT, value = meta.prelogin or 0 },
ip_oldcheck = { type = FILTER_TYPE_MOMENT, value = meta.oldcheck or 0 },
ip_newcheck = { type = FILTER_TYPE_MOMENT, value = meta.newcheck or 0 },
ip_failures = { type = FILTER_TYPE_NUMBER, value = meta.count_failures or 0 },
ip_attempts = { type = FILTER_TYPE_NUMBER, value = meta.count_attempts or 0 }
}, true ) or nil
auth_watchdog.on_attempt( convert_ipv4( player_ip ), player_name )
return res
end )
minetest.register_on_joinplayer( function ( player )
local player_name
local player_oj
local player_ip
if player ~= nil then
player_name = player:get_player_name( )
player_oj = minetest.get_player_information( player_name )
player_ip = player_oj.address -- this doesn't work in singleplayer!
auth_db.on_login_success( player_name, player_ip )
auth_db.on_session_opened( player_name )
auth_watchdog.on_success( convert_ipv4( player_ip ) )
minetest.log( "warning", "[auth_rx] in authentication database players success joined ".. player_name )
else
minetest.log( "error", "[auth_rx] incomplete auth process on player obj nil ip obj address" )
end
end )
minetest.register_on_leaveplayer( function ( player )
local name
if player ~= nil then
name = player:get_player_name( )
auth_db.on_session_closed( name )
minetest.log( "warning", "[auth_rx] player "..name.." leaving" )
else
minetest.log( "error", "[auth_rx] incomplete player leaving without writing in dbx due nil object" )
end
end )
minetest.register_on_shutdown( function( )
auth_db.disconnect( )
end )
minetest.register_authentication_handler( {
-- translate old auth hooks to new database backend
get_auth = function( username )
minetest.log( "verbose" , "[auth_rx] get_auth handler access to some resource for " .. username )
local rec = auth_db.select_record( username )
if rec then
local assigned_privs = rec.assigned_privs
if get_minetest_config( "name" ) == username then
-- grant server operator all privileges
assigned_privs = { }
for priv in pairs( minetest.registered_privileges ) do
table.insert( assigned_privs, priv )
end
end
return { password = rec.password, privileges = unpack_privileges( assigned_privs ), last_login = rec.newlogin }
end
end,
create_auth = function( username, password )
minetest.log( "verbose" , "[auth_rx] create_auth handler new user over server for " .. username )
local rec = auth_db.create_record( username, password )
if rec then
local assigned_privs = get_default_privs( )
if get_minetest_config( "name" ) == username then
-- grant server operator all privileges
assigned_privs = { }
for priv in pairs( minetest.registered_privileges ) do
table.insert( assigned_privs, priv )
end
end
auth_db.set_assigned_privs( username, assigned_privs )
end
end,
delete_auth = function( username )
minetest.log( "verbose" , "[auth_rx] delete_auth handler for " .. username )
-- server operator's privileges are immutable
if get_minetest_config( "name" ) ~= username then
local rec = auth_db.delete_record( username )
if rec then
minetest.log( "info", "Deleted player '" .. username .. "' in authenatication database" )
end
end
end,
set_password = function ( username, password )
minetest.log( "verbose" , "[auth_rx] set_password handler for " .. username )
local rec = auth_db.set_password( username, password )
if rec then
minetest.log( "info", "Reset password of player '" .. username .. "' in authentication database" )
end
end,
set_privileges = function ( username, privileges )
minetest.log( "verbose" , "[auth_rx] set_privileges handler grants for " .. username )
local assigned_privs = pack_privileges( privileges )
-- server operator's privileges are immutable
if get_minetest_config( "name" ) == username then
assigned_privs = { }
for priv in pairs( minetest.registered_privileges ) do
table.insert( assigned_privs, priv )
end
end
if auth_db.set_assigned_privs( username, assigned_privs ) then
minetest.notify_authentication_modified( username )
end
end,
record_login = function ( ) end,
reload = function ( ) end,
iterate = auth_db.records
} )
auth_db.connect( )
auth_filter.is_enabled = true
__commands( { auth_db = auth_db, auth_filter = auth_filter } )
print("[auth_rx] mod authentication database loaded" )