Files
Bubberstation/code/modules/admin/admin_ranks.dm
SkyratBot 4433c8491d [MIRROR] Turns the "Admin Permission Elevation Notification" into a macro [MDB IGNORE] (#25474)
* Turns the "Admin Permission Elevation Notification" into a macro (#80149)

## About The Pull Request
Turns this boilerplate message into a centralized macro because this
code is important, but not so important that every coder know the nitty
gritty details of it. In case someone wants to add more logging/handling
(or update the message), there's now a central macro to update for all
nine events in which we check.

This shouldn't break anything because it's the exact same thing we were
doing previously and the early return checks are still all there, but it
does work on my machine regardless ✔️

![image](https://github.com/tgstation/tgstation/assets/34697715/db4405d3-e57d-4c29-8a01-b32ba185041b)

* Turns the "Admin Permission Elevation Notification" into a macro

---------

Co-authored-by: san7890 <the@san7890.com>
2023-12-06 21:59:06 -05:00

305 lines
9.8 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
var/name = "NoRank"
var/rights = R_DEFAULT
var/exclude_rights = NONE
var/include_rights = NONE
var/can_edit_rights = NONE
/datum/admin_rank/New(init_name, 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
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
// 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
/proc/sync_ranks_with_db()
set waitfor = FALSE
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin rank DB Sync blocked: Advanced ProcCall detected.</span>", confidential = TRUE)
return
var/list/sql_ranks = list()
for(var/datum/admin_rank/R 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)
//load our rank - > rights associations
/proc/load_admin_ranks(dbfail, no_update)
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Reload blocked: Advanced ProcCall detected.</span>", 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/R = new(admin_ranks_regex.group[1])
if(!R)
continue
var/count = 1
for(var/i in admin_ranks_regex.group - admin_ranks_regex.group[1])
if(i)
R.process_keyword(i, count, previous_rank)
count++
GLOB.admin_ranks += R
GLOB.protected_ranks += R
previous_rank = R
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 = 1
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)
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/R = new(rank_name, rank_flags, rank_exclude_flags, rank_can_edit_flags)
if(!R)
continue
GLOB.admin_ranks += R
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/R = new("[J]", json["ranks"]["[J]"]["include rights"], json["ranks"]["[J]"]["exclude rights"], json["ranks"]["[J]"]["can edit rights"])
if(!R)
continue
GLOB.admin_ranks += R
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, "+")
/proc/load_admins(no_update)
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 = 1
//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)
//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/J in backup_file_json["admins"])
var/skip
for(var/A in GLOB.admin_datums + GLOB.deadmins)
if(A == "[J]") //this admin was already loaded from txt override
skip = TRUE
if(skip)
continue
new /datum/admins(ranks_from_rank_name(backup_file_json["admins"]["[J]"]), ckey("[J]"))
#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