mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-06-26 16:44:47 +01:00
c7faae3626
## About The Pull Request See title. I looked pretty hard and only found these as having this issue. ## Why It's Good For The Game I'm mostly working off of other peoples wisdom here but `rank` is a reserved word in sql, and as we do in MOST of our other querys. backticking it prevents that from being an issue (and therefore fixes some sql errors that CAN happen depending on version (my issues happened with mysql, but apparently dont occur with heididb)) ## Changelog ~~N/A as i dont acctually think this happens in TG rn unless yalls housekeeping tab on the admin panel is broken as well.~~ 🆑 fix: A handful of permissions panel querys now work again /🆑
437 lines
15 KiB
Plaintext
437 lines
15 KiB
Plaintext
GLOBAL_LIST_EMPTY(admin_ranks) //list of all admin_rank datums
|
|
GLOBAL_PROTECT(admin_ranks)
|
|
|
|
GLOBAL_LIST_EMPTY(protected_ranks) //admin ranks loaded from txt
|
|
GLOBAL_PROTECT(protected_ranks)
|
|
|
|
/datum/admin_rank
|
|
/// Rank name, key'd to the db
|
|
var/name = "NoRank"
|
|
/// Rank source, see RANK_SOURCE_TXT and friends
|
|
var/source = null
|
|
/// Our functional rights, these are what we actually use in game
|
|
var/rights = R_DEFAULT
|
|
/// Rights we're allowed to use, pre filtering
|
|
var/include_rights = NONE
|
|
/// These are the rights of include_rights we aren't allowed to use. Frankly I have no idea why this exists
|
|
var/exclude_rights = NONE
|
|
/// Rights we're allowed to edit on other folks, impact of this is dependent on R_PERMISSIONS and R_DBRANKS
|
|
var/can_edit_rights = NONE
|
|
|
|
/datum/admin_rank/New(init_name, init_source, init_rights, init_exclude_rights, init_edit_rights)
|
|
if(IsAdminAdvancedProcCall())
|
|
alert_to_permissions_elevation_attempt(usr)
|
|
if (name == "NoRank") //only del if this is a true creation (and not just a New() proc call), other wise trialmins/coders could abuse this to deadmin other admins
|
|
QDEL_IN(src, 0)
|
|
CRASH("Admin proc call creation of admin datum")
|
|
return
|
|
name = init_name
|
|
source = init_source
|
|
if(!source)
|
|
qdel(src)
|
|
CRASH("Admin rank created without a source.")
|
|
if(!name)
|
|
qdel(src)
|
|
CRASH("Admin rank created without name.")
|
|
if(init_rights)
|
|
rights = init_rights
|
|
include_rights = rights
|
|
if(init_exclude_rights)
|
|
exclude_rights = init_exclude_rights
|
|
rights &= ~exclude_rights
|
|
if(init_edit_rights)
|
|
can_edit_rights = init_edit_rights
|
|
|
|
/datum/admin_rank/Destroy()
|
|
if(IsAdminAdvancedProcCall())
|
|
alert_to_permissions_elevation_attempt(usr)
|
|
return QDEL_HINT_LETMELIVE
|
|
. = ..()
|
|
|
|
/datum/admin_rank/vv_edit_var(var_name, var_value)
|
|
return FALSE
|
|
|
|
/datum/admin_rank/allow_mark_datum()
|
|
alert_to_permissions_elevation_attempt(usr)
|
|
return FALSE
|
|
|
|
/datum/admin_rank/can_vv_mark()
|
|
alert_to_permissions_elevation_attempt(usr)
|
|
return FALSE
|
|
|
|
// Adds/removes rights to this admin_rank
|
|
/datum/admin_rank/proc/process_keyword(group, group_count, datum/admin_rank/previous_rank)
|
|
if(IsAdminAdvancedProcCall())
|
|
alert_to_permissions_elevation_attempt(usr)
|
|
return
|
|
var/list/keywords = splittext(group, " ")
|
|
var/flag = 0
|
|
for(var/k in keywords)
|
|
switch(k)
|
|
if("BUILD")
|
|
flag = R_BUILD
|
|
if("ADMIN")
|
|
flag = R_ADMIN
|
|
if("BAN")
|
|
flag = R_BAN
|
|
if("FUN")
|
|
flag = R_FUN
|
|
if("SERVER")
|
|
flag = R_SERVER
|
|
if("DEBUG")
|
|
flag = R_DEBUG
|
|
if("PERMISSIONS")
|
|
flag = R_PERMISSIONS
|
|
if("POSSESS")
|
|
flag = R_POSSESS
|
|
if("STEALTH")
|
|
flag = R_STEALTH
|
|
if("POLL")
|
|
flag = R_POLL
|
|
if("VAREDIT")
|
|
flag = R_VAREDIT
|
|
if("EVERYTHING")
|
|
flag = R_EVERYTHING
|
|
if("SOUND")
|
|
flag = R_SOUND
|
|
if("SPAWN")
|
|
flag = R_SPAWN
|
|
if("AUTOADMIN")
|
|
flag = R_AUTOADMIN
|
|
if("DBRANKS")
|
|
flag = R_DBRANKS
|
|
if("@")
|
|
if(previous_rank)
|
|
switch(group_count)
|
|
if(1)
|
|
flag = previous_rank.include_rights
|
|
if(2)
|
|
flag = previous_rank.exclude_rights
|
|
if(3)
|
|
flag = previous_rank.can_edit_rights
|
|
else
|
|
continue
|
|
switch(group_count)
|
|
if(1)
|
|
rights |= flag
|
|
include_rights |= flag
|
|
if(2)
|
|
rights &= ~flag
|
|
exclude_rights |= flag
|
|
if(3)
|
|
can_edit_rights |= flag
|
|
|
|
/datum/admin_rank/proc/pretty_print_source()
|
|
switch(source)
|
|
if(RANK_SOURCE_LOCAL)
|
|
return "Localhost"
|
|
if(RANK_SOURCE_TXT)
|
|
return "admin_ranks.txt"
|
|
if(RANK_SOURCE_DB)
|
|
return "Database"
|
|
if(RANK_SOURCE_BACKUP)
|
|
return "Backup JSON"
|
|
if(RANK_SOURCE_TEMPORARY)
|
|
return "Temporary"
|
|
|
|
/// Loads admin ranks.
|
|
/// Return a list containing the backup data if they were loaded from the database backup json
|
|
/proc/load_admin_ranks(dbfail, no_update)
|
|
if(IsAdminAdvancedProcCall())
|
|
to_chat(usr, span_adminprefix("Admin Reload blocked: Advanced ProcCall detected."), confidential = TRUE)
|
|
return
|
|
GLOB.admin_ranks.Cut()
|
|
GLOB.protected_ranks.Cut()
|
|
//load text from file and process each entry
|
|
var/ranks_text = file2text("[global.config.directory]/admin_ranks.txt")
|
|
var/datum/admin_rank/previous_rank
|
|
var/regex/admin_ranks_regex = new(@"^Name\s*=\s*(.+?)\s*\n+Include\s*=\s*([\l @]*?)\s*\n+Exclude\s*=\s*([\l @]*?)\s*\n+Edit\s*=\s*([\l @]*?)\s*\n*$", "gm")
|
|
while(admin_ranks_regex.Find(ranks_text))
|
|
var/datum/admin_rank/txt_rank = new(admin_ranks_regex.group[1], RANK_SOURCE_TXT)
|
|
if(QDELETED(txt_rank))
|
|
continue
|
|
var/count = 1
|
|
for(var/i in admin_ranks_regex.group - admin_ranks_regex.group[1])
|
|
if(i)
|
|
txt_rank.process_keyword(i, count, previous_rank)
|
|
count++
|
|
GLOB.admin_ranks += txt_rank
|
|
GLOB.protected_ranks += txt_rank
|
|
previous_rank = txt_rank
|
|
if(!CONFIG_GET(flag/admin_legacy_system) && !dbfail)
|
|
if(CONFIG_GET(flag/load_legacy_ranks_only))
|
|
if(!no_update)
|
|
sync_ranks_with_db()
|
|
else
|
|
var/datum/db_query/query_load_admin_ranks = SSdbcore.NewQuery("SELECT `rank`, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")]")
|
|
if(!query_load_admin_ranks.Execute())
|
|
message_admins("Error loading admin ranks from database. Loading from backup.")
|
|
log_sql("Error loading admin ranks from database. Loading from backup.")
|
|
dbfail = TRUE
|
|
else
|
|
while(query_load_admin_ranks.NextRow())
|
|
var/skip
|
|
var/rank_name = query_load_admin_ranks.item[1]
|
|
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
|
if(R.name == rank_name) //this rank was already loaded from txt override
|
|
skip = 1
|
|
break
|
|
if(skip)
|
|
continue
|
|
var/rank_flags = text2num(query_load_admin_ranks.item[2])
|
|
var/rank_exclude_flags = text2num(query_load_admin_ranks.item[3])
|
|
var/rank_can_edit_flags = text2num(query_load_admin_ranks.item[4])
|
|
var/datum/admin_rank/db_rank = new(rank_name, RANK_SOURCE_DB, rank_flags, rank_exclude_flags, rank_can_edit_flags)
|
|
if(QDELETED(db_rank))
|
|
continue
|
|
GLOB.admin_ranks += db_rank
|
|
qdel(query_load_admin_ranks)
|
|
//load ranks from backup file
|
|
if(dbfail)
|
|
var/backup_file = file2text("data/admins_backup.json")
|
|
if(backup_file == null)
|
|
log_world("Unable to locate admins backup file.")
|
|
return FALSE
|
|
var/list/json = json_decode(backup_file)
|
|
for(var/J in json["ranks"])
|
|
var/skip
|
|
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
|
if(R.name == "[J]") //this rank was already loaded from txt override
|
|
skip = TRUE
|
|
if(skip)
|
|
continue
|
|
var/datum/admin_rank/json_rank = new("[J]", RANK_SOURCE_BACKUP, json["ranks"]["[J]"]["include rights"], json["ranks"]["[J]"]["exclude rights"], json["ranks"]["[J]"]["can edit rights"])
|
|
if(QDELETED(json_rank))
|
|
continue
|
|
GLOB.admin_ranks += json_rank
|
|
return json
|
|
#ifdef TESTING
|
|
var/msg = "Permission Sets Built:\n"
|
|
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
|
msg += "\t[R.name]"
|
|
var/rights = rights2text(R.rights,"\n\t\t")
|
|
if(rights)
|
|
msg += "\t\t[rights]\n"
|
|
testing(msg)
|
|
#endif
|
|
|
|
/// Converts a rank name (such as "Coder+Moth") into a list of /datum/admin_rank
|
|
/proc/ranks_from_rank_name(rank_name)
|
|
var/list/rank_names = splittext(rank_name, "+")
|
|
var/list/ranks = list()
|
|
|
|
for (var/datum/admin_rank/rank as anything in GLOB.admin_ranks)
|
|
if (rank.name in rank_names)
|
|
rank_names -= rank.name
|
|
ranks += rank
|
|
|
|
if (rank_names.len == 0)
|
|
break
|
|
|
|
if (rank_names.len > 0)
|
|
log_config("Admin rank names were invalid: [jointext(ranks, ", ")]")
|
|
|
|
return ranks
|
|
|
|
/// Takes a list of rank names and joins them with +
|
|
/proc/join_admin_ranks(list/datum/admin_rank/ranks)
|
|
var/list/names = list()
|
|
|
|
for (var/datum/admin_rank/rank as anything in ranks)
|
|
names += rank.name
|
|
|
|
return jointext(names, "+")
|
|
|
|
/// (Re)Loads the admin list.
|
|
/// returns TRUE if database admins had to be loaded from the backup json
|
|
/proc/load_admins(no_update, initial = FALSE)
|
|
if(!initial)
|
|
if(!global.config.PreConfigReload())
|
|
return
|
|
|
|
var/dbfail
|
|
if(!CONFIG_GET(flag/admin_legacy_system) && !SSdbcore.Connect())
|
|
message_admins("Failed to connect to database while loading admins. Loading from backup.")
|
|
log_sql("Failed to connect to database while loading admins. Loading from backup.")
|
|
dbfail = TRUE
|
|
//clear the datums references
|
|
GLOB.admin_datums.Cut()
|
|
for(var/client/C in GLOB.admins)
|
|
C.remove_admin_verbs()
|
|
C.holder = null
|
|
GLOB.admins.Cut()
|
|
GLOB.protected_admins.Cut()
|
|
GLOB.deadmins.Cut()
|
|
var/list/backup_file_json = load_admin_ranks(dbfail, no_update)
|
|
dbfail = backup_file_json != null
|
|
//Clear profile access
|
|
for(var/A in world.GetConfig("admin"))
|
|
world.SetConfig("APP/admin", A, null)
|
|
var/list/rank_names = list()
|
|
for(var/datum/admin_rank/R in GLOB.admin_ranks)
|
|
rank_names[R.name] = R
|
|
//ckeys listed in admins.txt are always made admins before sql loading is attempted
|
|
var/admins_text = file2text("[global.config.directory]/admins.txt")
|
|
var/regex/admins_regex = new(@"^(?!#)(.+?)\s+=\s+(.+)", "gm")
|
|
|
|
while(admins_regex.Find(admins_text))
|
|
var/admin_key = admins_regex.group[1]
|
|
var/admin_rank = admins_regex.group[2]
|
|
new /datum/admins(ranks_from_rank_name(admin_rank), ckey(admin_key), force_active = FALSE, protected = TRUE)
|
|
|
|
if(!CONFIG_GET(flag/admin_legacy_system) && !dbfail)
|
|
var/datum/db_query/query_load_admins = SSdbcore.NewQuery("SELECT ckey, `rank`, feedback FROM [format_table_name("admin")] ORDER BY `rank`")
|
|
if(!query_load_admins.Execute())
|
|
message_admins("Error loading admins from database. Loading from backup.")
|
|
log_sql("Error loading admins from database. Loading from backup.")
|
|
dbfail = 1
|
|
else
|
|
while(query_load_admins.NextRow())
|
|
var/admin_ckey = ckey(query_load_admins.item[1])
|
|
var/admin_rank = query_load_admins.item[2]
|
|
var/admin_feedback = query_load_admins.item[3]
|
|
var/skip
|
|
|
|
var/list/admin_ranks = ranks_from_rank_name(admin_rank)
|
|
|
|
if(admin_ranks.len == 0)
|
|
message_admins("[admin_ckey] loaded with invalid admin rank [admin_rank].")
|
|
skip = 1
|
|
if(GLOB.admin_datums[admin_ckey] || GLOB.deadmins[admin_ckey])
|
|
skip = 1
|
|
if(!skip)
|
|
var/datum/admins/admin_holder = new(admin_ranks, admin_ckey)
|
|
admin_holder.cached_feedback_link = admin_feedback || NO_FEEDBACK_LINK
|
|
qdel(query_load_admins)
|
|
if (!no_update)
|
|
save_admin_backup()
|
|
sync_admins_with_db()
|
|
//load admins from backup file
|
|
if(dbfail)
|
|
if(!backup_file_json)
|
|
if(backup_file_json != null)
|
|
//already tried
|
|
return
|
|
var/backup_file = file2text("data/admins_backup.json")
|
|
if(backup_file == null)
|
|
log_world("Unable to locate admins backup file.")
|
|
return
|
|
backup_file_json = json_decode(backup_file)
|
|
for(var/backup_admin_ckey in backup_file_json["admins"])
|
|
var/skip
|
|
for(var/admin_ckey in GLOB.admin_datums + GLOB.deadmins)
|
|
if(ckey(admin_ckey) == ckey("[backup_admin_ckey]")) //this admin was already loaded from txt override
|
|
skip = TRUE
|
|
break
|
|
if(skip)
|
|
continue
|
|
new /datum/admins(ranks_from_rank_name(backup_file_json["admins"]["[backup_admin_ckey]"]), ckey("[backup_admin_ckey]"))
|
|
#ifdef TESTING
|
|
var/msg = "Admins Built:\n"
|
|
for(var/ckey in GLOB.admin_datums)
|
|
var/datum/admins/D = GLOB.admin_datums[ckey]
|
|
msg += "\t[ckey] - [D.rank_names()]\n"
|
|
testing(msg)
|
|
#endif
|
|
return dbfail
|
|
|
|
|
|
/proc/sync_ranks_with_db()
|
|
set waitfor = FALSE
|
|
|
|
if(IsAdminAdvancedProcCall())
|
|
to_chat(usr, span_adminprefix("Admin rank DB Sync blocked: Advanced ProcCall detected."), confidential = TRUE)
|
|
return
|
|
|
|
var/list/sql_ranks = list()
|
|
for(var/datum/admin_rank/R as anything in GLOB.protected_ranks)
|
|
sql_ranks += list(list("rank" = R.name, "flags" = R.include_rights, "exclude_flags" = R.exclude_rights, "can_edit_flags" = R.can_edit_rights))
|
|
SSdbcore.MassInsert(format_table_name("admin_ranks"), sql_ranks, duplicate_key = TRUE)
|
|
update_everything_flag_in_db()
|
|
|
|
|
|
/proc/update_everything_flag_in_db()
|
|
for(var/datum/admin_rank/R as anything in GLOB.admin_ranks)
|
|
var/list/flags = list()
|
|
if(R.include_rights == R_EVERYTHING)
|
|
flags += "flags"
|
|
if(R.exclude_rights == R_EVERYTHING)
|
|
flags += "exclude_flags"
|
|
if(R.can_edit_rights == R_EVERYTHING)
|
|
flags += "can_edit_flags"
|
|
if(!flags.len)
|
|
continue
|
|
var/flags_to_check = flags.Join(" != [R_EVERYTHING] AND ") + " != [R_EVERYTHING]"
|
|
var/datum/db_query/query_check_everything_ranks = SSdbcore.NewQuery(
|
|
"SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE `rank` = :rank AND ([flags_to_check])",
|
|
list("rank" = R.name)
|
|
)
|
|
if(!query_check_everything_ranks.Execute())
|
|
qdel(query_check_everything_ranks)
|
|
return
|
|
if(query_check_everything_ranks.NextRow()) //no row is returned if the rank already has the correct flag value
|
|
var/flags_to_update = flags.Join(" = [R_EVERYTHING], ") + " = [R_EVERYTHING]"
|
|
var/datum/db_query/query_update_everything_ranks = SSdbcore.NewQuery(
|
|
"UPDATE [format_table_name("admin_ranks")] SET [flags_to_update] WHERE `rank` = :rank",
|
|
list("rank" = R.name)
|
|
)
|
|
if(!query_update_everything_ranks.Execute())
|
|
qdel(query_update_everything_ranks)
|
|
return
|
|
qdel(query_update_everything_ranks)
|
|
qdel(query_check_everything_ranks)
|
|
|
|
|
|
/proc/sync_admins_with_db()
|
|
if(IsAdminAdvancedProcCall())
|
|
to_chat(usr, span_adminprefix("Admin rank DB Sync blocked: Advanced ProcCall detected."))
|
|
return
|
|
|
|
if(CONFIG_GET(flag/admin_legacy_system) || !SSdbcore.IsConnected()) //we're already using legacy system so there's nothing to save
|
|
return
|
|
sync_ranks_with_db()
|
|
var/list/sql_admins = list()
|
|
for(var/holder_ckey in GLOB.protected_admins)
|
|
var/datum/admins/holder = GLOB.protected_admins[holder_ckey]
|
|
sql_admins += list(list("ckey" = holder.target, "rank" = holder.rank_names()))
|
|
SSdbcore.MassInsert(format_table_name("admin"), sql_admins, duplicate_key = TRUE)
|
|
var/datum/db_query/query_admin_rank_update = SSdbcore.NewQuery("UPDATE [format_table_name("player")] AS p INNER JOIN [format_table_name("admin")] AS a ON p.ckey = a.ckey SET p.lastadminrank = a.rank")
|
|
query_admin_rank_update.Execute()
|
|
qdel(query_admin_rank_update)
|
|
|
|
|
|
/proc/save_admin_backup()
|
|
if(IsAdminAdvancedProcCall())
|
|
to_chat(usr, span_adminprefix("Admin rank DB Sync blocked: Advanced ProcCall detected."))
|
|
return
|
|
|
|
if(CONFIG_GET(flag/admin_legacy_system)) //we're already using legacy system so there's nothing to save
|
|
return
|
|
|
|
//json format backup file generation stored per server
|
|
var/json_file = file("data/admins_backup.json")
|
|
var/list/file_data = list(
|
|
"ranks" = list(),
|
|
"admins" = list()
|
|
)
|
|
for(var/datum/admin_rank/R as anything in GLOB.admin_ranks)
|
|
file_data["ranks"]["[R.name]"] = list()
|
|
file_data["ranks"]["[R.name]"]["include rights"] = R.include_rights
|
|
file_data["ranks"]["[R.name]"]["exclude rights"] = R.exclude_rights
|
|
file_data["ranks"]["[R.name]"]["can edit rights"] = R.can_edit_rights
|
|
|
|
for(var/admin_ckey in GLOB.admin_datums + GLOB.deadmins)
|
|
var/datum/admins/admin = GLOB.admin_datums[admin_ckey]
|
|
|
|
if(!admin)
|
|
admin = GLOB.deadmins[admin_ckey]
|
|
if (!admin)
|
|
continue
|
|
|
|
file_data["admins"][admin_ckey] = admin.rank_names()
|
|
|
|
admin.backup_connections()
|
|
|
|
fdel(json_file)
|
|
WRITE_FILE(json_file, json_encode(file_data, JSON_PRETTY_PRINT))
|