diff --git a/SQL/database_changelog.txt b/SQL/database_changelog.txt index 1707df65dae..6209e6872f4 100644 --- a/SQL/database_changelog.txt +++ b/SQL/database_changelog.txt @@ -2,12 +2,26 @@ Any time you make a change to the schema files, remember to increment the databa The latest database version is 5.17; The query to update the schema revision table is: -INSERT INTO `schema_revision` (`major`, `minor`) VALUES (5, 17); +INSERT INTO `schema_revision` (`major`, `minor`) VALUES (5, 18); or -INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (5, 17); +INSERT INTO `SS13_schema_revision` (`major`, `minor`) VALUES (5, 18); In any query remember to add a prefix to the table names if you use one. +----------------------------------------------------- +Version 5.18, 1 November 2021, by Mothblocks +Added `known_alts` table for tracking who not to create suspicious logins for. + +``` +CREATE TABLE `known_alts` ( + `id` INT NOT NULL AUTO_INCREMENT, + `ckey1` VARCHAR(32) NOT NULL, + `ckey2` VARCHAR(32) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `unique_contraints` (`ckey1` , `ckey2`) +); +``` + ----------------------------------------------------- Version 5.17, 8 October 2021, by MrStonedOne + Mothblocks Changes any table that requrired a NOT NULL round ID to now accept NULL. In the BSQL past, these were handled as 0, but in the move to rust-g this behavior was lost. diff --git a/SQL/tgstation_schema.sql b/SQL/tgstation_schema.sql index f0a4710227e..ac913be0149 100644 --- a/SQL/tgstation_schema.sql +++ b/SQL/tgstation_schema.sql @@ -663,6 +663,18 @@ CREATE TABLE `admin_connections` ( UNIQUE INDEX `unique_constraints` (`ckey`, `ip`, `cid`) ) ENGINE=InnoDB; +-- +-- Table structure for table `known_alts` +-- +DROP TABLE IF EXISTS `known_alts`; +CREATE TABLE `known_alts` ( + `id` INT NOT NULL AUTO_INCREMENT, + `ckey1` VARCHAR(32) NOT NULL, + `ckey2` VARCHAR(32) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `unique_contraints` (`ckey1` , `ckey2`) +); + /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; diff --git a/SQL/tgstation_schema_prefixed.sql b/SQL/tgstation_schema_prefixed.sql index 3307bf43b94..bcb26de7d25 100644 --- a/SQL/tgstation_schema_prefixed.sql +++ b/SQL/tgstation_schema_prefixed.sql @@ -663,6 +663,18 @@ CREATE TABLE `SS13_admin_connections` ( UNIQUE INDEX `unique_constraints` (`ckey`, `ip`, `cid`) ) ENGINE=InnoDB; +-- +-- Table structure for table `known_alts` +-- +DROP TABLE IF EXISTS `SS13_known_alts`; +CREATE TABLE `SS13_known_alts` ( + `id` INT NOT NULL AUTO_INCREMENT, + `ckey1` VARCHAR(32) NOT NULL, + `ckey2` VARCHAR(32) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE INDEX `unique_contraints` (`ckey1` , `ckey2`) +); + /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index db431d519f5..97e9142600d 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -143,3 +143,6 @@ GLOBAL_VAR_INIT(ghost_role_flags, (~0)) /// for asay pings, this is the index in the return list for [/proc/check_admin_pings] that contains the message modified with underlines for the spotted names #define ADMINSAY_PING_UNDERLINE_NAME_INDEX "!underlined_names" + +/// When passed in as the duration for ban_panel, will make the ban default to permanent +#define BAN_PANEL_PERMANENT "permanent" diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index 911b828a519..32a3be09510 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -20,7 +20,7 @@ * * make sure you add an update to the schema_version stable in the db changelog */ -#define DB_MINOR_VERSION 17 +#define DB_MINOR_VERSION 18 //! ## Timing subsystem diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 2faa38ed62e..cf006dfe0b0 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -75,6 +75,7 @@ GLOBAL_PROTECT(admin_verbs_admin) /datum/admins/proc/open_borgopanel, /datum/admins/proc/view_all_circuits, /datum/admins/proc/view_all_sdql_spells, + /datum/admins/proc/known_alts_panel, ) GLOBAL_LIST_INIT(admin_verbs_ban, list(/client/proc/unban_panel, /client/proc/ban_panel, /client/proc/stickybanpanel)) GLOBAL_PROTECT(admin_verbs_ban) diff --git a/code/modules/admin/known_alts.dm b/code/modules/admin/known_alts.dm new file mode 100644 index 00000000000..8ea595e1aca --- /dev/null +++ b/code/modules/admin/known_alts.dm @@ -0,0 +1,192 @@ +GLOBAL_DATUM_INIT(known_alts, /datum/known_alts, new) + +/datum/known_alts + var/list/cached_known_alts + COOLDOWN_DECLARE(cache_cooldown) + +/datum/known_alts/Topic(href, list/href_list) + if (!check_rights(R_ADMIN)) + return + + if (!SSdbcore.Connect()) + to_chat(usr, span_warning("Couldn't connect to the database.")) + return + + var/datum/admins/holder = usr.client?.holder + if (isnull(holder)) + return + + if (!holder.CheckAdminHref(href, href_list)) + return + + switch (href_list["action"]) + if ("add") + var/ckey1 = input(usr, "Put in the name of the main ckey") as null|text + if (!ckey1) + return + + var/ckey2 = input(usr, "Put in the name of their alt") as null|text + if (!ckey2) + return + + ckey1 = ckey(ckey1) + ckey2 = ckey(ckey2) + + var/datum/db_query/query_already_exists = SSdbcore.NewQuery({" + SELECT id FROM [format_table_name("known_alts")] + WHERE (ckey1 = :ckey1 AND ckey2 = :ckey2) + OR (ckey1 = :ckey2 AND ckey2 = :ckey1) + "}, list( + "ckey1" = ckey1, + "ckey2" = ckey2, + )) + + query_already_exists.warn_execute() + + if (query_already_exists.last_error) + qdel(query_already_exists) + return + + var/already_exists_row = query_already_exists.NextRow() + qdel(query_already_exists) + + if (already_exists_row) + alert(usr, "Those two are already in the list of known alts!") + return + + var/datum/db_query/query_add_known_alt = SSdbcore.NewQuery({" + INSERT INTO [format_table_name("known_alts")] (ckey1, ckey2) + VALUES (:ckey1, :ckey2) + "}, list( + "ckey1" = ckey1, + "ckey2" = ckey2, + )) + + if (query_add_known_alt.warn_execute()) + var/message = "[key_name(usr)] has added a new known alt connection between [ckey1] and [ckey2]." + message_admins(message) + log_admin_private(message) + + cached_known_alts = null + load_known_alts() + + qdel(query_add_known_alt) + show_panel(usr.client) + + if (!is_banned_from(ckey2, "Server")) + var/ban_choice = alert("[ckey2] is not banned from the server. Do you want to open up the ban panel as well?",,"Yes", "No") + if (ban_choice == "Yes") + holder.ban_panel(ckey2, role = "Server", duration = BAN_PANEL_PERMANENT) + if ("delete") + var/id = text2num(href_list["id"]) + if (!id) + log_admin_private("[key_name(usr)] tried to delete an invalid known alt ID: [href_list["id"]]") + return + + var/datum/db_query/query_known_alt_info = SSdbcore.NewQuery({" + SELECT ckey1, ckey2 FROM [format_table_name("known_alts")] + WHERE id = :id + "}, list( + "id" = id, + )) + + if (!query_known_alt_info.warn_execute()) + qdel(query_known_alt_info) + return + + if (!query_known_alt_info.NextRow()) + alert("Couldn't find the known alt with the ID [id]") + qdel(query_known_alt_info) + return + + var/list/result = query_known_alt_info.item + qdel(query_known_alt_info) + + if (alert("Are you sure you want to delete the alt connection between [result[1]] and [result[2]]?",,"Yes", "No") != "Yes") + return + + var/datum/db_query/query_delete_known_alt = SSdbcore.NewQuery({" + DELETE FROM [format_table_name("known_alts")] + WHERE id = :id + "}, list( + "id" = id, + )) + + if (query_delete_known_alt.warn_execute()) + var/message = "[key_name(usr)] has deleted the known alt connection between [result[1]] and [result[2]]." + message_admins(message) + log_admin_private(message) + + cached_known_alts = null + load_known_alts() + + qdel(query_delete_known_alt) + show_panel(usr.client) + +/// Returns the list of known alts, will return an empty list if the DB could not be connected to. +/// This proc can block. +/datum/known_alts/proc/load_known_alts() + if (!isnull(cached_known_alts) && !COOLDOWN_FINISHED(src, cache_cooldown)) + return cached_known_alts + + if (!SSdbcore.Connect()) + return cached_known_alts || list() + + var/datum/db_query/query_known_alts = SSdbcore.NewQuery("SELECT id, ckey1, ckey2 FROM [format_table_name("known_alts")] ORDER BY id DESC") + query_known_alts.warn_execute() + + if (query_known_alts.last_error) + qdel(query_known_alts) + return cached_known_alts || list() + + cached_known_alts = list() + + while (query_known_alts.NextRow()) + cached_known_alts += list(list( + query_known_alts.item[2], + query_known_alts.item[3], + + // The ID + query_known_alts.item[1], + )) + + COOLDOWN_START(src, cache_cooldown, 10 SECONDS) + qdel(query_known_alts) + + return cached_known_alts + +/datum/known_alts/proc/show_panel(client/client) + if (!check_rights_for(client, R_ADMIN)) + return + + if (!SSdbcore.Connect()) + to_chat(usr, span_warning("Couldn't connect to the database.")) + return + + var/list/known_alts_html = list() + + for (var/known_alt in load_known_alts()) + known_alts_html += "\[-\] Delete [known_alt[1]] is an alt of [known_alt[2]]." + + var/html = {" +
+Any two ckeys in this panel will not show in "banned connection history".
+Sometimes players switch account, and it's customary to perma-ban the old one.
+ +