checkpoint (nonfunctional)

master
y 2019-06-16 04:52:08 +01:00
parent 5c449e099d
commit 4e383f59f6
9 changed files with 387 additions and 305 deletions

View File

@ -10,7 +10,7 @@ local NETWORK_ASN_FILE = 'data-raw-table'
local load_file = verbana.util.load_file
local function refresh_asn_descriptions()
local contents = load_file(ASN_DESCRIPTION_FILE)
local contents = load_file(('%s/%s'):format(verbana.modpath, ASN_DESCRIPTION_FILE))
if not contents then return end
local description = {}
@ -29,7 +29,7 @@ local function refresh_asn_descriptions()
end
local function refresh_asn_table()
local contents = load_file(NETWORK_ASN_FILE)
local contents = load_file(('%s/%s'):format(verbana.modpath, NETWORK_ASN_FILE))
if not contents then return end
local networks = {}
@ -95,7 +95,7 @@ local function find(ipint)
local high = #t
while low <= high do
local mid = math.floor((low + high) / 2)
verbana.log('action', '%s %s %s %s %s', ipint, low, mid, high, #t)
-- verbana.log('action', '%s %s %s %s %s', ipint, low, mid, high, #t)
local element = t[mid]
local start = element[1]
local end_ = element[2]
@ -121,6 +121,6 @@ function verbana.asn.lookup(ipstr)
if asn then
return asn, verbana.asn.description[asn]
else
return nil, nil
return 0, 'IP not associated with a known ASN'
end
end

View File

@ -3,23 +3,29 @@ verbana.commands = {}
local mod_priv = verbana.privs.moderator
local admin_priv = verbana.privs.admin
function verbana.commands.import_sban(filename)
verbana.data.import_from_sban(filename)
return false, 'TODO: implement'
end
minetest.register_chatcommand('import_sban', {
params='<filename>',
description='import records from sban',
privs={[admin_priv]=true},
func=import_sban
func=function (_, filename)
if not filename or filename == '' then
filename = minetest.get_worldpath() .. '/sban.sqlite'
end
if not io.open(filename, 'r') then
return false, ('Could not open file %q.'):format(filename)
elseif verbana.data.import_from_sban(filename) then
return true, 'Successfully imported.'
else
return false, 'Error importing SBAN db (see server log)'
end
end
})
minetest.register_chatcommand('get_asn', {
params='<name> | <IP>',
description='get the ASN associated with an IP or player name',
privs={[mod_priv]=true},
func = function(caller, name_or_ipstr)
func = function(_, name_or_ipstr)
local ipstr
if verbana.ip.is_valid_ip(name_or_ipstr) then
@ -28,12 +34,12 @@ minetest.register_chatcommand('get_asn', {
ipstr = minetest.get_player_ip(name_or_ipstr)
end
if not ipstr then
if not ipstr or ipstr == '' then
return false, ('"%s" is not a valid ip nor a connected player'):format(name_or_ipstr)
end
local asn, description = verbana.asn.lookup(ipstr)
if not asn then
if not asn or asn == 0 then
return false, ('could not find ASN for "%s"'):format(ipstr)
end
@ -48,7 +54,7 @@ minetest.register_chatcommand('verify', {
description='verify a player',
privs={[mod_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -57,7 +63,7 @@ minetest.register_chatcommand('unverify', {
description='unverify a player',
privs={[mod_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -66,7 +72,7 @@ minetest.override_chatcommand('kick', {
description='kick a player',
privs={[mod_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -75,7 +81,7 @@ minetest.register_chatcommand('lock', {
description='lock a player\'s account',
privs={[mod_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -84,7 +90,7 @@ minetest.register_chatcommand('unlock', {
description='unlock a player\'s account',
privs={[mod_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -94,7 +100,7 @@ minetest.override_chatcommand('ban', {
privs={[mod_priv]=true},
func=function(caller, params)
-- todo: make sure that the begining of 'reason' doesn't look like a timespan =b
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -103,7 +109,7 @@ minetest.register_chatcommand('tempban', {
description='ban a player for a length of time',
privs={[mod_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -112,7 +118,7 @@ minetest.override_chatcommand('unban', {
description='unban a player',
privs={[mod_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -121,7 +127,7 @@ minetest.register_chatcommand('whitelist', {
description='whitelist a player',
privs={[admin_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -130,7 +136,7 @@ minetest.register_chatcommand('unwhitelist', {
description='whitelist a player',
privs={[admin_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -139,7 +145,7 @@ minetest.register_chatcommand('suspect', {
description='mark a player as suspicious',
privs={[mod_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -148,7 +154,7 @@ minetest.register_chatcommand('unsuspect', {
description='unmark a player as suspicious',
privs={[mod_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -157,7 +163,53 @@ minetest.register_chatcommand('ban_record', {
description='shows the ban record of a player',
privs={[mod_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
local name, numberstr = string.match(params, '^([%a%d_-]+) +(%d+)$')
if not name then
name = string.match(params, '^([%a%d_-]+)$')
if not name then
return false, 'invalid arguments'
end
end
local rows = verbana.data.get_ban_record(name)
if not rows then
return false, 'An error occurred (see server logs)'
end
if #rows == 0 then
return true, 'No records found.'
end
local starti
if numberstr then
local number = tonumber(numberstr)
starti = math.max(1, #rows - number)
else
starti = 1
end
for index = starti,#rows do
local row = rows[index]
local executor = row[1]
local status = row[2]
local timestamp = os.date("%c", row[3])
local reason = row[4]
local expires
if row[5] then
expires = os.date("%c", row[5])
end
local message = ('%s: %s set status to %s.'):format(timestamp, executor, status)
if reason and reason ~= '' then
message = ('%s Reason: %s'):format(message, reason)
end
if expires then
message = ('%s Expires: %s'):format(message, expires)
end
minetest.chat_send_player(caller, message)
end
return true
end
})
@ -166,7 +218,7 @@ minetest.register_chatcommand('login_record', {
description='shows the login record of a player',
privs={[admin_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -175,7 +227,7 @@ minetest.register_chatcommand('inspect', {
description='list data associated with a player or IP',
privs={[admin_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -184,7 +236,7 @@ minetest.register_chatcommand('inspect_asn', {
description='list player accounts and statuses associated with an ASN',
privs={[admin_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -193,7 +245,7 @@ minetest.register_chatcommand('set_ip_status', {
description='set the status of an IP (default, dangerous, blocked)',
privs={[admin_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})
@ -202,7 +254,7 @@ minetest.register_chatcommand('set_asn_status', {
description='set the status of an ASN (default, dangerous, blocked)',
privs={[admin_priv]=true},
func=function(caller, params)
return false, 'TODO: implement'
return false, 'TODO: implement' -- TODO
end
})

363
data.lua
View File

@ -8,152 +8,190 @@ local db = verbana.db
local load_file = verbana.util.load_file
verbana.data = {}
verbana.data.player_status_id = {
unknown=0,
default=1,
unverified=2,
banned=3,
tempbanned=4,
locked=5,
whitelisted=6,
suspicious=7,
unknown=1,
default=2,
unverified=3,
banned=4,
tempbanned=5,
locked=6,
whitelisted=7,
suspicious=8,
}
verbana.data.player_status = verbana.util.table_invert(verbana.data.player_status_id)
verbana.data.ip_status_id = {
default=0,
trusted=1,
suspicious=2,
blocked=3,
tempblocked=4,
default=1,
trusted=2,
suspicious=3,
blocked=4,
tempblocked=5,
}
verbana.data.ip_status = verbana.util.table_invert(verbana.data.ip_status_id)
verbana.data.asn_status_id = {
default=0,
suspicious=1,
blocked=2,
tempblocked=3,
default=1,
suspicious=2,
blocked=3,
tempblocked=4,
}
verbana.data.asn_status = verbana.util.table_invert(verbana.data.asn_status_id)
verbana.data.verbana_player = '!verbana!'
verbana.data.verbana_player_id = 1
local function db_exec(stmt)
local status = db:exec(stmt)
if status ~= sql.OK then
verbana.log('error', 'SQLite ERROR executing %q: %s', stmt, db:errmsg())
local function init_db()
local schema = load_file(verbana.modpath .. '/schema.sql')
if not schema then
error(('Could not find Verbana schema at %q'):format(verbana.modpath .. '/schema.sql'))
end
if db:exec(schema) ~= sql.OK then
error(('Verbana failed to initialize the database: %s'):format(db:error_message()))
end
end
init_db()
local function execute(code, description)
if db:exec(code) ~= sql.OK then
verbana.log('error', 'executing %s %q: %s', description, code, db:errmsg())
return false
end
return true
end
local function get_full_table(query)
local table = {}
local function populate_table(udata, cols, values, names)
local row = {}
for col_id = 1,cols do
row[names[col_id]] = values[col_id]
end
table.insert(row)
return 0
local function prepare(code, description)
local statement = db:prepare(code)
if not statement then
verbana.log('error', 'preparing %s %q: %s', description, code, db:errmsg())
return nil
end
local status = db:exec(query, populate_table)
if status ~= sql.OK then
verbana.log('error', 'SQLite ERROR executing %q: %s', query, db:errmsg())
return
end
return table
return statement
end
local function init_db()
local schema = load_file(verbana.modpath .. '/schema.sql')
local ret_code = db_exec(schema)
if ret_code ~= sql.OK then
error(('Verbana failed to initialize the database: %s'):format(db:error_message()))
local function bind(statement, description, ...)
if statement:bind_values(...) ~= sql.OK then
verbana.log('error', 'binding %s: %s', description, db:errmsg())
return false
end
end -- init_db()
return true
end
init_db()
local function bind_and_step(statement, description, ...)
if not bind(...) then return false end
if statement:step() ~= sql.DONE then
verbana.log('unbans: stepping %s: %s', description, db:errmsg())
return false
end
statement:reset()
return true
end
local function finalize(statement, description)
if statement:finalize() ~= sql.OK then
verbana.log('unbans: finalizing %s: %s', description, db:errmsg())
return false
end
return true
end
local function execute_bind_one(code, description, ...)
local statement = prepare(code)
if not statement then return false end
if not bind_and_step(statement, description, ...) then return false end
if not finalize(statement, description) then return false end
return true
end
local function get_full_table(code, description, ...)
local statement = prepare(code, description)
if not statement then return nil end
if not bind(statement, ...) then return nil end
local rows = {}
for row in statement:rows() do
table.insert(rows, row)
end
if not finalize(statement, description) then return nil end
return rows
end
function verbana.data.import_from_sban(filename)
-- this method isn't as complicated as it looks; 2/3 of it is repetative error handling
local start = os.clock()
-- this method isn't as complicated as it looks; 90% of it is repetative error handling
if not execute('BEGIN TRANSACTION', 'sban import transaction') then
return false
end
local sban_db, _, errormsg = sql.open(filename, sql.OPEN_READONLY)
if not sban_db then
return false, ('Error opening %s: %s'):format(filename, errormsg)
verbana.log('error', 'Error opening %s: %s', filename, errormsg)
return false
end
local function _error(message, ...)
if message then
verbana.log('error', message, ...)
end
execute('ROLLBACK', 'sban import rollback')
if sban_db:close() ~= sql.OK then
verbana.log('error', 'closing sban DB %s', sban_db:errmsg())
end
return false
end
-- IMPORT INTO VERBANA --
local insert_player_sql = 'INSERT OR IGNORE INTO player (name) VALUES (?)'
local insert_player_statement = db:prepare()
local insert_player_statement = prepare('INSERT OR IGNORE INTO player (name) VALUES (?)', 'insert player')
if not insert_player_statement then return _error() end
for name in sban_db:urows('SELECT DISTINCT name FROM playerdata') do
local rv = insert_player_statement:bind_values(name)
if rv ~= sql.OK then
verbana.log('error', 'error binding %q in %q: %s', name, insert_player_sql, rv)
return false, 'Error importing players (see server log)'
if not bind_and_step(insert_player_statement, 'insert player', name) then
return _error()
end
local rv = insert_player_statement:step()
if rv ~= sql.DONE then
verbana.log('error', 'error stepping %q: %s', insert_player_sql, rv)
return false, 'Error importing players (see server log)'
end
insert_player_statement:reset()
end
local rv = insert_player_statement:finalize()
if rv ~= sql.OK then
verbana.log('error', 'error finalizing %q: %s', insert_player_sql, rv)
return false, 'Error importing player names (see server log)'
end
if not finalize(insert_player_statement, 'insert player') then return _error() end
-- GET VERBANA PLAYER IDS --
local player_id_by_name = {}
for id, name in db:urows('SELECT id, name FROM player') do
player_id_by_name[name] = id
end
-- associations --
local insert_assoc_sql = 'INSERT OR IGNORE INTO assoc (player_id, ip, asn) VALUES (?, ?, ?)'
local insert_assoc_statement = db:prepare(insert_assoc_sql)
for name, ipstr in sban_db:urows('SELECT DISTINCT name, ip FROM playerdata') do
local insert_ip_statement = prepare('INSERT OR IGNORE INTO ip (ip) VALUES (?)', 'insert IP')
if not insert_ip_statement then return _error() end
local insert_asn_statement = prepare('INSERT OR IGNORE INTO asn (asn) VALUES (?)', 'insert ASN')
if not insert_asn_statement then return _error() end
local insert_assoc_statement = prepare('INSERT OR IGNORE INTO assoc (player_id, ip, asn) VALUES (?, ?, ?)', 'insert assoc')
if not insert_assoc_statement then return _error() end
local insert_log_statement = prepare('INSERT OR IGNORE INTO log (player_id, ip, asn, success, timestamp) VALUES (?, ?, ?, ?, ?)', 'insert log')
if not insert_log_statement then return _error() end
for name, ipstr, created, last_login in sban_db:urows('SELECT name, ip, created, last_login FROM playerdata') do
local player_id = player_id_by_name[name]
if not verbana.ip.is_valid_ip(ipstr) then
verbana.log('error', '%s is not a valid IPv4 address', ipstr)
return false, 'error processing IPs in SBAN (see server log)'
return _error('%s is not a valid IPv4 address', ipstr)
end
local ipint = verbana.ip.ipstr_to_number(ipstr)
local asn = verbana.asn.lookup(ipint)
local rv = insert_assoc_statement:bind_values(player_id, ipint, asn)
if rv ~= sql.OK then
verbana.log('error', 'error binding %q %q %q in %q: %s', player_id, ipint, asn, insert_assoc_sql, rv)
return false, 'Error importing associations (see server log)'
end
local rv = insert_assoc_statement:step()
if rv ~= sql.DONE then
verbana.log('error', 'error stepping %q: %s', insert_assoc_sql, rv)
return false, 'Error importing associations (see server log)'
end
insert_assoc_statement:reset()
if not bind_and_step(insert_ip_statement, 'insert IP', ipint) then return _error() end
if not bind_and_step(insert_asn_statement, 'insert ASN', asn) then return _error() end
if not bind_and_step(insert_assoc_statement, 'insert assoc', player_id, ipint, asn) then return _error() end
if not bind_and_step(insert_log_statement, 'insert log', player_id, ipint, asn, true, created) then return _error() end
end
local rv = insert_assoc_statement:finalize()
if rv ~= sql.OK then
verbana.log('error', 'error finalizing %q: %s', insert_assoc_sql, rv)
return false, 'Error IP importing associations (see server log)'
end
-- player action --
local insert_player_action_sql = [[
INSERT OR IGNORE
INTO player_action_log (executor_id, player_id, status_id, timestamp, reason, expires)
VALUES (?, ?, ?, ?, ?, ?)
]]
local insert_player_action_statement = db:prepare(insert_player_action_sql)
local select_bans_sql = [[
SELECT name, source, created, reason, expires, u_source, u_reason, u_date
FROM bans
ORDER BY name, created
]]
if not finalize(insert_ip_statement, 'insert IP') then return _error() end
if not finalize(insert_asn_statement, 'insert ASN') then return _error() end
if not finalize(insert_assoc_statement, 'insert assoc') then return _error() end
if not finalize(insert_log_statement, 'insert log') then return _error() end
-- player status --
local default_player_status_id = verbana.data.player_status_id.default
local banned_player_status_id = verbana.data.player_status_id.banned
local tempbanned_player_status_id = verbana.data.player_status_id.tempbanned
local insert_player_status_sql = [[
INSERT OR IGNORE
INTO player_status_log (executor_id, player_id, status_id, timestamp, reason, expires)
VALUES (?, ?, ?, ?, ?, ?)
]]
local insert_player_status_statement = prepare(insert_player_status_sql, 'insert player status')
if not insert_player_status_statement then return _error() end
local select_bans_sql = [[
SELECT name, source, created, reason, expires, u_source, u_reason, u_date
FROM bans
ORDER BY created
]]
for name, source, created, reason, expires, u_source, u_reason, u_date in sban_db:urows(select_bans_sql) do
local player_id = player_id_by_name[name]
local source_id = player_id_by_name[source]
@ -163,98 +201,61 @@ function verbana.data.import_from_sban(filename)
else
unban_source_id = player_id_by_name[u_source]
end
local status_id
if expires then
if expires and type(expires) == 'number' then
status_id = tempbanned_player_status_id
else
status_id = banned_player_status_id
expires = nil
end
local rv = insert_player_action_statement:bind_values(
source_id, player_id, status_id, created, reason, expires
)
if rv ~= sql.OK then
verbana.log('error', 'bans: error binding %q %q %q %q %q %q in %q: %s',
source_id, player_id, status_id, created, reason, expires, insert_player_action_sql, rv
)
return false, 'Error importing bans (see server log)'
end
local rv = insert_player_action_statement:step()
if rv ~= sql.DONE then
verbana.log('error', 'bans: error stepping %q: %s', insert_player_action_sql, rv)
return false, 'Error importing bans (see server log)'
end
insert_player_action_statement:reset()
-- BAN
if not bind_and_step(insert_player_status_statement, 'insert player status (ban)', source_id, player_id, status_id, created, reason, expires) then return _error() end
-- UNBAN
if unban_source_id then
local status_id = default_player_status_id
local rv = insert_player_action_statement:bind_values(
unban_source_id, player_id, status_id, u_date, u_reason, nil
)
if rv ~= sql.OK then
verbana.log('error', 'unbans: error binding %q %q %q %q %q %q in %q: %s',
unban_source_id, player_id, status_id, u_date, u_reason, nil, insert_player_action_sql, rv
)
return false, 'Error importing bans (see server log)'
end
local rv = insert_player_action_statement:step()
if rv ~= sql.DONE then
verbana.log('error', 'unbans: error stepping %q: %s', insert_player_action_sql, rv)
return false, 'Error importing bans (see server log)'
end
insert_player_action_statement:reset()
if not bind_and_step(insert_player_status_statement, 'insert player status (unban)', unban_source_id, player_id, default_player_status_id, u_date, u_reason, nil) then return _error() end
end
end
local rv = insert_player_action_statement.finalize()
if rv ~= sql.OK then
verbana.log('error', 'error finalizing %q: %s', insert_player_action_sql, rv)
return false, 'Error importing bans (see server log)'
if not finalize(insert_player_status_statement, 'insert player status') then return _error() end
-- SET LAST ACTION --
local set_last_status_id_sql = [[
UPDATE player
SET last_status_id = (SELECT MAX(player_status_log.id)
FROM player_status_log
WHERE player_status_log.player_id == player.id);
]]
if not execute(set_last_status_id_sql, 'set last status') then return _error() end
-- CLEANUP --
if not execute('COMMIT') then
if sban_db:close() ~= sql.OK then
verbana.log('error', 'closing sban DB %s', sban_db:errmsg())
end
return false
end
if sban_db:close() ~= sql.OK then
verbana.log('error', 'closing sban DB %s', sban_db:errmsg())
return false
end
verbana.log('action', 'imported from SBAN in %s seconds', os.clock() - start)
return true
end -- verbana.data.import_from_sban
-- set the current player actions
-- local player_data = get_full_table([[
-- SELECT id, name, ip, created, last_login FROM playerdata
-- ]])
-- local bans_data = get_full_table([[
-- SELECT id, name, source, created, reason, expires, u_source, u_reason, u_date, active, last_pos FROM bans
-- ]])
sban_db.close()
function verbana.data.get_player_id(name, create_if_new)
if create_if_new then
if not execute_bind_one('INSERT OR IGNORE INTO player (name) VALUES (?)', 'insert player', name) then return nil end
end
local table = get_full_table('SELECT id FROM player WHERE name = ? LIMIT 1', 'get player id')
if not (table and table[1]) then return nil end
return table[1][1]
end
function verbana.data.get_player_id(name) end
function verbana.data.get_player_status(player_id) return {} end
function verbana.data.set_player_status(player_id, executod_id, status_name, reason, expires) end
function verbana.data.ban_player(player_id, executor_id, reason) end
function verbana.data.tempban_player(player_id, executor_id, reason, expires) end
function verbana.data.unban_player(player_id, executor_id, reason) end
function verbana.data.verify_player(player_id, executor_id, reason) end
function verbana.data.unverify_player(player_id, executor_id, reason) end
function verbana.data.lock_player(player_id, executor_id, reason) end
function verbana.data.unlock_player(player_id, executor_id, reason) end
function verbana.data.whitelist_player(player_id, executor_id, reason) end
function verbana.data.unwhitelist_player(player_id, executor_id, reason) end
function verbana.data.suspect_player(player_id, executor_id, reason) end
function verbana.data.unsuspect_player(player_id, executor_id, reason) end
function verbana.data.set_player_status(player_id, executor_id, status_id, reason, expires) end
function verbana.data.get_ip_status(ipint) return {} end
function verbana.data.set_ip_status(ipint, executor_id, status_name, reason, expires) end
function verbana.data.block_ip(ipint, executor_id, reason) end
function verbana.data.tempblock_ip(ipint, executor_id, reason, expires) end
function verbana.data.unblock_ip(ipint, executor_id, reason) end
function verbana.data.trust_ip(ipint, executor_id, reason) end
function verbana.data.untrust_ip(ipint, executor_id, reason) end
function verbana.data.suspect_ip(ipint, executor_id, reason) end
function verbana.data.unsuspect_ip(ipint, executor_id, reason) end
function verbana.data.get_asn_status(asn) return {} end
function verbana.data.set_asn_status(asn, executor_id, status_name, reason, expires) end
function verbana.data.block_asn(asn, executor_id, reason) end
function verbana.data.tempblock_asn(asn, executor_id, reason, expires) end
function verbana.data.unblock_asn(asn, executor_id, reason) end
function verbana.data.suspect_asn(asn, executor_id, reason) end
function verbana.data.unsuspect_asn(asn, executor_id, reason) end
function verbana.data.log(player_id, ipint, asn, success) end
@ -262,6 +263,22 @@ function verbana.data.assoc(player_id, ipint, asn) end
function verbana.data.has_asn_assoc(player_id, asn) end
function verbana.data.has_ip_assoc(player_id, ipint) end
function verbana.data.get_ban_record(player_name)
local ban_record_sql = [[
SELECT executor.name
, player_status.name
, player_status_log.timestamp
, player_status_log.reason
, player_status_log.expires
FROM player_status_log
JOIN player ON player_status_log.player_id == player.id
JOIN player executor ON player_status_log.executor_id == executor.id
JOIN player_status ON player_status_log.status_id == player_status.id
WHERE LOWER(player.name) == LOWER(?)
ORDER BY player_status_log.timestamp
]]
return get_full_table(ban_record_sql, 'ban record')
end
-- TODO: methods to get logs of player_status, ip_status, asn_status
-- TODO: methods to get connection logs by player, ip, asn
-- TODO: methods to get association logs by player, ip, asn

View File

@ -1,54 +0,0 @@
verbana = {}
verbana.version = '1.0.0'
local modname = minetest.get_current_modname()
verbana.modname = modname
verbana.modpath = minetest.get_modpath(modname)
function verbana.log(level, message, ...)
minetest.log(level, ('[%s] %s'):format(modname, message:format(...)))
end
verbana.ie = minetest.request_insecure_environment()
if not verbana.ie then
error('Verbana will not work unless it has been listed under secure.trusted_mods in minetest.conf')
end
-- settings
dofile(verbana.modpath .. '/settings.lua')
dofile(verbana.modpath .. '/privs.lua')
-- libraries
dofile(verbana.modpath .. '/util.lua')
dofile(verbana.modpath .. '/lib_ip.lua')
dofile(verbana.modpath .. '/asn.lua')
-- connect to the DB
local sql = verbana.ie.require("lsqlite3")
local db_location = ('%s/verbana.sqlite'):format(minetest.get_worldpath()) -- TODO get path from settings
local db, _, errmsg = sql.open(db_location)
if not db then
error(('Verbana could not open its database @ %q: %q'):format(db_location, errmsg))
end
verbana.sql = sql
verbana.db = db
minetest.register_on_shutdown(function()
local ret_code = db:close()
if ret_code ~= sql.OK then
verbana.log('error', 'Error closing DB: %s', db:error_message())
end
end)
-- core
dofile(verbana.modpath .. '/chat.lua')
dofile(verbana.modpath .. '/data.lua')
dofile(verbana.modpath .. '/login_handling.lua')
dofile(verbana.modpath .. '/commands.lua')
-- cleanup (prevent access to insecure environment from any outside mod)
sqlite3 = nil
verbana.ie = nil
verbana.sql = nil
verbana.db = nil

View File

@ -0,0 +1,55 @@
verbana = {}
verbana.version = '1.0.0'
local modname = minetest.get_current_modname()
verbana.modname = modname
verbana.modpath = minetest.get_modpath(modname)
function verbana.log(level, message, ...)
minetest.log(level, ('[%s] %s'):format(modname, message:format(...)))
end
verbana.ie = minetest.request_insecure_environment()
if not verbana.ie then
error('Verbana will not work unless it has been listed under secure.trusted_mods in minetest.conf')
end
-- settings
dofile(verbana.modpath .. '/settings.lua')
dofile(verbana.modpath .. '/privs.lua')
-- libraries
dofile(verbana.modpath .. '/util.lua')
dofile(verbana.modpath .. '/lib_ip.lua')
dofile(verbana.modpath .. '/asn.lua')
-- connect to the DB - MAKE SURE TO CLEAN UP ALL "insecure" access points!
local sql = verbana.ie.require('lsqlite3') -- TODO what happens if this isn't installed? ....
verbana.sql = sql
local db_location = ('%s/verbana.sqlite'):format(minetest.get_worldpath()) -- TODO get path from settings
local db, _, errmsg = sql.open(db_location)
if not db then
error(('Verbana could not open its database @ %q: %q'):format(db_location, errmsg))
else
verbana.db = db
end
minetest.register_on_shutdown(function()
local ret_code = db:close()
if ret_code ~= sql.OK then
verbana.log('error', 'Error closing DB: %s', db:error_message())
end
end)
-- core
dofile(verbana.modpath .. '/chat.lua')
dofile(verbana.modpath .. '/data.lua')
dofile(verbana.modpath .. '/login_handling.lua')
dofile(verbana.modpath .. '/commands.lua')
-- cleanup (prevent access to insecure environment from any outside mod, or in-game)
sqlite3 = nil
verbana.ie = nil
verbana.sql = nil
verbana.db = nil

View File

@ -4,11 +4,11 @@ verbana.ip = {}
function verbana.ip.is_valid_ip(ipstr)
local a, b, c, d = ipstr:match('^(%d+)%.(%d+)%.(%d+)%.(%d+)$')
if not a and b and c and d then return false end
a = tonumber(a)
b = tonumber(b)
c = tonumber(c)
d = tonumber(d)
if not (a and b and c and d) then return false end
return 0 <= a and a < 256 and 0 <= b and b < 256 and 0 <= c and c < 256 and 0 <= d and d < 256
end

View File

@ -9,10 +9,10 @@ minetest.register_on_prejoinplayer(function(name, ipstr)
local ipint = verbana.ip.ipstr_to_number(ipstr)
local asn, asn_description = verbana.asn.lookup(ipint)
local player_id = verbana.data.get_player_id(name)
local player_status = verbana.data.get_player_status(player_id)
local ip_status = verbana.data.get_ip_status(ipint)
local asn_status = verbana.data.get_asn_status(asn)
local player_id = verbana.data.get_player_id(name, true) -- will create one if none exists
local player_status = verbana.data.get_player_status(player_id, true)
local ip_status = verbana.data.get_ip_status(ipint, true) -- will create one if none exists
local asn_status = verbana.data.get_asn_status(asn, true) -- will create one if none exists
-- check and clear temporary statuses
local now = os.time()
@ -39,7 +39,7 @@ minetest.register_on_prejoinplayer(function(name, ipstr)
end
local player_privs = minetest.get_player_privs(name)
local is_new_player = table_is_empty(player_privs)
local is_new_player = table_is_empty(player_privs) and player_status.name == 'unknown'
local suspicious = false
local return_value
@ -164,7 +164,11 @@ minetest.register_on_newplayer(function(player)
)
if need_to_verify then
verbana.data.unverify_player(player_id, 1, 'new player connected from suspicious network')
verbana.data.unverify_player(
player_id,
verbana.data.verbana_player_id,
'new player connected from suspicious network'
)
minetest.set_player_privs(name, verbana.settings.unverified_privs)
player:set_pos(verbana.settings.verification_pos)
-- wait a second before moving the player to the verification area
@ -172,6 +176,12 @@ minetest.register_on_newplayer(function(player)
minetest.after(1, function() move_to(name, verbana.settings.verification_pos) end)
verbana.log('action', 'new player %s sent to verification', name)
else
verbana.data.set_player_status(
player_id,
verbana.data.verbana_player_id,
verbana.data.player_status_id.default,
'new player'
)
verbana.log('action', 'new player %s', name)
end
end)

View File

@ -7,43 +7,44 @@ CREATE TABLE IF NOT EXISTS player_status (
CREATE INDEX IF NOT EXISTS player_status_name ON player_status(name);
INSERT OR IGNORE INTO player_status
(id, name)
VALUES ( 0, 'unknown')
, ( 1, 'default')
, ( 2, 'unverified')
, ( 3, 'banned')
, ( 4, 'tempbanned')
, ( 5, 'locked')
, ( 6, 'whitelisted')
, ( 7, 'suspicious');
VALUES ( 1, 'unknown')
, ( 2, 'default')
, ( 3, 'unverified')
, ( 4, 'banned')
, ( 5, 'tempbanned')
, ( 6, 'locked')
, ( 7, 'whitelisted')
, ( 8, 'suspicious');
CREATE TABLE IF NOT EXISTS player (
id INTEGER PRIMARY KEY AUTOINCREMENT
, name TEXT NOT NULL
, main_player_id INTEGER
, last_action_id INTEGER
, last_status_id INTEGER
, FOREIGN KEY (main_player_id) REFERENCES player(id)
, FOREIGN KEY (last_action_id) REFERENCES player_action_log(id)
, FOREIGN KEY (last_status_id) REFERENCES player_status_log(id)
);
CREATE UNIQUE INDEX IF NOT EXISTS player_name ON player(name);
CREATE INDEX IF NOT EXISTS player_main_player_id ON player(main_player_id);
CREATE INDEX IF NOT EXISTS player_last_action_id ON player(last_action_id);
CREATE INDEX IF NOT EXISTS player_last_status_id ON player(last_status_id);
INSERT OR IGNORE INTO player (name) VALUES ('!verbana!');
CREATE TABLE IF NOT EXISTS player_action_log (
CREATE TABLE IF NOT EXISTS player_status_log (
id INTEGER PRIMARY KEY AUTOINCREMENT
, executor_id INTEGER NOT NULL
, player_id INTEGER NOT NULL
, status_id INTEGER NOT NULL
, timestamp INTEGER NOT NULL
, reason TEXT
, expires INTEGER
, expires INTEGER -- only meaningful for temp bans
, FOREIGN KEY (executor_id) REFERENCES player(id)
, FOREIGN KEY (player_id) REFERENCES player(id)
, FOREIGN KEY (status_id) REFERENCES player_status(id)
, UNIQUE (player_id, status_id, timestamp)
);
CREATE INDEX IF NOT EXISTS player_action_log_player_id ON player_action_log(player_id);
CREATE INDEX IF NOT EXISTS player_action_log_timestamp ON player_action_log(timestamp);
CREATE INDEX IF NOT EXISTS player_action_log_reason ON player_action_log(reason);
CREATE INDEX IF NOT EXISTS player_status_log_player_id ON player_status_log(player_id);
CREATE INDEX IF NOT EXISTS player_status_log_timestamp ON player_status_log(timestamp);
CREATE INDEX IF NOT EXISTS player_status_log_reason ON player_status_log(reason);
-- END PLAYER
-- IP
CREATE TABLE IF NOT EXISTS ip_status (
@ -53,20 +54,20 @@ CREATE TABLE IF NOT EXISTS ip_status (
CREATE INDEX IF NOT EXISTS ip_status_name ON ip_status(name);
INSERT OR IGNORE INTO ip_status
(id, name)
VALUES ( 0, 'default')
, ( 1, 'trusted')
, ( 2, 'suspicious')
, ( 3, 'blocked')
, ( 4, 'tempblocked');
VALUES ( 1, 'default')
, ( 2, 'trusted')
, ( 3, 'suspicious')
, ( 4, 'blocked')
, ( 5, 'tempblocked');
CREATE TABLE IF NOT EXISTS ip (
ip INTEGER PRIMARY KEY
, last_action_id INTEGER
, FOREIGN KEY (last_action_id) REFERENCES ip_action_log(id)
, last_status_id INTEGER
, FOREIGN KEY (last_status_id) REFERENCES ip_status_log(id)
);
CREATE INDEX IF NOT EXISTS ip_last_action_id ON ip(last_action_id);
CREATE INDEX IF NOT EXISTS ip_last_status_id ON ip(last_status_id);
CREATE TABLE IF NOT EXISTS ip_action_log (
CREATE TABLE IF NOT EXISTS ip_status_log (
id INTEGER PRIMARY KEY AUTOINCREMENT
, executor_id INTEGER NOT NULL
, ip INTEGER NOT NULL
@ -78,9 +79,9 @@ CREATE TABLE IF NOT EXISTS ip_action_log (
, FOREIGN KEY (ip) REFERENCES ip(ip)
, FOREIGN KEY (status_id) REFERENCES ip_status(id)
);
CREATE INDEX IF NOT EXISTS ip_action_log_ip ON ip_action_log(ip);
CREATE INDEX IF NOT EXISTS ip_action_log_timestamp ON ip_action_log(timestamp);
CREATE INDEX IF NOT EXISTS ip_action_log_reason ON ip_action_log(reason);
CREATE INDEX IF NOT EXISTS ip_status_log_ip ON ip_status_log(ip);
CREATE INDEX IF NOT EXISTS ip_status_log_timestamp ON ip_status_log(timestamp);
CREATE INDEX IF NOT EXISTS ip_status_log_reason ON ip_status_log(reason);
-- END IP
-- ASN
CREATE TABLE IF NOT EXISTS asn_status (
@ -90,19 +91,19 @@ CREATE TABLE IF NOT EXISTS asn_status (
CREATE INDEX IF NOT EXISTS asn_status_name ON asn_status(name);
INSERT OR IGNORE INTO asn_status
(id, name)
VALUES ( 0, 'default')
, ( 1, 'suspicious')
, ( 2, 'blocked')
, ( 3, 'tempblocked');
VALUES ( 1, 'default')
, ( 2, 'suspicious')
, ( 3, 'blocked')
, ( 4, 'tempblocked');
CREATE TABLE IF NOT EXISTS asn (
asn INTEGER PRIMARY KEY
, last_action_id INTEGER
, FOREIGN KEY (last_action_id) REFERENCES asn_action_log(id)
, last_status_id INTEGER
, FOREIGN KEY (last_status_id) REFERENCES asn_status_log(id)
);
CREATE INDEX IF NOT EXISTS asn_last_action_id ON asn(last_action_id);
CREATE INDEX IF NOT EXISTS asn_last_status_id ON asn(last_status_id);
CREATE TABLE IF NOT EXISTS asn_action_log (
CREATE TABLE IF NOT EXISTS asn_status_log (
id INTEGER PRIMARY KEY AUTOINCREMENT
, executor_id INTEGER NOT NULL
, asn INTEGER NOT NULL
@ -114,9 +115,9 @@ CREATE TABLE IF NOT EXISTS asn_action_log (
, FOREIGN KEY (asn) REFERENCES asn(asn)
, FOREIGN KEY (status_id) REFERENCES asn_status(id)
);
CREATE INDEX IF NOT EXISTS asn_action_log_asn ON asn_action_log(asn);
CREATE INDEX IF NOT EXISTS asn_action_log_timestamp ON asn_action_log(timestamp);
CREATE INDEX IF NOT EXISTS asn_action_log_reason ON asn_action_log(reason);
CREATE INDEX IF NOT EXISTS asn_status_log_asn ON asn_status_log(asn);
CREATE INDEX IF NOT EXISTS asn_status_log_timestamp ON asn_status_log(timestamp);
CREATE INDEX IF NOT EXISTS asn_status_log_reason ON asn_status_log(reason);
-- END ASN
-- OTHER
CREATE TABLE IF NOT EXISTS log (
@ -128,6 +129,7 @@ CREATE TABLE IF NOT EXISTS log (
, FOREIGN KEY (player_id) REFERENCES player(id)
, FOREIGN KEY (ip) REFERENCES ip(ip)
, FOREIGN KEY (asn) REFERENCES asn(asn)
, UNIQUE (player_id, ip, success, timestamp)
);
CREATE INDEX IF NOT EXISTS log_player ON log(player_id);
CREATE INDEX IF NOT EXISTS log_ip ON log(ip);

View File

@ -1,7 +1,7 @@
verbana.util = {}
function verbana.util.load_file(filename)
local file = io.open(('%s/%s'):format(verbana.modpath, filename), 'r')
local file = io.open(filename, 'r')
if not file then
verbana.log('error', 'error opening "%s"', filename)
return