From 0204332c8d58c5ce09eb37eda1e342308e8aa57f Mon Sep 17 00:00:00 2001 From: Mothblocks <35135081+Mothblocks@users.noreply.github.com> Date: Tue, 3 May 2022 21:59:22 -0700 Subject: [PATCH] Granular admin perms (#66368) * Granular admin perms * Implement temporary user specific permissions menu * Restore config * Fix TESTING --- code/__HELPERS/roundend.dm | 4 +- code/modules/admin/admin_ranks.dm | 63 +++-- code/modules/admin/admin_verbs.dm | 4 +- code/modules/admin/holder2.dm | 46 +++- code/modules/admin/permissionedit.dm | 282 +++++++++++----------- code/modules/admin/sql_ban_system.dm | 2 +- code/modules/admin/verbs/admingame.dm | 2 +- code/modules/admin/verbs/deadsay.dm | 2 +- code/modules/admin/verbs/secrets.dm | 2 +- code/modules/cargo/centcom_podlauncher.dm | 2 +- code/modules/client/client_procs.dm | 21 +- code/modules/client/verbs/who.dm | 4 +- code/modules/mob/dead/new_player/poll.dm | 2 +- config/admins.txt | 2 +- 14 files changed, 239 insertions(+), 199 deletions(-) diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 6006e718227..87801cf418a 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -716,7 +716,7 @@ var/list/sql_admins = list() for(var/i in GLOB.protected_admins) var/datum/admins/A = GLOB.protected_admins[i] - sql_admins += list(list("ckey" = A.target, "rank" = A.rank.name)) + sql_admins += list(list("ckey" = A.target, "rank" = A.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")] p INNER JOIN [format_table_name("admin")] a ON p.ckey = a.ckey SET p.lastadminrank = a.rank") query_admin_rank_update.Execute() @@ -743,7 +743,7 @@ if (!admin) continue - file_data["admins"][admin_ckey] = admin.rank.name + file_data["admins"][admin_ckey] = admin.rank_names() if (admin.owner) file_data["connections"][admin_ckey] = list( diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm index fd531c64065..1f21960eaf9 100644 --- a/code/modules/admin/admin_ranks.dm +++ b/code/modules/admin/admin_ranks.dm @@ -7,9 +7,9 @@ GLOBAL_PROTECT(protected_ranks) /datum/admin_rank var/name = "NoRank" var/rights = R_DEFAULT - var/exclude_rights = 0 - var/include_rights = 0 - var/can_edit_rights = 0 + 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()) @@ -199,6 +199,33 @@ GLOBAL_PROTECT(protected_ranks) 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()) @@ -224,8 +251,12 @@ GLOBAL_PROTECT(protected_ranks) //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)) - new /datum/admins(rank_names[admins_regex.group[2]], ckey(admins_regex.group[1]), FALSE, TRUE) + 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` FROM [format_table_name("admin")] ORDER BY `rank`") if(!query_load_admins.Execute()) @@ -243,7 +274,7 @@ GLOBAL_PROTECT(protected_ranks) if(GLOB.admin_datums[admin_ckey] || GLOB.deadmins[admin_ckey]) skip = 1 if(!skip) - new /datum/admins(rank_names[admin_rank], admin_ckey) + new /datum/admins(ranks_from_rank_name(admin_rank), admin_ckey) qdel(query_load_admins) //load admins from backup file if(dbfail) @@ -263,30 +294,12 @@ GLOBAL_PROTECT(protected_ranks) skip = TRUE if(skip) continue - new /datum/admins(rank_names[backup_file_json["admins"]["[J]"]], ckey("[J]")) + 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.name]\n" + msg += "\t[ckey] - [D.rank_names()]\n" testing(msg) #endif return dbfail - -#ifdef TESTING -/client/verb/changerank(newrank in GLOB.admin_ranks) - if(holder) - holder.rank = newrank - else - holder = new /datum/admins(newrank, ckey) - remove_admin_verbs() - holder.associate(src) - -/client/verb/changerights(newrights as num) - if(holder) - holder.rank.rights = newrights - else - holder = new /datum/admins("testing", newrights, ckey) - remove_admin_verbs() - holder.associate(src) -#endif diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 3c7d27283ac..5e03c964032 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -275,7 +275,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) if(holder) control_freak = CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS - var/rights = holder.rank.rights + var/rights = holder.rank_flags() add_verb(src, GLOB.admin_verbs_default) if(rights & R_BUILD) add_verb(src, /client/proc/togglebuildmodeself) @@ -729,7 +729,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) /client/proc/togglebuildmodeself() set name = "Toggle Build Mode Self" set category = "Admin.Events" - if (!(holder.rank.rights & R_BUILD)) + if (!(holder.rank_flags() & R_BUILD)) return if(src.mob) togglebuildmode(src.mob) diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index 3390477f976..850452716a3 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -10,7 +10,7 @@ GLOBAL_PROTECT(href_token) #define RESULT_2FA_ID 2 /datum/admins - var/datum/admin_rank/rank + var/list/datum/admin_rank/ranks var/target var/name = "nobody's admin datum (no rank)" //Makes for better runtimes @@ -43,7 +43,7 @@ GLOBAL_PROTECT(href_token) /// A lazylist of tagged datums, for quick reference with the View Tags verb var/list/tagged_datums -/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected) +/datum/admins/New(list/datum/admin_rank/ranks, ckey, force_active = FALSE, protected) if(IsAdminAdvancedProcCall()) var/msg = " has tried to elevate permissions!" message_admins("[key_name_admin(usr)][msg]") @@ -55,21 +55,21 @@ GLOBAL_PROTECT(href_token) if(!ckey) QDEL_IN(src, 0) CRASH("Admin datum created without a ckey") - if(!istype(R)) + if(!istype(ranks)) QDEL_IN(src, 0) - CRASH("Admin datum created without a rank") + CRASH("Admin datum created with invalid ranks: [ranks] ([json_encode(ranks)])") target = ckey - name = "[ckey]'s admin datum ([R])" - rank = R + name = "[ckey]'s admin datum ([join_admin_ranks(ranks)])" + src.ranks = ranks admin_signature = "Nanotrasen Officer #[rand(0,9)][rand(0,9)][rand(0,9)]" href_token = GenerateToken() if(!CONFIG_GET(flag/forbid_admin_profiling)) - if(R.rights & R_DEBUG) //grant profile access, assuming admin profile access is enabled + if(rank_flags() & R_DEBUG) //grant profile access, assuming admin profile access is enabled world.SetConfig("APP/admin", ckey, "role=admin") //only admins with +ADMIN start admined if(protected) GLOB.protected_admins[target] = src - if (force_active || (R.rights & R_AUTOADMIN)) + if (force_active || (rank_flags() & R_AUTOADMIN)) activate() else deactivate() @@ -166,7 +166,7 @@ GLOBAL_PROTECT(href_token) owner = null /datum/admins/proc/check_for_rights(rights_required) - if(rights_required && !(rights_required & rank.rights)) + if(rights_required && !(rights_required & rank_flags())) return FALSE return TRUE @@ -174,12 +174,12 @@ GLOBAL_PROTECT(href_token) /datum/admins/proc/check_if_greater_rights_than_holder(datum/admins/other) if(!other) return TRUE //they have no rights - if(rank.rights == R_EVERYTHING) + if(rank_flags() == R_EVERYTHING) return TRUE //we have all the rights if(src == other) return TRUE //you always have more rights than yourself - if(rank.rights != other.rank.rights) - if( (rank.rights & other.rank.rights) == other.rank.rights ) + if(rank_flags() != other.rank_flags()) + if( (rank_flags() & other.rank_flags()) == other.rank_flags() ) return TRUE //we have all the rights they have and more return FALSE @@ -331,6 +331,28 @@ GLOBAL_PROTECT(href_token) confidential = TRUE, ) +/// Get the rank name of the admin +/datum/admins/proc/rank_names() + return join_admin_ranks(ranks) + +/// Get the rank flags of the admin +/datum/admins/proc/rank_flags() + var/combined_flags = NONE + + for (var/datum/admin_rank/rank as anything in ranks) + combined_flags |= rank.rights + + return combined_flags + +/// Get the permissions this admin is allowed to edit on other ranks +/datum/admins/proc/can_edit_rights_flags() + var/combined_flags = NONE + + for (var/datum/admin_rank/rank as anything in ranks) + combined_flags |= rank.can_edit_rights + + return combined_flags + /datum/admins/vv_edit_var(var_name, var_value) return FALSE //nice try trialmin diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm index d9ffcb3eaf3..5544f90fb74 100644 --- a/code/modules/admin/permissionedit.dm +++ b/code/modules/admin/permissionedit.dm @@ -101,9 +101,7 @@