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>
This commit is contained in:
AffectedArc07
2026-08-30 13:01:21 +00:00
committed by GitHub
parent edee60476d
commit ce34e64c21
14 changed files with 82 additions and 149 deletions
+12 -1
View File
@@ -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 }}'
+12 -8
View File
@@ -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 */;
+14
View File
@@ -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;
+2
View File
@@ -0,0 +1,2 @@
#define DATABASE_TASK_MARK_BANNED "MARK_BANNED"
#define DATABASE_TASK_MARK_UNBANNED "MARK_UNBANNED"
+1 -1
View File
@@ -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)
@@ -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"])
+19 -11
View File
@@ -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
@@ -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,
))
-4
View File
@@ -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("<big><b>You have been banned by [usr.client.ckey].\nReason: [reason].</b></big>"))
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
+3 -83
View File
@@ -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, "<B>You have no verified forum account. <a href='byond://?src=[UID()];link_forum_account=true'>VERIFY FORUM ACCOUNT</a></B>")
/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: <a href="[url]">[url]</a>"})
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: <a href="[url]">[url]</a>"})
src << link(url)
return
to_chat(src, "<b>You have not linked your BYOND account to your Paradise account. <a href='[GLOB.configuration.url.wiki_url]/Guide_to_account_linkage'>Click here for more information.</a></b>")
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
+1 -6
View File
@@ -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
+4 -12
View File
@@ -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"
+1
View File
@@ -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"
+13 -13
View File
@@ -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)