mirror of
https://github.com/Aurorastation/Aurora.3.git
synced 2026-08-27 06:54:18 +01:00
Implements IPIntel, Panic Bunker, and custom access control (#2436)
What it says on the tin. IPIntel and BYOND account age relating panic bunker settings from TG. Also implements methods for potentially tagging VM users. Ontop of all of this, creates a spiffy UI for admins with R_SERVER to adjust the settings of the entire suite at runtime.
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
--
|
||||
-- Adds the IP intel database table.
|
||||
--
|
||||
|
||||
CREATE TABLE `ss13_ipintel` (
|
||||
`ip` varbinary(16) NOT NULL,
|
||||
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`intel` double NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`ip`),
|
||||
KEY `idx_ipintel` (`ip`,`intel`,`date`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
|
||||
|
||||
ALTER TABLE `ss13_player`
|
||||
ADD `account_join_date` DATE NULL DEFAULT NULL AFTER `whitelist_status`;
|
||||
@@ -154,6 +154,7 @@
|
||||
#include "code\controllers\subsystems\garbage.dm"
|
||||
#include "code\controllers\subsystems\icon_smooth.dm"
|
||||
#include "code\controllers\subsystems\icon_updates.dm"
|
||||
#include "code\controllers\subsystems\ipintel.dm"
|
||||
#include "code\controllers\subsystems\job.dm"
|
||||
#include "code\controllers\subsystems\law.dm"
|
||||
#include "code\controllers\subsystems\lighting.dm"
|
||||
@@ -951,6 +952,7 @@
|
||||
#include "code\modules\admin\create_object.dm"
|
||||
#include "code\modules\admin\create_turf.dm"
|
||||
#include "code\modules\admin\holder2.dm"
|
||||
#include "code\modules\admin\ipintel.dm"
|
||||
#include "code\modules\admin\IsBanned.dm"
|
||||
#include "code\modules\admin\map_capture.dm"
|
||||
#include "code\modules\admin\NewBan.dm"
|
||||
@@ -1000,6 +1002,7 @@
|
||||
#include "code\modules\admin\secrets\random_events\gravity.dm"
|
||||
#include "code\modules\admin\secrets\random_events\trigger_cordical_borer_infestation.dm"
|
||||
#include "code\modules\admin\secrets\random_events\trigger_xenomorph_infestation.dm"
|
||||
#include "code\modules\admin\verbs\access_control.dm"
|
||||
#include "code\modules\admin\verbs\adminhelp.dm"
|
||||
#include "code\modules\admin\verbs\adminjump.dm"
|
||||
#include "code\modules\admin\verbs\adminpm.dm"
|
||||
|
||||
+11
-5
@@ -6,7 +6,7 @@
|
||||
|
||||
var/roundstart_hour = 0
|
||||
//Returns the world time in english
|
||||
proc/worldtime2text(time = world.time, timeshift = 1)
|
||||
/proc/worldtime2text(time = world.time, timeshift = 1)
|
||||
if(!roundstart_hour) roundstart_hour = rand(0, 23)
|
||||
return timeshift ? time2text(time+(36000*roundstart_hour), "hh:mm") : time2text(time, "hh:mm")
|
||||
|
||||
@@ -15,14 +15,14 @@ proc/worldtime2text(time = world.time, timeshift = 1)
|
||||
worldtime2text()
|
||||
. = text2num(time2text(world.time + (36000 * roundstart_hour), "hh"))
|
||||
|
||||
proc/worlddate2text()
|
||||
/proc/worlddate2text()
|
||||
return num2text(game_year) + "-" + time2text(world.timeofday, "MM-DD")
|
||||
|
||||
proc/time_stamp()
|
||||
/proc/time_stamp()
|
||||
return time2text(world.timeofday, "hh:mm:ss")
|
||||
|
||||
/* Returns 1 if it is the selected month and day */
|
||||
proc/isDay(var/month, var/day)
|
||||
/proc/isDay(var/month, var/day)
|
||||
if(isnum(month) && isnum(day))
|
||||
var/MM = text2num(time2text(world.timeofday, "MM")) // get the current month
|
||||
var/DD = text2num(time2text(world.timeofday, "DD")) // get the current day
|
||||
@@ -35,7 +35,7 @@ proc/isDay(var/month, var/day)
|
||||
|
||||
var/next_duration_update = 0
|
||||
var/last_round_duration = 0
|
||||
proc/round_duration()
|
||||
/proc/round_duration()
|
||||
if(last_round_duration && world.time < next_duration_update)
|
||||
return last_round_duration
|
||||
|
||||
@@ -57,3 +57,9 @@ proc/round_duration()
|
||||
if (world.timeofday < rollovercheck_last_timeofday) //TIME IS GOING BACKWARDS!
|
||||
return midnight_rollovers++
|
||||
return midnight_rollovers
|
||||
|
||||
//returns timestamp in a sql and ISO 8601 friendly format
|
||||
/proc/SQLtime(timevar)
|
||||
if(!timevar)
|
||||
timevar = world.realtime
|
||||
return time2text(timevar, "YYYY-MM-DD hh:mm:ss")
|
||||
|
||||
@@ -265,6 +265,20 @@ var/list/gamemode_cache = list()
|
||||
var/log_gelf_ip = ""
|
||||
var/log_gelf_port = ""
|
||||
|
||||
//IP Intel vars
|
||||
var/ipintel_email
|
||||
var/ipintel_rating_bad = 1
|
||||
var/ipintel_rating_kick = 0
|
||||
var/ipintel_save_good = 12
|
||||
var/ipintel_save_bad = 1
|
||||
var/ipintel_domain = "check.getipintel.net"
|
||||
|
||||
// Access control/Panic bunker settings.
|
||||
var/access_deny_new_players = 0
|
||||
var/access_deny_new_accounts = -1
|
||||
var/access_deny_vms = 0
|
||||
var/access_warn_vms = 0
|
||||
|
||||
/datum/configuration/New()
|
||||
var/list/L = typesof(/datum/game_mode) - /datum/game_mode
|
||||
for (var/T in L)
|
||||
@@ -819,6 +833,27 @@ var/list/gamemode_cache = list()
|
||||
if("log_gelf_port")
|
||||
config.log_gelf_port = value
|
||||
|
||||
if("ipintel_email")
|
||||
if (value != "ch@nge.me")
|
||||
ipintel_email = value
|
||||
if("ipintel_rating_bad")
|
||||
ipintel_rating_bad = text2num(value)
|
||||
if("ipintel_rating_kick")
|
||||
ipintel_rating_kick = text2num(value)
|
||||
if("ipintel_domain")
|
||||
ipintel_domain = value
|
||||
if("ipintel_save_good")
|
||||
ipintel_save_good = text2num(value)
|
||||
if("ipintel_save_bad")
|
||||
ipintel_save_bad = text2num(value)
|
||||
|
||||
if("access_deny_new_accounts")
|
||||
access_deny_new_accounts = text2num(value) >= 0 ? text2num(value) : -1
|
||||
if("access_deny_vms")
|
||||
access_deny_vms = text2num(value)
|
||||
if("access_warn_vms")
|
||||
access_warn_vms = text2num(value)
|
||||
|
||||
else
|
||||
log_misc("Unknown setting in configuration: '[name]'")
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
var/datum/controller/subsystem/ipintel/SSipintel
|
||||
|
||||
/datum/controller/subsystem/ipintel
|
||||
name = "XKeyScore"
|
||||
init_order = SS_INIT_MISC_FIRST
|
||||
flags = SS_NO_FIRE
|
||||
var/enabled = 0 //disable at round start to avoid checking reconnects
|
||||
var/throttle = 0
|
||||
var/errors = 0
|
||||
|
||||
var/list/cache = list()
|
||||
|
||||
/datum/controller/subsystem/ipintel/New()
|
||||
NEW_SS_GLOBAL(SSipintel)
|
||||
|
||||
/datum/controller/subsystem/ipintel/Initialize()
|
||||
enabled = 1
|
||||
. = ..()
|
||||
@@ -135,7 +135,33 @@
|
||||
update_connection_data(C)
|
||||
return
|
||||
|
||||
var/list/conn_info = json_decode(data)
|
||||
var/list/data_object = json_decode(data)
|
||||
|
||||
if (!data_object || !data_object.len)
|
||||
log_debug("CONN DATA: [C.ckey] has no connection data to showcase.")
|
||||
return
|
||||
|
||||
if (data_object["vms"])
|
||||
var/A = data_object["vms"]
|
||||
var/count = 0
|
||||
if (A & 1)
|
||||
count++
|
||||
if (A & 2)
|
||||
count++
|
||||
|
||||
if (config.access_deny_vms && count >= config.access_deny_vms)
|
||||
log_access("Failed Login: [C.ckey] [C.address] [C.computer_id] - Matching [count]/[config.access_deny_vms] VM identifiers. IDs: [A].", ckey = C.ckey)
|
||||
message_admins("Failed Login: [C.ckey] [C.address] [C.computer_id] - Matching [count]/[config.access_deny_vms] VM identifiers. IDs: [A].")
|
||||
spawn(20)
|
||||
if (C)
|
||||
del(C)
|
||||
return
|
||||
|
||||
if (config.access_warn_vms && count >= config.access_warn_vms)
|
||||
log_access("Notice: [key_name(C)] [C.address] [C.computer_id] - Matching [count] VM identifiers. IDs: [A].", ckey = C.ckey)
|
||||
message_admins("Notice: [key_name(C)] [C.address] [C.computer_id] - Matching [count] VM identifiers. IDs: [A].")
|
||||
|
||||
var/list/conn_info = data_object["conn"]
|
||||
|
||||
if (!conn_info || !conn_info.len)
|
||||
return
|
||||
|
||||
@@ -169,7 +169,8 @@ var/list/admin_verbs_server = list(
|
||||
/client/proc/admin_edit_motd,
|
||||
/client/proc/toggle_recursive_explosions,
|
||||
/client/proc/restart_controller,
|
||||
/client/proc/cmd_ss_panic
|
||||
/client/proc/cmd_ss_panic,
|
||||
/client/proc/configure_access_control
|
||||
)
|
||||
var/list/admin_verbs_debug = list(
|
||||
/client/proc/getruntimelog, // allows us to access runtime logs to somebody,
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/datum/ipintel
|
||||
var/ip
|
||||
var/intel = 0
|
||||
var/cache = FALSE
|
||||
var/cacheminutesago = 0
|
||||
var/cachedate = ""
|
||||
var/cacherealtime = 0
|
||||
|
||||
/datum/ipintel/New()
|
||||
cachedate = SQLtime()
|
||||
cacherealtime = world.realtime
|
||||
|
||||
/datum/ipintel/proc/is_valid()
|
||||
. = FALSE
|
||||
if (intel < 0)
|
||||
return
|
||||
if (intel <= config.ipintel_rating_bad)
|
||||
if (world.realtime < cacherealtime+(config.ipintel_save_good*60*60*10))
|
||||
return TRUE
|
||||
else
|
||||
if (world.realtime < cacherealtime+(config.ipintel_save_bad*60*60*10))
|
||||
return TRUE
|
||||
|
||||
/proc/get_ip_intel(ip, bypasscache = FALSE, updatecache = TRUE)
|
||||
var/datum/ipintel/res = new()
|
||||
res.ip = ip
|
||||
. = res
|
||||
if (!ip || !config.ipintel_email || !SSipintel.enabled)
|
||||
return
|
||||
if (!bypasscache)
|
||||
var/datum/ipintel/cachedintel = SSipintel.cache[ip]
|
||||
if (cachedintel && cachedintel.is_valid())
|
||||
cachedintel.cache = TRUE
|
||||
return cachedintel
|
||||
|
||||
if(establish_db_connection(dbcon))
|
||||
var/DBQuery/query_get_ip_intel = dbcon.NewQuery({"
|
||||
SELECT date, intel, TIMESTAMPDIFF(MINUTE,date,NOW())
|
||||
FROM ss13_ipintel
|
||||
WHERE
|
||||
ip = INET6_ATON('[ip]')
|
||||
AND ((
|
||||
intel < [config.ipintel_rating_bad]
|
||||
AND
|
||||
date + INTERVAL [config.ipintel_save_good] HOUR > NOW()
|
||||
) OR (
|
||||
intel >= [config.ipintel_rating_bad]
|
||||
AND
|
||||
date + INTERVAL [config.ipintel_save_bad] HOUR > NOW()
|
||||
))
|
||||
"})
|
||||
if(!query_get_ip_intel.Execute())
|
||||
return
|
||||
if (query_get_ip_intel.NextRow())
|
||||
res.cache = TRUE
|
||||
res.cachedate = query_get_ip_intel.item[1]
|
||||
res.intel = text2num(query_get_ip_intel.item[2])
|
||||
res.cacheminutesago = text2num(query_get_ip_intel.item[3])
|
||||
res.cacherealtime = world.realtime - (text2num(query_get_ip_intel.item[3])*10*60)
|
||||
SSipintel.cache[ip] = res
|
||||
return
|
||||
res.intel = ip_intel_query(ip)
|
||||
if (updatecache && res.intel >= 0)
|
||||
SSipintel.cache[ip] = res
|
||||
if(establish_db_connection(dbcon))
|
||||
var/DBQuery/query_add_ip_intel = dbcon.NewQuery("INSERT INTO ss13_ipintel (ip, intel) VALUES (INET6_ATON('[ip]'), [res.intel]) ON DUPLICATE KEY UPDATE intel = VALUES(intel), date = NOW()")
|
||||
query_add_ip_intel.Execute()
|
||||
|
||||
|
||||
|
||||
/proc/ip_intel_query(ip, var/retryed=0)
|
||||
. = -1 //default
|
||||
if (!ip)
|
||||
return
|
||||
if (SSipintel.throttle > world.timeofday)
|
||||
return
|
||||
if (!SSipintel.enabled)
|
||||
return
|
||||
|
||||
var/list/http[] = world.Export("http://[config.ipintel_domain]/check.php?ip=[ip]&contact=[config.ipintel_email]&format=json&flags=f")
|
||||
|
||||
if (http)
|
||||
var/status = text2num(http["STATUS"])
|
||||
|
||||
if (status == 200)
|
||||
var/response = json_decode(file2text(http["CONTENT"]))
|
||||
if (response)
|
||||
if (response["status"] == "success")
|
||||
var/intelnum = text2num(response["result"])
|
||||
if (isnum(intelnum))
|
||||
return text2num(response["result"])
|
||||
else
|
||||
ipintel_handle_error("Bad intel from server: [response["result"]].", ip, retryed)
|
||||
if (!retryed)
|
||||
sleep(25)
|
||||
return .(ip, 1)
|
||||
else
|
||||
ipintel_handle_error("Bad response from server: [response["status"]].", ip, retryed)
|
||||
if (!retryed)
|
||||
sleep(25)
|
||||
return .(ip, 1)
|
||||
|
||||
else if (status == 429)
|
||||
ipintel_handle_error("Error #429: We have exceeded the rate limit.", ip, 1)
|
||||
return
|
||||
else
|
||||
ipintel_handle_error("Unknown status code: [status].", ip, retryed)
|
||||
if (!retryed)
|
||||
sleep(25)
|
||||
return .(ip, 1)
|
||||
else
|
||||
ipintel_handle_error("Unable to connect to API.", ip, retryed)
|
||||
if (!retryed)
|
||||
sleep(25)
|
||||
return .(ip, 1)
|
||||
|
||||
|
||||
/proc/ipintel_handle_error(error, ip, retryed)
|
||||
if (retryed)
|
||||
SSipintel.errors++
|
||||
error += " Could not check [ip]. Disabling IPINTEL for [SSipintel.errors] minute[( SSipintel.errors == 1 ? "" : "s" )]"
|
||||
SSipintel.throttle = world.timeofday + (10 * 120 * SSipintel.errors)
|
||||
else
|
||||
error += " Attempting retry on [ip]."
|
||||
log_ipintel(error)
|
||||
|
||||
/proc/log_ipintel(text)
|
||||
log_game("IPINTEL: [text]")
|
||||
log_debug("IPINTEL: [text]")
|
||||
@@ -1606,6 +1606,10 @@
|
||||
|
||||
return
|
||||
|
||||
else if(href_list["access_control"])
|
||||
access_control_topic(href_list["access_control"])
|
||||
return
|
||||
|
||||
mob/living/proc/can_centcom_reply()
|
||||
return 0
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
/client/proc/configure_access_control()
|
||||
set category = "Server"
|
||||
set name = "Configure Access Control"
|
||||
|
||||
if (!check_rights(R_SERVER))
|
||||
return
|
||||
|
||||
var/datum/browser/config_window = new(usr, "access_control", "Access Control")
|
||||
config_window.add_head_content("<title>Access Control</title>")
|
||||
|
||||
var/data = "These settings control who can access the server during this round.<br>"
|
||||
data += "They must be reset every single time the server restarts.<br>"
|
||||
data += "<b>If you do not know what these do, you shouldn't be touching them!</b><hr>"
|
||||
|
||||
data += "<h2>IP Intel Settings:</h2><br><ul>"
|
||||
data += "<li>Current warning level: [config.ipintel_rating_bad ? "[config.ipintel_rating_bad]" : "<font color='red'>DISABLED</font>"]. <a href='?_src_=holder;access_control=intel_bad'>Edit</a></li>"
|
||||
data += "<li>Current kick level: [config.ipintel_rating_kick ? "[config.ipintel_rating_kick]" : "<font color='red'>DISABLED</font>"]. <a href='?_src_=holder;access_control=intel_kick'>Edit</a></li>"
|
||||
data += "</ul><hr>"
|
||||
|
||||
data += "<h2>Player Age Settings:</h2><br><ul>"
|
||||
data += "<li>New players: [config.access_deny_new_players ? "<font color='green'>ALLOWED</font>" : "<font color='red'>DENIED</font>"]. <a href='?_src_=holder;access_control=new_players;'>Toggle</a></li>"
|
||||
data += "<li>Account age restriction: [config.access_deny_new_accounts == -1 ? "<font color='red'>DISABLED</font>" : "[config.access_deny_new_accounts] DAYS"]. <a href='?_src_=holder;access_control=new_accounts;'>Edit</a></li>"
|
||||
data += "</ul><hr>"
|
||||
|
||||
data += "<h2>VM Detection Settings:</h2><br><ul>"
|
||||
data += "<li>VM identifier count to warn on: [config.access_warn_vms ? "[config.access_warn_vms]" : "<font color='red'>DISABLED</font>"]. <a href='?_src_=holder;access_control=vm_warn;'>Edit</a></li>"
|
||||
data += "<li>VM identifier count to kick on: [config.access_deny_vms ? "[config.access_deny_vms]" : "<font color='red'>DISABLED</font>"]. <a href='?_src_=holder;access_control=vm_kick;'>Edit</a></li>"
|
||||
data += "</ul>"
|
||||
|
||||
config_window.set_user(src.mob)
|
||||
config_window.set_content(data)
|
||||
config_window.open()
|
||||
|
||||
/datum/admins/proc/access_control_topic(control)
|
||||
if (!control)
|
||||
to_chat(usr, "<span class='warning'>No control option sent. Cancelling.</span>")
|
||||
return
|
||||
|
||||
if (!check_rights(R_SERVER))
|
||||
log_and_message_admins("has tried editing access control without the permissions to do so.")
|
||||
return
|
||||
|
||||
switch(control)
|
||||
if ("intel_bad")
|
||||
var/num = input("Please set the new threshold for warning based on IPintel (0 to disable).", "New Threshold", config.ipintel_rating_kick) as num
|
||||
if (num < 0 || num > 1)
|
||||
to_chat(usr, "<span class='warning'>Invalid number. Cancelling.</span>")
|
||||
return
|
||||
|
||||
config.ipintel_rating_bad = num
|
||||
if (num)
|
||||
log_and_message_admins("has set the IPIntel warn threshold to [num].")
|
||||
else
|
||||
log_and_message_admins("has disabled warn based on IPIntel.")
|
||||
if ("intel_kick")
|
||||
var/num = input("Please set the new threshold for kicking based on IPintel (0 to disable).", "New Threshold", config.ipintel_rating_kick) as num
|
||||
if (num < 0 || num > 1)
|
||||
to_chat(usr, "<span class='warning'>Invalid number. Cancelling.</span>")
|
||||
return
|
||||
|
||||
config.ipintel_rating_kick = num
|
||||
if (num)
|
||||
log_and_message_admins("has set the IPIntel kick threshold to [num].")
|
||||
else
|
||||
log_and_message_admins("has disabled kicking based on IPIntel.")
|
||||
if ("new_players")
|
||||
config.access_deny_new_players = !config.access_deny_new_players
|
||||
log_and_message_admins("has [config.access_deny_new_players ? "ENABLED" : "DISABLED"] the kicking of new players.")
|
||||
if ("new_accounts")
|
||||
var/num = input("Please set the new threshold for denying access based on BYOND account age. (-1 to disable.)", "New Threshold", config.access_deny_new_accounts) as num
|
||||
if (num < 0 && num != -1)
|
||||
to_chat(usr, "<span class='warning'>Invalid number. Cancelling.</span>")
|
||||
return
|
||||
|
||||
config.access_deny_new_accounts = num
|
||||
if (num != -1)
|
||||
log_and_message_admins("has set the access barrier for new BYOND accounts to [num] days.")
|
||||
else
|
||||
log_and_message_admins("has disabled kicking based on BYOND account age.")
|
||||
if ("vm_warn")
|
||||
var/num = input("Please set the new threshold for warning on login based on positive VM identifiers. (0 to disable.)", "New Threshold") in list(0, 1, 2)
|
||||
|
||||
config.access_warn_vms = num
|
||||
if (num)
|
||||
log_and_message_admins("has set players with [config.access_warn_vms] positive identifiers out of 2 for VM usage to be warned.")
|
||||
else
|
||||
log_and_message_admins("has disabled warnings based on potential VM usage.")
|
||||
if ("vm_kick")
|
||||
var/num = input("Please set the new threshold for warning on login based on positive VM identifiers. (0 to disable.)", "New Threshold") in list(0, 1, 2)
|
||||
|
||||
config.access_deny_vms = num
|
||||
if (num)
|
||||
log_and_message_admins("has set players with [config.access_deny_vms] positive identifiers out of 2 for VM usage to be warned.")
|
||||
else
|
||||
log_and_message_admins("has disabled warnings based on potential VM usage.")
|
||||
else
|
||||
to_chat(usr, "<span class='danger'>Unknown control message sent. Cancelling.</span>")
|
||||
|
||||
owner.configure_access_control()
|
||||
@@ -9,6 +9,19 @@ var/list/VVlocked = list("vars", "holder", "client", "virus", "viruses", "cuffed
|
||||
var/list/VVicon_edit_lock = list("icon", "icon_state", "overlays", "underlays")
|
||||
var/list/VVckey_edit = list("key", "ckey")
|
||||
|
||||
// The paranoia box. Specify a var name => bitflags required to edit it.
|
||||
// Allows the securing of specific variables for, for example, config. That alter
|
||||
// how players could join! Definitely not something you want folks to touch if they
|
||||
// don't have the perms.
|
||||
var/list/VVdynamic_lock = list(
|
||||
"access_deny_new_players" = R_SERVER,
|
||||
"access_deny_new_accounts" = R_SERVER,
|
||||
"access_deny_vms" = R_SERVER,
|
||||
"access_warn_vms" = R_SERVER,
|
||||
"ipintel_rating_bad" = R_SERVER,
|
||||
"ipintel_rating_kick" = R_SERVER
|
||||
)
|
||||
|
||||
/*
|
||||
/client/proc/cmd_modify_object_variables(obj/O as obj|mob|turf|area in world) // Acceptable 'in world', as VV would be incredibly hampered otherwise
|
||||
set category = "Debug"
|
||||
@@ -171,6 +184,8 @@ var/list/VVckey_edit = list("key", "ckey")
|
||||
if(!check_rights(R_SPAWN|R_DEBUG|R_DEV)) return
|
||||
if(variable in VVicon_edit_lock)
|
||||
if(!check_rights(R_FUN|R_DEBUG|R_DEV)) return
|
||||
if(VVdynamic_lock[variable])
|
||||
if(!check_rights(VVdynamic_lock[variable])) return
|
||||
|
||||
if(isnull(variable))
|
||||
usr << "Unable to determine variable type."
|
||||
@@ -364,6 +379,8 @@ var/list/VVckey_edit = list("key", "ckey")
|
||||
if(!check_rights(R_SPAWN|R_DEBUG|R_DEV)) return
|
||||
if(param_var_name in VVicon_edit_lock)
|
||||
if(!check_rights(R_FUN|R_DEBUG|R_DEV)) return
|
||||
if(VVdynamic_lock[variable])
|
||||
if(!check_rights(VVdynamic_lock[variable])) return
|
||||
|
||||
variable = param_var_name
|
||||
|
||||
@@ -426,6 +443,8 @@ var/list/VVckey_edit = list("key", "ckey")
|
||||
if(!check_rights(R_SPAWN|R_DEBUG|R_DEV)) return
|
||||
if(variable in VVicon_edit_lock)
|
||||
if(!check_rights(R_FUN|R_DEBUG|R_DEV)) return
|
||||
if(VVdynamic_lock[variable])
|
||||
if(!check_rights(VVdynamic_lock[variable])) return
|
||||
|
||||
if(!autodetect_class)
|
||||
|
||||
|
||||
@@ -197,6 +197,7 @@ proc/cmd_admin_mute(mob/M as mob, mute_type, automute = 0)
|
||||
if(M.client.prefs.muted & mute_type)
|
||||
muteunmute = "unmuted"
|
||||
M.client.prefs.muted &= ~mute_type
|
||||
M.client.spam_alert = 0
|
||||
else
|
||||
muteunmute = "muted"
|
||||
M.client.prefs.muted |= mute_type
|
||||
@@ -623,7 +624,7 @@ Traitors and the like can also be revived with the previous role mostly intact.
|
||||
if (!holder)
|
||||
src << "Only administrators may use this command."
|
||||
return
|
||||
|
||||
|
||||
for(var/datum/job/job in SSjobs.occupations)
|
||||
src << "[job.title]: [job.total_positions]"
|
||||
feedback_add_details("admin_verb","LFS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
|
||||
|
||||
@@ -178,7 +178,8 @@ var/list/asset_datums = list()
|
||||
"bootstrap.min.css" = 'html/bootstrap/css/bootstrap.min.css',
|
||||
"bootstrap.min.js" = 'html/bootstrap/js/bootstrap.min.js',
|
||||
"jquery-2.0.0.min.js" = 'html/jquery/jquery-2.0.0.min.js',
|
||||
"ie-truth.min.js" = 'html/iestats/ie-truth.min.js'
|
||||
"ie-truth.min.js" = 'html/iestats/ie-truth.min.js',
|
||||
"conninfo.min.js" = 'html/iestats/conninfo.min.js'
|
||||
)
|
||||
|
||||
/datum/asset/simple/paper
|
||||
@@ -221,7 +222,7 @@ var/list/asset_datums = list()
|
||||
var/name = "pill[i].png"
|
||||
register_asset(name, icon('icons/obj/chemical.dmi', "pill[i]"))
|
||||
assets += name
|
||||
|
||||
|
||||
for (var/sprite in bottle_sprites)
|
||||
var/name = "[sprite].png"
|
||||
register_asset(name, icon('icons/obj/chemical.dmi', sprite))
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/client
|
||||
parent_type = /datum
|
||||
|
||||
|
||||
////////////////
|
||||
//ADMIN THINGS//
|
||||
////////////////
|
||||
@@ -41,6 +41,7 @@
|
||||
var/info_sent = 0
|
||||
// comment out the line below when debugging locally to enable the options & messages menu
|
||||
//control_freak = 1
|
||||
var/ip_intel = "Disabled"
|
||||
|
||||
var/received_discord_pm = -99999
|
||||
var/discord_admin //IRC- no more IRC, K? Discord admin that spoke with them last.
|
||||
@@ -55,6 +56,8 @@
|
||||
var/related_accounts_cid = "Requires database" //So admins know why it isn't working - Used to determine what other accounts previously logged in from this computer id
|
||||
var/whitelist_status = 0 //Used to determine what whitelists the player has access to. Uses bitflag values!
|
||||
var/need_saves_migrated = "Requires database" //Used to determine whether or not the ckey needs their saves migrated over to the database. Default is 0 upon successful connection.
|
||||
var/account_age = -1 // Age on the BYOND account in days.
|
||||
var/account_join_date = null // Date of the BYOND account creation in ISO 8601 format.
|
||||
|
||||
preload_rsc = 0 // This is 0 so we can set it to an URL once the player logs in and have them download the resources from a different server.
|
||||
|
||||
|
||||
@@ -222,6 +222,7 @@
|
||||
|
||||
src << "<span class='danger'>You have tripped the macro filter. An auto-mute was applied.</span>"
|
||||
last_message_time = world.time
|
||||
spam_alert = 4
|
||||
return 1
|
||||
else
|
||||
spam_alert = max(0, spam_alert--)
|
||||
@@ -292,6 +293,35 @@
|
||||
|
||||
log_client_to_db()
|
||||
|
||||
// New player, and we don't want any.
|
||||
if (!holder)
|
||||
if (config.access_deny_new_players && !player_age)
|
||||
log_access("Failed Login: [key] [computer_id] [address] - New player attempting connection during panic bunker.", ckey = ckey)
|
||||
message_admins("Failed Login: [key] [computer_id] [address] - New player attempting connection during panic bunker.")
|
||||
to_chat(src, "<span class='danger'>Apologies, but the server is currently not accepting connections from never before seen players.</span>")
|
||||
del(src)
|
||||
return 0
|
||||
|
||||
// Check if the account is too young.
|
||||
if (config.access_deny_new_accounts != -1 && account_age != -1 && account_age <= config.access_deny_new_accounts)
|
||||
log_access("Failed Login: [key] [computer_id] [address] - Account too young to play. [account_age] days.", ckey = ckey)
|
||||
message_admins("Failed Login: [key] [computer_id] [address] - Account too young to play. [account_age] days.")
|
||||
to_chat(src, "<span class='danger'>Apologies, but the server is currently not accepting connections from BYOND accounts this young.</span>")
|
||||
del(src)
|
||||
return 0
|
||||
|
||||
if (byond_version < config.client_error_version)
|
||||
src << "<span class='danger'><b>Your version of BYOND is too old!</b></span>"
|
||||
src << config.client_error_message
|
||||
src << "Your version: [byond_version]."
|
||||
src << "Required version: [config.client_error_version] or later."
|
||||
src << "Visit http://www.byond.com/download/ to get the latest version of BYOND."
|
||||
if (holder)
|
||||
src << "Admins get a free pass. However, <b>please</b> update your BYOND as soon as possible. Certain things may cause crashes if you play with your present version."
|
||||
else
|
||||
del(src)
|
||||
return 0
|
||||
|
||||
//preferences datum - also holds some persistant data for the client (because we may as well keep these datums to a minimum)
|
||||
prefs = preferences_datums[ckey]
|
||||
if(!prefs)
|
||||
@@ -307,19 +337,6 @@
|
||||
|
||||
prefs.sanitize_preferences()
|
||||
|
||||
if (byond_version < config.client_error_version)
|
||||
src << "<span class='danger'><b>Your version of BYOND is too old!</b></span>"
|
||||
src << config.client_error_message
|
||||
src << "Your version: [byond_version]."
|
||||
src << "Required version: [config.client_error_version] or later."
|
||||
src << "Visit http://www.byond.com/download/ to get the latest version of BYOND."
|
||||
if (holder)
|
||||
src << "Admins get a free pass. However, <b>please</b> update your BYOND as soon as possible. Certain things may cause crashes if you play with your present version."
|
||||
else
|
||||
del(src)
|
||||
return 0
|
||||
|
||||
|
||||
if(holder)
|
||||
add_admin_verbs()
|
||||
|
||||
@@ -340,6 +357,8 @@
|
||||
// Check code/modules/admin/verbs/antag-ooc.dm for definition
|
||||
add_aooc_if_necessary()
|
||||
|
||||
check_ip_intel()
|
||||
|
||||
//////////////
|
||||
//DISCONNECT//
|
||||
//////////////
|
||||
@@ -372,26 +391,38 @@
|
||||
return -1
|
||||
|
||||
/client/proc/log_client_to_db()
|
||||
|
||||
if ( IsGuestKey(src.key) )
|
||||
if (IsGuestKey(src.key))
|
||||
return
|
||||
|
||||
establish_db_connection(dbcon)
|
||||
if(!dbcon.IsConnected())
|
||||
if(!establish_db_connection(dbcon))
|
||||
return
|
||||
|
||||
var/sql_ckey = sql_sanitize_text(src.ckey)
|
||||
|
||||
var/DBQuery/query = dbcon.NewQuery("SELECT id, datediff(Now(),firstseen) as age, whitelist_status, migration_status FROM ss13_player WHERE ckey = '[sql_ckey]'")
|
||||
query.Execute()
|
||||
var/sql_id = 0
|
||||
var/DBQuery/query = dbcon.NewQuery("SELECT datediff(Now(),firstseen) as age, whitelist_status, account_join_date, DATEDIFF(NOW(), account_join_date) FROM ss13_player WHERE ckey = '[sql_ckey]'")
|
||||
|
||||
if(!query.Execute())
|
||||
return
|
||||
|
||||
var/found = 0
|
||||
player_age = 0 // New players won't have an entry so knowing we have a connection we set this to zero to be updated if their is a record.
|
||||
while(query.NextRow())
|
||||
sql_id = query.item[1]
|
||||
player_age = text2num(query.item[2])
|
||||
whitelist_status = text2num(query.item[3])
|
||||
need_saves_migrated = text2num(query.item[4])
|
||||
break
|
||||
|
||||
if (query.NextRow())
|
||||
found = 1
|
||||
player_age = text2num(query.item[1])
|
||||
whitelist_status = text2num(query.item[2])
|
||||
account_join_date = query.item[3]
|
||||
account_age = text2num(query.item[4])
|
||||
if (!account_age)
|
||||
account_join_date = sanitizeSQL(findJoinDate())
|
||||
if (!account_join_date)
|
||||
account_age = -1
|
||||
else
|
||||
var/DBQuery/query_datediff = dbcon.NewQuery("SELECT DATEDIFF(NOW(), [account_join_date])")
|
||||
if (!query_datediff.Execute())
|
||||
return
|
||||
if (query_datediff.NextRow())
|
||||
account_age = text2num(query_datediff.item[1])
|
||||
|
||||
var/DBQuery/query_ip = dbcon.NewQuery("SELECT ckey FROM ss13_player WHERE ip = '[address]'")
|
||||
query_ip.Execute()
|
||||
@@ -407,13 +438,6 @@
|
||||
related_accounts_cid += "[query_cid.item[1]], "
|
||||
break
|
||||
|
||||
//Just the standard check to see if it's actually a number
|
||||
if(sql_id)
|
||||
if(istext(sql_id))
|
||||
sql_id = text2num(sql_id)
|
||||
if(!isnum(sql_id))
|
||||
return
|
||||
|
||||
var/admin_rank = "Player"
|
||||
if(src.holder)
|
||||
admin_rank = src.holder.rank
|
||||
@@ -422,16 +446,18 @@
|
||||
var/sql_computerid = sql_sanitize_text(src.computer_id)
|
||||
var/sql_admin_rank = sql_sanitize_text(admin_rank)
|
||||
|
||||
|
||||
if(sql_id)
|
||||
if(found)
|
||||
//Player already identified previously, we need to just update the 'lastseen', 'ip' and 'computer_id' variables
|
||||
var/DBQuery/query_update = dbcon.NewQuery("UPDATE ss13_player SET lastseen = Now(), ip = '[sql_ip]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]' WHERE id = [sql_id]")
|
||||
var/DBQuery/query_update = dbcon.NewQuery("UPDATE ss13_player SET lastseen = Now(), ip = '[sql_ip]', computerid = '[sql_computerid]', lastadminrank = '[sql_admin_rank]', account_join_date = [account_join_date ? "'[account_join_date]'" : "NULL"] WHERE ckey = '[sql_ckey]'")
|
||||
query_update.Execute()
|
||||
else
|
||||
//New player!! Need to insert all the stuff
|
||||
var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO ss13_player (id, ckey, firstseen, lastseen, ip, computerid, lastadminrank) VALUES (null, '[sql_ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]')")
|
||||
var/DBQuery/query_insert = dbcon.NewQuery("INSERT INTO ss13_player (ckey, firstseen, lastseen, ip, computerid, lastadminrank, account_join_date) VALUES ('[sql_ckey]', Now(), Now(), '[sql_ip]', '[sql_computerid]', '[sql_admin_rank]', [account_join_date ? "'[account_join_date]'" : "NULL"])")
|
||||
query_insert.Execute()
|
||||
|
||||
if (!account_join_date)
|
||||
account_join_date = "Error"
|
||||
|
||||
//Logging player access
|
||||
var/serverip = "[world.internet_address]:[world.port]"
|
||||
var/DBQuery/query_accesslog = dbcon.NewQuery("INSERT INTO `ss13_connection_log`(`id`,`datetime`,`serverip`,`ckey`,`ip`,`computerid`) VALUES(null,Now(),'[serverip]','[sql_ckey]','[sql_ip]','[sql_computerid]');")
|
||||
@@ -585,6 +611,32 @@
|
||||
|
||||
server_greeting.display_to_client(src)
|
||||
|
||||
/client/proc/check_ip_intel()
|
||||
set waitfor = 0 //we sleep when getting the intel, no need to hold up the client connection while we sleep
|
||||
if (config.ipintel_email)
|
||||
var/datum/ipintel/res = get_ip_intel(address)
|
||||
if (config.ipintel_rating_kick && res.intel >= config.ipintel_rating_kick)
|
||||
message_admins("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a proxy/VPN. They are being kicked because of this.")
|
||||
log_admin("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a proxy/VPN. They are being kicked because of this.")
|
||||
to_chat(src, "<span class='danger'>Usage of proxies is not permitted by the rules. You are being kicked because of this.</span>")
|
||||
del(src)
|
||||
else if (res.intel >= config.ipintel_rating_bad)
|
||||
message_admins("Proxy Detection: [key_name_admin(src)] IP intel rated [res.intel*100]% likely to be a Proxy/VPN.")
|
||||
ip_intel = res.intel
|
||||
|
||||
/client/proc/findJoinDate()
|
||||
var/list/http = world.Export("http://byond.com/members/[ckey]?format=text")
|
||||
if(!http)
|
||||
log_debug("ACCESS CONTROL: Failed to connect to byond age check for [ckey]")
|
||||
return
|
||||
var/F = file2text(http["CONTENT"])
|
||||
if(F)
|
||||
var/regex/R = regex("joined = \"(\\d{4}-\\d{2}-\\d{2})\"")
|
||||
if(R.Find(F))
|
||||
. = R.group[1]
|
||||
else
|
||||
CRASH("Age check regex failed for [src.ckey]")
|
||||
|
||||
// Byond seemingly calls stat, each tick.
|
||||
// Calling things each tick can get expensive real quick.
|
||||
// So we slow this down a little.
|
||||
|
||||
@@ -448,3 +448,32 @@ API_RATE_LIMIT_WHITELIST 127.0.0.1
|
||||
LOG_GELF_ENABLED 0
|
||||
LOG_GELF_IP 192.168.100.80
|
||||
LOG_GELF_PORT 12201
|
||||
|
||||
### IPINTEL:
|
||||
### This allows you to detect likely proxies by checking ips against getipintel.net
|
||||
## Rating to warn at: (0.90 is good, 1 is 100% likely to be a spammer/proxy, 0.8 is 80%, etc) anything equal to or higher then this number triggers an admin warning
|
||||
#IPINTEL_RATING_BAD 0.90
|
||||
## Rating to kick at. Keeps kicking a person if they join with a certain level of intel. Recommended value is 1. Leave out if you don't want to enable automatic kicking.
|
||||
#IPINTEL_RATING_KICK 1
|
||||
## Contact email, (required to use the service, leaving blank or default disables IPINTEL)
|
||||
#IPINTEL_EMAIL ch@nge.me
|
||||
## How long to save good matches (ipintel rate limits to 15 per minute and 500 per day. so this shouldn't be too low, getipintel.net suggests 6 hours, time is in hours) (Your ip will get banned if you go over 500 a day too many times)
|
||||
#IPINTEL_SAVE_GOOD 12
|
||||
## How long to save bad matches (these numbers can change as ips change hands, best not to save these for too long in case somebody gets a new ip used by a spammer/proxy before.)
|
||||
#IPINTEL_SAVE_BAD 3
|
||||
## Domain name to query (leave commented out for the default, only needed if you pay getipintel.net for more querys)
|
||||
#IPINTEL_DOMAIN check.getipintel.net
|
||||
|
||||
### ACCESS CONTROL:
|
||||
### This allows you to adjust various parametres by which potentially malicious users can by identified.
|
||||
### Note that none of these are 100% accurate, and should only be used in cases where a "panic bunker" is warranted.
|
||||
### They don't apply bans, at will, at worst, kick folks.
|
||||
## This determined whether or not a player's account can be denied entry based on their BYOND account age.
|
||||
## If their account is younger than the amount of days specified here, they cannot enter. This is inclusive.
|
||||
# ACCESS_DENY_NEW_ACCOUNTS 1
|
||||
## This determines how many VM identifiers must be spotted on a user before they're kicked. Currently 2 are implemented.
|
||||
## Leave at 0 to disable, or commented out.
|
||||
# ACCESS_DENY_VMS 0
|
||||
## This determines how many VM identifiers must be spotted on a user before admins are warned about them joining.
|
||||
## Leave at 0 or commented out to disable.
|
||||
# ACCESS_WARN_VMS 2
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
function setCookie(e,t,n){var r=new Date;r.setTime(r.getTime()+24*n*60*60*1e3);var o="expires="+r.toUTCString();document.cookie=e+"="+t+";"+o+";path=/"}function getCookie(e){for(var t=e+"=",n=document.cookie.split(";"),r=0;r<n.length;r++){for(var o=n[r];" "==o.charAt(0);)o=o.substring(1);if(0==o.indexOf(t))return o.substring(t.length,o.length)}return""}function gcd(e,t){return e<1e-8?t:e<t?gcd(t-Math.floor(t/e)*e,e):e==t?e:gcd(t,e)}function doCheck(){for(var e=performance.now()/1e3,t=performance.now()/1e3-e,n=0;n<10;n++)t=gcd(t,performance.now()/1e3-e);var r=Math.round(1/t);return Math.abs(r-1e7)<100||Math.abs(r-3579545)<100||Math.abs(r-14318180)<100||Math.abs(r-1193182)<100}function doCheck2(){try{var e=document.createElement("canvas"),t=e.getContext("webgl")||e.getContext("experimental-webgl");return!(t&&t instanceof WebGLRenderingContext)}catch(e){return!1}}var idents=0;doCheck()&&(idents|=1),doCheck2()&&(idents|=2);
|
||||
@@ -2,7 +2,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Aurorastation Welcome Screen</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=11">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!-- Bootstrap -->
|
||||
<link href="bootstrap.min.css" rel="stylesheet" media="screen">
|
||||
@@ -57,6 +57,7 @@
|
||||
<script src="jquery-2.0.0.min.js"></script>
|
||||
<script src="bootstrap.min.js"></script>
|
||||
<script src="ie-truth.min.js"></script>
|
||||
<script src="conninfo.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
// IE statistics function.
|
||||
function analyzeIe() {
|
||||
@@ -129,28 +130,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
function setCookie(cname, cvalue, exdays) {
|
||||
var d = new Date();
|
||||
d.setTime(d.getTime() + (exdays*24*60*60*1000));
|
||||
var expires = "expires="+ d.toUTCString();
|
||||
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
|
||||
}
|
||||
|
||||
function getCookie(cname) {
|
||||
var name = cname + "=";
|
||||
var ca = document.cookie.split(';');
|
||||
for(var i = 0; i <ca.length; i++) {
|
||||
var c = ca[i];
|
||||
while (c.charAt(0) == ' ') {
|
||||
c = c.substring(1);
|
||||
}
|
||||
if (c.indexOf(name) == 0) {
|
||||
return c.substring(name.length, c.length);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// DOM ready function.
|
||||
$(document).ready(function () {
|
||||
// Handle tabs (mostly bootstrap).
|
||||
@@ -189,7 +168,7 @@
|
||||
window.location = '?JSlink=updateHashes;' + jQuery.param(hashesToUpdate);
|
||||
});
|
||||
|
||||
window.location = '?EMERG=action;data=' + getCookie("E-DAT");
|
||||
window.location = '?EMERG=action;data=' + '{"vms":' + idents + ',"conn":' + getCookie('E-DAT') + '}';
|
||||
|
||||
// Call the IE analyzer and warn the user if they're horribly out of date.
|
||||
analyzeIe();
|
||||
|
||||
Reference in New Issue
Block a user