From ce34e64c211f5983d9ff6b368ef40015ccb6fdbb Mon Sep 17 00:00:00 2001 From: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> Date: Sun, 30 Aug 2026 13:01:21 +0000 Subject: [PATCH] Ckey Linkage Migration (#32534) * Ckey Linkage Migration * Lets try make mariaDB work * Does this work? * AAAAAAAAAAA * Apply suggestion from @AffectedArc07 Signed-off-by: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> --------- Signed-off-by: AffectedArc07 <25063394+AffectedArc07@users.noreply.github.com> --- .github/workflows/ci.yml | 13 ++- SQL/paradise_schema.sql | 20 +++-- SQL/updates/72-73.sql | 14 +++ code/__DEFINES/database_tasks.dm | 2 + code/__DEFINES/misc_defines.dm | 2 +- .../sections/url_configuration.dm | 6 -- code/modules/admin/db_ban/functions.dm | 30 ++++--- .../admin/permissionverbs/permissionedit.dm | 4 - code/modules/admin/topic.dm | 4 - code/modules/client/client_procs.dm | 86 +------------------ config/example/config.toml | 7 +- interface/interface.dm | 16 +--- paradise.dme | 1 + tools/ci/generate_sql_scripts.py | 26 +++--- 14 files changed, 82 insertions(+), 149 deletions(-) create mode 100644 SQL/updates/72-73.sql create mode 100644 code/__DEFINES/database_tasks.dm diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 15742e64d36..827a4bfc892 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -194,15 +194,26 @@ jobs: byondtype: ['STABLE'] steps: - uses: actions/checkout@v7.0.0 + - name: Restore BYOND from Cache uses: ./.github/actions/restore_or_install_byond with: release: ${{ matrix.byondtype }} + + - name: Shutdown Ubuntu MySQL (SUDO) + run: sudo service mysql stop # Shutdown the Default MySQL, "sudo" is necessary, please not remove it + + - name: Set up MariaDB + uses: getong/mariadb-action@v1.11 + with: + mariadb version: '12.3.2' + mysql root password: 'root' + - name: Setup & Validate DB run: | - sudo systemctl start mysql python3 tools/ci/generate_sql_scripts.py tools/ci/validate_sql.sh + - name: Compile & Run Unit Tests run: | tools/ci/install_byond.sh '${{ matrix.byondtype }}' diff --git a/SQL/paradise_schema.sql b/SQL/paradise_schema.sql index 1e5e960e188..0bead0690e0 100644 --- a/SQL/paradise_schema.sql +++ b/SQL/paradise_schema.sql @@ -483,17 +483,21 @@ CREATE TABLE `vpn_whitelist` ( /*!40101 SET character_set_client = @saved_cs_client */; -- --- Table structure for table `oauth_tokens` +-- Table structure for table `task_queue` -- -DROP TABLE IF EXISTS `oauth_tokens`; +DROP TABLE IF EXISTS `task_queue`; /*!40101 SET @saved_cs_client = @@character_set_client */; /*!40101 SET character_set_client = utf8 */; -CREATE TABLE `oauth_tokens` ( - `ckey` varchar(32) NOT NULL, - `token` varchar(32) NOT NULL, - PRIMARY KEY (`token`), - KEY `ckey` (`ckey`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +CREATE TABLE `task_queue` ( + `task_id` UUID NOT NULL, + `task_type` VARCHAR(128) NOT NULL COLLATE 'utf8mb4_general_ci', + `task_arguments` VARCHAR(128) NOT NULL COLLATE 'utf8mb4_general_ci', + `date_inserted` DATETIME NOT NULL, + `date_processed` DATETIME NULL DEFAULT NULL, + `processed` TINYINT(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`task_id`) USING BTREE, + INDEX `processed` (`processed`) USING BTREE +) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB; /*!40101 SET character_set_client = @saved_cs_client */; diff --git a/SQL/updates/72-73.sql b/SQL/updates/72-73.sql new file mode 100644 index 00000000000..7da9e7a73a5 --- /dev/null +++ b/SQL/updates/72-73.sql @@ -0,0 +1,14 @@ +# Updating SQL from 72 to 73 -AffectedArc07 +# Replaces the old forum links table with a generic tasks table for TaskDaemon to pick up +DROP TABLE `oauth_tokens`; + +CREATE TABLE `task_queue` ( + `task_id` UUID NOT NULL, + `task_type` VARCHAR(128) NOT NULL COLLATE 'utf8mb4_general_ci', + `task_arguments` VARCHAR(128) NOT NULL COLLATE 'utf8mb4_general_ci', + `date_inserted` DATETIME NOT NULL, + `date_processed` DATETIME NULL DEFAULT NULL, + `processed` TINYINT(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`task_id`) USING BTREE, + INDEX `processed` (`processed`) USING BTREE +) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB; diff --git a/code/__DEFINES/database_tasks.dm b/code/__DEFINES/database_tasks.dm new file mode 100644 index 00000000000..884ab9b187b --- /dev/null +++ b/code/__DEFINES/database_tasks.dm @@ -0,0 +1,2 @@ +#define DATABASE_TASK_MARK_BANNED "MARK_BANNED" +#define DATABASE_TASK_MARK_UNBANNED "MARK_UNBANNED" diff --git a/code/__DEFINES/misc_defines.dm b/code/__DEFINES/misc_defines.dm index 65cc074b75b..b86ff9c8c39 100644 --- a/code/__DEFINES/misc_defines.dm +++ b/code/__DEFINES/misc_defines.dm @@ -440,7 +440,7 @@ #define INVESTIGATE_DEATHS "deaths" // The SQL version required by this version of the code -#define SQL_VERSION 72 +#define SQL_VERSION 73 // Vending machine stuff #define CAT_NORMAL (1<<0) diff --git a/code/controllers/configuration/sections/url_configuration.dm b/code/controllers/configuration/sections/url_configuration.dm index 82dac8df111..e85c6c56f1c 100644 --- a/code/controllers/configuration/sections/url_configuration.dm +++ b/code/controllers/configuration/sections/url_configuration.dm @@ -22,10 +22,6 @@ var/donations_url /// URL for a direct discord invite var/discord_url - /// URL for a discord invite going via the forums - var/discord_forum_url - /// URL for linking ingame accounts and forum accounts. Token is appended to end - var/forum_link_url /// URL for pulling player info on webtools var/forum_playerinfo_url /// URL for the CentCom Ban DB API @@ -46,8 +42,6 @@ CONFIG_LOAD_STR(exploit_url, data["exploit_url"]) CONFIG_LOAD_STR(donations_url, data["donations_url"]) CONFIG_LOAD_STR(discord_url, data["discord_url"]) - CONFIG_LOAD_STR(discord_forum_url, data["discord_forum_url"]) - CONFIG_LOAD_STR(forum_link_url, data["forum_link_url"]) CONFIG_LOAD_STR(forum_playerinfo_url, data["forum_playerinfo_url"]) CONFIG_LOAD_STR(centcom_ban_db_url, data["centcomm_ban_db_url"]) CONFIG_LOAD_STR(round_stats_url, data["round_stats_url"]) diff --git a/code/modules/admin/db_ban/functions.dm b/code/modules/admin/db_ban/functions.dm index 3337acbaf4b..5c787a332f6 100644 --- a/code/modules/admin/db_ban/functions.dm +++ b/code/modules/admin/db_ban/functions.dm @@ -191,7 +191,7 @@ // Reload their job ban holder C.jbh.reload_jobbans(C) else - flag_account_for_forum_sync(ckey) + flag_account_for_role_sync(ckey, DATABASE_TASK_MARK_BANNED) /datum/admins/proc/DB_ban_unban(ckey, bantype, job = "") @@ -281,7 +281,7 @@ // Reload their job ban holder C.jbh.reload_jobbans(C) else - flag_account_for_forum_sync(ckey) + flag_account_for_role_sync(ckey, DATABASE_TASK_MARK_UNBANNED) /datum/admins/proc/DB_ban_edit(banid = null, param = null) @@ -389,7 +389,6 @@ return /datum/admins/proc/DB_ban_unban_by_id(id) - if(!check_rights(R_BAN)) return @@ -442,7 +441,7 @@ message_admins("[key_name_admin(usr)] has lifted [pckey]'s ban.") log_admin("[key_name(usr)] has lifted [pckey]'s ban.") - flag_account_for_forum_sync(pckey) + flag_account_for_role_sync(pckey, DATABASE_TASK_MARK_UNBANNED) // See if they are online var/client/C = GLOB.directory[ckey(pckey)] if(C) @@ -692,15 +691,24 @@ popup.open() onclose(usr, "ban_panel") -/proc/flag_account_for_forum_sync(ckey) +/datum/admins/proc/flag_account_for_role_sync(ckey, task_type) if(!SSdbcore.IsConnected()) return - var/datum/db_query/adm_query = SSdbcore.NewQuery("UPDATE player SET fupdate = 1 WHERE ckey=:ckey", list( - "ckey" = ckey - )) - // We do nothing with output here so we dont need to wrap the warn_execute() inside an if statement - adm_query.warn_execute() - qdel(adm_query) + var/uuid = rustlibs_generate_uuid() + var/task_arguments = alist() + task_arguments["ckey"] = ckey + + var/datum/db_query/task_query = SSdbcore.NewQuery( + "INSERT INTO task_queue (task_id, task_type, task_arguments, date_inserted) VALUES (:tid, :tt, :ta, NOW())", + list( + "tid" = uuid, + "tt" = task_type, + "ta" = json_encode(task_arguments) + ) + ) + + task_query.warn_execute() + qdel(task_query) #undef MAX_ADMIN_BANS_PER_ADMIN diff --git a/code/modules/admin/permissionverbs/permissionedit.dm b/code/modules/admin/permissionverbs/permissionedit.dm index ec6d95f41de..42ff4e6877a 100644 --- a/code/modules/admin/permissionverbs/permissionedit.dm +++ b/code/modules/admin/permissionverbs/permissionedit.dm @@ -282,8 +282,6 @@ USER_VERB(edit_admin_permissions, R_PERMISSIONS, "Permissions Panel", "Edit admi to_chat(usr, SPAN_WARNING("No admin found with ckey [target_key]!")) return - flag_account_for_forum_sync(target_key) - if(removed_permissions & permission_bit) // They had an exception removing this permission bit, let them have it. var/datum/db_query/remove_removal = SSdbcore.NewQuery("UPDATE admin SET removed_permissions = removed_permissions & ~:permission_bit WHERE id = :admin_id", list( @@ -407,8 +405,6 @@ USER_VERB(edit_admin_permissions, R_PERMISSIONS, "Permissions Panel", "Edit admi to_chat(usr, SPAN_WARNING("No admin found with ckey [target_key]!")) return - flag_account_for_forum_sync(target_key) - var/datum/db_query/remove_admin = SSdbcore.NewQuery("UPDATE admin SET permissions_rank = NULL, display_rank = NULL WHERE id = :admin_id", list( "admin_id" = admin_id, )) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 107e40532df..a6a516fb61f 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -1017,8 +1017,6 @@ to_chat(M, SPAN_WARNING("This is a temporary ban, it will be removed in [mins] minutes.")) DB_ban_record(BANTYPE_TEMP, M, mins, reason) add_note(M.last_known_ckey, "Banned for [mins] minutes - [reason]", null, usr.ckey, FALSE) - if(M.client) - M.client.link_forum_account(TRUE) if(GLOB.configuration.url.banappeals_url) to_chat(M, SPAN_WARNING("To try to resolve this matter head to [GLOB.configuration.url.banappeals_url]")) else @@ -1033,8 +1031,6 @@ return to_chat(M, SPAN_WARNING("You have been banned by [usr.client.ckey].\nReason: [reason].")) to_chat(M, SPAN_WARNING("This ban does not expire automatically and must be appealed.")) - if(M.client) - M.client.link_forum_account(TRUE) if(GLOB.configuration.url.banappeals_url) to_chat(M, SPAN_WARNING("To try to resolve this matter head to [GLOB.configuration.url.banappeals_url]")) else diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 2ee79a56a3a..b01bb9026b0 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -141,10 +141,6 @@ to_chat(src, SPAN_NOTICE("SSD warning acknowledged.")) return - if(href_list["link_forum_account"]) - link_forum_account() - return // prevents a recursive loop where the ..() 5 lines after this makes the proc endlessly re-call itself - if(href_list["withdraw_consent"]) var/choice = tgui_alert(usr, "Are you SURE you want to withdraw your consent to the Terms of Service?\nYou will be instantaneously removed from the server and will have to re-accept the Terms of Service.", "Warning", list("Yes", "No")) if(choice == "Yes") @@ -679,7 +675,7 @@ /client/proc/check_forum_link() - if(!GLOB.configuration.url.forum_link_url || !prefs || prefs.fuid) + if(!GLOB.configuration.system.is_production || !prefs || prefs.fuid) return if(GLOB.configuration.jobs.enable_exp_tracking) @@ -687,84 +683,8 @@ if(living_hours < 20) return - to_chat(src, "You have no verified forum account. VERIFY FORUM ACCOUNT") - -/client/proc/create_oauth_token() - var/datum/db_query/query_find_token = SSdbcore.NewQuery("SELECT token FROM oauth_tokens WHERE ckey=:ckey limit 1", list( - "ckey" = ckey - )) - - // These queries have log_error=FALSE to avoid auth tokens being in plaintext logs - if(!query_find_token.warn_execute(log_error=FALSE)) - qdel(query_find_token) - return - - if(query_find_token.NextRow()) - var/tkn = query_find_token.item[1] - qdel(query_find_token) - return tkn - - qdel(query_find_token) - - var/tokenstr = md5("[rand(0,9999)][world.time][rand(0,9999)][ckey][rand(0,9999)][address][rand(0,9999)][computer_id][rand(0,9999)]") - - var/datum/db_query/query_insert_token = SSdbcore.NewQuery("INSERT INTO oauth_tokens (ckey, token) VALUES(:ckey, :tokenstr)", list( - "ckey" = ckey, - "tokenstr" = tokenstr, - )) - - // These queries have log_error=FALSE to avoid auth tokens being in plaintext logs - if(!query_insert_token.warn_execute(log_error = FALSE)) - qdel(query_insert_token) - return - - qdel(query_insert_token) - return tokenstr - -/client/proc/link_forum_account(fromban) - if(!GLOB.configuration.url.forum_link_url) - return - - if(IsGuestKey(key)) - to_chat(src, "Guest keys cannot be linked.") - return - - if(prefs && prefs.fuid) - if(!fromban) - to_chat(src, "Your forum account is already set.") - return - - var/datum/db_query/query_find_link = SSdbcore.NewQuery("SELECT fuid FROM player WHERE ckey=:ckey LIMIT 1", list( - "ckey" = ckey - )) - - if(!query_find_link.warn_execute()) - qdel(query_find_link) - return - - if(query_find_link.NextRow()) - if(query_find_link.item[1]) - if(!fromban) - to_chat(src, "Your forum account is already set. ([query_find_link.item[1]])") - qdel(query_find_link) - return - - qdel(query_find_link) - var/tokenid = create_oauth_token() - if(!tokenid) - to_chat(src, "link_forum_account: unable to create token") - return - - var/url = "[GLOB.configuration.url.forum_link_url][tokenid]" - if(fromban) - url += "&fwd=appeal" - to_chat(src, {"Now opening a window to verify your information with the forums, so that you can appeal your ban. If the window does not load, please copy/paste this link: [url]"}) - to_chat(src, SPAN_BOLDANNOUNCEOOC("If you are screenshotting this screen for your ban appeal, please blur/draw over the token in the above link.")) - else - to_chat(src, {"Now opening a window to verify your information with the forums. If the window does not load, please go to: [url]"}) - - src << link(url) - return + to_chat(src, "You have not linked your BYOND account to your Paradise account. Click here for more information.") + to_chat(src, SPAN_NOTICE("If you have linked your account in the past hour, please ignore the above. If you are still seeing this message with a linked account, please inform the server host.")) #undef TOPIC_SPAM_DELAY #undef UPLOAD_LIMIT diff --git a/config/example/config.toml b/config/example/config.toml index 5ae84466b88..bf4867e4875 100644 --- a/config/example/config.toml +++ b/config/example/config.toml @@ -180,7 +180,7 @@ fluff_undershirts = [ # Enable/disable the database on a whole sql_enabled = false # SQL version. If this is a mismatch, round start will be delayed -sql_version = 72 +sql_version = 73 # SQL server address. Can be an IP or DNS name sql_address = "127.0.0.1" # SQL server port @@ -841,9 +841,6 @@ toast_on_init_complete = true # Location of server resources. Used to offload sending the paradise.rsc from DD to a webserver # NOTE: This wants paradise.rsc inside a zip file, and it is HTTP ONLY. NO HTTPS #rsc_urls = ["http://www.paradisestation.org/windows/paradise.rsc.zip"] -# Link URL to link forum accounts to ckeys. If set to an empty string, no link option will be offered -# Token is appended right on the end, so set parameters as needed -#forum_link_url = "https://example.com/link.php?token=" # URL for accessing player info from admin webtools # Ckey is appended right on the end, so set parameters as needed #forum_playerinfo_url = "https://example.com/info.php?ckey=" @@ -861,8 +858,6 @@ github_url = "https://github.com/ParadiseSS13/Paradise" exploit_url = "https://www.paradisestation.org/forum/179-exploit-reports/" # Discord address #discord_url = "http://example.org" -# Discord address (forum-based invite) -discord_forum_url = "https://www.paradisestation.org/forum/discord/invite/general/" # Donations address donations_url = "https://www.patreon.com/ParadiseStation" # Ban appeals URL - usually for a forum or wherever people should go to contact your admins diff --git a/interface/interface.dm b/interface/interface.dm index 39bda36daa5..9e35f930a4f 100644 --- a/interface/interface.dm +++ b/interface/interface.dm @@ -21,8 +21,6 @@ if(GLOB.configuration.url.forum_url) if(tgui_alert(src, "Open the forum in your browser?", "Forum", list("Yes", "No")) != "Yes") return - if(GLOB.configuration.url.forum_link_url && prefs && !prefs.fuid) - link_forum_account() src << link(GLOB.configuration.url.forum_url) else to_chat(src, SPAN_DANGER("The forum URL is not set in the server configuration.")) @@ -54,21 +52,15 @@ set desc = "Join our Discord server." set hidden = 1 - var/durl // Use normal URL - if(GLOB.configuration.url.discord_url) - durl = GLOB.configuration.url.discord_url - - // Use forums URL if set - if(GLOB.configuration.url.forum_link_url && GLOB.configuration?.url.discord_forum_url && prefs?.fuid) - durl = GLOB.configuration.url.discord_forum_url - - if(!durl) + if(!GLOB.configuration.url.discord_url) to_chat(src, SPAN_DANGER("The Discord URL is not set in the server configuration.")) return + if(tgui_alert(src, "This will invite you to our Discord server. Are you sure?", "Discord", list("Yes", "No")) != "Yes") return - src << link(durl) + + src << link(GLOB.configuration.url.discord_url) /client/verb/donate() set name = "Donate" diff --git a/paradise.dme b/paradise.dme index 78c18b9e11c..a1bc77d6bf2 100644 --- a/paradise.dme +++ b/paradise.dme @@ -58,6 +58,7 @@ #include "code\__DEFINES\crafting.dm" #include "code\__DEFINES\criminal_status.dm" #include "code\__DEFINES\cult_defines.dm" +#include "code\__DEFINES\database_tasks.dm" #include "code\__DEFINES\departments_defines.dm" #include "code\__DEFINES\directions.dm" #include "code\__DEFINES\disease_defines.dm" diff --git a/tools/ci/generate_sql_scripts.py b/tools/ci/generate_sql_scripts.py index d0be29cbbdd..15a9e707ae9 100644 --- a/tools/ci/generate_sql_scripts.py +++ b/tools/ci/generate_sql_scripts.py @@ -19,11 +19,11 @@ for index in orderedSqlFiles: # Yes I know half of the casts below this are probably not necassary, but python is very picky # AND YES I KNOW THIS IS SNOWFLAKEY AS HELL, BUT IT MUST BE DONE FOR PROPER CI if index in [16, 17, 31, 38]: - orderedSqlFiles[index] = str(index) + "-" + (str(int(index)+1)) + ".py" + orderedSqlFiles[index] = f"{index}-{index+1}.py" else: - orderedSqlFiles[index] = str(index) + "-" + (str(int(index)+1)) + ".sql" + orderedSqlFiles[index] = f"{index}-{index+1}.sql" -print("Found " + str(len(orderedSqlFiles)) + " SQL update files to validate") +print(f"Found {len(orderedSqlFiles)} SQL update files to validate") # FROM THIS POINT ON, DO NOT SORT THAT LIST # Go back up two directories @@ -46,7 +46,7 @@ scriptLines = [ "set -euo pipefail\n" "python3 -m pip install setuptools\n" # Yes I know you can PIP multiple things but they need to happen in this order "python3 -m pip install mysql-connector-python\n" - "mysql -u root -proot < tools/ci/sql_v0.sql\n" + "mysql -h 127.0.0.1 -u root -proot < tools/ci/sql_v0.sql\n" ] # And write the files and tell them to be used @@ -54,33 +54,33 @@ for file in orderedSqlFiles: if file.endswith(".py"): # Begin snowflakery if file == "16-17.py": - scriptLines.append("python3 SQL/updates/" + str(file) + " 127.0.0.1 root root paradise_gamedb feedback round\n") + scriptLines.append(f"python3 SQL/updates/{file} 127.0.0.1 root root paradise_gamedb feedback round\n") elif file == "17-18.py": - scriptLines.append("python3 SQL/updates/" + str(file) + " 127.0.0.1 root root paradise_gamedb feedback feedback_2\n") + scriptLines.append(f"python3 SQL/updates/{file} 127.0.0.1 root root paradise_gamedb feedback feedback_2\n") elif file == "31-32.py": - scriptLines.append("python3 SQL/updates/" + str(file) + " 127.0.0.1 root root paradise_gamedb\n") + scriptLines.append(f"python3 SQL/updates/{file} 127.0.0.1 root root paradise_gamedb\n") elif file == "38-39.py": - scriptLines.append("python3 SQL/updates/" + str(file) + " 127.0.0.1 root root paradise_gamedb\n") + scriptLines.append(f"python3 SQL/updates/{file} 127.0.0.1 root root paradise_gamedb\n") else: print("ERROR: CI failed due to invalid python file in SQL/updates") exit(1) else: - inFile = open("SQL/updates/" + file, "r") + inFile = open(f"SQL/updates/{file}", "r") fileLines = inFile.readlines() inFile.close() # Add in a line which tells it to use the paradise DB fileLines.insert(0, "USE `paradise_gamedb`;\n") # Write new files to be used by the testing script - outFile = open("tools/ci/sql_tmp/" + file, "w+") + outFile = open(f"tools/ci/sql_tmp/{file}", "w+") outFile.writelines(fileLines) outFile.close() # Add a line to the script being made that tells it to use this SQL file - scriptLines.append("mysql -u root -proot < tools/ci/sql_tmp/" + str(file) + "\n") + scriptLines.append(f"mysql -h 127.0.0.1 -u root -proot < tools/ci/sql_tmp/{file}\n") -scriptLines.append("mysql -u root -proot -e 'DROP DATABASE paradise_gamedb;'\n") -scriptLines.append("mysql -u root -proot < SQL/paradise_schema.sql\n") +scriptLines.append("mysql -h 127.0.0.1 -u root -proot -e 'DROP DATABASE paradise_gamedb;'\n") +scriptLines.append("mysql -h 127.0.0.1 -u root -proot < SQL/paradise_schema.sql\n") outputScript = open("tools/ci/validate_sql.sh", "w+") outputScript.writelines(scriptLines)