several updates

master
flux 2019-07-31 13:38:24 +00:00
parent 1cf079fac8
commit cd8384738a
9 changed files with 487 additions and 217 deletions

11
TODO
View File

@ -1,23 +1,22 @@
rework /asn_inspect
- count number of entries a player has in the status table
- only report players that have more than one entry in that table
rework /ban_record
- make it a summary of whether a player might be suspicious based on known associations
- like sban's ban record
- function currently named ban_record would be renamed "status"
- function currently named ban_record would be renamed "record"
- "so you want to specify his name, and see a list of banned/troublesome accounts from his ASN."
- "so with one command, I should see, perhaps, IP-linked accounts, first and last login, ban record, and flagged accounts sharing their asn"
create a way to search for players by IP/net instead of just ASN
add "last login" to players table
make way to show last login IP on certain commands (e.g. /inspect_asn)
make settings for the hard-coded values for reporting timing
alert staff if there are new reports since their last login
a way to resolve reports?
DOCUMENTATION....
--------------
# Administration

View File

@ -60,7 +60,7 @@ register_chatcommand('sban_import', {
if not filename or filename == '' then
filename = minetest.get_worldpath() .. '/sban.sqlite'
end
if not io.open(filename, 'r') then
if not util.file_exists(filename) then
return false, ('Could not open file %q.'):format(filename)
end
chat_send_player(caller, 'Importing SBAN. This can take a while...')
@ -834,7 +834,7 @@ register_chatcommand('asn_unblock', {
end
})
---------------- GET LOGS ---------------
register_chatcommand('ban_record', {
register_chatcommand('record', {
description='shows the status log of a player',
params='<player_name> [<number>]',
privs={[mod_priv]=true},
@ -885,6 +885,68 @@ register_chatcommand('ban_record', {
end
})
register_chatcommand('ban_record', {
description = 'shows relevant info for a player',
params = '<player_name>',
privs = { [mod_priv] = true },
func = function(caller, params)
local player_name = params:match('^([a-zA-Z0-9_-]+)$')
if not player_name or player_name:len() > 20 then
return false, 'Invalid argument'
end
local player_id = data.get_player_id(player_name)
if not player_id then
return false, 'Unknown player'
end
local ipint = data.fumble_about_for_an_ip(player_name, player_id)
local asn = lib_asn.lookup(ipint)
chat_send_player(caller, "Accounts associated by IP:")
local rows = data.get_player_cluster(player_id)
if not rows then return false, 'An error occurred (see server logs)' end
local clustered = {}
for _, row in ipairs(rows) do
local color = data.player_status_color[row.player_status_id] or data.player_status.default.color
table.insert(clustered, minetest.colorize(color, row.player_name))
end
chat_send_player(caller, table.concat(clustered, ', '))
chat_send_player(caller, "Flagged accounts on the same ASN:")
rows = data.get_asn_associations(asn, '1y')
if not rows then return false, 'An error occurred (see server logs)' end
local assocs = {}
for _, row in ipairs(rows) do
local color = data.player_status_color[row.player_status_id] or data.player_status.default.color
table.insert(assocs, minetest.colorize(color, row.player_name))
end
chat_send_player(caller, table.concat(clustered, ', '))
chat_send_player(caller, "Account status:")
rows = data.get_player_status_log(player_id)
if not rows then
return false, 'An error occurred (see server logs)'
end
for index = 1,#rows do
local row = rows[index]
local message = ('%s: %s set status to %s.'):format(
iso_date(row.timestamp),
row.executor_name,
row.status_name
)
local reason = row.reason
if reason and reason ~= '' then
message = ('%s Reason: %s'):format(message, reason)
end
local expires = row.expires
if expires then
message = ('%s Expires: %s'):format(message, iso_date(expires))
end
chat_send_player(caller, message)
end
return true
end
})
register_chatcommand('ip_record', {
description='shows the status log of an IP',
params='<IP> [<number>]',
@ -1061,7 +1123,7 @@ register_chatcommand('inspect', {
register_chatcommand('ip_inspect', {
description='list player accounts and statuses associated with an IP',
params='<IP> [<timespan>=1w]',
params='<IP> [<timespan>=1m]',
privs={[mod_priv]=true},
func=function(caller, params)
local ipstr, timespan_str = params:match('^(%d+%.%d+%.%d+%.%d+)%s+(%w+)$')
@ -1078,7 +1140,7 @@ register_chatcommand('ip_inspect', {
return false, 'Invalid timespan'
end
else
timespan = 60*60*24*7
timespan = parse_time('1m')
end
local ipint = lib_ip.ipstr_to_ipint(ipstr)
local rows = data.get_ip_associations(ipint, timespan)
@ -1102,7 +1164,7 @@ register_chatcommand('ip_inspect', {
register_chatcommand('asn_inspect', {
description='list player accounts and statuses associated with an ASN',
params='<ASN> [<timespan>=1w]',
params='<ASN> [<timespan>=1y]',
privs={[mod_priv]=true},
func=function(caller, params)
local asn, timespan_str = params:match('^A?(%d+)%s+(%w+)$')
@ -1119,7 +1181,7 @@ register_chatcommand('asn_inspect', {
return false, 'Invalid timespan'
end
else
timespan = 60*60*24*7
timespan = parse_time('1y')
end
asn = tonumber(asn)
local description = lib_asn.get_description(asn)
@ -1201,6 +1263,7 @@ register_chatcommand('cluster', {
return true
end
})
alias_chatcommand('/whois', 'cluster')
--
register_chatcommand('who2', {
description='Show current connected players, statuses, and sources',

474
data.lua
View File

@ -1,42 +1,73 @@
verbana.data = {}
local data = verbana.data
local lib_asn = verbana.lib_asn
local lib_ip = verbana.lib_ip
local util = verbana.util
local log = verbana.log
local sql = verbana.sql
local db = verbana.db
data.version = 1
-- constants
verbana.data.player_status = {
default={name='default', id=1},
suspicious={name='suspicious', id=2},
banned={name='banned', id=3},
whitelisted={name='whitelisted', id=4},
unverified={name='unverified', id=5},
kicked={name='kicked', id=6}, -- for logging kicks
data.player_status = {
default={name='default', id=1, color='#0F0'},
suspicious={name='suspicious', id=2, color='#FF0'},
banned={name='banned', id=3, color='#F00'},
whitelisted={name='whitelisted', id=4, color='#FFF'},
unverified={name='unverified', id=5, color='#00F'},
kicked={name='kicked', id=6, color='#F0F'}, -- for logging kicks
}
verbana.data.player_status_name = {}
for _, value in pairs(verbana.data.player_status) do verbana.data.player_status_name[value.id] = value.name end
verbana.data.ip_status = {
default={name='default', id=1},
suspicious={name='suspicious', id=2},
blocked={name='blocked', id=3},
trusted={name='trusted', id=4},
data.player_status_name = {}
data.player_status_color = {}
for _, value in pairs(data.player_status) do
data.player_status_name[value.id] = value.name
data.player_status_color[value.id] = value.color
end
data.ip_status = {
default={name='default', id=1, color='#0F0'},
suspicious={name='suspicious', id=2, color='#FF0'},
blocked={name='blocked', id=3, color='#F00'},
trusted={name='trusted', id=4, color='#FFF'},
}
verbana.data.ip_status_name = {}
for _, value in pairs(verbana.data.ip_status) do verbana.data.ip_status_name[value.id] = value.name end
verbana.data.asn_status = {
default={name='default', id=1},
suspicious={name='suspicious', id=2},
blocked={name='blocked', id=3},
data.ip_status_name = {}
data.ip_status_color = {}
for _, value in pairs(data.ip_status) do
data.ip_status_name[value.id] = value.name
data.ip_status_color[value.id] = value.color
end
data.asn_status = {
default={name='default', id=1, color='#0F0'},
suspicious={name='suspicious', id=2, color='#FF0'},
blocked={name='blocked', id=3, color='#FF0'},
}
verbana.data.asn_status_name = {}
for _, value in pairs(verbana.data.asn_status) do verbana.data.asn_status_name[value.id] = value.name end
verbana.data.verbana_player = '!verbana!'
verbana.data.verbana_player_id = 1
data.asn_status_name = {}
data.asn_status_color = {}
for _, value in pairs(data.asn_status) do
data.asn_status_name[value.id] = value.name
data.asn_status_color[value.id] = value.color
end
data.verbana_player = '!verbana!'
data.verbana_player_id = 1
-- wrap sqllite API to make error reporting less messy
local function check_description(description)
return (
type(description) == 'string' and
description ~= ''
)
end
local function execute(code, description)
if not check_description(description) then
log('error', 'bad description for execute: %q', tostring(description))
return false
end
if db:exec(code) ~= sql.OK then
log('error', 'executing %s %q: %s', description, code, db:errmsg())
return false
@ -45,15 +76,23 @@ local function execute(code, description)
end
local function prepare(code, description)
if not check_description(description) then
log('error', 'bad description for prepare: %q', tostring(description))
return false
end
local statement = db:prepare(code)
if not statement then
log('error', 'preparing %s %q: %s', description, code, db:errmsg())
return nil
return
end
return statement
end
local function bind(statement, description, ...)
if not check_description(description) then
log('error', 'bad description for bind: %q', tostring(description))
return false
end
if statement:bind_values(...) ~= sql.OK then
log('error', 'binding %s: %s %q', description, db:errmsg(), minetest.serialize({...}))
return false
@ -62,6 +101,10 @@ local function bind(statement, description, ...)
end
local function bind_and_step(statement, description, ...)
if not check_description(description) then
log('error', 'bad description for bind_and_step: %q', tostring(description))
return false
end
if not bind(statement, description, ...) then return false end
if statement:step() ~= sql.DONE then
log('error', 'stepping %s: %s %q', description, db:errmsg(), minetest.serialize({...}))
@ -72,6 +115,10 @@ local function bind_and_step(statement, description, ...)
end
local function finalize(statement, description)
if not check_description(description) then
log('error', 'bad description for finalize: %q', tostring(description))
return false
end
if statement:finalize() ~= sql.OK then
log('error', 'finalizing %s: %s', description, db:errmsg())
return false
@ -80,6 +127,10 @@ local function finalize(statement, description)
end
local function execute_bind_one(code, description, ...)
if not check_description(description) then
log('error', 'bad description for execute_bind_one: %q', tostring(description))
return false
end
local statement = prepare(code, description)
if not statement then return false end
if not bind_and_step(statement, description, ...) then return false end
@ -88,6 +139,10 @@ local function execute_bind_one(code, description, ...)
end
local function get_full_table(code, description, ...)
if not check_description(description) then
log('error', 'bad description for get_full_table: %q', tostring(description))
return false
end
local statement = prepare(code, description)
if not statement then return nil end
if not bind(statement, description, ...) then return nil end
@ -100,6 +155,10 @@ local function get_full_table(code, description, ...)
end
local function get_full_ntable(code, description, ...)
if not check_description(description) then
log('error', 'bad description for get_full_ntable: %q', tostring(description))
return false
end
local statement = prepare(code, description)
if not statement then return nil end
if not bind(statement, description, ...) then return nil end
@ -111,18 +170,46 @@ local function get_full_ntable(code, description, ...)
return rows
end
local function sort_status_table(data)
local function sort_status_table(status_table)
local sortable = {}
for _, value in pairs(data) do table.insert(sortable, value) end
for _, value in pairs(status_table) do table.insert(sortable, value) end
table.sort(sortable, function (a, b) return a.id < b.id end)
return sortable
end
local function init_status_table(table_name, data)
-- SCHEMA INITIALIZATION
local function get_current_schema_version()
local code = [[
SELECT name
FROM sqlite_master
WHERE type == 'table'
AND name == ?;
]]
local rows = get_full_ntable(code, 'does version table exist?', 'version')
if not rows or #rows > 1 then
log('error', 'error checking if version table exists')
return nil
end -- ERROR
if #rows == 0 then return 0 end -- if version table doesn't exist, assume DB is version 1
code = [[SELECT version FROM version]]
rows = get_full_ntable(code, 'get current version')
if not rows or #rows ~= 1 then
log('error', 'error querying version table')
return nil
end -- ERROR
return rows[1].version
end
local function set_current_schema_version(version)
local code = [[UPDATE version SET version = ?]]
execute_bind_one(code, 'set current schema version', version)
end
local function init_status_table(table_name, status_table)
local status_sql = ('INSERT OR IGNORE INTO %s_status (id, name) VALUES (?, ?)'):format(table_name)
local status_statement = prepare(status_sql, ('initialize %s_status'):format(table_name))
if not status_statement then return false end
for _, status in ipairs(sort_status_table(data)) do
for _, status in ipairs(sort_status_table(status_table)) do
if not bind_and_step(status_statement, 'insert status', status.id, status.name) then
return false
end
@ -133,32 +220,47 @@ local function init_status_table(table_name, data)
return true
end
local function init_db()
local schema = verbana.util.load_file(verbana.modpath .. '/schema.sql')
local function intialize_schema()
verbana.log('action', 'initializing schema')
local schema = util.load_file(verbana.modpath .. '/schema.sql')
if not schema then
error(('Could not find Verbana schema at %q'):format(verbana.modpath .. '/schema.sql'))
error(('[Verbana] 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
if not init_status_table('player', verbana.data.player_status) then
error('error initializing player_status: see server log')
end
if not init_status_table('ip', verbana.data.ip_status) then
error('error initializing ip_status: see server log')
end
if not init_status_table('asn', verbana.data.asn_status) then
error('error initializing asn_status: see server log')
end
local verbana_player_sql = 'INSERT OR IGNORE INTO player (name) VALUES (?)'
if not execute_bind_one(verbana_player_sql, 'verbana player', verbana.data.verbana_player) then
error('error initializing verbana internal player: see server log')
error(('[Verbana] failed to initialize the database: %s'):format(db:error_message()))
end
end
init_db()
---- data API -----
function verbana.data.reset_db()
local function migrate_db(version)
verbana.log('action', 'migrating DB to version %s', version)
local filename = ('%s/migrations/%s.sql'):format(verbana.modpath, version)
local schema = util.load_file(filename)
if not schema then
error(('[Verbana] Could not find Verbana migration schema at %q'):format(filename))
end
if db:exec(schema) ~= sql.OK then
error(('[Verbana] failed to migrate the database to version %s: %s'):format(version, db:error_message()))
end
end
local function initialize_static_data()
verbana.log('action', 'initializing static data')
if not init_status_table('player', data.player_status) then
error('[Verbana] error initializing player_status: see server log')
end
if not init_status_table('ip', data.ip_status) then
error('[Verbana] error initializing ip_status: see server log')
end
if not init_status_table('asn', data.asn_status) then
error('[Verbana] error initializing asn_status: see server log')
end
local verbana_player_sql = 'INSERT OR IGNORE INTO player (name) VALUES (?)'
if not execute_bind_one(verbana_player_sql, 'verbana player', data.verbana_player) then
error('[Verbana] error initializing verbana internal player: see server log')
end
end
local function clean_db()
local code = [[
PRAGMA writable_schema = 1;
DELETE FROM sqlite_master WHERE type IN ('table', 'index', 'trigger');
@ -166,13 +268,45 @@ function verbana.data.reset_db()
VACUUM;
PRAGMA INTEGRITY_CHECK;
]]
if not execute(code, 'erase current DB') then
return false
end
init_db()
return execute(code, 'erase current DB')
end
function verbana.data.import_from_sban(filename)
local function init_db()
local initialized = false
local current_version = get_current_schema_version()
if not current_version then
error('[Verbana] error getting current DB version; aborting.')
elseif current_version > data.version then
error('[Verbana] database version is more recent than code version; please upgrade code.')
elseif current_version == data.version then
return -- everything is up to date
elseif current_version == 0 then
-- wipe any pre-existing copies of the schema
if not clean_db() then
error('[Verbana] error wiping existing DB')
end
intialize_schema()
current_version = 1
initialized = true
end
for i = current_version + 1, data.version do
migrate_db(i)
set_current_schema_version(i)
end
initialize_static_data()
local sban_path = minetest.get_worldpath() .. '/sban.sqlite'
if util.file_exists(sban_path) then
log('action', 'automatically importing existing sban DB')
if not data.import_from_sban(sban_path) then
log('error', 'failed to import existing sban DB')
end
end
end
-- initialize DB after registering import_from_sban
---- data API -----
function data.import_from_sban(filename)
-- apologies for the very long method
local start = os.clock()
if not execute('BEGIN TRANSACTION', 'sban import transaction') then
@ -188,10 +322,14 @@ function verbana.data.import_from_sban(filename)
local function _error(message, ...)
if message then
log('error', message, ...)
else
log('error', 'An error occurred while importing from sban')
end
execute('ROLLBACK', 'sban import rollback')
if sban_db:close() ~= sql.OK then
log('error', 'closing sban DB %s', sban_db:errmsg())
log('error', 'Error closing sban DB %s', sban_db:errmsg())
else
log('error', 'closed sban DB')
end
return false
end
@ -236,8 +374,8 @@ function verbana.data.import_from_sban(filename)
if not finalize(insert_assoc_statement, 'insert assoc') then return _error() end
if not finalize(insert_log_statement, 'insert connection log') then return _error() end
-- player status --
local default_player_status_id = verbana.data.player_status.default.id
local banned_player_status_id = verbana.data.player_status.banned.id
local default_player_status_id = data.player_status.default.id
local banned_player_status_id = data.player_status.banned.id
local insert_player_status_sql = [[
INSERT OR IGNORE
INTO player_status_log (executor_id, player_id, status_id, timestamp, reason, expires)
@ -245,13 +383,13 @@ function verbana.data.import_from_sban(filename)
]]
local insert_player_status_statement = prepare(insert_player_status_sql, 'insert player status')
if not insert_player_status_statement then return _error() end
-- local update_player_current_status_sql = [[
-- UPDATE player
-- SET current_status_id = ?
-- WHERE id = ?
-- ]]
-- local update_player_current_status_statement = prepare(update_player_current_status_sql, 'update player current status')
-- if not update_player_current_status_statement then return _error() end
local flag_player_sql = [[
UPDATE player
SET flagged = TRUE
WHERE id == ?
]]
local flag_player_statement = prepare(flag_player_sql, 'flag player')
if not flag_player_statement then return _error() end
local select_bans_sql = [[
SELECT name, source, created, reason, expires, u_source, u_reason, u_date
FROM bans
@ -260,9 +398,10 @@ function verbana.data.import_from_sban(filename)
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]
if not bind_and_step(flag_player_statement, 'flag player', player_id) then return _error() end
local unban_source_id
if u_source and u_source == 'sban' then
unban_source_id = source_id
unban_source_id = source_id -- if a temp ban expired, mark it as unban by the original banner
else
unban_source_id = player_id_by_name[u_source]
end
@ -274,10 +413,9 @@ function verbana.data.import_from_sban(filename)
if unban_source_id then
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
-- bind_and_step(update_player_current_status_statement, 'update player current status', ?, player_id)
end
if not finalize(insert_player_status_statement, 'insert player status') then return _error() end
-- if not finalize(update_player_current_status_statement, 'update player current status') then return _error() end
if not finalize(flag_player_statement, 'flag player') then return _error() end
-- SET LAST ACTION --
local set_current_status_id_sql = [[
UPDATE player
@ -286,8 +424,16 @@ function verbana.data.import_from_sban(filename)
WHERE player_status_log.player_id == player.id);
]]
if not execute(set_current_status_id_sql, 'set last status') then return _error() end
-- SET LAST LOGIN --
local set_last_login_id_sql = [[
UPDATE player
SET last_login_id = (SELECT MAX(connection_log.id)
FROM connection_log
WHERE connection_log.player_id == player.id);
]]
if not execute(set_last_login_id_sql, 'set last login') then return _error() end
-- CLEANUP --
if not execute('COMMIT') then
if not execute('COMMIT', 'commit sban import') then
if sban_db:close() ~= sql.OK then
log('error', 'closing sban DB %s', sban_db:errmsg())
end
@ -299,11 +445,12 @@ function verbana.data.import_from_sban(filename)
end
log('action', 'imported from SBAN in %s seconds', os.clock() - start)
return true
end -- verbana.data.import_from_sban
end -- data.import_from_sban
init_db() -- initialize DB after registering import_from_sban
local player_id_cache = {}
function verbana.data.get_player_id(name, create_if_new)
function data.get_player_id(name, create_if_new)
local cached_id = player_id_cache[name]
if cached_id then return cached_id end
if create_if_new then
@ -321,9 +468,19 @@ function verbana.data.get_player_id(name, create_if_new)
return table[1][1]
end
function data.flag_player(player_id, flag)
local code = [[
UPDATE player
SET flagged = ?
WHERE player_id = ?
]]
if not flag then flag = true end
return execute_bind_one(code, 'flag player', player_id)
end
local player_status_cache = {}
function verbana.data.get_player_status(player_id, create_if_new)
player_id = verbana.data.get_master(player_id) or player_id
function data.get_player_status(player_id, create_if_new)
player_id = data.get_master(player_id) or player_id
local cached_status = player_status_cache[player_id]
if cached_status then return cached_status, false end
local code = [[
@ -351,14 +508,14 @@ function verbana.data.get_player_status(player_id, create_if_new)
elseif not create_if_new then
return nil, nil
end
if not verbana.data.set_player_status(player_id, verbana.data.verbana_player_id, verbana.data.player_status.default.id, 'creating initial player status') then
if not data.set_player_status(player_id, data.verbana_player_id, data.player_status.default.id, 'creating initial player status') then
log('error', 'failed to set initial player status')
return nil, true
end
return verbana.data.get_player_status(player_id, false), true
return data.get_player_status(player_id, false), true
end
function verbana.data.set_player_status(player_id, executor_id, status_id, reason, expires, no_update_current)
player_id = verbana.data.get_master(player_id) or player_id
function data.set_player_status(player_id, executor_id, status_id, reason, expires, no_update_current)
player_id = data.get_master(player_id) or player_id
player_status_cache[player_id] = nil
local code = [[
INSERT INTO player_status_log (player_id, executor_id, status_id, reason, expires, timestamp)
@ -370,16 +527,19 @@ function verbana.data.set_player_status(player_id, executor_id, status_id, reaso
code = 'UPDATE player SET current_status_id = ? WHERE id = ?'
if not execute_bind_one(code, 'update player last status id', last_id, player_id) then return false end
end
if status_id ~= data.player_status.default.id then
return data.flag_player(player_id, true)
end
return true
end
function verbana.data.register_ip(ipint)
function data.register_ip(ipint)
local code = 'INSERT OR IGNORE INTO ip (ip) VALUES (?)'
return execute_bind_one(code, 'register ip', ipint)
end
local ip_status_cache = {}
function verbana.data.get_ip_status(ipint, create_if_new)
function data.get_ip_status(ipint, create_if_new)
local cached_status = ip_status_cache[ipint]
if cached_status then return cached_status end
local code = [[
@ -407,13 +567,13 @@ function verbana.data.get_ip_status(ipint, create_if_new)
elseif not create_if_new then
return nil
end
if not verbana.data.set_ip_status(ipint, verbana.data.verbana_player_id, verbana.data.ip_status.default.id, 'creating initial ip status') then
if not data.set_ip_status(ipint, data.verbana_player_id, data.ip_status.default.id, 'creating initial ip status') then
log('error', 'failed to set initial ip status')
return nil
end
return verbana.data.get_ip_status(ipint, false)
return data.get_ip_status(ipint, false)
end
function verbana.data.set_ip_status(ipint, executor_id, status_id, reason, expires)
function data.set_ip_status(ipint, executor_id, status_id, reason, expires)
ip_status_cache[ipint] = nil
local code = [[
INSERT INTO ip_status_log (ip, executor_id, status_id, reason, expires, timestamp)
@ -426,13 +586,13 @@ function verbana.data.set_ip_status(ipint, executor_id, status_id, reason, expir
return true
end
function verbana.data.register_asn(asn)
function data.register_asn(asn)
local code = 'INSERT OR IGNORE INTO asn (asn) VALUES (?)'
return execute_bind_one(code, 'register asn', asn)
end
local asn_status_cache = {}
function verbana.data.get_asn_status(asn, create_if_new)
function data.get_asn_status(asn, create_if_new)
local cached_status = asn_status_cache[asn]
if cached_status then return cached_status end
local code = [[
@ -460,13 +620,13 @@ function verbana.data.get_asn_status(asn, create_if_new)
elseif not create_if_new then
return nil
end
if not verbana.data.set_asn_status(asn, verbana.data.verbana_player_id, verbana.data.asn_status.default.id, 'creating initial asn status') then
if not data.set_asn_status(asn, data.verbana_player_id, data.asn_status.default.id, 'creating initial asn status') then
log('error', 'failed to set initial asn status')
return nil
end
return verbana.data.get_asn_status(asn, false)
return data.get_asn_status(asn, false)
end
function verbana.data.set_asn_status(asn, executor_id, status_id, reason, expires)
function data.set_asn_status(asn, executor_id, status_id, reason, expires)
asn_status_cache[asn] = nil
local code = [[
INSERT INTO asn_status_log (asn, executor_id, status_id, reason, expires, timestamp)
@ -479,17 +639,30 @@ function verbana.data.set_asn_status(asn, executor_id, status_id, reason, expire
return true
end
function verbana.data.log(player_id, ipint, asn, success)
function data.log(player_id, ipint, asn, success)
local code = [[
INSERT INTO connection_log (player_id, ip, asn, success, timestamp)
VALUES (?, ?, ?, ?, ?)
]]
if not execute_bind_one(code, 'log connection', player_id, ipint, asn, success, os.time()) then return false end
if not execute_bind_one(code, 'log connection', player_id, ipint, asn, success, os.time()) then
return false
end
if success then
local last_login_id = db:last_insert_rowid()
code = [[
UPDATE player
SET last_login_id = ?
WHERE id = ?
]]
if not execute_bind_one(code, 'set last login', last_login_id, player_id) then
return false
end
end
return true
end
function verbana.data.assoc(player_id, ipint, asn)
player_id = verbana.data.get_master(player_id) or player_id
function data.assoc(player_id, ipint, asn)
player_id = data.get_master(player_id) or player_id
local insert_code = [[
INSERT OR IGNORE INTO assoc (player_id, ip, asn, first_seen, last_seen)
VALUES (?, ?, ?, ?, ?)
@ -505,21 +678,21 @@ function verbana.data.assoc(player_id, ipint, asn)
if not execute_bind_one(update_code, 'update assoc', os.time(), player_id, ipint, asn) then return false end
return true
end
function verbana.data.has_asn_assoc(player_id, asn)
player_id = verbana.data.get_master(player_id) or player_id
function data.has_asn_assoc(player_id, asn)
player_id = data.get_master(player_id) or player_id
local code = 'SELECT 1 FROM assoc WHERE player_id = ? AND asn == ? LIMIT 1'
local table = get_full_table(code, 'find player asn assoc', player_id, asn)
return #table == 1
end
function verbana.data.has_ip_assoc(player_id, ipint)
player_id = verbana.data.get_master(player_id) or player_id
function data.has_ip_assoc(player_id, ipint)
player_id = data.get_master(player_id) or player_id
local code = 'SELECT 1 FROM assoc WHERE player_id = ? AND ip == ? LIMIT 1'
local table = get_full_table(code, 'find player asn assoc', player_id, ipint)
return #table == 1
end
function verbana.data.get_player_status_log(player_id)
player_id = verbana.data.get_master(player_id) or player_id
function data.get_player_status_log(player_id)
player_id = data.get_master(player_id) or player_id
local code = [[
SELECT executor.name executor_name
, status.name status_name
@ -535,7 +708,7 @@ function verbana.data.get_player_status_log(player_id)
]]
return get_full_ntable(code, 'player status log', player_id)
end
function verbana.data.get_ip_status_log(ipint)
function data.get_ip_status_log(ipint)
local code = [[
SELECT executor.name executor_name
, status.name status_name
@ -550,7 +723,7 @@ function verbana.data.get_ip_status_log(ipint)
]]
return get_full_ntable(code, 'ip status log', ipint)
end
function verbana.data.get_asn_status_log(asn)
function data.get_asn_status_log(asn)
local code = [[
SELECT executor.name executor_name
, status.name status_name
@ -566,7 +739,7 @@ function verbana.data.get_asn_status_log(asn)
return get_full_ntable(code, 'asn status log', asn)
end
function verbana.data.get_first_login(player_id)
function data.get_first_login(player_id)
local code = [[
SELECT timestamp
FROM connection_log
@ -576,7 +749,7 @@ function verbana.data.get_first_login(player_id)
]]
return get_full_ntable(code, 'first login', player_id)
end
function verbana.data.get_player_connection_log(player_id, limit)
function data.get_player_connection_log(player_id, limit)
local code = [[
SELECT log.ip ipint
, log.asn asn
@ -598,9 +771,9 @@ function verbana.data.get_player_connection_log(player_id, limit)
limit = 20
end
local t = get_full_ntable(code, 'player connection log', player_id, limit)
return verbana.util.table_reversed(t)
return util.table_reversed(t)
end
function verbana.data.get_ip_connection_log(ipint, limit)
function data.get_ip_connection_log(ipint, limit)
local code = [[
SELECT player.name player_name
, player.id player_id
@ -622,9 +795,9 @@ function verbana.data.get_ip_connection_log(ipint, limit)
limit = 20
end
local t = get_full_ntable(code, 'ip connection log', ipint, limit)
return verbana.util.table_reversed(t)
return util.table_reversed(t)
end
function verbana.data.get_asn_connection_log(asn, limit)
function data.get_asn_connection_log(asn, limit)
local code = [[
SELECT player.name player_name
, player.id player_id
@ -646,10 +819,10 @@ function verbana.data.get_asn_connection_log(asn, limit)
limit = 20
end
local t = get_full_ntable(code, 'asn connection log', asn, limit)
return verbana.util.table_reversed(t)
return util.table_reversed(t)
end
function verbana.data.get_network_connection_log(asn, limit)
function data.get_network_connection_log(asn, limit)
local code = [[
SELECT player.name player_name
, player.id player_id
@ -671,11 +844,10 @@ function verbana.data.get_network_connection_log(asn, limit)
limit = 20
end
local t = get_full_ntable(code, 'asn connection log', asn, limit)
return verbana.util.table_reversed(t)
return util.table_reversed(t)
end
function verbana.data.get_player_associations(player_id)
function data.get_player_associations(player_id)
local code = [[
SELECT assoc.ip ipint
, assoc.asn asn
@ -692,7 +864,7 @@ function verbana.data.get_player_associations(player_id)
]]
return get_full_ntable(code, 'player associations', player_id)
end
function verbana.data.get_ip_associations(ipint, from_time)
function data.get_ip_associations(ipint, from_time)
local code = [[
SELECT
DISTINCT player.name player_name
@ -700,47 +872,55 @@ function verbana.data.get_ip_associations(ipint, from_time)
FROM assoc
JOIN player ON player.id == assoc.player_id
LEFT JOIN player_status_log ON player_status_log.id == player.current_status_id
JOIN connection_log ON connection_log.timestamp >= ? AND connection_log.ip == assoc.ip AND connection_log.asn == assoc.asn
JOIN connection_log ON connection_log.ip == assoc.ip
AND connection_log.asn == assoc.asn
WHERE assoc.ip == ?
AND connection_log.timestamp >= ?
ORDER BY LOWER(player.name)
]]
return get_full_ntable(code, 'ip associations', from_time, ipint)
return get_full_ntable(code, 'ip associations', ipint, from_time)
end
function verbana.data.get_asn_associations(asn, from_time)
function data.get_asn_associations(asn, from_time)
local code = [[
SELECT
DISTINCT player.name player_name
, player_status_log.status_id player_status_id
, connection_log.ip ipint
, connection_log.asn asn
FROM assoc
JOIN player ON player.id == assoc.player_id
LEFT JOIN player_status_log ON player_status_log.id == player.current_status_id
JOIN connection_log ON connection_log.timestamp >= ?
AND connection_log.ip == assoc.ip
AND connection_log.asn == assoc.asn
JOIN connection_log USING (ip, asn)
JOIN player ON player.id == assoc.player_id
LEFT JOIN player_status_log ON player_status_log.id == player.current_status_id
LEFT JOIN connection_log ON connection_log.id == player.last_login_id
WHERE assoc.asn == ?
AND connection_log.timestamp >= ?
AND player.flagged == TRUE
ORDER BY LOWER(player.name)
]]
return get_full_ntable(code, 'asn associations', from_time, asn)
return get_full_ntable(code, 'asn associations', asn, from_time)
end
function verbana.data.get_player_cluster(player_id)
function data.get_player_cluster(player_id)
local code = [[
SELECT
DISTINCT other.name player_name
DISTINCT other.name player_name
, player_status_log.status_id player_status_id
, connection_log.ip ipint
, connection_log.asn asn
FROM player
JOIN assoc player_assoc ON player_assoc.player_id == player.id
JOIN assoc other_assoc ON other_assoc.ip == player_assoc.ip
AND player_assoc.player_id != other_assoc.player_id
JOIN player other ON other.id == other_assoc.player_id
LEFT JOIN player_status_log ON player_status_log.id == other.current_status_id
LEFT JOIN connection_log ON connection_log.id == player.last_login_id
WHERE player.id == ?
AND player.id != other_assoc.player_id
ORDER BY LOWER(other.name)
]]
return get_full_ntable(code, 'player cluster', player_id)
end
function verbana.data.get_all_banned_players()
function data.get_all_banned_players()
local code = [[
SELECT player.name player_name
, player_status_log.status_id player_status_id
@ -751,11 +931,11 @@ function verbana.data.get_all_banned_players()
WHERE player_status_log.status_id == ?
]]
return get_full_ntable(code, 'all banned',
verbana.data.player_status.banned
data.player_status.banned
)
end
function verbana.data.fumble_about_for_an_ip(name, player_id)
function data.fumble_about_for_an_ip(name, player_id)
-- for some reason, get_player_ip is unreliable during register_on_newplayer
local ipstr = minetest.get_player_ip(name)
if not ipstr then
@ -765,8 +945,8 @@ function verbana.data.fumble_about_for_an_ip(name, player_id)
end
end
if not ipstr then
if not player_id then player_id = verbana.data.get_player_id(name) end
local connection_log = verbana.data.get_player_connection_log(player_id, 1)
if not player_id then player_id = data.get_player_id(name) end
local connection_log = data.get_player_connection_log(player_id, 1)
if not connection_log or #connection_log ~= 1 then
log('warning', 'player %s exists but has no connection log?', player_id)
else
@ -777,7 +957,7 @@ function verbana.data.fumble_about_for_an_ip(name, player_id)
return ipstr
end
function verbana.data.get_ban_log(limit)
function data.get_ban_log(limit)
local code = [[
SELECT player.name player_name
, executor.name executor_name
@ -795,12 +975,12 @@ function verbana.data.get_ban_log(limit)
]]
if not limit or type(limit) ~= 'number' or limit < 0 then limit = 20 end
return get_full_ntable(code, 'ban log',
verbana.data.verbana_player_id,
data.verbana_player_id,
limit
)
end
function verbana.data.add_report(reporter_id, report)
function data.add_report(reporter_id, report)
local code = [[
INSERT INTO report
(reporter_id, report, timestamp)
@ -809,7 +989,7 @@ function verbana.data.add_report(reporter_id, report)
return execute_bind_one(code, 'add report', reporter_id, report, os.time())
end
function verbana.data.get_reports(from_time)
function data.get_reports(from_time)
local code = [[
SELECT player.name reporter
, report.report report
@ -821,7 +1001,7 @@ function verbana.data.get_reports(from_time)
return get_full_ntable(code, 'get reports', from_time)
end
function verbana.data.get_asn_stats(asn)
function data.get_asn_stats(asn)
local code = [[
SELECT COALESCE(player_status_log.status_id, ?) player_status_id
, COUNT(COALESCE(player_status_log.status_id, ?)) count
@ -832,15 +1012,15 @@ function verbana.data.get_asn_stats(asn)
ORDER BY COALESCE(player_status_log.status_id, ?)
]]
return get_full_ntable(code, 'asn stats',
verbana.data.player_status.default.id,
verbana.data.player_status.default.id,
data.player_status.default.id,
data.player_status.default.id,
asn,
verbana.data.player_status.default.id,
verbana.data.player_status.default.id
data.player_status.default.id,
data.player_status.default.id
)
end
function verbana.data.get_master(player_id)
function data.get_master(player_id)
local code = [[
SELECT master.id id
, master.name name
@ -854,7 +1034,7 @@ function verbana.data.get_master(player_id)
end
end
function verbana.data.set_master(player_id, master_id)
function data.set_master(player_id, master_id)
--[[
case 1: master has no master
just set player's master
@ -867,9 +1047,9 @@ function verbana.data.set_master(player_id, master_id)
loops can not be created this way, because we've ensured that a "true" master can't have
a master of its own.
]]
local master_master_id = verbana.data.get_master(master_id)
local master_master_id = data.get_master(master_id)
if master_master_id == player_id then
return verbana.data.swap_master(player_id, master_id)
return data.swap_master(player_id, master_id)
elseif master_master_id then
master_id = master_master_id
end
@ -892,8 +1072,8 @@ function verbana.data.set_master(player_id, master_id)
return true
end
function verbana.data.swap_master(player_id, master_id)
if verbana.data.get_master(player_id) ~= master_id then
function data.swap_master(player_id, master_id)
if data.get_master(player_id) ~= master_id then
return false, 'not player\'s master'
end
local code = [[
@ -918,7 +1098,7 @@ function verbana.data.swap_master(player_id, master_id)
return true
end
function verbana.data.unset_master(player_id)
function data.unset_master(player_id)
local code = [[
UPDATE player
SET master_id = NULL
@ -927,7 +1107,7 @@ function verbana.data.unset_master(player_id)
return execute_bind_one(code, 'unset master', player_id)
end
function verbana.data.get_alts(player_id)
function data.get_alts(player_id)
local code = [[
SELECT master.name name
FROM player master
@ -938,7 +1118,7 @@ function verbana.data.get_alts(player_id)
JOIN player alt ON alt.master_id == master.id
WHERE master.id == ?
]]
local master_id = verbana.data.get_master() or player_id
local master_id = data.get_master() or player_id
local rows = get_full_ntable(code, 'get alts', master_id)
if rows then
local alts = {}
@ -949,7 +1129,7 @@ function verbana.data.get_alts(player_id)
end
end
function verbana.data.grep_player(pattern, limit)
function data.grep_player(pattern, limit)
local code = [[
SELECT player.name name
, COALESCE(player_status_log.status_id, ?) player_status_id
@ -959,5 +1139,5 @@ function verbana.data.grep_player(pattern, limit)
ORDER BY LOWER(player.name)
LIMIT ?
]]
return get_full_ntable(code, 'grep player', verbana.data.player_status.default.id, pattern, limit)
return get_full_ntable(code, 'grep player', data.player_status.default.id, pattern, limit)
end

View File

@ -18,6 +18,10 @@ end
dofile(verbana.modpath .. '/settings.lua')
dofile(verbana.modpath .. '/privs.lua')
if verbana.settings.debug_mode then
verbana.log('warning', 'Verbana is running in debug mode.')
end
-- libraries
dofile(verbana.modpath .. '/util.lua')
dofile(verbana.modpath .. '/lib_ip.lua')
@ -25,6 +29,9 @@ dofile(verbana.modpath .. '/lib_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? ....
if not sql then
error('Verbana will not work unless lsqlite3 is installed. See README.txt')
end
verbana.sql = sql
local db_location = verbana.settings.db_path
local db, _, errmsg = sql.open(db_location)

View File

@ -1,8 +1,10 @@
TODO: turn this into proper documentation
check and reset tempban or tempblock, then...
new player logs in from a DEFAULT ip/network
status = default
new player logs in from a DANGEROUS ip/network
new player logs in from a SUSPICIOUS ip/network
if player is whitelisted: status = default
else: status = unverified; alert mods
new player logs in from a BLOCKED ip/network
@ -10,18 +12,16 @@ new player logs in from a BLOCKED ip/network
else: status is not changed (we should refuse to allow them to register the account)
old player logs in from a DEFAULT ip/network
if status is banned, boot them
if status is temp banned, check expiry and conditionally boot them
if status is locked, boot them BUT ALERT MODS
if status is banned boot them
if status is suspicious, let them in BUT ALERT MODS
else allow them in
old player logs in from a DANGEROUS ip/network
old player logs in from a SUSPICIOUS ip/network
if player has never used that ip/network
if the player is whitelisted let them in
else refuse entry
else
if the player is whitelisted, let them in
if the player is banned/tempbanned/locked, as for default ip/net status
if the player is banned, as for default ip/net status
else let them in, but alert mods
old player logs in from a BLOCKED ip/network
if the player is whitelisted, let them in
@ -29,30 +29,21 @@ old player logs in from a BLOCKED ip/network
when status == default:
account can be banned
account can be temp banned
account can be locked
account can be whitelisted by admins
account can be be marked suspicious
account can be unverified (sent back to verification area)
when status == unverified
account can be verified (status -> default)
account can be banned, tempbanned, locked
account can be banned
account can be whitelisted by admins (also lets them out of verification area)
account can be marked suspicious (also lets them out of verification area)
when status == banned
account can be unbanned (status -> default | suspicious depending on network)
account can be tempbanned (override previous behavior)
account can be locked (override previous behavior)
when status == tempbanned
account can be unbanned (status -> default | suspicious depending on network)
account can be banned (override previous behavior)
account can be locked (override previous behavior)
when status == locked
account can be unlocked (status -> default | suspicious depending on network)
account can be banned (override previous behavior)
account can be tempbanned (override previous behavior)
when status == whitelisted
account can be locked by mods
account can be unwhitelisted by admins (status -> default)
account can be banned by admins
account can be tempbanned by admins

1
migrations/placeholder Normal file
View File

@ -0,0 +1 @@
this folder is for possible future DB migration scripts

View File

@ -1,24 +1,31 @@
PRAGMA foreign_keys = OFF;
BEGIN EXCLUSIVE TRANSACTION;
CREATE TABLE version (version INTEGER);
INSERT INTO version (version) VALUES (1);
-- PLAYER
CREATE TABLE IF NOT EXISTS player_status (
CREATE TABLE player_status (
id INTEGER PRIMARY KEY
, name TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS player_status_name ON player_status(name);
CREATE INDEX player_status_name ON player_status(name);
CREATE TABLE IF NOT EXISTS player (
CREATE TABLE player (
id INTEGER PRIMARY KEY AUTOINCREMENT
, name TEXT NOT NULL
, master_id INTEGER
, current_status_id INTEGER
, last_login_id INTEGER
, flagged BOOLEAN NOT NULL DEFAULT FALSE
, FOREIGN KEY (master_id) REFERENCES player(id)
, FOREIGN KEY (current_status_id) REFERENCES player_status_log(id)
, FOREIGN KEY (last_login_id) REFERENCES connection_log(id)
);
CREATE UNIQUE INDEX IF NOT EXISTS player_name ON player(LOWER(name));
CREATE INDEX IF NOT EXISTS player_master_id ON player(master_id);
CREATE INDEX IF NOT EXISTS player_current_status_id ON player(current_status_id);
CREATE UNIQUE INDEX player_name ON player(LOWER(name));
CREATE INDEX player_master_id ON player(master_id);
CREATE INDEX player_current_status_id ON player(current_status_id);
CREATE INDEX player_last_login_id ON player(last_login_id);
CREATE TABLE IF NOT EXISTS player_status_log (
CREATE TABLE player_status_log (
id INTEGER PRIMARY KEY AUTOINCREMENT
, executor_id INTEGER NOT NULL
, player_id INTEGER NOT NULL
@ -31,25 +38,25 @@ CREATE TABLE IF NOT EXISTS player_status_log (
, FOREIGN KEY (status_id) REFERENCES player_status(id)
, UNIQUE (player_id, status_id, timestamp)
);
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);
CREATE INDEX player_status_log_player_id ON player_status_log(player_id);
CREATE INDEX player_status_log_timestamp ON player_status_log(timestamp);
CREATE INDEX player_status_log_reason ON player_status_log(reason);
-- END PLAYER
-- IP
CREATE TABLE IF NOT EXISTS ip_status (
CREATE TABLE ip_status (
id INTEGER PRIMARY KEY
, name TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS ip_status_name ON ip_status(name);
CREATE INDEX ip_status_name ON ip_status(name);
CREATE TABLE IF NOT EXISTS ip (
CREATE TABLE ip (
ip INTEGER PRIMARY KEY
, current_status_id INTEGER
, FOREIGN KEY (current_status_id) REFERENCES ip_status_log(id)
);
CREATE INDEX IF NOT EXISTS ip_current_status_id ON ip(current_status_id);
CREATE INDEX ip_current_status_id ON ip(current_status_id);
CREATE TABLE IF NOT EXISTS ip_status_log (
CREATE TABLE ip_status_log (
id INTEGER PRIMARY KEY AUTOINCREMENT
, executor_id INTEGER NOT NULL
, ip INTEGER NOT NULL
@ -61,25 +68,25 @@ CREATE TABLE IF NOT EXISTS ip_status_log (
, FOREIGN KEY (ip) REFERENCES ip(ip)
, FOREIGN KEY (status_id) REFERENCES ip_status(id)
);
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);
CREATE INDEX ip_status_log_ip ON ip_status_log(ip);
CREATE INDEX ip_status_log_timestamp ON ip_status_log(timestamp);
CREATE INDEX ip_status_log_reason ON ip_status_log(reason);
-- END IP
-- ASN
CREATE TABLE IF NOT EXISTS asn_status (
CREATE TABLE asn_status (
id INTEGER PRIMARY KEY
, name TEXT NOT NULL
);
CREATE INDEX IF NOT EXISTS asn_status_name ON asn_status(name);
CREATE INDEX asn_status_name ON asn_status(name);
CREATE TABLE IF NOT EXISTS asn (
CREATE TABLE asn (
asn INTEGER PRIMARY KEY
, current_status_id INTEGER
, FOREIGN KEY (current_status_id) REFERENCES asn_status_log(id)
);
CREATE INDEX IF NOT EXISTS asn_current_status_id ON asn(current_status_id);
CREATE INDEX asn_current_status_id ON asn(current_status_id);
CREATE TABLE IF NOT EXISTS asn_status_log (
CREATE TABLE asn_status_log (
id INTEGER PRIMARY KEY AUTOINCREMENT
, executor_id INTEGER NOT NULL
, asn INTEGER NOT NULL
@ -91,13 +98,14 @@ CREATE TABLE IF NOT EXISTS asn_status_log (
, FOREIGN KEY (asn) REFERENCES asn(asn)
, FOREIGN KEY (status_id) REFERENCES asn_status(id)
);
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);
CREATE INDEX asn_status_log_asn ON asn_status_log(asn);
CREATE INDEX asn_status_log_timestamp ON asn_status_log(timestamp);
CREATE INDEX asn_status_log_reason ON asn_status_log(reason);
-- END ASN
-- LOGS AND ASSOCIATIONS
CREATE TABLE IF NOT EXISTS connection_log (
player_id INTEGER NOT NULL
CREATE TABLE connection_log (
id INTEGER PRIMARY KEY AUTOINCREMENT
, player_id INTEGER NOT NULL
, ip INTEGER NOT NULL
, asn INTEGER NOT NULL
, success INTEGER NOT NULL
@ -107,12 +115,12 @@ CREATE TABLE IF NOT EXISTS connection_log (
, FOREIGN KEY (asn) REFERENCES asn(asn)
, UNIQUE (player_id, ip, success, timestamp)
);
CREATE INDEX IF NOT EXISTS log_player ON connection_log(player_id);
CREATE INDEX IF NOT EXISTS log_ip ON connection_log(ip);
CREATE INDEX IF NOT EXISTS log_asn ON connection_log(asn);
CREATE INDEX IF NOT EXISTS log_timestamp ON connection_log(timestamp);
CREATE INDEX log_player ON connection_log(player_id);
CREATE INDEX log_ip ON connection_log(ip);
CREATE INDEX log_asn ON connection_log(asn);
CREATE INDEX log_timestamp ON connection_log(timestamp);
CREATE TABLE IF NOT EXISTS assoc (
CREATE TABLE assoc (
player_id INTEGER NOT NULL
, ip INTEGER NOT NULL
, asn INTEGER NOT NULL
@ -123,18 +131,19 @@ CREATE TABLE IF NOT EXISTS assoc (
, FOREIGN KEY (ip) REFERENCES ip(ip)
, FOREIGN KEY (asn) REFERENCES asn(asn)
);
CREATE INDEX IF NOT EXISTS assoc_player ON assoc(player_id);
CREATE INDEX IF NOT EXISTS assoc_ip ON assoc(ip);
CREATE INDEX IF NOT EXISTS assoc_asn ON assoc(asn);
CREATE INDEX assoc_player ON assoc(player_id);
CREATE INDEX assoc_ip ON assoc(ip);
CREATE INDEX assoc_asn ON assoc(asn);
-- END LOGS AND ASSOCIATIONS
-- REPORTS
CREATE TABLE IF NOT EXISTS report (
CREATE TABLE report (
id INTEGER PRIMARY KEY AUTOINCREMENT
, reporter_id INTEGER NOT NULL
, report TEXT NOT NULL
, timestamp INTEGER NOT NULL
, FOREIGN KEY (reporter_id) REFERENCES player(id)
);
CREATE INDEX IF NOT EXISTS report_reporter ON report(reporter_id);
CREATE INDEX report_reporter ON report(reporter_id);
CREATE INDEX report_timestamp ON report(timestamp);
-- END REPORTS
PRAGMA foreign_keys = ON;
COMMIT TRANSACTION;

View File

@ -63,6 +63,16 @@ verbana.settings.universal_verification = settings:get_bool('verbana.universal_v
verbana.settings.jail_bounds = get_jail_bounds()
verbana.settings.jail_check_period = get_setting('verbana.jail_check_period')
-- TODO: remove the default 'true' setting when we are ready
verbana.settings.debug_mode = get_setting('verbana.debug_mode', 'true') == 'true'
local debug_is_default -- we revert to debug mode if verification or sban is enabled
if ((
minetest.get_modpath('sban') and
minetest.registered_chatcommands['bang'] -- sban doesn't publish an API, so use this as a proxy
) or (
minetest.global_exists('verification')
)) then
debug_is_default = 'true'
else
debug_is_default = 'false'
end
verbana.settings.debug_mode = get_setting('verbana.debug_mode', debug_is_default) == 'true'

View File

@ -19,6 +19,16 @@ function verbana.util.parse_time(text)
return n * time_units[unit]
end
function verbana.util.file_exists(filename)
local handle = io.open(filename,"r")
if handle then
io.close(handle)
return true
else
return false
end
end
function verbana.util.load_file(filename)
local file = io.open(filename, 'r')
if not file then