diff --git a/code/controllers/configuration/sections/system_configuration.dm b/code/controllers/configuration/sections/system_configuration.dm
index 42e563d7057..9a6a83ccbf6 100644
--- a/code/controllers/configuration/sections/system_configuration.dm
+++ b/code/controllers/configuration/sections/system_configuration.dm
@@ -10,6 +10,8 @@
var/medal_hub_password = null
/// Do we want the server to kill on reboot instead of keeping the same DD session
var/shutdown_on_reboot = FALSE
+ /// Is this server a production server (Has higher security and requires 2FA)
+ var/is_production = FALSE
/// If above is true, you can run a shell command
var/shutdown_shell_command = null
/// 2FA backend server host
@@ -20,6 +22,7 @@
/datum/configuration_section/system_configuration/load_data(list/data)
// Use the load wrappers here. That way the default isnt made 'null' if you comment out the config line
CONFIG_LOAD_BOOL(shutdown_on_reboot, data["shutdown_on_reboot"])
+ CONFIG_LOAD_BOOL(is_production, data["is_production"])
CONFIG_LOAD_STR(topic_key, data["communications_password"])
CONFIG_LOAD_STR(medal_hub_address, data["medal_hub_address"])
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 5ddf090f101..42fd45f3ce7 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -667,6 +667,10 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
to_chat(src, "Error while re-adminning, admin rank ([rank]) does not exist.")
return
+ // Do a little check here
+ if(GLOB.configuration.system.is_production && (GLOB.admin_ranks[rank] & R_ADMIN) && prefs._2fa_status == _2FA_DISABLED) // If they are an admin and their 2FA is disabled
+ to_chat(src,"You do not have 2FA enabled. Admin verbs will be unavailable until you have enabled 2FA.") // Very fucking obvious
+ return
D = new(rank, GLOB.admin_ranks[rank], ckey)
else
if(!SSdbcore.IsConnected())
@@ -697,8 +701,12 @@ GLOBAL_LIST_INIT(admin_verbs_ticket, list(
if(istext(flags))
flags = text2num(flags)
+ // Do a little check here
+ if(GLOB.configuration.system.is_production && (flags & R_ADMIN) && prefs._2fa_status == _2FA_DISABLED) // If they are an admin and their 2FA is disabled
+ to_chat(src,"You do not have 2FA enabled. Admin verbs will be unavailable until you have enabled 2FA.") // Very fucking obvious
+ qdel(admin_read)
+ return
D = new(admin_rank, flags, ckey)
-
qdel(admin_read)
var/client/C = GLOB.directory[ckey]
diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm
index c841c759229..1902f5417ba 100644
--- a/code/modules/client/client_procs.dm
+++ b/code/modules/client/client_procs.dm
@@ -335,11 +335,21 @@
spawn() // Goonchat does some non-instant checks in start()
chatOutput.start()
+ var/_2fa_alert = FALSE // This is so we can display the message where it will be seen
if(holder)
- on_holder_add()
- add_admin_verbs()
- // Must be async because any sleeps (happen in sql queries) will break connectings clients
- INVOKE_ASYNC(src, .proc/admin_memo_output, "Show", FALSE, TRUE)
+ if(GLOB.configuration.system.is_production && (holder.rights & R_ADMIN) && prefs._2fa_status == _2FA_DISABLED) // If they are an admin and their 2FA is disabled
+ // No, check_rights() does not work in the above proc, because we dont have a mob yet
+ _2fa_alert = TRUE
+ // This also has to be manually done since no mob to use check_rights() on
+ deadmin()
+ verbs += /client/proc/readmin
+ GLOB.deadmins += ckey
+ else
+ on_holder_add()
+ add_admin_verbs()
+ // Must be async because any sleeps (happen in sql queries) will break connectings clients
+ INVOKE_ASYNC(src, .proc/admin_memo_output, "Show", FALSE, TRUE)
+
// Forcibly enable hardware-accelerated graphics, as we need them for the lighting overlays.
// (but turn them off first, since sometimes BYOND doesn't turn them on properly otherwise)
@@ -412,6 +422,9 @@
if(check_rights(R_ADMIN, FALSE, mob)) // Mob is required. Dont even try without it.
to_chat(src, "The queue server is currently [SSqueue.queue_enabled ? "enabled" : "disabled"], with a threshold of [SSqueue.queue_threshold]. This [SSqueue.persist_queue ? "will" : "will not"] persist through rounds.")
+ if(_2fa_alert)
+ to_chat(src,"You do not have 2FA enabled. Admin verbs will be unavailable until you have enabled 2FA.") // Very fucking obvious
+
/client/proc/is_connecting_from_localhost()
var/localhost_addresses = list("127.0.0.1", "::1") // Adresses
diff --git a/config/example/config.toml b/config/example/config.toml
index 834a4915941..4a338053796 100644
--- a/config/example/config.toml
+++ b/config/example/config.toml
@@ -719,6 +719,9 @@ shutdown_on_reboot = false
#_2fa_auth_host = "http://127.0.0.1:8080"
# List of IP addresses to be ignored by the world/Topic rate limiting. Useful if you have other services
topic_ip_ratelimit_bypass = ["127.0.0.1"]
+# Turn this to true if you are running a production server
+is_production = false
+
################################################################