diff --git a/code/__DEFINES/admin.dm b/code/__DEFINES/admin.dm index 2a3f8f4e6dc2..d1a5fb7fb417 100644 --- a/code/__DEFINES/admin.dm +++ b/code/__DEFINES/admin.dm @@ -37,8 +37,8 @@ #define R_SOUNDS (1<<11) #define R_SPAWN (1<<12) #define R_AUTOLOGIN (1<<13) -#define R_DBRANKS (1<<14) -#define R_DEV (1<<15) // Stuff NOONE should be touching except for head-dev/maints, I guess council too.. +#define R_DEV (1<<14) // Stuff NOONE should be touching except for head-dev/maints, I guess council too.. +#define R_PERSIST_PERMS (1<<15) // Allow modification of persistent perms #define R_EVERYTHING (1<<16)-1 //the sum of all other rank permissions, used for +EVERYTHING #define R_DEFAULT R_AUTOLOGIN diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 107f442a2ba6..c3fa8f6ad0ca 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -294,7 +294,7 @@ GLOBAL_LIST_INIT(pda_styles, list(MONO, VT, ORBITRON, SHARE)) #define debug_usr(msg) if (GLOB.Debug2&&usr) to_chat(usr, \ type = MESSAGE_TYPE_DEBUG, \ text = "DEBUG: [msg]") -#define debug_admins(msg) if (GLOB.Debug2) to_chat(GLOB.admins, \ +#define debug_admins(msg) if (GLOB.Debug2) to_chat(GLOB.permissions.admins, \ type = MESSAGE_TYPE_DEBUG, \ text = "DEBUG: [msg]") #define debug_world_log(msg) if (GLOB.Debug2) log_world("DEBUG: [msg]") diff --git a/code/__HELPERS/roundend.dm b/code/__HELPERS/roundend.dm index 92f9452b4c35..a9111b40369c 100644 --- a/code/__HELPERS/roundend.dm +++ b/code/__HELPERS/roundend.dm @@ -622,76 +622,6 @@ count++ return objective_parts.Join("
") -/datum/controller/subsystem/ticker/proc/save_admin_data() - if(IsAdminAdvancedProcCall()) - to_chat(usr, "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 - else if(load_admins(TRUE)) //returns true if there was a database failure and the backup was loaded from - return - sync_ranks_with_db() - 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)) - SSdbcore.MassInsert(format_table_name("admin"), sql_admins, duplicate_key = TRUE) - var/datum/DBQuery/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() - qdel(query_admin_rank_update) - - //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 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/i in GLOB.admin_datums+GLOB.deadmins) - var/datum/admins/A = GLOB.admin_datums[i] - if(!A) - A = GLOB.deadmins[i] - if (!A) - continue - file_data["admins"]["[i]"] = list() - file_data["admins"]["[i]"]["rank"] = A.rank.name - file_data["admins"]["[i]"]["ip_cache"] = A.ip_cache - file_data["admins"]["[i]"]["cid_cache"] = A.cid_cache - fdel(json_file) - WRITE_FILE(json_file, json_encode(file_data)) - -/datum/controller/subsystem/ticker/proc/update_everything_flag_in_db() - for(var/datum/admin_rank/R 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/DBQuery/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/DBQuery/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) - /datum/controller/subsystem/ticker/proc/cargoking() var/datum/achievement/cargoking/CK = SSachievements.get_achievement(/datum/achievement/cargoking) var/cargoking = FALSE diff --git a/code/__HELPERS/type2type.dm b/code/__HELPERS/type2type.dm index f7153ea82f5a..407002edc4ce 100644 --- a/code/__HELPERS/type2type.dm +++ b/code/__HELPERS/type2type.dm @@ -195,38 +195,38 @@ //Converts a rights bitfield into a string /proc/rights2text(rights, seperator="", prefix = "+") seperator += prefix - if(rights & R_BUILDMODE) - . += "[seperator]BUILDMODE" if(rights & R_ADMIN) . += "[seperator]ADMIN" + if(rights & R_AUTOLOGIN) + . += "[seperator]AUTOLOGIN" if(rights & R_BAN) . += "[seperator]BAN" - if(rights & R_FUN) - . += "[seperator]FUN" - if(rights & R_SERVER) - . += "[seperator]SERVER" + if(rights & R_BUILDMODE) + . += "[seperator]BUILDMODE" if(rights & R_DEBUG) . += "[seperator]DEBUG" - if(rights & R_POSSESS) - . += "[seperator]POSSESS" + if(rights & R_DEV) + . += "[seperator]DEV" + if(rights & R_FUN) + . += "[seperator]FUN" if(rights & R_PERMISSIONS) . += "[seperator]PERMISSIONS" - if(rights & R_STEALTH) - . += "[seperator]STEALTH" + if(rights & R_PERSIST_PERMS) + . += "[seperator]PERSISTPERMS" if(rights & R_POLL) . += "[seperator]POLL" - if(rights & R_VAREDIT) - . += "[seperator]VAREDIT" + if(rights & R_POSSESS) + . += "[seperator]POSSESS" + if(rights & R_SERVER) + . += "[seperator]SERVER" if(rights & R_SOUNDS) . += "[seperator]SOUND" if(rights & R_SPAWN) . += "[seperator]SPAWN" - if(rights & R_AUTOLOGIN) - . += "[seperator]AUTOLOGIN" - if(rights & R_DBRANKS) - . += "[seperator]DBRANKS" - if(rights & R_DEV) - . += "[seperator]DEV" + if(rights & R_STEALTH) + . += "[seperator]STEALTH" + if(rights & R_VAREDIT) + . += "[seperator]VAREDIT" if(!.) . = "NONE" return . diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 88b5379df2f1..6c19e0fdbf20 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -68,8 +68,8 @@ GLOBAL_LIST_INIT(bitfields, list( "SOUNDS" = R_SOUNDS, "SPAWN" = R_SPAWN, "AUTOLOGIN" = R_AUTOLOGIN, - "DBRANKS" = R_DBRANKS, - "DEV" = R_DEV + "DEV" = R_DEV, + "PERSISTPERMS" = R_PERSIST_PERMS ), "interaction_flags_atom" = list( "INTERACT_ATOM_REQUIRES_ANCHORED" = INTERACT_ATOM_REQUIRES_ANCHORED, diff --git a/code/_globalvars/lists/mobs.dm b/code/_globalvars/lists/mobs.dm index 08b8ce9b6cb8..5c9b24e4efdd 100644 --- a/code/_globalvars/lists/mobs.dm +++ b/code/_globalvars/lists/mobs.dm @@ -1,7 +1,4 @@ GLOBAL_LIST_EMPTY(clients) //all clients -GLOBAL_LIST_EMPTY(admins) //all clients whom are admins -GLOBAL_PROTECT(admins) -GLOBAL_LIST_EMPTY(deadmins) //all ckeys who have used the de-admin verb. GLOBAL_LIST_EMPTY(directory) //all ckeys with associated client GLOBAL_LIST_EMPTY(stealthminID) //reference list with IDs that store ckeys, for stealthmins diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 9f3c7fec2810..a6b5182cff8a 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -161,9 +161,6 @@ min_val = 0 //oranges warned us integer = FALSE -/datum/config_entry/flag/admin_legacy_system //Defines whether the server uses the legacy admin system with admins.txt or the SQL system - protection = CONFIG_ENTRY_LOCKED - /datum/config_entry/flag/protect_legacy_admins //Stops any admins loaded by the legacy system from having their rank edited by the permissions panel protection = CONFIG_ENTRY_LOCKED @@ -173,7 +170,7 @@ /datum/config_entry/flag/enable_localhost_rank //Gives the !localhost! rank to any client connecting from 127.0.0.1 or ::1 protection = CONFIG_ENTRY_LOCKED -/datum/config_entry/flag/load_legacy_ranks_only //Loads admin ranks only from legacy admin_ranks.txt, while enabled ranks are mirrored to the database +/datum/config_entry/string/permissions_backend // Sets the permissions backend to use protection = CONFIG_ENTRY_LOCKED /datum/config_entry/string/hostedby diff --git a/code/controllers/failsafe.dm b/code/controllers/failsafe.dm index 3f5a2bc15f3d..f33505f8a6ed 100644 --- a/code/controllers/failsafe.dm +++ b/code/controllers/failsafe.dm @@ -59,20 +59,20 @@ GLOBAL_REAL(Failsafe, /datum/controller/failsafe) message_admins(span_adminnotice("Notice: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks.")) --defcon if(2) - to_chat(GLOB.admins, span_boldannounce("Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.")) + to_chat(GLOB.permissions.admins, span_boldannounce("Warning: DEFCON [defcon_pretty()]. The Master Controller has not fired in the last [(5-defcon) * processing_interval] ticks. Automatic restart in [processing_interval] ticks.")) --defcon if(1) - to_chat(GLOB.admins, span_boldannounce("Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...")) + to_chat(GLOB.permissions.admins, span_boldannounce("Warning: DEFCON [defcon_pretty()]. The Master Controller has still not fired within the last [(5-defcon) * processing_interval] ticks. Killing and restarting...")) --defcon var/rtn = Recreate_MC() if(rtn > 0) defcon = 4 master_iteration = 0 - to_chat(GLOB.admins, span_adminnotice("MC restarted successfully")) + to_chat(GLOB.permissions.admins, span_adminnotice("MC restarted successfully")) else if(rtn < 0) log_game("FailSafe: Could not restart MC, runtime encountered. Entering defcon 0") - to_chat(GLOB.admins, span_boldannounce("ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.")) + to_chat(GLOB.permissions.admins, span_boldannounce("ERROR: DEFCON [defcon_pretty()]. Could not restart MC, runtime encountered. I will silently keep retrying.")) //if the return number was 0, it just means the mc was restarted too recently, and it just needs some time before we try again //no need to handle that specially when defcon 0 can handle it if(0) //DEFCON 0! (mc failed to restart) @@ -80,7 +80,7 @@ GLOBAL_REAL(Failsafe, /datum/controller/failsafe) if(rtn > 0) defcon = 4 master_iteration = 0 - to_chat(GLOB.admins, span_adminnotice("MC restarted successfully")) + to_chat(GLOB.permissions.admins, span_adminnotice("MC restarted successfully")) else defcon = min(defcon + 1,5) master_iteration = Master.iteration diff --git a/code/controllers/master.dm b/code/controllers/master.dm index dd2c7cf3f115..19bc6200eda9 100644 --- a/code/controllers/master.dm +++ b/code/controllers/master.dm @@ -152,7 +152,7 @@ GLOBAL_REAL(Master, /datum/controller/master) = new msg = "The [BadBoy.name] subsystem seems to be destabilizing the MC and will be offlined." BadBoy.flags |= SS_NO_FIRE if(msg) - to_chat(GLOB.admins, span_boldannounce("[msg]")) + to_chat(GLOB.permissions.admins, span_boldannounce("[msg]")) log_world(msg) if (istype(Master.subsystems)) diff --git a/code/controllers/subsystem.dm b/code/controllers/subsystem.dm index fa94554f5988..053e1946078d 100644 --- a/code/controllers/subsystem.dm +++ b/code/controllers/subsystem.dm @@ -172,7 +172,7 @@ SEND_SIGNAL(src, COMSIG_SUBSYSTEM_POST_INITIALIZE, start_timeofday) var/time = (REALTIMEOFDAY - start_timeofday)/10 var/msg = "Initialized [name] subsystem within [time] second[time == 1 ? "" : "s"]!" // Yogs -- quieter subsystem initialization - to_chat(GLOB.admins, + to_chat(GLOB.permissions.admins, type = MESSAGE_TYPE_DEBUG, html = span_notice(msg), confidential = FALSE) diff --git a/code/controllers/subsystem/air.dm b/code/controllers/subsystem/air.dm index 627f23c5db61..5407d6714d9b 100644 --- a/code/controllers/subsystem/air.dm +++ b/code/controllers/subsystem/air.dm @@ -395,7 +395,7 @@ SUBSYSTEM_DEF(air) //Yogs start -- prettier atmos notices var/msg = "HEY! LISTEN! [(world.timeofday - timer)/10] seconds were wasted processing [starting_ats] turf(s) (connected to [ending_ats] other turfs) with atmos differences at round start." - to_chat(GLOB.admins, + to_chat(GLOB.permissions.admins, type = MESSAGE_TYPE_DEBUG, html = span_notice(msg), confidential = FALSE) diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm index 32df6f5f0029..58a6f75b940d 100755 --- a/code/controllers/subsystem/ticker.dm +++ b/code/controllers/subsystem/ticker.dm @@ -738,5 +738,3 @@ SUBSYSTEM_DEF(ticker) /datum/controller/subsystem/ticker/Shutdown() gather_newscaster() //called here so we ensure the log is created even upon admin reboot - save_admin_data() - update_everything_flag_in_db() diff --git a/code/controllers/subsystem/vote.dm b/code/controllers/subsystem/vote.dm index a319169159b3..35ab0c4671eb 100644 --- a/code/controllers/subsystem/vote.dm +++ b/code/controllers/subsystem/vote.dm @@ -134,7 +134,7 @@ SUBSYSTEM_DEF(vote) SSmapping.map_voted = TRUE if(restart) var/active_admins = FALSE - for(var/client/C in GLOB.admins + GLOB.deadmins) + for(var/client/C in GLOB.permissions.admins + GLOB.permissions.deadmins) if(!C.is_afk() && check_rights_for(C, R_SERVER)) active_admins = TRUE break @@ -170,7 +170,7 @@ SUBSYSTEM_DEF(vote) return FALSE var/lower_admin = FALSE var/ckey = ckey(initiator_key) - if(GLOB.admin_datums[ckey]) + if(GLOB.permissions.admin_datums[ckey]) lower_admin = TRUE if(!mode) diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm index 82f9f113a6c4..28cf4f98522a 100644 --- a/code/game/gamemodes/game_mode.dm +++ b/code/game/gamemodes/game_mode.dm @@ -610,7 +610,7 @@ continue //Ghosted while alive - for (var/C in GLOB.admins) + for (var/C in GLOB.permissions.admins) to_chat(C, msg.Join()) log_admin(msg.Join()) diff --git a/code/game/objects/items/charter.dm b/code/game/objects/items/charter.dm index 40f5442cdec3..00c4fd2308f6 100644 --- a/code/game/objects/items/charter.dm +++ b/code/game/objects/items/charter.dm @@ -70,7 +70,7 @@ // Autoapproves after a certain time rename_callback = CALLBACK(src, .proc/rename_station, new_name, user.name, user.real_name, key_name(user)) response_timer_id = addtimer(rename_callback, approval_time, TIMER_STOPPABLE) - to_chat(GLOB.admins, + to_chat(GLOB.permissions.admins, span_adminnotice("CUSTOM STATION RENAME:[ADMIN_LOOKUPFLW(user)] proposes to rename the [name_type] to [new_name] (will auto-approve in [DisplayTimeText(approval_time)]).\ (ACCEPT or REJECT) [ADMIN_SMITE(user)] [ADMIN_CENTCOM_REPLY(user)]")) diff --git a/code/game/world.dm b/code/game/world.dm index 2461de4df184..1563b988d653 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -36,7 +36,9 @@ GLOBAL_VAR(restart_counter) config.Load(params[OVERRIDE_CONFIG_DIRECTORY_PARAMETER]) - load_admins() + init_permissions() + + GLOB.permissions.start() //SetupLogs depends on the RoundID, so lets check //DB schema and set RoundID if we can diff --git a/code/modules/admin/IsBanned.dm b/code/modules/admin/IsBanned.dm index b4d6338d3235..6a0add29dadc 100644 --- a/code/modules/admin/IsBanned.dm +++ b/code/modules/admin/IsBanned.dm @@ -34,7 +34,7 @@ //magic voodo to check for a key in a list while also adding that key to the list without having to do two associated lookups var/message = !checkedckeys[ckey]++ - if(GLOB.admin_datums[ckey] || GLOB.deadmins[ckey]) + if(GLOB.permissions.admin_datums[ckey] || GLOB.permissions.deadmins[ckey]) admin = TRUE var/client/C = GLOB.directory[ckey] diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index bcb43b841bc7..0d8dab63a605 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -2,14 +2,14 @@ //////////////////////////////// /proc/message_admins(msg) msg = span_admin("ADMIN LOG: [msg]") - to_chat(GLOB.admins, + to_chat(GLOB.permissions.admins, type = MESSAGE_TYPE_ADMINLOG, html = msg, confidential = TRUE) /proc/relay_msg_admins(msg) msg = span_admin("RELAY: [msg]") - to_chat(GLOB.admins, + to_chat(GLOB.permissions.admins, type = MESSAGE_TYPE_ADMINLOG, html = msg, confidential = TRUE) @@ -42,7 +42,7 @@ body += "Options panel for [M]" if(M.client) body += " played by [M.client] " - body += "\[[M.client.holder ? M.client.holder.rank : "Player"]\]" + body += "\[[M.client.holder ? M.client.holder.rank_name() : "Player"]\]" if(CONFIG_GET(flag/use_exp_tracking)) body += "\[" + M.client.get_exp_living() + " | " body += " Toggle Exempt\]" diff --git a/code/modules/admin/admin_ranks.dm b/code/modules/admin/admin_ranks.dm deleted file mode 100644 index 3489c03c5372..000000000000 --- a/code/modules/admin/admin_ranks.dm +++ /dev/null @@ -1,305 +0,0 @@ -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 = 0 - var/include_rights = 0 - var/can_edit_rights = 0 - -/datum/admin_rank/New(init_name, init_rights, init_exclude_rights, init_edit_rights) - if(IsAdminAdvancedProcCall()) - var/msg = " has tried to elevate permissions!" - message_admins("[key_name_admin(usr)][msg]") - log_admin("[key_name(usr)][msg]") - 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()) - var/msg = " has tried to elevate permissions!" - message_admins("[key_name_admin(usr)][msg]") - log_admin("[key_name(usr)][msg]") - return QDEL_HINT_LETMELIVE - . = ..() - -/datum/admin_rank/vv_edit_var(var_name, var_value) - return FALSE - -/proc/admin_keyword_to_flag(word, previous_rights=0) - var/flag = 0 - switch(ckey(word)) - if("buildmode","build") - flag = R_BUILDMODE - 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","rights") - 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","host","all") - flag = R_EVERYTHING - if("sound","sounds") - flag = R_SOUNDS - if("spawn","create") - flag = R_SPAWN - if("autologin", "autoadmin") - flag = R_AUTOLOGIN - if("dbranks") - flag = R_DBRANKS - if("dev") - flag = R_DEV - if("@","prev") - flag = previous_rights - return flag - -// Adds/removes rights to this admin_rank -/datum/admin_rank/proc/process_keyword(word, previous_rights=0) - if(IsAdminAdvancedProcCall()) - var/msg = " has tried to elevate permissions!" - message_admins("[key_name_admin(usr)][msg]") - log_admin("[key_name(usr)][msg]") - return - var/flag = admin_keyword_to_flag(word, previous_rights) - if(flag) - switch(text2ascii(word,1)) - if(43) - rights |= flag //+ - include_rights |= flag - if(45) - rights &= ~flag //- - exclude_rights |= flag - if(42) - can_edit_rights |= flag //* - -// Checks for (keyword-formatted) rights on this admin -/datum/admins/proc/check_keyword(word) - var/flag = admin_keyword_to_flag(word) - if(flag) - return ((rank.rights & flag) == flag) //true only if right has everything in flag - -/proc/sync_ranks_with_db() - set waitfor = FALSE - - if(IsAdminAdvancedProcCall()) - to_chat(usr, "Admin rank DB Sync blocked: Advanced ProcCall detected.", 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, "Admin Reload blocked: Advanced ProcCall detected.", confidential=TRUE) - return - GLOB.admin_ranks.Cut() - GLOB.protected_ranks.Cut() - var/previous_rights = 0 - //load text from file and process each line separately - for(var/line in world.file2list("[global.config.directory]/admin_ranks.txt")) - if(!line || findtextEx(line,"#",1,2) || line == " ") //YOGS - added our DB support - continue - var/next = findtext(line, "=") - var/datum/admin_rank/R = new(trim(copytext(line, 1, next))) - if(!R) - continue - GLOB.admin_ranks += R - GLOB.protected_ranks += R - var/prev = findchar(line, "+-*", next, 0) - while(prev) - next = findchar(line, "+-*", prev + 1, 0) - R.process_keyword(copytext(line, prev, next), previous_rights) - prev = next - previous_rights = R.rights - 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/DBQuery/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 = trim(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 - -/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/list/lines = world.file2list("[global.config.directory]/admins.txt") - for(var/line in lines) - if(!length(line) || findtextEx(line, "#", 1, 2) || line == " ") //yogs - added our DB support - continue - var/list/entry = splittext(line, "=") - if(entry.len < 2) - continue - var/ckey = ckey(entry[1]) - var/rank = trim(entry[2]) - if(!ckey || !rank) - continue - new /datum/admins(rank_names[rank], ckey, 0, 1) - if(!CONFIG_GET(flag/admin_legacy_system) || dbfail) - var/datum/DBQuery/query_load_admins = SSdbcore.NewQuery("SELECT ckey, rank 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 = trim(query_load_admins.item[2]) - var/skip - if(rank_names[admin_rank] == null) - 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) - new /datum/admins(rank_names[admin_rank], admin_ckey) - 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 - var/datum/admins/A = new /datum/admins(trim(rank_names[backup_file_json["admins"]["[J]"]["rank"]]), ckey("[J]")) - A.ip_cache = backup_file_json["admins"]["[J]"]["ip_cache"] - A.cid_cache = backup_file_json["admins"]["[J]"]["cid_cache"] - #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" - 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 28435a297b61..581060cdc82b 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -262,7 +262,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) if(holder) control_freak = CONTROL_FREAK_SKIN | CONTROL_FREAK_MACROS - var/rights = holder.rank.rights + var/rights = GLOB.permissions.get_rights_for(src) add_verb(src, GLOB.admin_verbs_default) add_verb(src, GLOB.mentor_verbs) // yogs - give admins mentor verbs if(rights & R_BUILDMODE) @@ -650,7 +650,7 @@ GLOBAL_PROTECT(admin_verbs_hideable) /client/proc/togglebuildmodeself() set name = "Toggle Build Mode Self" set category = "Admin.Round Interaction" - if (!(holder.rank.rights & R_BUILDMODE)) + if (!(check_rights(R_BUILDMODE))) return if(src.mob) togglebuildmode(src.mob) @@ -703,10 +703,10 @@ GLOBAL_PROTECT(admin_verbs_hideable) set category = "Admin" set desc = "Regain your admin powers." - var/datum/admins/A = GLOB.deadmins[ckey] + var/datum/admins/A = GLOB.permissions.deadmins[ckey] if(!A) - A = GLOB.admin_datums[ckey] + A = GLOB.permissions.admin_datums[ckey] if (!A) var/msg = " is trying to readmin but they have no deadmin entry" message_admins("[key_name_admin(src)][msg]") diff --git a/code/modules/admin/chat_commands.dm b/code/modules/admin/chat_commands.dm index ecb94b75c5fb..411d0c0472ba 100644 --- a/code/modules/admin/chat_commands.dm +++ b/code/modules/admin/chat_commands.dm @@ -116,8 +116,7 @@ GLOBAL_LIST(round_end_notifiees) /datum/tgs_chat_command/reload_admins/proc/ReloadAsync() set waitfor = FALSE - refresh_admin_files() //yogs - DB support - load_admins() + GLOB.permissions.start() /datum/tgs_chat_command/reload_mentors name = "reload_mentors" @@ -131,4 +130,4 @@ GLOBAL_LIST(round_end_notifiees) /datum/tgs_chat_command/reload_mentors/proc/ReloadAsync() set waitfor = FALSE - load_mentors() \ No newline at end of file + load_mentors() diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index a439f6112048..5510156c793b 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -1,13 +1,7 @@ -GLOBAL_LIST_EMPTY(admin_datums) -GLOBAL_PROTECT(admin_datums) -GLOBAL_LIST_EMPTY(protected_admins) -GLOBAL_PROTECT(protected_admins) - GLOBAL_VAR_INIT(href_token, GenerateToken()) GLOBAL_PROTECT(href_token) /datum/admins - var/datum/admin_rank/rank var/target var/name = "nobody's admin datum (no rank)" //Makes for better runtimes @@ -33,7 +27,7 @@ GLOBAL_PROTECT(href_token) var/cid_cache -/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE, protected) +/datum/admins/New(ckey, rights, force_active = FALSE) if(IsAdminAdvancedProcCall()) var/msg = " has tried to elevate permissions!" message_admins("[key_name_admin(usr)][msg]") @@ -45,20 +39,13 @@ GLOBAL_PROTECT(href_token) if(!ckey) QDEL_IN(src, 0) CRASH("Admin datum created without a ckey") - if(!istype(R)) - QDEL_IN(src, 0) - CRASH("Admin datum created without a rank") target = ckey - name = "[ckey]'s admin datum ([R])" - rank = R + name = "[ckey]'s admin datum" admin_signature = "Nanotrasen Officer #[rand(0,9)][rand(0,9)][rand(0,9)]" href_token = GenerateToken() - if(R.rights & R_DEBUG) //grant profile access + if(rights & R_DEBUG) //grant profile access 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_AUTOLOGIN)) + if (force_active || (rights & R_AUTOLOGIN)) activate() else deactivate() @@ -69,6 +56,12 @@ GLOBAL_PROTECT(href_token) message_admins("[key_name_admin(usr)][msg]") log_admin("[key_name(usr)][msg]") return QDEL_HINT_LETMELIVE + var/client/C = owner + deactivate() + if(GLOB.permissions.deadmins[target] == src) + GLOB.permissions.deadmins -= target + if(C) + remove_verb(C, /client/proc/readmin) . = ..() /datum/admins/proc/activate() @@ -77,8 +70,8 @@ GLOBAL_PROTECT(href_token) message_admins("[key_name_admin(usr)][msg]") log_admin("[key_name(usr)][msg]") return - GLOB.deadmins -= target - GLOB.admin_datums[target] = src + GLOB.permissions.deadmins -= target + GLOB.permissions.admin_datums[target] = src deadmined = FALSE if (GLOB.directory[target]) associate(GLOB.directory[target]) //find the client for a ckey if they are connected and associate them with us @@ -90,8 +83,8 @@ GLOBAL_PROTECT(href_token) message_admins("[key_name_admin(usr)][msg]") log_admin("[key_name(usr)][msg]") return - GLOB.deadmins[target] = src - GLOB.admin_datums -= target + GLOB.permissions.deadmins[target] = src + GLOB.permissions.admin_datums -= target deadmined = TRUE var/client/C if ((C = owner) || (C = GLOB.directory[target])) @@ -126,7 +119,7 @@ GLOBAL_PROTECT(href_token) owner.add_admin_verbs() //TODO <--- todo what? the proc clearly exists and works since its the backbone to our entire admin system remove_verb(owner, /client/proc/readmin) owner.init_verbs() //re-initialize the verb list - GLOB.admins |= C + GLOB.permissions.admins |= C return TRUE /datum/admins/proc/disassociate() @@ -136,71 +129,20 @@ GLOBAL_PROTECT(href_token) log_admin("[key_name(usr)][msg]") return if(owner) - GLOB.admins -= owner + GLOB.permissions.admins -= owner owner.remove_admin_verbs() owner.init_verbs() owner.holder = null owner = null -/datum/admins/proc/check_for_rights(rights_required) - if(rights_required && !(rights_required & rank.rights)) - return 0 - return 1 - - -/datum/admins/proc/check_if_greater_rights_than_holder(datum/admins/other) - if(!other) - return 1 //they have no rights - if(rank.rights == R_EVERYTHING) - return 1 //we have all the rights - if(src == other) - return 1 //you always have more rights than yourself - if(rank.rights != other.rank.rights) - if( (rank.rights & other.rank.rights) == other.rank.rights ) - return 1 //we have all the rights they have and more - return 0 +/datum/admins/proc/rank_name() + if(owner) + return GLOB.permissions.get_rank_name(owner) + return "Unknown" /datum/admins/vv_edit_var(var_name, var_value) return FALSE //nice try trialmin -/* -checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags) -if rights_required == 0, then it simply checks if they are an admin. -if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed -generally it would be used like so: - -/proc/admin_proc() - if(!check_rights(R_ADMIN)) - return - to_chat(world, "you have enough rights!") - -NOTE: it checks usr! not src! So if you're checking somebody's rank in a proc which they did not call -you will have to do something like if(client.rights & R_ADMIN) yourself. -*/ -/proc/check_rights(rights_required, show_msg=1) - if(usr && usr.client) - if (check_rights_for(usr.client, rights_required)) - return 1 - else - if(show_msg) - to_chat(usr, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].", confidential=TRUE) - return 0 - -//probably a bit iffy - will hopefully figure out a better solution -/proc/check_if_greater_rights_than(client/other) - if(usr && usr.client) - if(usr.client.holder) - if(!other || !other.holder) - return 1 - return usr.client.holder.check_if_greater_rights_than_holder(other.holder) - return 0 - -//This proc checks whether subject has at least ONE of the rights specified in rights_required. -/proc/check_rights_for(client/subject, rights_required) - if(subject && subject.holder) - return subject.holder.check_for_rights(rights_required) - return 0 - /proc/GenerateToken() . = "" for(var/I in 1 to 32) diff --git a/code/modules/admin/mfa.dm b/code/modules/admin/mfa.dm index af1b9213feda..f0b48baf0ab4 100644 --- a/code/modules/admin/mfa.dm +++ b/code/modules/admin/mfa.dm @@ -21,7 +21,7 @@ /client/proc/mfa_check_cache() CHECK_MFA_ENABLED - var/datum/admins/tmp_holder = GLOB.admin_datums[ckey] || GLOB.deadmins[ckey] + var/datum/admins/tmp_holder = GLOB.permissions.admin_datums[ckey] || GLOB.permissions.deadmins[ckey] if(tmp_holder && tmp_holder.cid_cache == computer_id && tmp_holder.ip_cache == address) return TRUE @@ -282,7 +282,7 @@ qdel(mfa_addverify) - var/datum/admins/tmp_holder = GLOB.admin_datums[ckey] || GLOB.deadmins[ckey] + var/datum/admins/tmp_holder = GLOB.permissions.admin_datums[ckey] || GLOB.permissions.deadmins[ckey] if(tmp_holder) // These values are cached even if the user says not to remember the session, but are only used if the DB is down during admin loading tmp_holder.cid_cache = computer_id diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm index ffb5933b13a2..2d1e395b79b0 100644 --- a/code/modules/admin/permissionedit.dm +++ b/code/modules/admin/permissionedit.dm @@ -4,296 +4,251 @@ set desc = "Edit admin permissions" if(!check_rights(R_PERMISSIONS)) return - usr.client.holder.edit_admin_permissions() + new /datum/permissions_panel(usr) -/datum/admins/proc/edit_admin_permissions(action, target, operation, page) - if(!check_rights(R_PERMISSIONS)) - return - var/datum/asset/asset_cache_datum = get_asset_datum(/datum/asset/group/permissions) - asset_cache_datum.send(usr) - var/list/output = list("\[Permissions\]") - if(action) - output += " | \[Log\] | \[Management\]
" - else - output += "
\[Log\]
\[Management\]" - if(action == 1) - var/logcount = 0 - var/logssperpage = 20 - var/pagecount = 0 - page = text2num(page) - var/datum/DBQuery/query_count_admin_logs = SSdbcore.NewQuery( - "SELECT COUNT(id) FROM [format_table_name("admin_log")] WHERE (:target IS NULL OR adminckey = :target) AND (:operation IS NULL OR operation = :operation)", - list("target" = target, "operation" = operation) - ) - if(!query_count_admin_logs.warn_execute()) - qdel(query_count_admin_logs) - return - if(query_count_admin_logs.NextRow()) - logcount = text2num(query_count_admin_logs.item[1]) - qdel(query_count_admin_logs) - if(logcount > logssperpage) - output += "
Page: " - while(logcount > 0) - output += "|[pagecount == page ? "\[[pagecount]\]" : "\[[pagecount]\]"]" - logcount -= logssperpage - pagecount++ - output += "|" - var/datum/DBQuery/query_search_admin_logs = SSdbcore.NewQuery({" - SELECT - datetime, - round_id, - IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), - operation, - IF(ckey IS NULL, target, byond_key), - log - FROM [format_table_name("admin_log")] - LEFT JOIN [format_table_name("player")] ON target = ckey - WHERE (:target IS NULL OR ckey = :target) AND (:operation IS NULL OR operation = :operation) - ORDER BY datetime DESC - LIMIT :skip, :take - "}, list("target" = target, "operation" = operation, "skip" = logssperpage * page, "take" = logssperpage)) - if(!query_search_admin_logs.warn_execute()) - qdel(query_search_admin_logs) - return - while(query_search_admin_logs.NextRow()) - var/datetime = query_search_admin_logs.item[1] - var/round_id = query_search_admin_logs.item[2] - var/admin_key = query_search_admin_logs.item[3] - operation = query_search_admin_logs.item[4] - target = query_search_admin_logs.item[5] - var/log = query_search_admin_logs.item[6] - output += "

[datetime] | Round ID [round_id] | Admin [admin_key] | Operation [operation] on [target]
[log]


" - qdel(query_search_admin_logs) - if(action == 2) - output += "

Admin ckeys with invalid ranks

" - var/datum/DBQuery/query_check_admin_errors = SSdbcore.NewQuery("SELECT (SELECT byond_key FROM [format_table_name("player")] WHERE [format_table_name("player")].ckey = [format_table_name("admin")].ckey), [format_table_name("admin")].rank FROM [format_table_name("admin")] LEFT JOIN [format_table_name("admin_ranks")] ON [format_table_name("admin_ranks")].rank = [format_table_name("admin")].rank WHERE [format_table_name("admin_ranks")].rank IS NULL") - if(!query_check_admin_errors.warn_execute()) - qdel(query_check_admin_errors) - return - while(query_check_admin_errors.NextRow()) - var/admin_key = query_check_admin_errors.item[1] - var/admin_rank = query_check_admin_errors.item[2] - output += "[admin_key] has non-existent rank [admin_rank] | \[Change Rank\] | \[Remove\]" - output += "
" - qdel(query_check_admin_errors) - output += "

Unused ranks

" - var/datum/DBQuery/query_check_unused_rank = SSdbcore.NewQuery("SELECT [format_table_name("admin_ranks")].rank, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] LEFT JOIN [format_table_name("admin")] ON [format_table_name("admin")].rank = [format_table_name("admin_ranks")].rank WHERE [format_table_name("admin")].rank IS NULL") - if(!query_check_unused_rank.warn_execute()) - qdel(query_check_unused_rank) - return - while(query_check_unused_rank.NextRow()) - var/admin_rank = query_check_unused_rank.item[1] - output += {"Rank [admin_rank] is not held by any admin | \[Remove\] -
Permissions: [rights2text(text2num(query_check_unused_rank.item[2])," ")] -
Denied: [rights2text(text2num(query_check_unused_rank.item[3])," ", "-")] -
Allowed to edit: [rights2text(text2num(query_check_unused_rank.item[4])," ", "*")] -
"} - qdel(query_check_unused_rank) - else if(!action) - output += {" - - Permissions Panel - - - -
- - - - - - - - "} - for(var/adm_ckey in GLOB.admin_datums+GLOB.deadmins) - var/datum/admins/D = GLOB.admin_datums[adm_ckey] - if(!D) - D = GLOB.deadmins[adm_ckey] - if (!D) - continue - var/deadminlink = "" - if(D.owner) - adm_ckey = D.owner.key - if (D.deadmined) - deadminlink = " \[RA\]" - else - deadminlink = " \[DA\]" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "" - output += "
CKEY \[+\]RANKPERMISSIONSDENIEDALLOWED TO EDIT
[adm_ckey]
[deadminlink]\[-\]\[2FA\]
\[SYNC TGDB\]
[D.rank.name][rights2text(D.rank.include_rights," ")][rights2text(D.rank.exclude_rights," ", "-")][rights2text(D.rank.can_edit_rights," ", "*")]
Search:
" - if(QDELETED(usr)) - return - usr << browse("[jointext(output, "")]","window=editrights;size=1000x650") +/datum/permissions_panel/New(mob/user) + ui_interact(user) -/datum/admins/proc/edit_rights_topic(list/href_list) - if(!check_rights(R_PERMISSIONS)) - message_admins("[key_name_admin(usr)] attempted to edit admin permissions without sufficient rights.") - log_admin("[key_name(usr)] attempted to edit admin permissions without sufficient rights.") +/datum/permissions_panel/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + // Open UI + ui = new(user, src, "PermissionsPanel") + ui.open() + +/datum/permissions_panel/ui_data(mob/user) + . = GLOB.permissions.pp_data(user) + +/datum/permissions_panel/ui_state(mob/user) + return GLOB.permissions_state + +/datum/permissions_panel/ui_act(action, list/params) + if(..()) return - if(IsAdminAdvancedProcCall()) - to_chat(usr, "Admin Edit blocked: Advanced ProcCall detected.", confidential=TRUE) - return - if(!owner.mfa_query()) - return - var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/namespaced/common) - permissions_assets.send(src) - var/admin_key = href_list["key"] - var/admin_ckey = ckey(admin_key) - var/datum/admins/D = GLOB.admin_datums[admin_ckey] - var/use_db - var/task = href_list["editrights"] - var/skip - var/legacy_only - if(task == "activate" || task == "deactivate" || task == "sync" || task == "mfareset") - skip = TRUE - if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_admins) && task == "rank") - if(admin_ckey in GLOB.protected_admins) - to_chat(usr, "Editing the rank of this admin is blocked by server configuration.", confidential=TRUE) - return - if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && task == "permissions") - if(D.rank in GLOB.protected_ranks) - to_chat(usr, "Editing the flags of this rank is blocked by server configuration.", confidential=TRUE) - return - if(CONFIG_GET(flag/load_legacy_ranks_only) && (task == "add" || task == "rank" || task == "permissions")) - to_chat(usr, "Database rank loading is disabled, only temporary changes can be made to a rank's permissions and permanently creating a new rank is blocked.", confidential=TRUE) - legacy_only = TRUE - if(check_rights(R_DBRANKS, FALSE)) - if(!skip) - if(!SSdbcore.Connect()) - to_chat(usr, span_danger("Unable to connect to database, changes are temporary only."), confidential=TRUE) - use_db = FALSE - else - use_db = alert("Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", "Permanent", "Temporary", "Cancel") - if(use_db == "Cancel") - return - if(use_db == "Permanent") - use_db = TRUE - else - use_db = FALSE - if(QDELETED(usr)) + switch(action) + if("addNewAdmin") + GLOB.permissions.add_admin() + return TRUE + if("forceSwapAdmin") + var/ckey = params["ckey"] + var/adminned_datum = GLOB.permissions.admin_datums[ckey] + var/deadminned_datum = GLOB.permissions.deadmins[ckey] + if(adminned_datum) + force_deadmin(ckey, adminned_datum) + if(deadminned_datum) + force_readmin(ckey, deadminned_datum) + return TRUE + if("resetMFA") + var/admin_ckey = params["ckey"] + if(alert("WARNING! This will reset the 2FA code and backup for [admin_ckey], possibly comprimising the security of the server. Are you sure you wish to continue?", "Confirmation", "Cancel", "Continue") != "Continue") return - if(task != "add") - D = GLOB.admin_datums[admin_ckey] - if(!D) - D = GLOB.deadmins[admin_ckey] - if(!D) - return - if((task != "sync") && !check_if_greater_rights_than_holder(D)) - message_admins("[key_name_admin(usr)] attempted to change the rank of [admin_key] without sufficient rights.") - log_admin("[key_name(usr)] attempted to change the rank of [admin_key] without sufficient rights.") - return - switch(task) - if("add") - admin_ckey = add_admin(admin_ckey, admin_key, use_db) - if(!admin_ckey) - return - change_admin_rank(admin_ckey, admin_key, use_db, null, legacy_only) - if("remove") - remove_admin(admin_ckey, admin_key, use_db, D) - if("rank") - change_admin_rank(admin_ckey, admin_key, use_db, D, legacy_only) - if("permissions") - change_admin_flags(admin_ckey, admin_key, use_db, D, legacy_only) - if("activate") - force_readmin(admin_key, D) - if("deactivate") - force_deadmin(admin_key, D) - if("sync") - sync_lastadminrank(admin_ckey, admin_key, D) - if("mfareset") - if(alert("WARNING! This will reset the 2FA code and backup for [admin_key], possibly comprimising the security of the server. Are you sure you wish to continue?", "Confirmation", "Cancel", "Continue") != "Continue") - return - if(alert("If you have been requested to reset the MFA credentials for someone, please confirm that you have verified their identity. Resetting MFA for an unverified person can result in a break of server security.", "Confirmation", "I Understand", "Cancel") != "I Understand") + if(alert("If you have been requested to reset the MFA credentials for someone, please confirm that you have verified their identity. Resetting MFA for an unverified person can result in a breach of server security.", "Confirmation", "I Understand", "Cancel") != "I Understand") return message_admins("MFA for [admin_ckey] has been reset by [usr]!") log_admin("MFA Reset for [admin_ckey] by [usr]!") mfa_reset(admin_ckey) - edit_admin_permissions() + return TRUE + if("editRank") + GLOB.permissions.edit_rank(params["ckey"]) + return TRUE + if("editPerms") + GLOB.permissions.edit_perms(params["ckey"]) + return TRUE + if("removeAdmin") + GLOB.permissions.remove_admin(params["ckey"]) + return TRUE + -/datum/admins/proc/add_admin(admin_ckey, admin_key, use_db) - if(admin_ckey) - . = admin_ckey - else - admin_key = input("New admin's key","Admin key") as text|null - . = ckey(admin_key) - if(!.) - return FALSE - if(!admin_ckey && (. in GLOB.admin_datums+GLOB.deadmins)) - to_chat(usr, span_danger("[admin_key] is already an admin."), confidential=TRUE) - return FALSE - if(use_db) - //if an admin exists without a datum they won't be caught by the above - var/datum/DBQuery/query_admin_in_db = SSdbcore.NewQuery( - "SELECT 1 FROM [format_table_name("admin")] WHERE ckey = :ckey", - list("ckey" = .) - ) - if(!query_admin_in_db.warn_execute()) - qdel(query_admin_in_db) - return FALSE - if(query_admin_in_db.NextRow()) - qdel(query_admin_in_db) - to_chat(usr, span_danger("[admin_key] already listed in admin database. Check the Management tab if they don't appear in the list of admins."), confidential=TRUE) - return FALSE - qdel(query_admin_in_db) - var/datum/DBQuery/query_add_admin = SSdbcore.NewQuery( - "INSERT INTO [format_table_name("admin")] (ckey, `rank`) VALUES (:ckey, 'NEW ADMIN')", - list("ckey" = .) - ) - if(!query_add_admin.warn_execute()) - qdel(query_add_admin) - return FALSE - qdel(query_add_admin) - var/datum/DBQuery/query_add_admin_log = SSdbcore.NewQuery({" - INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) - VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'add admin', :target, CONCAT('New admin added: ', :target)) - "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = .)) - if(!query_add_admin_log.warn_execute()) - qdel(query_add_admin_log) - return FALSE - qdel(query_add_admin_log) +// /datum/admins/proc/edit_admin_permissions(action, target, operation, page) +// if(!check_rights(R_PERMISSIONS)) +// return +// var/datum/asset/asset_cache_datum = get_asset_datum(/datum/asset/group/permissions) +// asset_cache_datum.send(usr) +// var/list/output = list("\[Permissions\]") +// if(action) +// output += " | \[Log\]
" +// else +// output += "
\[Log\]" +// if(action == 1) +// var/logcount = 0 +// var/logssperpage = 20 +// var/pagecount = 0 +// page = text2num(page) +// var/datum/DBQuery/query_count_admin_logs = SSdbcore.NewQuery( +// "SELECT COUNT(id) FROM [format_table_name("admin_log")] WHERE (:target IS NULL OR adminckey = :target) AND (:operation IS NULL OR operation = :operation)", +// list("target" = target, "operation" = operation) +// ) +// if(!query_count_admin_logs.warn_execute()) +// qdel(query_count_admin_logs) +// return +// if(query_count_admin_logs.NextRow()) +// logcount = text2num(query_count_admin_logs.item[1]) +// qdel(query_count_admin_logs) +// if(logcount > logssperpage) +// output += "
Page: " +// while(logcount > 0) +// output += "|[pagecount == page ? "\[[pagecount]\]" : "\[[pagecount]\]"]" +// logcount -= logssperpage +// pagecount++ +// output += "|" +// var/datum/DBQuery/query_search_admin_logs = SSdbcore.NewQuery({" +// SELECT +// datetime, +// round_id, +// IFNULL((SELECT byond_key FROM [format_table_name("player")] WHERE ckey = adminckey), adminckey), +// operation, +// IF(ckey IS NULL, target, byond_key), +// log +// FROM [format_table_name("admin_log")] +// LEFT JOIN [format_table_name("player")] ON target = ckey +// WHERE (:target IS NULL OR ckey = :target) AND (:operation IS NULL OR operation = :operation) +// ORDER BY datetime DESC +// LIMIT :skip, :take +// "}, list("target" = target, "operation" = operation, "skip" = logssperpage * page, "take" = logssperpage)) +// if(!query_search_admin_logs.warn_execute()) +// qdel(query_search_admin_logs) +// return +// while(query_search_admin_logs.NextRow()) +// var/datetime = query_search_admin_logs.item[1] +// var/round_id = query_search_admin_logs.item[2] +// var/admin_key = query_search_admin_logs.item[3] +// operation = query_search_admin_logs.item[4] +// target = query_search_admin_logs.item[5] +// var/log = query_search_admin_logs.item[6] +// output += "

[datetime] | Round ID [round_id] | Admin [admin_key] | Operation [operation] on [target]
[log]


" +// qdel(query_search_admin_logs) +// else if(!action) +// output += {" +// +// Permissions Panel +// +// +// +//
+// +// +// +// +// +// "} +// for(var/adm_ckey in GLOB.permissions.admin_datums+GLOB.permissions.deadmins) +// var/datum/admins/D = GLOB.permissions.admin_datums[adm_ckey] +// if(!D) +// D = GLOB.permissions.deadmins[adm_ckey] +// if (!D) +// continue +// var/deadminlink = "" +// if(D.owner) +// adm_ckey = D.owner.key +// if (D.deadmined) +// deadminlink = " \[RA\]" +// else +// deadminlink = " \[DA\]" +// output += "" +// output += "" +// output += "" +// output += "" +// output += "" +// output += "
CKEY \[+\]RANKPERMISSIONS
[adm_ckey]
[deadminlink]\[-\]\[2FA\]
[D.rank_name()][rights2text(D.rights," ")]
Search:
" +// if(QDELETED(usr)) +// return +// usr << browse("[jointext(output, "")]","window=editrights;size=1000x650") -/datum/admins/proc/remove_admin(admin_ckey, admin_key, use_db, datum/admins/D) - if(alert("Are you sure you want to remove [admin_ckey]?","Confirm Removal","Do it","Cancel") == "Do it") - GLOB.admin_datums -= admin_ckey - GLOB.deadmins -= admin_ckey - if(D) - D.disassociate() - var/m1 = "[key_name_admin(usr)] removed [admin_key] from the admins list [use_db ? "permanently" : "temporarily"]" - var/m2 = "[key_name(usr)] removed [admin_key] from the admins list [use_db ? "permanently" : "temporarily"]" - if(use_db) - var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery( - "DELETE FROM [format_table_name("admin")] WHERE ckey = :ckey", - list("ckey" = admin_ckey) - ) - if(!query_add_rank.warn_execute()) - qdel(query_add_rank) - return - qdel(query_add_rank) - var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery({" - INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) - VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'remove admin', :admin_ckey, CONCAT('Admin removed: ', :admin_ckey)) - "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "admin_ckey" = admin_ckey)) - if(!query_add_rank_log.warn_execute()) - qdel(query_add_rank_log) - return - qdel(query_add_rank_log) - sync_lastadminrank(admin_ckey, admin_key) - message_admins(m1) - log_admin(m2) +// /datum/admins/proc/edit_rights_topic(list/href_list) +// if(!check_rights(R_PERMISSIONS)) +// message_admins("[key_name_admin(usr)] attempted to edit admin permissions without sufficient rights.") +// log_admin("[key_name(usr)] attempted to edit admin permissions without sufficient rights.") +// return +// if(IsAdminAdvancedProcCall()) +// to_chat(usr, "Admin Edit blocked: Advanced ProcCall detected.", confidential=TRUE) +// return +// if(!owner.mfa_query()) +// return +// var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/namespaced/common) +// permissions_assets.send(owner) +// var/admin_key = href_list["key"] +// var/admin_ckey = ckey(admin_key) +// var/datum/admins/D = GLOB.permissions.admin_datums[admin_ckey] +// var/task = href_list["editrights"] +// if(CONFIG_GET(flag/protect_legacy_admins) && task == "rank") +// if(admin_ckey in GLOB.protected_admins) +// to_chat(usr, "Editing the rank of this admin is blocked by server configuration.", confidential=TRUE) +// return +// if(task != "add") +// D = GLOB.permissions.admin_datums[admin_ckey] +// if(!D) +// D = GLOB.permissions.deadmins[admin_ckey] +// if(!D) +// return +// switch(task) +// if("add") +// admin_key = input("New admin's key","Admin key") as text|null +// admin_ckey = ckey(admin_key) +// if(admin_ckey in GLOB.permissions.admin_datums+GLOB.permissions.deadmins) +// to_chat(usr, span_danger("[admin_key] is already an admin."), confidential=TRUE) +// return +// change_admin_rank(admin_ckey, admin_key, null) +// if("remove") +// remove_admin(admin_ckey, admin_key, D) +// if("rank") +// change_admin_rank(admin_ckey, admin_key, D) +// if("permissions") +// change_admin_flags(admin_ckey, admin_key, D) +// if("activate") +// force_readmin(admin_key, D) +// if("deactivate") +// force_deadmin(admin_key, D) +// if("mfareset") +// if(alert("WARNING! This will reset the 2FA code and backup for [admin_key], possibly comprimising the security of the server. Are you sure you wish to continue?", "Confirmation", "Cancel", "Continue") != "Continue") +// return +// if(alert("If you have been requested to reset the MFA credentials for someone, please confirm that you have verified their identity. Resetting MFA for an unverified person can result in a break of server security.", "Confirmation", "I Understand", "Cancel") != "I Understand") +// return +// message_admins("MFA for [admin_ckey] has been reset by [usr]!") +// log_admin("MFA Reset for [admin_ckey] by [usr]!") +// mfa_reset(admin_ckey) +// edit_admin_permissions() -/datum/admins/proc/force_readmin(admin_key, datum/admins/D) +// /datum/admins/proc/add_admin(admin_ckey, admin_key) +// if(admin_ckey) +// . = admin_ckey +// else +// admin_key = input("New admin's key","Admin key") as text|null +// . = ckey(admin_key) +// if(!.) +// return FALSE +// if(!admin_ckey && (. in GLOB.permissions.admin_datums+GLOB.permissions.deadmins)) +// to_chat(usr, span_danger("[admin_key] is already an admin."), confidential=TRUE) +// return FALSE + +// /datum/admins/proc/remove_admin(admin_ckey, admin_key, datum/admins/D) +// if(alert("Are you sure you want to remove [admin_ckey]?","Confirm Removal","Do it","Cancel") == "Do it") +// GLOB.permissions.admin_datums -= admin_ckey +// GLOB.permissions.deadmins -= admin_ckey +// if(D) +// D.disassociate() +// var/m1 = "[key_name_admin(usr)] removed [admin_key] from the admins list temporarily" +// var/m2 = "[key_name(usr)] removed [admin_key] from the admins list temporarily" +// message_admins(m1) +// log_admin(m2) + +/datum/permissions_panel/proc/force_readmin(admin_key, datum/admins/D) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return if(!D || !D.deadmined) return D.activate() message_admins("[key_name_admin(usr)] forcefully readmined [admin_key]") log_admin("[key_name(usr)] forcefully readmined [admin_key]") -/datum/admins/proc/force_deadmin(admin_key, datum/admins/D) +/datum/permissions_panel/proc/force_deadmin(admin_key, datum/admins/D) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return if(!D || D.deadmined) return message_admins("[key_name_admin(usr)] forcefully deadmined [admin_key]") @@ -301,7 +256,12 @@ D.deactivate() //after logs so the deadmined admin can see the message. /datum/admins/proc/auto_deadmin() - if(GLOB.admins.len < CONFIG_GET(number/auto_deadmin_threshold)) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(GLOB.permissions.admins.len < CONFIG_GET(number/auto_deadmin_threshold)) log_admin("[owner] auto-deadmin failed due to low admin count.") to_chat(owner, span_userdanger("You have not be auto-deadminned due to lack of admins on the server, you can still deadmin manually.")) return FALSE @@ -312,238 +272,38 @@ log_admin("[old_owner] deadmined via auto-deadmin config.") return TRUE -/datum/admins/proc/change_admin_rank(admin_ckey, admin_key, use_db, datum/admins/D, legacy_only) - if(!check_rights(R_PERMISSIONS)) - return - var/datum/admin_rank/R - var/list/rank_names = list() - if(!use_db || (use_db && !legacy_only)) - rank_names += "*New Rank*" - for(R in GLOB.admin_ranks) - if((R.rights & usr.client.holder.rank.can_edit_rights) == R.rights) - rank_names[R.name] = R - var/new_rank = input("Please select a rank", "New rank") as null|anything in rank_names - if(new_rank == "*New Rank*") - new_rank = trim(input("Please input a new rank", "New custom rank") as text|null) - if(!new_rank) - if(!D) - remove_admin(admin_ckey, admin_key, use_db, null) - return - R = rank_names[new_rank] - if(!R) //rank with that name doesn't exist yet - make it - if(D) - R = new(new_rank, D.rank.rights) //duplicate our previous admin_rank but with a new name - else - R = new(new_rank) //blank new admin_rank - GLOB.admin_ranks += R - var/m1 = "[key_name_admin(usr)] edited the admin rank of [admin_key] to [new_rank] [use_db ? "permanently" : "temporarily"]" - var/m2 = "[key_name(usr)] edited the admin rank of [admin_key] to [new_rank] [use_db ? "permanently" : "temporarily"]" - if(use_db) - //if a player was tempminned before having a permanent change made to their rank they won't yet be in the db - var/old_rank - var/datum/DBQuery/query_admin_in_db = SSdbcore.NewQuery( - "SELECT `rank` FROM [format_table_name("admin")] WHERE ckey = :admin_ckey", - list("admin_ckey" = admin_ckey) - ) - if(!query_admin_in_db.warn_execute()) - qdel(query_admin_in_db) - return - if(!query_admin_in_db.NextRow()) - add_admin(admin_ckey, admin_key, TRUE) - old_rank = "NEW ADMIN" - else - old_rank = query_admin_in_db.item[1] - qdel(query_admin_in_db) - //similarly if a temp rank is created it won't be in the db if someone is permanently changed to it +// /datum/admins/proc/change_admin_rank(admin_ckey, admin_key, datum/admins/D) +// if(!check_rights(R_PERMISSIONS)) +// return +// var/list/rank_names = list() +// rank_names += "*New Rank*" +// for(var/R in GLOB.legacy_ranks) +// rank_names += R +// var/new_rank = input("Please select a rank", "New rank") as null|anything in rank_names +// if(new_rank == "*New Rank*") +// new_rank = trim(input("Please input a new rank", "New custom rank") as text|null) +// if(!new_rank) +// return +// D.rank_name = new_rank +// if(new_rank in GLOB.legacy_ranks) +// D.rights = GLOB.legacy_ranks[new_rank] +// var/client/C = D.owner +// D.disassociate() +// D.associate(C) +// var/m1 = "[key_name_admin(usr)] edited the admin rank of [admin_key] to [new_rank] temporarily" +// var/m2 = "[key_name(usr)] edited the admin rank of [admin_key] to [new_rank] temporarily" +// message_admins(m1) +// log_admin(m2) - var/datum/DBQuery/query_rank_in_db = SSdbcore.NewQuery( - "SELECT 1 FROM [format_table_name("admin_ranks")] WHERE `rank` = :new_rank", - list("new_rank" = new_rank) - ) - if(!query_rank_in_db.warn_execute()) - qdel(query_rank_in_db) - return - if(!query_rank_in_db.NextRow()) - QDEL_NULL(query_rank_in_db) - var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery({" - INSERT INTO [format_table_name("admin_ranks")] (`rank`, flags, exclude_flags, can_edit_flags) - VALUES (:new_rank, '0', '0', '0') - "}, list("new_rank" = new_rank)) - if(!query_add_rank.warn_execute()) - qdel(query_add_rank) - return - qdel(query_add_rank) - var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery({" - INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) - VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'add rank', :new_rank, CONCAT('New rank added: ', :new_rank)) - "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "new_rank" = new_rank)) - if(!query_add_rank_log.warn_execute()) - qdel(query_add_rank_log) - return - qdel(query_add_rank_log) - qdel(query_rank_in_db) - var/datum/DBQuery/query_change_rank = SSdbcore.NewQuery( - "UPDATE [format_table_name("admin")] SET `rank` = :new_rank WHERE ckey = :admin_ckey", - list("new_rank" = new_rank, "admin_ckey" = admin_ckey) - ) - if(!query_change_rank.warn_execute()) - qdel(query_change_rank) - return - qdel(query_change_rank) - var/datum/DBQuery/query_change_rank_log = SSdbcore.NewQuery({" - INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) - VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'change admin rank', :target, CONCAT('Rank of ', :target, ' changed from ', :old_rank, ' to ', :new_rank)) - "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = admin_ckey, "old_rank" = old_rank, "new_rank" = new_rank)) - if(!query_change_rank_log.warn_execute()) - qdel(query_change_rank_log) - return - qdel(query_change_rank_log) - if(D) //they were previously an admin - D.disassociate() //existing admin needs to be disassociated - D.rank = R //set the admin_rank as our rank - var/client/C = GLOB.directory[admin_ckey] - D.associate(C) - else - D = new(R, admin_ckey, TRUE) //new admin - message_admins(m1) - log_admin(m2) - -/datum/admins/proc/change_admin_flags(admin_ckey, admin_key, use_db, datum/admins/D, legacy_only) - var/new_flags = input_bitfield(usr, "Include permission flags
[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_key]"]", "admin_flags", D.rank.include_rights, 350, 590, allowed_edit_list = usr.client.holder.rank.can_edit_rights) - if(isnull(new_flags)) - return - var/new_exclude_flags = input_bitfield(usr, "Exclude permission flags
Flags enabled here will be removed from a rank.
Note these take precedence over included flags.
[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_key]"]", "admin_flags", D.rank.exclude_rights, 350, 670, "red", usr.client.holder.rank.can_edit_rights) - if(isnull(new_exclude_flags)) - return - var/new_can_edit_flags = input_bitfield(usr, "Editable permission flags
These are the flags this rank is allowed to edit if they have access to the permissions panel.
They will be unable to modify admins to a rank that has a flag not included here.
[use_db ? "This will affect ALL admins with this rank." : "This will affect only the current admin [admin_key]"]", "admin_flags", D.rank.can_edit_rights, 350, 710, allowed_edit_list = usr.client.holder.rank.can_edit_rights) - if(isnull(new_can_edit_flags)) - return - var/m1 = "[key_name_admin(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_key] temporarily"]" - var/m2 = "[key_name(usr)] edited the permissions of [use_db ? " rank [D.rank.name] permanently" : "[admin_key] temporarily"]" - if(use_db || legacy_only) - var/rank_name = D.rank.name - var/old_flags - var/old_exclude_flags - var/old_can_edit_flags - var/datum/DBQuery/query_get_rank_flags = SSdbcore.NewQuery( - "SELECT flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] WHERE `rank` = :rank_name", - list("rank_name" = rank_name) - ) - if(!query_get_rank_flags.warn_execute()) - qdel(query_get_rank_flags) - return - if(query_get_rank_flags.NextRow()) - old_flags = text2num(query_get_rank_flags.item[1]) - old_exclude_flags = text2num(query_get_rank_flags.item[2]) - old_can_edit_flags = text2num(query_get_rank_flags.item[3]) - qdel(query_get_rank_flags) - var/datum/DBQuery/query_change_rank_flags = SSdbcore.NewQuery( - "UPDATE [format_table_name("admin_ranks")] SET flags = :new_flags, exclude_flags = :new_exclude_flags, can_edit_flags = :new_can_edit_flags WHERE `rank` = :rank_name", - list("new_flags" = new_flags, "new_exclude_flags" = new_exclude_flags, "new_can_edit_flags" = new_can_edit_flags, "rank_name" = rank_name) - ) - if(!query_change_rank_flags.warn_execute()) - qdel(query_change_rank_flags) - return - qdel(query_change_rank_flags) - var/log_message = "Permissions of [rank_name] changed from[rights2text(old_flags," ")][rights2text(old_exclude_flags," ", "-")][rights2text(old_can_edit_flags," ", "*")] to[rights2text(new_flags," ")][rights2text(new_exclude_flags," ", "-")][rights2text(new_can_edit_flags," ", "*")]" - var/datum/DBQuery/query_change_rank_flags_log = SSdbcore.NewQuery({" - INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) - VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'change rank flags', :rank_name, :log) - "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "rank_name" = rank_name, "log" = log_message)) - if(!query_change_rank_flags_log.warn_execute()) - qdel(query_change_rank_flags_log) - return - qdel(query_change_rank_flags_log) - for(var/datum/admin_rank/R in GLOB.admin_ranks) - if(R.name != D.rank.name) - continue - R.rights = new_flags &= ~new_exclude_flags - R.exclude_rights = new_exclude_flags - R.include_rights = new_flags - R.can_edit_rights = new_can_edit_flags - for(var/i in GLOB.admin_datums+GLOB.deadmins) - var/datum/admins/A = GLOB.admin_datums[i] - if(!A) - A = GLOB.deadmins[i] - if (!A) - continue - if(A.rank.name != D.rank.name) - continue - var/client/C = GLOB.directory[A.target] - A.disassociate() - A.associate(C) - else - D.disassociate() - if(!findtext(D.rank.name, "([admin_ckey])")) //not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too - D.rank = new("[D.rank.name]([admin_ckey])", new_flags, new_exclude_flags, new_can_edit_flags) //duplicate our previous admin_rank but with a new name - //we don't add this clone to the admin_ranks list, as it is unique to that ckey - else - D.rank.rights = new_flags &= ~new_exclude_flags - D.rank.include_rights = new_flags - D.rank.exclude_rights = new_exclude_flags - D.rank.can_edit_rights = new_can_edit_flags - var/client/C = GLOB.directory[admin_ckey] //find the client with the specified ckey (if they are logged in) - D.associate(C) //link up with the client and add verbs - message_admins(m1) - log_admin(m2) - -/datum/admins/proc/remove_rank(admin_rank) - if(!admin_rank) - return - for(var/datum/admin_rank/R in GLOB.admin_ranks) - if(R.name == admin_rank && (!(R.rights & usr.client.holder.rank.can_edit_rights) == R.rights)) - to_chat(usr, "You don't have edit rights to all the rights this rank has, rank deletion not permitted.", confidential=TRUE) - return - if(!CONFIG_GET(flag/admin_legacy_system) && CONFIG_GET(flag/protect_legacy_ranks) && (admin_rank in GLOB.protected_ranks)) - to_chat(usr, "Deletion of protected ranks is not permitted, it must be removed from admin_ranks.txt.", confidential=TRUE) - return - if(CONFIG_GET(flag/load_legacy_ranks_only)) - to_chat(usr, "Rank deletion not permitted while database rank loading is disabled.", confidential=TRUE) - return - var/datum/DBQuery/query_admins_with_rank = SSdbcore.NewQuery( - "SELECT 1 FROM [format_table_name("admin")] WHERE `rank` = :admin_rank", - list("admin_rank" = admin_rank) - ) - if(!query_admins_with_rank.warn_execute()) - qdel(query_admins_with_rank) - return - if(query_admins_with_rank.NextRow()) - qdel(query_admins_with_rank) - to_chat(usr, span_danger("Error: Rank deletion attempted while rank still used; Tell a coder, this shouldn't happen."), confidential=TRUE) - return - qdel(query_admins_with_rank) - if(alert("Are you sure you want to remove [admin_rank]?","Confirm Removal","Do it","Cancel") == "Do it") - var/m1 = "[key_name_admin(usr)] removed rank [admin_rank] permanently" - var/m2 = "[key_name(usr)] removed rank [admin_rank] permanently" - var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery( - "DELETE FROM [format_table_name("admin_ranks")] WHERE `rank` = :admin_rank", - list("admin_rank" = admin_rank) - ) - if(!query_add_rank.warn_execute()) - qdel(query_add_rank) - return - qdel(query_add_rank) - var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery({" - INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) - VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'remove rank', :admin_rank, CONCAT('Rank removed: ', :admin_rank)) - "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "admin_rank" = admin_rank)) - if(!query_add_rank_log.warn_execute()) - qdel(query_add_rank_log) - return - qdel(query_add_rank_log) - message_admins(m1) - log_admin(m2) - -/datum/admins/proc/sync_lastadminrank(admin_ckey, admin_key, datum/admins/D) - var/sqlrank = "Player" - if (D) - sqlrank = D.rank.name - var/datum/DBQuery/query_sync_lastadminrank = SSdbcore.NewQuery( - "UPDATE [format_table_name("player")] SET lastadminrank = :rank WHERE ckey = :ckey", - list("rank" = sqlrank, "ckey" = admin_ckey) - ) - if(!query_sync_lastadminrank.warn_execute()) - qdel(query_sync_lastadminrank) - return - qdel(query_sync_lastadminrank) - to_chat(usr, span_admin("Sync of [admin_key] successful."), confidential=TRUE) +// /datum/admins/proc/change_admin_flags(admin_ckey, admin_key, datum/admins/D) +// var/new_flags = input_bitfield(usr, "Permission flags
This will affect only the current admin [admin_key]", "admin_flags", D.rights, 350, 590) +// if(isnull(new_flags)) +// return +// var/m1 = "[key_name_admin(usr)] edited the permissions of [admin_key] temporarily" +// var/m2 = "[key_name(usr)] edited the permissions of [admin_key] temporarily" +// D.rights = new_flags +// var/client/C = D.owner +// D.disassociate() +// D.associate(C) +// message_admins(m1) +// log_admin(m2) diff --git a/code/modules/admin/permissions/database.dm b/code/modules/admin/permissions/database.dm new file mode 100644 index 000000000000..63454cd68a92 --- /dev/null +++ b/code/modules/admin/permissions/database.dm @@ -0,0 +1,375 @@ +/datum/permissions_controller/db + var/list/dbranks = list() + var/list/dbadmins = list() + +/datum/permissions_controller/db/clear_admins() + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + ..() + dbranks.Cut() + dbadmins.Cut() + +/datum/permissions_controller/db/load_admins() + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + + . = ..() + + if(!SSdbcore.Connect()) + message_admins("Failed to connect to database to load admins.") + log_sql("Failed to connect to database to load admins") + return + + var/datum/DBQuery/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.") + log_sql("Error loading admin ranks from database.") + else + while(query_load_admin_ranks.NextRow()) + var/rank_name = trim(query_load_admin_ranks.item[1]) + 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]) + dbranks[rank_name] = list("flags" = rank_flags & (~rank_exclude_flags), "can_edit_flags" = rank_can_edit_flags) + qdel(query_load_admin_ranks) + + var/datum/DBQuery/query_load_admins = SSdbcore.NewQuery("SELECT ckey, rank 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.") + else + while(query_load_admins.NextRow()) + var/admin_ckey = ckey(query_load_admins.item[1]) + var/admin_rank = trim(query_load_admins.item[2]) + if(dbranks[admin_rank] == null) + message_admins("[admin_ckey] loaded with invalid admin rank [admin_rank].") + continue + if(admin_ckey in legacy_admins) + continue + dbadmins[admin_ckey] = admin_rank + qdel(query_load_admins) + +/datum/permissions_controller/db/_load_permissions_for(var/client/C) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + + if(..()) + return TRUE + + var/rank = dbadmins[C.ckey] + if(!rank) + return FALSE + var/rights = dbranks[rank] + if(!rights) + return FALSE + new /datum/admins(C.ckey, rights["flags"]) + return TRUE + +/datum/permissions_controller/db/get_rights_for_ckey(ckey) + . = ..() + if(.) + return + + var/rank = dbadmins[ckey] + if(!rank) + return + var/rights = dbranks[rank] + if(!rights) + return + else + return rights["flags"] + +/datum/permissions_controller/db/get_rank_name(client/subject) + . = ..() + if(.) + return + if(subject.ckey in dbadmins) + return dbadmins[subject.ckey] + +/datum/permissions_controller/db/pp_data(mob/user) + . = ..() + var/perm_rights = check_rights_for(user.client, R_PERSIST_PERMS) + var/grant_flags = 0 + if(user.ckey in legacy_admins) + grant_flags = legacy_ranks[legacy_admins[user.ckey]] || 0 + else if(user.ckey in dbadmins) + var/dbrank = dbadmins[user.ckey] + if(dbranks[dbrank]) + grant_flags = dbranks[dbrank]["can_edit_flags"] + for(var/admin in dbadmins) + var/data = list() + data["ckey"] = admin + data["rank"] = dbadmins[admin] + var/rights_text = "Rights: [rights2text(dbranks[data["rank"]]["flags"], " ")]\nGrant Rights: [rights2text(dbranks[data["rank"]]["can_edit_flags"], " ", "*")]" + data["rights"] = rights_text + data["deadminned"] = (admin in deadmins) + data["protected_admin"] = (dbranks[data["rank"]]["flags"] & grant_flags) != dbranks[data["rank"]]["flags"] + data["protected_rank"] = !perm_rights || data["protected_admin"] + .["admins"] |= list(data) + +/datum/permissions_controller/db/should_add_admin(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(!..()) + return FALSE + return !(ckey in dbadmins) + +/// Sets a rank in the DB +/// Returns true of the rank was set +/datum/permissions_controller/db/proc/set_db_rank(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + + var/grant_flags = 0 + if(usr.ckey in legacy_admins) + grant_flags = legacy_ranks[legacy_admins[usr.ckey]] || 0 + else if(usr.ckey in dbadmins) + var/dbrank = dbadmins[usr.ckey] + if(dbranks[dbrank]) + grant_flags = dbranks[dbrank]["can_edit_flags"] + + if((ckey in dbadmins) && (dbadmins[ckey] in dbranks) && (dbranks[dbadmins[ckey]]["flags"] & grant_flags) != dbranks[dbadmins[ckey]]["flags"]) + return FALSE + + var/list/ranks = list() + ranks += "*New Rank*" + for(var/rank in dbranks) + var/flags = dbranks[rank]["flags"] + if((flags & grant_flags) == flags) + ranks += rank + + var/new_rank = input("Please select a rank", "New rank") as null|anything in ranks + if(new_rank == "*New Rank*") + new_rank = trim(input("Please input a new rank", "New custom rank") as text|null) + if(!new_rank) + return FALSE + + if(!(new_rank in dbranks)) + var/current_flags = list("flags" = 0, "can_edit_flags" = 0) + if((ckey in dbadmins) && (dbadmins[ckey] in dbranks)) + current_flags = dbranks[dbadmins[ckey]] + dbranks[new_rank] = current_flags + var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery({" + INSERT INTO [format_table_name("admin_ranks")] (`rank`, flags, exclude_flags, can_edit_flags) + VALUES (:new_rank, :flags, '0', :edit_flags) ON DUPLICATE KEY UPDATE + "}, list("new_rank" = new_rank, "flags" = current_flags["flags"], "edit_flags" = current_flags["can_edit_flags"])) + if(!query_add_rank.warn_execute()) + qdel(query_add_rank) + return + qdel(query_add_rank) + var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery({" + INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) + VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'add rank', :new_rank, CONCAT('New rank added: ', :new_rank)) + "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "new_rank" = new_rank)) + if(!query_add_rank_log.warn_execute()) + qdel(query_add_rank_log) + return + qdel(query_add_rank_log) + var/old_rank = dbadmins[ckey] || "NEW ADMIN" + dbadmins[ckey] = new_rank + var/datum/DBQuery/query_change_rank = SSdbcore.NewQuery( + "INSERT INTO [format_table_name("admin")] (`rank`, ckey) VALUES(:new_rank, :admin_ckey)", + list("new_rank" = new_rank, "admin_ckey" = ckey) + ) + if(!query_change_rank.warn_execute()) + qdel(query_change_rank) + return + qdel(query_change_rank) + var/datum/DBQuery/query_change_rank_log = SSdbcore.NewQuery({" + INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) + VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'change admin rank', :target, CONCAT('Rank of ', :target, ' changed from ', :old_rank, ' to ', :new_rank)) + "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = ckey, "old_rank" = old_rank, "new_rank" = new_rank)) + if(!query_change_rank_log.warn_execute()) + qdel(query_change_rank_log) + return + qdel(query_change_rank_log) + var/datum/admins/holder = admin_datums[ckey] + if(holder) + holder.deactivate() + holder.activate() + return TRUE + +/datum/permissions_controller/db/make_admin(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + + var/permanent = (SSdbcore.Connect() && check_rights(R_PERSIST_PERMS, FALSE) && (alert("Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", "Permanent", "Temporary", "Cancel") == "Permanent")) + if(!permanent) + return ..() + + set_db_rank(ckey) + return TRUE + +/datum/permissions_controller/db/edit_rank(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(..()) + return TRUE + + if(!(ckey in dbadmins)) + return FALSE + + var/permanent = ((ckey in dbadmins) && SSdbcore.Connect() && check_rights(R_PERSIST_PERMS, FALSE) && (alert("Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", "Permanent", "Temporary", "Cancel") == "Permanent")) + if(!permanent) + if(set_legacy_rank(ckey)) + dbadmins -= ckey + return TRUE + + set_db_rank(ckey) + + return TRUE + +/datum/permissions_controller/db/edit_perms(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(..()) + return TRUE + + if(!(ckey in dbadmins)) + return FALSE + + if(!SSdbcore.Connect()) + to_chat(usr, "Cannot connect to database to edit rank [dbadmins[ckey]]", confidential = TRUE) + return TRUE + + if(!check_rights(R_PERSIST_PERMS, FALSE)) + to_chat(usr, "You do not have access to modify this rank.") + return TRUE + + var/grant_flags = 0 + if(usr.ckey in legacy_admins) + grant_flags = legacy_ranks[legacy_admins[usr.ckey]] || 0 + else if(usr.ckey in dbadmins) + var/dbrank = dbadmins[usr.ckey] + if(dbranks[dbrank]) + grant_flags = dbranks[dbrank]["can_edit_flags"] + + if(!((dbadmins[ckey] in dbranks) && (dbranks[dbadmins[ckey]]["flags"] & grant_flags) == dbranks[dbadmins[ckey]]["flags"])) + to_chat(usr, "You do not have access to modify this rank.") + return TRUE + + if(alert(usr, "This rank cannot be modified temporarily, and modifying it will change all admins using this rank. Do you wish to continue?", "Confirmation", "Yes", "No") != "Yes") + return TRUE + + var/rank = dbranks[dbadmins[ckey]] + + var/new_flags = input_bitfield(usr, "Permission flags
This will affect ALL admins with this rank.", "admin_flags", rank["flags"], 350, 590, allowed_edit_list = grant_flags) + if(isnull(new_flags)) + return + + var/new_can_edit_flags = input_bitfield(usr, "Editable permission flags
These are the flags this rank is allowed to edit if they have access to the permissions panel.
They will be unable to modify admins to a rank that has a flag not included here.
This will affect ALL admins with this rank.", "admin_flags", rank["can_edit_flags"], 350, 710, grant_flags) + if(isnull(new_can_edit_flags)) + return + + var/rank_name = dbadmins[ckey] + var/m = "edited the permissions of rank [rank_name] permanently" + var/datum/DBQuery/query_change_rank_flags = SSdbcore.NewQuery( + "UPDATE [format_table_name("admin_ranks")] SET flags = :new_flags, exclude_flags = :new_exclude_flags, can_edit_flags = :new_can_edit_flags WHERE `rank` = :rank_name", + list("new_flags" = new_flags, "new_exclude_flags" = 0, "new_can_edit_flags" = new_can_edit_flags, "rank_name" = rank_name) + ) + if(!query_change_rank_flags.warn_execute()) + qdel(query_change_rank_flags) + return + qdel(query_change_rank_flags) + var/log_message = "Permissions of [rank_name] changed from[rights2text(rank["flags"]," ")][rights2text(rank["can_edit_flags"]," ", "*")] to[rights2text(new_flags," ")][rights2text(new_can_edit_flags," ", "*")]" + var/datum/DBQuery/query_change_rank_flags_log = SSdbcore.NewQuery({" + INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) + VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'change rank flags', :rank_name, :log) + "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "rank_name" = rank_name, "log" = log_message)) + if(!query_change_rank_flags_log.warn_execute()) + qdel(query_change_rank_flags_log) + return + qdel(query_change_rank_flags_log) + rank["flags"] = new_flags + rank["can_edit_flags"] = new_can_edit_flags + + for(var/admin in admin_datums) + if(dbadmins[admin] == rank_name) + var/datum/admins/holder = admin_datums[admin] + holder.deactivate() + holder.activate() + + message_admins("[key_name_admin(usr)] [m]") + log_admin("[key_name(usr)] [m]") + return TRUE + +/datum/permissions_controller/db/remove_admin(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(..()) + return TRUE + + if(!(ckey in dbadmins)) + return FALSE + + var/grant_flags = 0 + if(usr.ckey in legacy_admins) + grant_flags = legacy_ranks[legacy_admins[usr.ckey]] || 0 + else if(usr.ckey in dbadmins) + var/dbrank = dbadmins[usr.ckey] + if(dbranks[dbrank]) + grant_flags = dbranks[dbrank]["can_edit_flags"] + + if(!((dbadmins[ckey] in dbranks) && (dbranks[dbadmins[ckey]]["flags"] & grant_flags) == dbranks[dbadmins[ckey]]["flags"])) + to_chat(usr, "You do not have access to delete this admin") + return TRUE + + var/permanent = ((ckey in dbadmins) && SSdbcore.Connect() && check_rights(R_PERSIST_PERMS, FALSE) && (alert("Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", "Permanent", "Temporary", "Cancel") == "Permanent")) + + if(permanent) + if(alert(usr, "Are you sure you wish to permanently remove [ckey] from the admins list?", "Confirmation", "Yes", "No") != "Yes") + return TRUE + var/datum/DBQuery/query_add_rank = SSdbcore.NewQuery( + "DELETE FROM [format_table_name("admin")] WHERE ckey = :ckey", + list("ckey" = ckey) + ) + if(!query_add_rank.warn_execute()) + qdel(query_add_rank) + return + qdel(query_add_rank) + var/datum/DBQuery/query_add_rank_log = SSdbcore.NewQuery({" + INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) + VALUES (:time, :round_id, :adminckey, INET_ATON(:adminip), 'remove admin', :admin_ckey, CONCAT('Admin removed: ', :admin_ckey)) + "}, list("time" = SQLtime(), "round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "admin_ckey" = ckey)) + if(!query_add_rank_log.warn_execute()) + qdel(query_add_rank_log) + return + qdel(query_add_rank_log) + else + if(alert(usr, "Are you sure you wish to temporarily remove [ckey] from the admins list?", "Confirmation", "Yes", "No") != "Yes") + return TRUE + + var/datum/admins/holder = admin_datums[ckey] || deadmins[ckey] + if(holder) + qdel(holder) + + dbadmins -= ckey + return TRUE + diff --git a/code/modules/admin/permissions/forums.dm b/code/modules/admin/permissions/forums.dm new file mode 100644 index 000000000000..c8a0791c4956 --- /dev/null +++ b/code/modules/admin/permissions/forums.dm @@ -0,0 +1,171 @@ +/datum/permissions_controller/forums + /// Admins from the forums + var/list/forums_admins = list() + + /// Admins who are overriden for this round + var/list/overrides = list() + +/datum/permissions_controller/forums/clear_admins() + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + ..() + forums_admins.Cut() + overrides.Cut() + +/datum/permissions_controller/forums/_load_permissions_for(client/C) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(C.ckey in overrides) + return ..() + + forums_admins -= C.ckey // In case they have been demoted since last login + + var/permissions = query_permissions_for(C.ckey) + + if(permissions) + forums_admins[C.ckey] = permissions + new /datum/admins(C.ckey, permissions["rights"]) + return TRUE + + return ..() + +/// Queries forums to find permissions +/datum/permissions_controller/forums/proc/query_permissions_for(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + + if(!CONFIG_GET(string/xenforo_key)) + CRASH("Trying to load forums permisisons without xenforo key") + + var/datum/http_request/req = new() + req.prepare(RUSTG_HTTP_METHOD_GET, "[CONFIG_GET(string/apiurl)]/linking/byond/[ckey(ckey)]", "", list("XF-Api-Key" = CONFIG_GET(string/xenforo_key))) + req.begin_async() + + UNTIL(req.is_complete()) + + var/datum/http_response/response = req.into_response() + if(response.errored) + CRASH("Errored loading forums rank: [response.error]") + var/list/body + try + body = json_decode(response.body) + catch + CRASH("Malformed JSON from forums permssions endpoint. [response.body]") + if(!body["success"]) + return FALSE + + var/flags = 0 + var/list/permissions = body["user"]["permissions"] + for(var/permission in permissions) + var/list/parts = splittext(permission, ".") + if(parts[1] != "ingame") + continue + var/newflag = admin_keyword_to_flag(parts[2]) + if(!newflag) + stack_trace("WARNING: Permission \"[parts[2]]\" not found.") + flags |= newflag + if(flags) + return list("rank" = body["display_group"]["title"], "rights" = flags) + else + return FALSE + +/datum/permissions_controller/forums/get_rights_for_ckey(ckey) + . = ..() + if(.) + return + if(!(ckey in overrides) && (ckey in forums_admins)) + return forums_admins[ckey]["rights"] + +/datum/permissions_controller/forums/get_rank_name(client/subject) + . = ..() + if(.) + return + if(!(subject.ckey in overrides) && (subject.ckey in forums_admins)) + return forums_admins[subject.ckey]["rank"] + +/datum/permissions_controller/forums/pp_data(mob/user) + . = ..() + for(var/admin in forums_admins) + if(admin in overrides) + continue + var/data = list() + data["ckey"] = admin + data["rank"] = forums_admins[admin]["rank"] + data["rights"] = rights2text(forums_admins[admin]["rights"], seperator = " ") + data["deadminned"] = (admin in deadmins) + data["protected_rank"] = TRUE + .["admins"] |= list(data) + +/datum/permissions_controller/forums/should_add_admin(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(!..()) + return FALSE + if(ckey in overrides) + return TRUE + return !query_permissions_for(ckey) + +/datum/permissions_controller/forums/edit_rank(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(..()) + return TRUE + + if((ckey in forums_admins) && !(ckey in overrides)) + if(alert("Forums permissions cannot be edited from this panel. Would you like to add an override for this round?", "Confirmation", "Yes", "No") != "Yes") + return TRUE + if(set_legacy_rank(ckey)) + overrides += ckey + return TRUE + return FALSE + +/datum/permissions_controller/forums/edit_perms(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(..()) + return TRUE + + if((ckey in forums_admins) && !(ckey in overrides)) + to_chat(usr, "Permissions for forums ranks cannot be edited in game. To temporarily modify a users permissions first give them a temporary rank.", confidential = TRUE) + return TRUE + + return FALSE + +/datum/permissions_controller/forums/remove_admin(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(..()) + return TRUE + + if((ckey in forums_admins) && !(ckey in overrides)) + if(alert("Forums permissions cannot be edited from this panel. Would you like to demote for this round?", "Confirmation", "Yes", "No") != "Yes") + return TRUE + overrides += ckey + forums_admins -= ckey + if(ckey in admin_datums) + qdel(admin_datums[ckey]) + if(ckey in deadmins) + qdel(deadmins[ckey]) + return TRUE + return FALSE diff --git a/code/modules/admin/permissions/permissions.dm b/code/modules/admin/permissions/permissions.dm new file mode 100644 index 000000000000..3f9c397b5e20 --- /dev/null +++ b/code/modules/admin/permissions/permissions.dm @@ -0,0 +1,454 @@ +GLOBAL_DATUM(permissions, /datum/permissions_controller) +GENERAL_PROTECT_DATUM(/datum/permissions_controller) + +// Creates permissions controller based on the config +/proc/init_permissions() + if(GLOB.permissions != null) + CRASH("Permissions controller loaded twice") + + var/controller_type = /datum/permissions_controller + switch(ckey(CONFIG_GET(string/permissions_backend))) + if("database") + if(CONFIG_GET(flag/sql_enabled)) + controller_type = /datum/permissions_controller/db + else + stack_trace("Attempted to load database permissions with sql disabled.") + if("forums") + if(CONFIG_GET(string/xenforo_key)) + controller_type = /datum/permissions_controller/forums + else + stack_trace("Attempted to load forums permissions without a xenforo api key") + GLOB.permissions = new controller_type() + +// Handles admin permissions management, overriden to support external backends +// Base datum supports legacy file based admin loading +/datum/permissions_controller + /// Admins that should not be allowed to be modified by the permissions panel + var/list/protected_admins = list() + + /// Ranks that should not be allowed to be modified by the permissions panel + var/list/protected_ranks = list() + + /// Admins loaded with the legacy system + var/list/legacy_admins = list() + + /// Ranks loaded with the legacy system + var/list/legacy_ranks = list() + + // These lists are mostly handled by the datums themselves + /// Associated list of ckey -> admin datums + var/list/admin_datums = list() + + /// List of all admin clients + var/list/admins = list() + + /// List of all deadmins + var/list/deadmins = list() + +/// Clears any existing stored admins and (re) loads the data from the backend +/datum/permissions_controller/proc/start() + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + + clear_admins() + load_admins() + for(var/client/C in GLOB.clients) + load_permissions_for(C) + +/// Removes all admin status from everyone +/datum/permissions_controller/proc/clear_admins() + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + //clear the datums references + admin_datums.Cut() + for(var/client/C in GLOB.permissions.admins) + C.remove_admin_verbs() + C.holder = null + admins.Cut() + protected_admins.Cut() + protected_ranks.Cut() + legacy_admins.Cut() + legacy_ranks.Cut() + deadmins.Cut() + //Clear profile access + for(var/A in world.GetConfig("admin")) + world.SetConfig("APP/admin", A, null) + +/// Pulls in admin data, for if the backend caches the admin data +/datum/permissions_controller/proc/load_admins() + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + var/previous_rights = 0 + for(var/line in world.file2list("[global.config.directory]/admin_ranks.txt")) + if(!line || findtextEx(line,"#",1,2) || line == " ") //YOGS - added our DB support + continue + var/next = findtext(line, "=") + var/prev = findchar(line, "+-*", next, 0) + var/rank_name = trim(copytext(line, 1, next)) + if(!rank_name) continue + var/rights = 0 + while(prev) + next = findchar(line, "+-*", prev + 1, 0) + rights |= admin_keyword_to_flag(copytext(line, prev, next), previous_rights) + prev = next + previous_rights = rights + legacy_ranks[rank_name] = rights + if(CONFIG_GET(flag/protect_legacy_ranks)) + protected_ranks |= rank_name + + //ckeys listed in admins.txt are always made admins before sql loading is attempted + var/list/lines = world.file2list("[global.config.directory]/admins.txt") + for(var/line in lines) + if(!length(line) || findtextEx(line, "#", 1, 2) || line == " ") //yogs - added our DB support + continue + var/list/entry = splittext(line, "=") + if(entry.len < 2) + continue + var/ckey = ckey(entry[1]) + var/rank = trim(entry[2]) + if(!ckey || !rank) + continue + if(rank in legacy_ranks) + legacy_admins[ckey] = rank + if(CONFIG_GET(flag/protect_legacy_admins)) + protected_admins |= ckey + +/// Queries the backend permissions system then creates their datum if they should have one +/// Returns true if a datum was created +/datum/permissions_controller/proc/load_permissions_for(client/C) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(C.ckey in legacy_admins) + new /datum/admins(C.ckey, legacy_ranks[legacy_admins[C.ckey]]) + return TRUE + + if(_load_permissions_for(C)) + return TRUE + + if(CONFIG_GET(flag/autoadmin)) + var/auto_rank = CONFIG_GET(string/autoadmin_rank) + if(auto_rank in legacy_ranks) + legacy_admins[C.ckey] = auto_rank + new /datum/admins(C.ckey, legacy_ranks[C.ckey]) + return TRUE + + if(CONFIG_GET(flag/enable_localhost_rank)) + var/localhost_addresses = list("127.0.0.1", "::1") + if(isnull(C.address) || (C.address in localhost_addresses)) + legacy_ranks["!localhost!"] = R_EVERYTHING - R_PERSIST_PERMS + legacy_admins[C.ckey] = "!localhost!" + new /datum/admins(C.ckey, R_EVERYTHING - R_PERSIST_PERMS) + return TRUE + + return FALSE + +/// Most backends probably want to override this one +/// Loads after legacy but before autoadmin/localhost +/// If you woud like your backend to load before legacy, or after autoadmin/localhost +/// Override the above function instead +/datum/permissions_controller/proc/_load_permissions_for(client/C) + PROTECTED_PROC(TRUE) + return FALSE + +/datum/permissions_controller/proc/check_for_rights(client/subject, rights_required) + if(!subject || !subject.holder) // Null and deadmins have no rights + return FALSE + if(rights_required) + return !!(get_rights_for(subject) & rights_required) + return TRUE + +/datum/permissions_controller/proc/get_rights_for(client/subject) + . = 0 + if(!subject || !subject.holder) + return + return get_rights_for_ckey(subject.ckey) + +/datum/permissions_controller/proc/get_rights_for_ckey(ckey) + if(ckey in legacy_admins) + if(legacy_admins[ckey] in legacy_ranks) + return legacy_ranks[legacy_admins[ckey]] + +/// Returns -1 if fewer, 0 if same, 1 if more +/datum/permissions_controller/proc/compare_rights(client/A, client/B) + if(!A && !B) // If both null, same + return 0 + if(!A) + return -1 + if(!B) + return 1 + + if(!A.holder && !B.holder) + return 0 + if(!A.holder) + return -1 + if(!B.holder) + return 1 + + var/A_rights = get_rights_for(A) + var/B_rights = get_rights_for(B) + if(A_rights == B_rights) + return 0 + if(A_rights > B_rights) + return 1 + return -1 + + +/datum/permissions_controller/proc/get_rank_name(client/subject) + var/ckey = subject.ckey + if(ckey in legacy_admins) + return legacy_admins[ckey] + +/datum/permissions_controller/proc/pp_data(mob/user) + var/user_rights = get_rights_for(user.client) + . = list() + .["admins"] = list() + for(var/legmin in legacy_admins) + var/data = list() + data["ckey"] = legmin + data["rank"] = legacy_admins[legmin] + var/rights = legacy_ranks[data["rank"]] + data["rights"] = rights2text(rights, seperator = " ") + var/can_edit = (rights & user_rights) == rights + data["protected_admin"] = (legmin in protected_admins) || !can_edit + data["protected_rank"] = (data["rank"] in protected_ranks) || !can_edit + data["deadminned"] = (legmin in deadmins) + .["admins"] |= list(data) + +// Functions used to modify permissions +// Returns true if permissions modification was handled + +/// This proc prompts the user for the name that want to add +/// Then uses should_add_admin(ckey) to determine if there is a reason +/// that ckey shouldn't be added (usually due to already being adminned) +/// make_admin(ckey) is then called to handle the actual adding +/datum/permissions_controller/proc/add_admin() + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + var/ckey = ckey(input("New admin's key","Admin key") as text|null) + if(!ckey) + return FALSE + + if(!should_add_admin(ckey)) + to_chat(usr, span_warning("Unable to admin [ckey]. Do they already hold a rank?"), confidential = TRUE) + return FALSE + + return make_admin(ckey) + +/datum/permissions_controller/proc/should_add_admin(ckey) + return !(ckey in legacy_admins) + +/// Returns true if the rank was changed +/datum/permissions_controller/proc/set_legacy_rank(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + var/list/rank_names = list() + var/usr_rights = get_rights_for(usr.client) + + rank_names += "*New Rank*" + for(var/R in legacy_ranks) + if((usr_rights & legacy_ranks[R]) == legacy_ranks[R]) // Cannot grant permissions you do not have + rank_names += R + var/new_rank = input("Please select a rank", "New rank") as null|anything in rank_names + if(new_rank == "*New Rank*") + new_rank = trim(input("Please input a new rank", "New custom rank") as text|null) + if(!new_rank) + return FALSE + var/old_rights = 0 + if(ckey in legacy_admins) + if(legacy_admins[ckey] in legacy_ranks) + old_rights = legacy_ranks[legacy_admins[ckey]] + if(!(new_rank in legacy_ranks)) + legacy_ranks[new_rank] = old_rights + legacy_admins[ckey] = new_rank + + var/m = "edited the admin rank of [ckey] to [new_rank] temporarily" + message_admins("[key_name_admin(usr)] [m]") + log_admin("[key_name(usr)] [m]") + + if(ckey in admin_datums) + var/datum/admins/holder = admin_datums[ckey] + holder.deactivate() + holder.activate() + return TRUE + if(ckey in deadmins) + return TRUE + if(ckey in GLOB.directory) + new /datum/admins(ckey, legacy_ranks[new_rank]) + return TRUE + return TRUE + +/datum/permissions_controller/proc/make_admin(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + set_legacy_rank(ckey) + return TRUE + +/datum/permissions_controller/proc/edit_rank(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(ckey in protected_admins) + to_chat(usr, span_warning("Editing this admin blocked by config"), confidential = TRUE) + return TRUE // Nothing was changed, but nothing should be changed + + if(!(ckey in legacy_admins)) + return FALSE + + set_legacy_rank(ckey) + return TRUE + +/datum/permissions_controller/proc/edit_perms(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(!(ckey in legacy_admins)) + return FALSE + var/rank = legacy_admins[ckey] + if(rank in protected_ranks) + to_chat(usr, span_warning("Editing this rank blocked by config"), confidential = TRUE) + return TRUE // Nothing was changed, but nothing should be changed + + if(alert("This will modify all admins with the same rank, are you sure you wish to continue?", "Confirmation", "Yes", "No") != "Yes") + return TRUE + + var/new_flags = input_bitfield(usr, "Permission flags
This will affect all admins with rank [rank]", "admin_flags", legacy_ranks[rank], 350, 590) + if(isnull(new_flags)) + return + legacy_ranks[rank] = new_flags + + var/m = "edited the admin rank of [rank] temporarily" + message_admins("[key_name_admin(usr)] [m]") + log_admin("[key_name(usr)] [m]") + + for(var/admin in admin_datums) + if((admin in legacy_admins) && legacy_admins[admin] == rank) + var/datum/admins/holder = admin_datums[ckey] + holder.deactivate() + holder.activate() + +/datum/permissions_controller/proc/remove_admin(ckey) + if(IsAdminAdvancedProcCall()) + var/msg = " has tried to elevate permissions!" + message_admins("[key_name_admin(usr)][msg]") + log_admin("[key_name(usr)][msg]") + return + if(!(ckey in legacy_admins)) + return FALSE + if(ckey in protected_admins) + to_chat(usr, span_warning("Editing this admin blocked by config"), confidential = TRUE) + return TRUE // Nothing was changed, but nothing should be changed + + if(alert("This will remove all admin access for the rest of the round. Are you sure?", "Confirmation", "Yes", "No", "Cancel") != "Yes") + return TRUE + + var/m = "removed [ckey] from the admin list temporarily" + message_admins("[key_name_admin(usr)] [m]") + log_admin("[key_name(usr)] [m]") + + legacy_admins[ckey] = null + if(ckey in admin_datums) + qdel(admin_datums[ckey]) + if(ckey in deadmins) + qdel(deadmins[ckey]) + +/* +checks if usr is an admin with at least ONE of the flags in rights_required. (Note, they don't need all the flags) +if rights_required == 0, then it simply checks if they are an admin. +if it doesn't return 1 and show_msg=1 it will prints a message explaining why the check has failed +generally it would be used like so: + +/proc/admin_proc() + if(!check_rights(R_ADMIN)) + return + to_chat(world, "you have enough rights!") + +NOTE: it checks usr! not src! So if you're checking somebody's rank in a proc which they did not call +use check_rights_for +*/ +/proc/check_rights(rights_required, show_msg=TRUE) + if(usr && usr.client) + if (check_rights_for(usr.client, rights_required)) + return TRUE + else + if(show_msg) + to_chat(usr, "Error: You do not have sufficient rights to do that. You require one of the following flags:[rights2text(rights_required," ")].", confidential=TRUE) + return FALSE + +//This proc checks whether subject has at least ONE of the rights specified in rights_required. +/proc/check_rights_for(client/subject, rights_required) + return GLOB.permissions.check_for_rights(subject, rights_required) + +//probably a bit iffy - will hopefully figure out a better solution +/proc/check_if_greater_rights_than(client/other) + if(usr && usr.client) + if(usr.client.holder) + if(!other || !other.holder) + return TRUE + return GLOB.permissions.compare_rights(usr.client, other) > 0 + return FALSE + +/proc/admin_keyword_to_flag(word, previous_rights=0) + var/flag = 0 + switch(ckey(word)) + if("buildmode","build") + flag = R_BUILDMODE + 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","rights") + 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","host","all") + flag = R_EVERYTHING + if("sound","sounds") + flag = R_SOUNDS + if("spawn","create") + flag = R_SPAWN + if("autologin", "autoadmin") + flag = R_AUTOLOGIN + if("dev") + flag = R_DEV + if("dbranks", "persistperms") + flag = R_PERSIST_PERMS + if("@","prev") + flag = previous_rights + return flag diff --git a/code/modules/admin/secrets.dm b/code/modules/admin/secrets.dm index 49bac28282a5..bac4c1ca8dc2 100644 --- a/code/modules/admin/secrets.dm +++ b/code/modules/admin/secrets.dm @@ -115,10 +115,10 @@ if("show_admins") var/dat = "Current admins:
" - if(GLOB.admin_datums) - for(var/ckey in GLOB.admin_datums) - var/datum/admins/D = GLOB.admin_datums[ckey] - dat += "[ckey] - [D.rank.name]
" + if(GLOB.permissions.admin_datums) + for(var/ckey in GLOB.permissions.admin_datums) + var/datum/admins/D = GLOB.permissions.admin_datums[ckey] + dat += "[ckey] - [D.rank_name()]
" dat += "" usr << browse(dat, "window=showadmins;size=600x500") diff --git a/code/modules/admin/sql_ban_system.dm b/code/modules/admin/sql_ban_system.dm index 8199bacb3c70..07c62dd69230 100644 --- a/code/modules/admin/sql_ban_system.dm +++ b/code/modules/admin/sql_ban_system.dm @@ -19,7 +19,7 @@ else var/values = list( "player_ckey" = player_ckey, - "must_apply_to_admins" = !!(GLOB.admin_datums[player_ckey] || GLOB.deadmins[player_ckey]), + "must_apply_to_admins" = !!(GLOB.permissions.admin_datums[player_ckey] || GLOB.permissions.deadmins[player_ckey]), ) var/sql_roles if(islist(roles)) @@ -113,7 +113,7 @@ if(C && istype(C)) C.ban_cache = list() var/is_admin = FALSE - if(GLOB.admin_datums[C.ckey] || GLOB.deadmins[C.ckey]) + if(GLOB.permissions.admin_datums[C.ckey] || GLOB.permissions.deadmins[C.ckey]) is_admin = TRUE var/datum/DBQuery/query_build_ban_cache = SSdbcore.NewQuery( "SELECT [format_table_name("ban")].role, applies_to_admins FROM [format_table_name("ban")] WHERE ckey = :ckey AND unbanned_datetime IS NULL AND (expiration_time IS NULL OR expiration_time > NOW())", @@ -487,7 +487,7 @@ if(query_check_adminban_count.NextRow()) var/adminban_count = text2num(query_check_adminban_count.item[1]) var/max_adminbans = MAX_ADMINBANS_PER_ADMIN - if(R_EVERYTHING && !(R_EVERYTHING & rank.can_edit_rights)) //edit rights are a more effective way to check hierarchical rank since many non-headmins have R_PERMISSIONS now + if(check_rights(R_PERMISSIONS, FALSE)) //edit rights are a more effective way to check hierarchical rank since many non-headmins have R_PERMISSIONS now max_adminbans = MAX_ADMINBANS_PER_HEADMIN if(adminban_count >= max_adminbans) to_chat(usr, span_danger("You've already logged [max_adminbans] admin ban(s) or more. Do not abuse this function!"), confidential=TRUE) @@ -557,7 +557,7 @@ if(C) build_ban_cache(C) to_chat(C, "[span_boldannounce("You have been [applies_to_admins ? "admin " : ""]banned by [usr.client.key] from [roles_to_ban[1] == "Server" ? "the server" : " Roles: [roles_to_ban.Join(", ")]"].\nReason: [reason]")]
[span_danger("This ban is [isnull(duration) ? "permanent." : "temporary, it will be removed in [time_message]."] The round ID is [GLOB.round_id].")]
[span_danger("To appeal this ban go to [appeal_url]")]", confidential=TRUE) - if(GLOB.admin_datums[C.ckey] || GLOB.deadmins[C.ckey]) + if(GLOB.permissions.admin_datums[C.ckey] || GLOB.permissions.deadmins[C.ckey]) is_admin = TRUE if(roles_to_ban[1] == "Server" && (!is_admin || (is_admin && applies_to_admins))) qdel(C) @@ -567,7 +567,7 @@ if(i.address == player_ip || i.computer_id == player_cid) build_ban_cache(i) to_chat(i, "[span_boldannounce("You have been [applies_to_admins ? "admin " : ""]banned by [usr.client.key] from [roles_to_ban[1] == "Server" ? "the server" : " Roles: [roles_to_ban.Join(", ")]"].\nReason: [reason]")]
[span_danger("This ban is [isnull(duration) ? "permanent." : "temporary, it will be removed in [time_message]."] The round ID is [GLOB.round_id].")]
[span_danger("To appeal this ban go to [appeal_url]")]") - if(GLOB.admin_datums[i.ckey] || GLOB.deadmins[i.ckey]) + if(GLOB.permissions.admin_datums[i.ckey] || GLOB.permissions.deadmins[i.ckey]) is_admin = TRUE if(roles_to_ban[1] == "Server" && (!is_admin || (is_admin && applies_to_admins))) qdel(i) @@ -805,7 +805,7 @@ if(query_check_adminban_count.NextRow()) var/adminban_count = text2num(query_check_adminban_count.item[1]) var/max_adminbans = MAX_ADMINBANS_PER_ADMIN - if(R_EVERYTHING && !(R_EVERYTHING & rank.can_edit_rights)) //edit rights are a more effective way to check hierarchical rank since many non-headmins have R_PERMISSIONS now + if(check_rights(R_PERMISSIONS, FALSE)) //edit rights are a more effective way to check hierarchical rank since many non-headmins have R_PERMISSIONS now max_adminbans = MAX_ADMINBANS_PER_HEADMIN if(adminban_count >= max_adminbans) to_chat(usr, span_danger("You've already logged [max_adminbans] admin ban(s) or more. Do not abuse this function!"), confidential=TRUE) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 781db60b883d..dbbf4ad8a06f 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -240,23 +240,14 @@ log_admin("[key_name(usr)] has triggered an event. ([E.name])") return - else if(href_list["editrightsbrowser"]) - edit_admin_permissions(0) + // else if(href_list["editrightsbrowser"]) + // edit_admin_permissions(0) - else if(href_list["editrightsbrowserlog"]) - edit_admin_permissions(1, href_list["editrightstarget"], href_list["editrightsoperation"], href_list["editrightspage"]) + // else if(href_list["editrightsbrowserlog"]) + // edit_admin_permissions(1, href_list["editrightstarget"], href_list["editrightsoperation"], href_list["editrightspage"]) - if(href_list["editrightsbrowsermanage"]) - if(href_list["editrightschange"]) - change_admin_rank(ckey(href_list["editrightschange"]), href_list["editrightschange"], TRUE) - else if(href_list["editrightsremove"]) - remove_admin(ckey(href_list["editrightsremove"]), href_list["editrightsremove"], TRUE) - else if(href_list["editrightsremoverank"]) - remove_rank(href_list["editrightsremoverank"]) - edit_admin_permissions(2) - - else if(href_list["editrights"]) - edit_rights_topic(href_list) + // else if(href_list["editrights"]) + // edit_rights_topic(href_list) else if(href_list["gamemode_panel"]) if(!check_rights(R_ADMIN)) diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm index 961d46b97c39..7a922e7856e9 100644 --- a/code/modules/admin/verbs/adminpm.dm +++ b/code/modules/admin/verbs/adminpm.dm @@ -260,7 +260,7 @@ if(irc) log_admin_private("PM: [key_name(src)]->IRC: [rawmsg]") - for(var/client/X in GLOB.admins) + for(var/client/X in GLOB.permissions.admins) to_chat(X, type = MESSAGE_TYPE_ADMINPM, html = span_notice("PM: [key_name(src, X, 0)]->External: [keywordparsedmsg]"), @@ -269,7 +269,7 @@ window_flash(recipient, ignorepref = TRUE) log_admin_private("PM: [key_name(src)]->[key_name(recipient)]: [rawmsg]") //we don't use message_admins here because the sender/receiver might get it too - for(var/client/X in GLOB.admins) + for(var/client/X in GLOB.permissions.admins) if(X.key!=key && X.key!=recipient.key) //check client/X is an admin and isn't the sender or recipientD to_chat(X, type = MESSAGE_TYPE_ADMINPM, diff --git a/code/modules/admin/verbs/adminsay.dm b/code/modules/admin/verbs/adminsay.dm index cd0cffb8abd1..fc2b77a9ee28 100644 --- a/code/modules/admin/verbs/adminsay.dm +++ b/code/modules/admin/verbs/adminsay.dm @@ -15,7 +15,7 @@ msg = keywords_lookup(msg) var/custom_asay_color = (CONFIG_GET(flag/allow_admin_asaycolor) && prefs.asaycolor) ? "" : null // Yogs -- yogs asay msg = "[span_prefix("ADMIN:")] [key_name(usr, 1)] [ADMIN_FLW(mob)]: [custom_asay_color][msg][custom_asay_color ? "":null]" - to_chat(GLOB.admins, + to_chat(GLOB.permissions.admins, type = MESSAGE_TYPE_ADMINCHAT, html = msg, confidential = TRUE) diff --git a/code/modules/admin/verbs/deadsay.dm b/code/modules/admin/verbs/deadsay.dm index d08f9ec0fd84..ed433033fe5b 100644 --- a/code/modules/admin/verbs/deadsay.dm +++ b/code/modules/admin/verbs/deadsay.dm @@ -19,7 +19,7 @@ if (!msg) return - var/rank_name = holder.rank + var/rank_name = holder.rank_name() var/admin_name = key var/follow_link = "" if(holder.fakekey) diff --git a/code/modules/admin/verbs/debug.dm b/code/modules/admin/verbs/debug.dm index a22bf4158d65..014bd9421ab1 100644 --- a/code/modules/admin/verbs/debug.dm +++ b/code/modules/admin/verbs/debug.dm @@ -894,7 +894,7 @@ GLOBAL_PROTECT(AdminProcCallSpamPrevention) if("Players") to_chat(usr, jointext(GLOB.player_list,","), confidential=TRUE) if("Admins") - to_chat(usr, jointext(GLOB.admins,","), confidential=TRUE) + to_chat(usr, jointext(GLOB.permissions.admins,","), confidential=TRUE) if("Mobs") to_chat(usr, jointext(GLOB.mob_list,","), confidential=TRUE) if("Living Mobs") diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 950bf0ff96c6..8afc26c02bf1 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -92,8 +92,7 @@ if(confirm !="Yes") return - refresh_admin_files() //yogs - DB support - load_admins() + GLOB.permissions.start() SSblackbox.record_feedback("tally", "admin_verb", 1, "Reload All Admins") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! message_admins("[key_name_admin(usr)] manually reloaded admins") diff --git a/code/modules/admin/verbs/pray.dm b/code/modules/admin/verbs/pray.dm index 27a27f65c38b..a8dbb77b682c 100644 --- a/code/modules/admin/verbs/pray.dm +++ b/code/modules/admin/verbs/pray.dm @@ -40,9 +40,9 @@ prayer_type = "SPIRITUAL PRAYER" var/msg_tmp = msg - msg = span_adminnotice("[icon2html(cross, GLOB.admins)][prayer_type][deity ? " (to [deity])" : ""]: [ADMIN_FULLMONTY(src)] [ADMIN_SC(src)]: [msg]") + msg = span_adminnotice("[icon2html(cross, GLOB.permissions.admins)][prayer_type][deity ? " (to [deity])" : ""]: [ADMIN_FULLMONTY(src)] [ADMIN_SC(src)]: [msg]") - for(var/client/C in GLOB.admins) + for(var/client/C in GLOB.permissions.admins) if(C.prefs.chat_toggles & CHAT_PRAYER) to_chat(C, msg, confidential=TRUE) if(C.prefs.toggles & SOUND_PRAYERS) @@ -57,7 +57,7 @@ /proc/message_centcom(text, mob/sender) var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN) msg = span_adminnotice("CENTCOM:[ADMIN_FULLMONTY(sender)] [ADMIN_CENTCOM_REPLY(sender)]: [msg]") - to_chat(GLOB.admins, msg, confidential = TRUE) + to_chat(GLOB.permissions.admins, msg, confidential = TRUE) for(var/obj/machinery/computer/communications/console in GLOB.machines) console.override_cooldown() @@ -65,7 +65,7 @@ /proc/message_syndicate(text, mob/sender) var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN) msg = span_adminnotice("SYNDICATE:[ADMIN_FULLMONTY(sender)] [ADMIN_SYNDICATE_REPLY(sender)]: [msg]") - to_chat(GLOB.admins, msg, confidential = TRUE) + to_chat(GLOB.permissions.admins, msg, confidential = TRUE) for(var/obj/machinery/computer/communications/console in GLOB.machines) console.override_cooldown() @@ -73,13 +73,13 @@ /proc/nuke_request(text, mob/sender) var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN) msg = span_adminnotice("NUKE CODE REQUEST:[ADMIN_FULLMONTY(sender)] [ADMIN_CENTCOM_REPLY(sender)] [ADMIN_SET_SD_CODE] [ADMIN_SET_BC_CODE]: [msg]") - to_chat(GLOB.admins, msg, confidential = TRUE) + to_chat(GLOB.permissions.admins, msg, confidential = TRUE) for(var/obj/machinery/computer/communications/console in GLOB.machines) console.override_cooldown() /proc/Clown_announce(text , mob/Sender) var/msg = copytext_char(sanitize(text), 1, MAX_MESSAGE_LEN) msg = span_adminnotice("CLOWN PLANET:[ADMIN_FULLMONTY(Sender)] [ADMIN_SYNDICATE_REPLY(Sender)]: [msg]") - to_chat(GLOB.admins, msg, confidential=TRUE) + to_chat(GLOB.permissions.admins, msg, confidential=TRUE) for(var/obj/machinery/computer/communications/C in GLOB.machines) C.override_cooldown() diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 28315341329c..af91100cfed1 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -241,32 +241,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( tgui_panel.send_connected() GLOB.ahelp_tickets.ClientLogin(src) - var/connecting_admin = FALSE //because de-admined admins connecting should be treated like admins. - //Admin Authorisation - holder = GLOB.admin_datums[ckey] - if(holder) - if(!holder.associate(src, FALSE)) // Prevent asking for MFA at this point, it likely won't work - holder = null - connecting_admin = TRUE - else if(GLOB.deadmins[ckey]) - add_verb(src, /client/proc/readmin) - connecting_admin = TRUE - if(CONFIG_GET(flag/autoadmin)) - if(!GLOB.admin_datums[ckey]) - var/datum/admin_rank/autorank - for(var/datum/admin_rank/R in GLOB.admin_ranks) - if(R.name == CONFIG_GET(string/autoadmin_rank)) - autorank = R - break - if(!autorank) - to_chat(world, "Autoadmin rank not found") - else - new /datum/admins(autorank, ckey) - if(CONFIG_GET(flag/enable_localhost_rank) && !connecting_admin) - var/localhost_addresses = list("127.0.0.1", "::1") - if(isnull(address) || (address in localhost_addresses)) - var/datum/admin_rank/localhost_rank = new("!localhost!", R_EVERYTHING, R_DBRANKS, R_EVERYTHING) //+EVERYTHING -DBRANKS *EVERYTHING - new /datum/admins(localhost_rank, ckey, 1, 1) + var/connecting_admin = GLOB.permissions.load_permissions_for(src) //because de-admined admins connecting should be treated like admins. + if(connecting_admin && !holder) + stack_trace("[ckey] is an admin but has no holder") // yogs start - mentor stuff if(ckey in GLOB.mentor_datums) @@ -523,8 +500,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if(holder) adminGreet(1) holder.owner = null - GLOB.admins -= src - if (!GLOB.admins.len && SSticker.IsRoundInProgress()) //Only report this stuff if we are currently playing. + GLOB.permissions.admins -= src + if (!GLOB.permissions.admins.len && SSticker.IsRoundInProgress()) //Only report this stuff if we are currently playing. var/cheesy_message = pick( "I have no admins online!",\ "I'm all alone :(",\ @@ -541,6 +518,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( ) send2irc("Server", "[cheesy_message] (No admins online)") + qdel(holder) + if(ckey in GLOB.permissions.deadmins) + qdel(GLOB.permissions.deadmins[ckey]) GLOB.ahelp_tickets.ClientLogout(src) GLOB.directory -= ckey @@ -588,10 +568,10 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( related_accounts_cid += "[query_get_related_cid.item[1]], " qdel(query_get_related_cid) var/admin_rank = "Player" - if (src.holder && src.holder.rank) - admin_rank = src.holder.rank.name + if (src.holder) + admin_rank = src.holder.rank_name() else - if (!GLOB.deadmins[ckey] && check_randomizer(connectiontopic)) + if (!GLOB.permissions.deadmins[ckey] && check_randomizer(connectiontopic)) return var/new_player var/datum/DBQuery/query_client_in_db = SSdbcore.NewQuery( @@ -602,7 +582,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( qdel(query_client_in_db) return if(!query_client_in_db.NextRow()) - if (CONFIG_GET(flag/panic_bunker) && !holder && !GLOB.deadmins[ckey]) + if (CONFIG_GET(flag/panic_bunker) && !holder && !GLOB.permissions.deadmins[ckey]) log_access("Failed Login: [key] - New account attempting to connect during panic bunker") message_admins(span_adminnotice("Failed Login: [key] - New account attempting to connect during panic bunker")) to_chat(src, CONFIG_GET(string/panic_bunker_message)) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 4210ee673dce..f327bdd07eef 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -1355,7 +1355,7 @@ GLOBAL_LIST_EMPTY(preferences_datums) if(href_list["bancheck"]) var/list/ban_details = is_banned_from_with_details(user.ckey, user.client.address, user.client.computer_id, href_list["bancheck"]) var/admin = FALSE - if(GLOB.admin_datums[user.ckey] || GLOB.deadmins[user.ckey]) + if(GLOB.permissions.admin_datums[user.ckey] || GLOB.permissions.deadmins[user.ckey]) admin = TRUE for(var/i in ban_details) if(admin && !text2num(i["applies_to_admins"])) diff --git a/code/modules/client/verbs/who.dm b/code/modules/client/verbs/who.dm index 4b3207126785..bc72a376f7a7 100644 --- a/code/modules/client/verbs/who.dm +++ b/code/modules/client/verbs/who.dm @@ -62,8 +62,8 @@ var/msg = "Current Admins:\n" if(holder) - for(var/client/C in GLOB.admins) - msg += "\t[C] is a [C.holder.rank]" + for(var/client/C in GLOB.permissions.admins) + msg += "\t[C] is a [C.holder.rank_name()]" if(C.holder.fakekey) msg += " (as [C.holder.fakekey])" @@ -79,11 +79,11 @@ msg += " (AFK)" msg += "\n" else - for(var/client/C in GLOB.admins) + for(var/client/C in GLOB.permissions.admins) if(C.is_afk()) continue //Don't show afk admins to adminwho if(!C.holder.fakekey) - msg += "\t[C] is a [C.holder.rank]\n" + msg += "\t[C] is a [C.holder.rank_name()]\n" msg += span_info("Adminhelps are also sent to Discord. If no admins are available in game adminhelp anyways and an admin on Discord will see it and respond.") //yogs - IRC -> discord to_chat(src, msg) diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm index 1d7e46d33eb0..549bd241ff05 100644 --- a/code/modules/mob/dead/new_player/new_player.dm +++ b/code/modules/mob/dead/new_player/new_player.dm @@ -143,7 +143,7 @@ LateChoices() return - if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(key) in GLOB.admin_datums))) + if(SSticker.queued_players.len || (relevant_cap && living_player_count() >= relevant_cap && !(ckey(key) in GLOB.permissions.admin_datums))) //yogs start -- donors bypassing the queue if(ckey(key) in get_donators()) to_chat(usr, span_notice("Because you are a donator, you have bypassed the queue! Thank you for donating!")) @@ -176,7 +176,7 @@ to_chat(usr, span_notice("There is an administrative lock on entering the game!")) return - if(SSticker.queued_players.len && !(ckey(key) in GLOB.admin_datums)) + if(SSticker.queued_players.len && !(ckey(key) in GLOB.permissions.admin_datums)) if((living_player_count() >= relevant_cap) || (src != SSticker.queued_players[1])) to_chat(usr, span_warning("Server is full.")) return diff --git a/code/modules/mob/dead/new_player/poll.dm b/code/modules/mob/dead/new_player/poll.dm index 35ae961300b8..8d3e0bc14bfd 100644 --- a/code/modules/mob/dead/new_player/poll.dm +++ b/code/modules/mob/dead/new_player/poll.dm @@ -373,7 +373,7 @@ /mob/dead/new_player/proc/poll_rank() . = "Player" if(client.holder) - . = client.holder.rank.name + . = client.holder.rank_name() /mob/dead/new_player/proc/vote_rig_check() @@ -423,7 +423,7 @@ var/datum/admins/holder = client.holder var/rank = "Player" if (holder) - rank = holder.rank.name + rank = holder.rank_name() var/ckey = client.ckey var/address = client.address @@ -566,7 +566,7 @@ qdel(query_numval_hasvoted) var/adminrank = "Player" if(client.holder) - adminrank = client.holder.rank.name + adminrank = client.holder.rank_name() if(isnull(rating)) rating = "null" var/datum/DBQuery/query_numval_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime ,pollid ,optionid ,ckey ,ip ,adminrank, rating) VALUES (Now(), :pollid, :optionid, :ckey, INET_ATON(:address), :adminrank, :rating", list("pollid" = pollid, "optionid" = optionid, "ckey" = ckey, "address" = client.address, "adminrank" = adminrank, "rating" = rating)) @@ -611,7 +611,7 @@ return 2 var/adminrank = "Player" if(!QDELETED(client) && client.holder) - adminrank = client.holder.rank.name + adminrank = client.holder.rank_name() var/datum/DBQuery/query_multi_vote = SSdbcore.NewQuery("INSERT INTO [format_table_name("poll_vote")] (datetime, pollid, optionid, ckey, ip, adminrank) VALUES (Now(), :pollid, :optionid, :ckey, INET_ATON(:address), :adminrank)", list("pollid" = pollid, "optionid" = optionid, "ckey" = ckey, "address" = client.address, "adminrank" = adminrank)) if(!query_multi_vote.warn_execute()) qdel(query_multi_vote) @@ -619,4 +619,4 @@ qdel(query_multi_vote) if(!QDELETED(usr)) usr << browse(null,"window=playerpoll") - return 0 \ No newline at end of file + return 0 diff --git a/code/modules/paperwork/faxmachine.dm b/code/modules/paperwork/faxmachine.dm index 1b2c0c9da68a..49c9955a40b2 100644 --- a/code/modules/paperwork/faxmachine.dm +++ b/code/modules/paperwork/faxmachine.dm @@ -168,7 +168,7 @@ GLOBAL_LIST_EMPTY(adminfaxes) /obj/machinery/photocopier/faxmachine/proc/send_adminmessage(var/mob/sender, var/faxname, var/obj/item/sent, var/reply_type, font_colour="#006100") var/msg = "[faxname]: [key_name(sender, 1)] (PP) (VV) (SM) (JMP) (REPLY): Receiving '[sent.name]' via secure connection ... view message" msg = span_admin("[msg]") - to_chat(GLOB.admins, + to_chat(GLOB.permissions.admins, type = MESSAGE_TYPE_ADMINLOG, html = msg, confidential = TRUE) diff --git a/code/modules/shuttle/computer.dm b/code/modules/shuttle/computer.dm index 829da6224a1c..93998c08e31b 100644 --- a/code/modules/shuttle/computer.dm +++ b/code/modules/shuttle/computer.dm @@ -124,7 +124,7 @@ return COOLDOWN_START(src, request_cooldown, 1 MINUTES) to_chat(usr, span_notice("Your request has been received by CentCom.")) - to_chat(GLOB.admins, "FERRY: [ADMIN_LOOKUPFLW(usr)] (Move Ferry) is requesting to move the transport ferry to CentCom.") + to_chat(GLOB.permissions.admins, "FERRY: [ADMIN_LOOKUPFLW(usr)] (Move Ferry) is requesting to move the transport ferry to CentCom.") return TRUE /obj/machinery/computer/shuttle/emag_act(mob/user) diff --git a/code/modules/tgui/states/permissions.dm b/code/modules/tgui/states/permissions.dm new file mode 100644 index 000000000000..a129c4a187ea --- /dev/null +++ b/code/modules/tgui/states/permissions.dm @@ -0,0 +1,15 @@ +/** + * tgui state: admin_state + * + * Checks that the user is an admin, end-of-story. + * + * Copyright (c) 2020 Aleksej Komarov + * SPDX-License-Identifier: MIT + */ + +GLOBAL_DATUM_INIT(permissions_state, /datum/ui_state/permissions_state, new) + +/datum/ui_state/permissions_state/can_use_topic(src_object, mob/user) + if(check_rights_for(user.client, R_PERMISSIONS)) + return UI_INTERACTIVE + return UI_CLOSE diff --git a/config/admin_ranks.txt b/config/admin_ranks.txt index 46ea5245f847..ea37384eaef6 100644 --- a/config/admin_ranks.txt +++ b/config/admin_ranks.txt @@ -4,13 +4,12 @@ # Rank is CASE-SENSITIVE, all punctuation save for '-', '_' and '@' will be stripped so spaces don't matter. # # You can then define permissions for each rank by adding a '=' followed by keywords # # These keywords represent groups of verbs and abilities. # -# keywords are preceded by a '+', '-' or '*' + adds permissions, - takes them away. # -# * is used only with SQL-based admin loading to denote what permissions the rank is allowed to edit # +# keywords are preceded by a '+'. # # +@ (or +prev) is a special shorthand which adds all the rights of the rank above it. # # Ranks with no keywords will just be given the most basic verbs and abilities ~Carn # ############################################################################################################## # PLEASE NOTE: depending on config options, some abilities will be unavailable regardless if you have permission to use them! -# If SQL-based admin loading is enabled, ranks and their keywords listed here will be loaded first and override any with the same name loaded from the database. +# If network-based admin loading is enabled, ranks and their keywords listed here will be used for protected admins and temporary rank changes # Follow the format below when documenting new keywords so the server tools may parse it @@ -35,37 +34,16 @@ # END_KEYWORDS -Host = +EVERYTHING *EVERYTHING +Host = +EVERYTHING -Council Member = +EVERYTHING *EVERYTHING +Council Member = +EVERYTHING -Head Developer = +EVERYTHING *EVERYTHING +Head Developer = +EVERYTHING -SysOp = +EVERYTHING *EVERYTHING - -#Senior Admin = +EVERYTHING *EVERYTHING -DEV - -#Community Manager = +EVERYTHING *EVERYTHING -DEV - -#Primary Admin = +STEALTH +ADMIN +BASIC +BAN +SPAWN +VAREDIT +DEBUG +SERVER +FUN +POSSESS +BUILD +TICKET +SOUND - -#Administrator-Mainterino = +STEALTH +ADMIN +BASIC +BAN +SPAWN +VAREDIT +DEBUG +SERVER +FUN +TICKET +BUILD +POLL +DEV - -#Administrator = +STEALTH +ADMIN +BASIC +BAN +SPAWN +VAREDIT +DEBUG +SERVER +FUN +TICKET +BUILD - -#Moderator-Mainterino = +STEALTH +ADMIN +VAREDIT +DEBUG +SERVER +BASIC +SPAWN +POLL +BAN +DEV - -#Moderator = +STEALTH +ADMIN +BASIC +BAN +TICKET - -#Retired Admin = +ADMIN +STEALTH +BASIC +SERVER +BAN +TICKET +VAREDIT - -#Admin Observer = +STEALTH - -#Maintainer = +STEALTH +ADMIN +VAREDIT +DEBUG +SERVER +BASIC +SPAWN +POLL -AUTOLOGIN +DEV - -#LogDiver = - -#Forum Mod = - -#Bot = +EVERYTHING *EVERYTHING +SysOp = +EVERYTHING +Administrator = +STEALTH +ADMIN +BASIC +BAN +SPAWN +VAREDIT +DEBUG +SERVER +FUN +TICKET +BUILD +AUTOLOGIN + +Admin Observer = +STEALTH +AUTOLOGIN + +Maintainer = +STEALTH +ADMIN +VAREDIT +DEBUG +SERVER +BASIC +SPAWN +POLL +DEV diff --git a/config/config.txt b/config/config.txt index c6ee4f68bef8..5e8fc0cc0aab 100644 --- a/config/config.txt +++ b/config/config.txt @@ -25,10 +25,6 @@ ROUND_END_COUNTDOWN 90 ##Uncomment this to stop any admins loaded by the legacy system from having their rank edited by the permissions panel PROTECT_LEGACY_ADMINS -##Uncomment this to have admin ranks only loaded from the legacy admin_ranks.txt -##If enabled, each time admins are loaded ranks the database will be updated with the current ranks and their flags -#LOAD_LEGACY_RANKS_ONLY - ## Uncomment this entry to have certain jobs require your account to be at least a certain number of days old to select. You can configure the exact age requirement for different jobs by editing ## the minimal_player_age variable in the files in folder /code/game/jobs/job/.. for the job you want to edit. Set minimal_player_age to 0 to disable age requirement for that job. ## REQUIRES the database set up to work. Keep it hashed if you don't have a database set up. diff --git a/config/private_default.txt b/config/private_default.txt index cec9cc86226e..1363c001bb95 100644 --- a/config/private_default.txt +++ b/config/private_default.txt @@ -19,10 +19,11 @@ SERVERSQLNAME yogstation ## Put on byond hub: Uncomment this to put your server on the byond hub. #HUB -## Comment this out if you want to use the SQL based admin system, the legacy system uses admins.txt. -## You need to set up your database to use the SQL based system. -## This flag is automatically enabled if SQL_ENABLED isn't -ADMIN_LEGACY_SYSTEM +## Determines the backend provider of admin rank data +# Use 'database' to load from an SQL database, requires SQL_ENABLED +# Use 'forums' to load from the Xenforo plugin, requires XENFORO_KEY +# Any other value, or missing the required configuration settings will use the legacy system only +PERMISSIONS_BACKEND none ##Uncomment this to stop any ranks loaded by the legacy system from having their flags edited by the permissions panel # PROTECT_LEGACY_RANKS diff --git a/config/private_server.txt b/config/private_server.txt index 4a7d2f7cb851..23633030ec14 100644 --- a/config/private_server.txt +++ b/config/private_server.txt @@ -19,10 +19,11 @@ SERVERSQLNAME yogstation ## Put on byond hub: Uncomment this to put your server on the byond hub. HUB -## Comment this out if you want to use the SQL based admin system, the legacy system uses admins.txt. -## You need to set up your database to use the SQL based system. -## This flag is automatically enabled if SQL_ENABLED isn't -# ADMIN_LEGACY_SYSTEM +## Determines the backend provider of admin rank data +# Use 'database' to load from an SQL database, requires SQL_ENABLED +# Use 'forums' to load from the Xenforo plugin, requires XENFORO_KEY +# Any other value, or missing the required configuration settings will use the legacy system only +PERMISSIONS_BACKEND forums ##Uncomment this to stop any ranks loaded by the legacy system from having their flags edited by the permissions panel PROTECT_LEGACY_RANKS diff --git a/tgui/packages/tgui/interfaces/PermissionsPanel.js b/tgui/packages/tgui/interfaces/PermissionsPanel.js new file mode 100644 index 000000000000..ad32c4c6a078 --- /dev/null +++ b/tgui/packages/tgui/interfaces/PermissionsPanel.js @@ -0,0 +1,88 @@ +import { useBackend, useLocalState } from "../backend"; +import { Box, Button, Collapsible, Flex, Section, Tabs } from "../components"; +import { Layout, Window } from "../layouts"; +import { createLogger } from "../logging"; + +const logger = createLogger('PermissionsPanel'); + +export const PermissionsPanel = (props, context) => { + const { act, data } = useBackend(context); + return ( + + + + {data.admins.map((admin, i) => ( + + ))} + + + ); +}; + +export const AdminDisplay = props => { + const { data, act } = props; + const { + ckey, + rank, + protected_admin, + protected_rank, + rights, + deadminned, + } = data; + return ( +
act('removeAdmin', { + ckey: ckey, + })} /> + )} > + + + + act('editPerms', { + ckey: ckey, + })} /> + )}> + { rights } + +
+ ); +}; diff --git a/yogstation.dme b/yogstation.dme index 64e9489b28fd..13365d848b91 100644 --- a/yogstation.dme +++ b/yogstation.dme @@ -1301,7 +1301,6 @@ #include "code\game\turfs\space\transit.dm" #include "code\modules\admin\admin.dm" #include "code\modules\admin\admin_investigate.dm" -#include "code\modules\admin\admin_ranks.dm" #include "code\modules\admin\admin_verbs.dm" #include "code\modules\admin\adminmenu.dm" #include "code\modules\admin\antag_panel.dm" @@ -1327,6 +1326,9 @@ #include "code\modules\admin\team_panel.dm" #include "code\modules\admin\topic.dm" #include "code\modules\admin\whitelist.dm" +#include "code\modules\admin\permissions\database.dm" +#include "code\modules\admin\permissions\forums.dm" +#include "code\modules\admin\permissions\permissions.dm" #include "code\modules\admin\verbs\adminjump.dm" #include "code\modules\admin\verbs\adminpm.dm" #include "code\modules\admin\verbs\atmosdebug.dm" @@ -3248,6 +3250,7 @@ #include "code\modules\tgui\states\not_incapacitated.dm" #include "code\modules\tgui\states\notcontained.dm" #include "code\modules\tgui\states\observer.dm" +#include "code\modules\tgui\states\permissions.dm" #include "code\modules\tgui\states\physical.dm" #include "code\modules\tgui\states\self.dm" #include "code\modules\tgui\states\zlevel.dm" @@ -3507,7 +3510,6 @@ #include "yogstation\code\game\turfs\simulated\minerals.dm" #include "yogstation\code\game\turfs\simulated\floor\fancy_floor.dm" #include "yogstation\code\modules\admin\admin.dm" -#include "yogstation\code\modules\admin\admin_ranks.dm" #include "yogstation\code\modules\admin\admin_verbs.dm" #include "yogstation\code\modules\admin\holder2.dm" #include "yogstation\code\modules\admin\moja.dm" diff --git a/yogstation/code/__HELPERS/mobs.dm b/yogstation/code/__HELPERS/mobs.dm index 040a51ef40c1..8240ba02db12 100644 --- a/yogstation/code/__HELPERS/mobs.dm +++ b/yogstation/code/__HELPERS/mobs.dm @@ -40,12 +40,12 @@ if(ismob(user)) var/mob/temp = user if(temp) - return (temp.ckey in GLOB.deadmins) + return (temp.ckey in GLOB.permissions.deadmins) if(istype(user, /client)) var/client/temp = user if(temp) - return (temp.ckey in GLOB.deadmins) + return (temp.ckey in GLOB.permissions.deadmins) return FALSE diff --git a/yogstation/code/datums/world_topic.dm b/yogstation/code/datums/world_topic.dm index 29598ee600c7..54d31600e0d3 100644 --- a/yogstation/code/datums/world_topic.dm +++ b/yogstation/code/datums/world_topic.dm @@ -5,7 +5,7 @@ GLOBAL_VAR_INIT(mentornoot, FALSE) require_comms_key = TRUE /datum/world_topic/asay/Run(list/input) - to_chat(GLOB.admins, span_adminsay("[span_prefix("DISCORD:")] [input["admin"]]: [span_message("[input["asay"]]")]"), confidential=TRUE) + to_chat(GLOB.permissions.admins, span_adminsay("[span_prefix("DISCORD:")] [input["admin"]]: [span_message("[input["asay"]]")]"), confidential=TRUE) /datum/world_topic/ooc keyword = "ooc" @@ -29,7 +29,7 @@ GLOBAL_VAR_INIT(mentornoot, FALSE) /datum/world_topic/adminwho/Run(list/input) var/list/message = list("Admins: ") var/list/admin_keys = list() - for(var/adm in GLOB.admins) + for(var/adm in GLOB.permissions.admins) var/client/C = adm if(input["adminchannel"]) admin_keys += "[C][C.holder.fakekey ? "(Stealth)" : ""][C.is_afk() ? "(AFK)" : ""]" @@ -66,7 +66,7 @@ GLOBAL_VAR_INIT(mentornoot, FALSE) require_comms_key = TRUE /datum/world_topic/msay/Run(list/input) - to_chat(GLOB.admins | GLOB.mentors, "[span_prefix("DISCORD MENTOR:")] [input["admin"]]: [span_message("[input["msay"]]")]") + to_chat(GLOB.permissions.admins | GLOB.mentors, "[span_prefix("DISCORD MENTOR:")] [input["admin"]]: [span_message("[input["msay"]]")]") /datum/world_topic/mhelp keyword = "mhelp" @@ -86,7 +86,7 @@ GLOBAL_VAR_INIT(mentornoot, FALSE) SEND_SOUND(C, sound('sound/items/bikehorn.ogg')) to_chat(C, "Mentor PM from-[discord_mentor_link(from, from_id)]: [msg]") var/show_char_recip = !C.is_mentor() && CONFIG_GET(flag/mentors_mobname_only) - for(var/client/X in GLOB.mentors | GLOB.admins) + for(var/client/X in GLOB.mentors | GLOB.permissions.admins) if(X != C) to_chat(X, "Mentor PM: [discord_mentor_link(from, from_id)]->[key_name_mentor(C, X, 0, 0, show_char_recip)]: [msg]") return 1 diff --git a/yogstation/code/modules/admin/admin_ranks.dm b/yogstation/code/modules/admin/admin_ranks.dm deleted file mode 100644 index 958bcd99efcb..000000000000 --- a/yogstation/code/modules/admin/admin_ranks.dm +++ /dev/null @@ -1,44 +0,0 @@ -// Kn0ss0s: This proc allows your to use a database for admin ranks, whilst immediately providing a functioning actual fallback in the case of failure -/proc/refresh_admin_files() - return //this is a stupid system - /* - // Generate the Admins and Admins Ranks config files - if(SSdbcore.IsConnected()) - var/datum/DBQuery/query_ranks = SSdbcore.NewQuery("SELECT `name`, `byond`, `rank_group` FROM `web_groups` ORDER BY `web_groups`.`rank_group` ASC, `web_groups`.`name` DESC") - if(query_ranks.Execute()) - fdel("config/admin_ranks.txt") - var/ranksFile = file("config/admin_ranks.txt") - WRITE_FILE(ranksFile, "##############################################################################################################\n# ADMIN RANK DEFINES #\n# The format of this is very simple. Rank name goes first. #\n# Rank is CASE-SENSITIVE, all punctuation save for '-', '_' and '@' will be stripped so spaces don't matter. #\n# You can then define permissions for each rank by adding a '=' followed by keywords #\n# These keywords represent groups of verbs and abilities. #\n# keywords are preceded by either a '+' or a '-', + adds permissions, - takes them away. #\n# +@ (or +prev) is a special shorthand which adds all the rights of the rank above it. #\n# You can also specify verbs like so +/client/proc/some_added_verb or -/client/proc/some_restricted_verb #\n# Ranks with no keywords will just be given the most basic verbs and abilities ~Carn #\n##############################################################################################################\n# PLEASE NOTE: depending on config options, some abilities will be unavailable regardless if you have permission to use them!\n\n# KEYWORDS:\n# +ADMIN = general admin tools, verbs etc\n# +FUN = events, other event-orientated actions. Access to the fun secrets in the secrets panel.\n# +BAN = the ability to ban, jobban and fullban\n# +STEALTH = the ability to stealthmin (make yourself appear with a fake name to everyone but other admins\n# +POSSESS = the ability to possess objects\n# +REJUV (or +REJUVINATE) = the ability to heal, respawn, modify damage and use godmode\n# +BUILD (or +BUILDMODE) = the ability to use buildmode\n# +SERVER = higher-risk admin verbs and abilities, such as those which affect the server configuration.\n# +DEBUG = debug tools used for diagnosing and fixing problems. It's useful to give this to coders so they can investigate problems on a live server.\n# +VAREDIT = everyone may view viewvars/debugvars/whatever you call it. This keyword allows you to actually EDIT those variables.\n# +RIGHTS (or +PERMISSIONS) = allows you to promote and/or demote people.\n# +SOUND (or +SOUNDS) = allows you to upload and play sounds\n# +SPAWN (or +CREATE) = mob transformations, spawning of most atoms including mobs (high-risk atoms, e.g. blackholes, will require the +FUN flag too)\n# +EVERYTHING (or +HOST or +ALL) = Simply gives you everything without having to type every flag\n\n# DO NOT EDIT THIS FILE DIRECTLY\n# IT IS AUTOMATICALLY GENERATED FROM THE SERVER DATABASE\n") - - var/lastGroup = 1 - // Write out each rank to the rank file - while(query_ranks.NextRow()) - var/rank_name = query_ranks.item[1] - var/rank_byond = query_ranks.item[2] - var/rank_group = text2num(query_ranks.item[3]) - if(lastGroup != rank_group) - lastGroup = rank_group - WRITE_FILE(ranksFile, " ") - WRITE_FILE(ranksFile, "[rank_name]\t=\t[rank_byond]") - - qdel(query_ranks) - - var/datum/DBQuery/query_admin = SSdbcore.NewQuery("SELECT `web_admins`.`username` AS admin, `web_groups`.`name` AS adminrank, `web_groups`.`rank_group` AS rank_group FROM `web_admins`, `web_groups` WHERE `web_admins`.`rank` = `web_groups`.`rankid` ORDER BY `web_groups`.`rank_group` ASC, adminrank DESC") - if(query_admin.Execute()) - fdel("config/admins.txt") - var/adminsFile = file("config/admins.txt") - WRITE_FILE(adminsFile, "###############################################################################################\n# Basically, ckey goes first. Rank goes after the '=' #\n# Case is not important for ckey. #\n# Case IS important for the rank. #\n# All punctuation (spaces etc) EXCEPT '-', '_' and '@' will be stripped from rank names. #\n# Ranks can be anything defined in admin_ranks.txt #\n# NOTE: if the rank-name cannot be found in admin_ranks.txt, they will not be adminned! ~Carn #\n# NOTE: syntax was changed to allow hyphenation of ranknames, since spaces are stripped. #\n###############################################################################################\n\n# DO NOT EDIT THIS FILE DIRECTLY\n# IT IS AUTOMATICALLY GENERATED FROM THE SERVER DATABASE\n") - - var/lastGroup = 1 - // Write out each admin to the admins file - while(query_admin.NextRow()) - var/name = query_admin.item[1] - var/adminrank = query_admin.item[2] - var/rank_group = text2num(query_admin.item[3]) - if(lastGroup != rank_group) - lastGroup = rank_group - WRITE_FILE(adminsFile, " ") - WRITE_FILE(adminsFile, "[name]\t=\t[adminrank]") - - qdel(query_admin) -*/ diff --git a/yogstation/code/modules/admin/verbs/adminhelp.dm b/yogstation/code/modules/admin/verbs/adminhelp.dm index af1ef44a4e52..46f347a01c64 100644 --- a/yogstation/code/modules/admin/verbs/adminhelp.dm +++ b/yogstation/code/modules/admin/verbs/adminhelp.dm @@ -203,10 +203,10 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) return // There are no admins online, try deadmins var/found_deadmin = FALSE - if(GLOB.deadmins.len > 0) - for(var/deadmin_ckey in GLOB.deadmins) - var/datum/admins/A = GLOB.deadmins[deadmin_ckey] - if(!A.check_for_rights(R_BAN)) + if(GLOB.permissions.deadmins.len > 0) + for(var/deadmin_ckey in GLOB.permissions.deadmins) + var/datum/admins/A = GLOB.permissions.deadmins[deadmin_ckey] + if(!check_rights_for(A.owner, R_BAN)) continue var/client/client = GLOB.directory[deadmin_ckey] if(!client) @@ -231,7 +231,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) /datum/admin_help/proc/check_owner() // Handles unclaimed tickets; returns TRUE if no longer unclaimed if(!handling_admin && state == AHELP_ACTIVE) var/msg = span_admin("ADMIN LOG: Ticket [TicketHref("#[id]")] Unclaimed!") - for(var/client/X in GLOB.admins) + for(var/client/X in GLOB.permissions.admins) if(check_rights_for(X,R_BAN)) to_chat(X, type = MESSAGE_TYPE_ADMINLOG, @@ -248,7 +248,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) if(world.time > last_bwoinking) last_bwoinking = world.time + 1 SECONDS - for(var/client/X in GLOB.admins) + for(var/client/X in GLOB.permissions.admins) if(check_rights_for(X,R_BAN) && (X.prefs.toggles & SOUND_ADMINHELP)) // Can't use check_rights here since it's dependent on $usr SEND_SOUND(X, sound('sound/effects/adminhelp.ogg')) return FALSE @@ -304,7 +304,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) AddInteraction(msg) //send this msg to all admins - for(var/client/X in GLOB.admins) + for(var/client/X in GLOB.permissions.admins) if(X.prefs.toggles & SOUND_ADMINHELP) SEND_SOUND(X, sound('sound/effects/adminhelp.ogg')) window_flash(X, ignorepref = TRUE) @@ -945,7 +945,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) /proc/get_admin_counts(requiredflags = R_BAN) . = list("total" = list(), "noflags" = list(), "afk" = list(), "stealth" = list(), "present" = list()) - for(var/client/X in GLOB.admins) + for(var/client/X in GLOB.permissions.admins) .["total"] += X if(requiredflags != 0 && !check_rights_for(X, requiredflags)) .["noflags"] += X @@ -999,7 +999,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new) /proc/ircadminwho() var/list/message = list("Admins: ") var/list/admin_keys = list() - for(var/adm in GLOB.admins) + for(var/adm in GLOB.permissions.admins) var/client/C = adm admin_keys += "[C][C.holder.fakekey ? "(Stealth)" : ""][C.is_afk() ? "(AFK)" : ""]" diff --git a/yogstation/code/modules/admin/verbs/adminsay.dm b/yogstation/code/modules/admin/verbs/adminsay.dm index d72393d29582..42e078493bcd 100644 --- a/yogstation/code/modules/admin/verbs/adminsay.dm +++ b/yogstation/code/modules/admin/verbs/adminsay.dm @@ -20,10 +20,10 @@ msg = keywords_lookup(msg) if(check_rights(R_ADMIN,0)) msg = span_adminsay("[span_prefix("ADMIN:")] [key_name(usr, 1)] [ADMIN_FLW(mob)]: [span_message("[msg]")]") - to_chat(GLOB.admins, msg, confidential=TRUE) + to_chat(GLOB.permissions.admins, msg, confidential=TRUE) else msg = span_adminsay("[span_prefix("OBSERVER:")] [key_name(usr, 1)] [ADMIN_FLW(mob)]: [span_message("[msg]")]") - to_chat(GLOB.admins, msg, confidential=TRUE) + to_chat(GLOB.permissions.admins, msg, confidential=TRUE) SSblackbox.record_feedback("tally", "admin_verb", 1, "Asay") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! diff --git a/yogstation/code/modules/client/verbs/antag_token.dm b/yogstation/code/modules/client/verbs/antag_token.dm index f271eb045562..34a7129a51c6 100644 --- a/yogstation/code/modules/client/verbs/antag_token.dm +++ b/yogstation/code/modules/client/verbs/antag_token.dm @@ -31,8 +31,8 @@ GLOBAL_LIST_EMPTY(antag_token_users) return to_chat(src, span_userdanger("You will be notified if your antag token is used")) C.antag_token_timer = addtimer(CALLBACK(src, .proc/deny_antag_token_request), 45 SECONDS, TIMER_STOPPABLE) - to_chat(GLOB.admins, span_adminnotice("ANTAG TOKEN REQUEST:[ADMIN_LOOKUPFLW(usr)] wants to use their antag token! (will auto-DENY in [DisplayTimeText(45 SECONDS)]). (APPROVE)")) - for(var/client/A in GLOB.admins) + to_chat(GLOB.permissions.admins, span_adminnotice("ANTAG TOKEN REQUEST:[ADMIN_LOOKUPFLW(usr)] wants to use their antag token! (will auto-DENY in [DisplayTimeText(45 SECONDS)]). (APPROVE)")) + for(var/client/A in GLOB.permissions.admins) if(check_rights_for(A, R_ADMIN) && (A.prefs.toggles & SOUND_ADMINHELP)) // Can't use check_rights here since it's dependent on $usr SEND_SOUND(A, sound('sound/effects/adminhelp.ogg')) else diff --git a/yogstation/code/modules/client/verbs/looc.dm b/yogstation/code/modules/client/verbs/looc.dm index fa9d4954cb31..769528886fcf 100644 --- a/yogstation/code/modules/client/verbs/looc.dm +++ b/yogstation/code/modules/client/verbs/looc.dm @@ -85,7 +85,7 @@ for(var/T in GLOB.clients) var/client/C = T - if(C in GLOB.admins) + if(C in GLOB.permissions.admins) if(C in clients_to_hear) to_chat(C, message_admin) else diff --git a/yogstation/code/modules/client/verbs/ooc.dm b/yogstation/code/modules/client/verbs/ooc.dm index b9d2bce37879..cda696fe80c6 100644 --- a/yogstation/code/modules/client/verbs/ooc.dm +++ b/yogstation/code/modules/client/verbs/ooc.dm @@ -1,7 +1,7 @@ /client/proc/find_admin_rank(client) var/client/C = client - switch(C.holder.rank.name) + switch(C.holder.rank_name()) if("CouncilMember") return "\[Council\]" @@ -31,7 +31,7 @@ return "\[Retmin-tainer\]" else - return "\[[C.holder.rank.name]\]" + return "\[[C.holder.rank_name()]\]" /client/verb/give_tip() set name = "Give Random Tip" diff --git a/yogstation/code/modules/client/verbs/who.dm b/yogstation/code/modules/client/verbs/who.dm index 9c5038c61291..36d9f2e07edb 100644 --- a/yogstation/code/modules/client/verbs/who.dm +++ b/yogstation/code/modules/client/verbs/who.dm @@ -30,9 +30,9 @@ var/msg = "" var/list/Lines = list() - if(length(GLOB.admins)) + if(length(GLOB.permissions.admins)) Lines += "Admins:" - for(var/X in GLOB.admins) + for(var/X in GLOB.permissions.admins) var/client/C = X if(C && C.holder && !C.holder.fakekey) Lines += "\t [C.key][show_admin_info(C)] ([round(C.avgping, 1)]ms)" diff --git a/yogstation/code/modules/mentor/follow.dm b/yogstation/code/modules/mentor/follow.dm index bb1e31225a90..2ef4ebb1462f 100644 --- a/yogstation/code/modules/mentor/follow.dm +++ b/yogstation/code/modules/mentor/follow.dm @@ -25,7 +25,7 @@ if(mentor_datum) mentor_datum.following = M - to_chat(GLOB.admins, span_mentor("[span_prefix("MENTOR:")] [key_name(usr)] is now following [key_name(M)]"), confidential=TRUE) + to_chat(GLOB.permissions.admins, span_mentor("[span_prefix("MENTOR:")] [key_name(usr)] is now following [key_name(M)]"), confidential=TRUE) to_chat(usr, span_info("Click the \"Stop Following\" button in the Mentor tab to stop following [key_name(M)]."), confidential=TRUE) log_mentor("[key_name(usr)] began following [key_name(M)]") @@ -40,7 +40,7 @@ usr.reset_perspective() remove_verb(src, /client/proc/mentor_unfollow) if(mentor_datum) - to_chat(GLOB.admins, span_mentor("[span_prefix("MENTOR:")] [key_name(usr)] is no longer following [key_name(mentor_datum.following)]"), confidential=TRUE) + to_chat(GLOB.permissions.admins, span_mentor("[span_prefix("MENTOR:")] [key_name(usr)] is no longer following [key_name(mentor_datum.following)]"), confidential=TRUE) log_mentor("[key_name(usr)] stopped following [key_name(mentor_datum.following)]") mentor_datum.following = null diff --git a/yogstation/code/modules/mentor/mentorhelp.dm b/yogstation/code/modules/mentor/mentorhelp.dm index fbde99cebfda..d2ff43597d5a 100644 --- a/yogstation/code/modules/mentor/mentorhelp.dm +++ b/yogstation/code/modules/mentor/mentorhelp.dm @@ -32,7 +32,7 @@ var/mentor_msg = "[admininfo] [key_name_mentor(src, 1, 0, 1, show_char)]: [msg]" log_mentor("[admininfo] [key_name_mentor(src, 0, 0, 0, 0)]: [msg]") - for(var/client/X in GLOB.mentors | GLOB.admins) + for(var/client/X in GLOB.mentors | GLOB.permissions.admins) if(X.prefs.toggles & SOUND_ADMINHELP) send_mentor_sound(X) to_chat(X, mentor_msg, confidential=TRUE) diff --git a/yogstation/code/modules/mentor/mentorpm.dm b/yogstation/code/modules/mentor/mentorpm.dm index 431d96d75f09..78b8f7f92202 100644 --- a/yogstation/code/modules/mentor/mentorpm.dm +++ b/yogstation/code/modules/mentor/mentorpm.dm @@ -54,12 +54,12 @@ //get message text, limit it's length.and clean/escape html if(!msg) if(is_mentor()) - to_chat((GLOB.admins - GLOB.deadmins) | GLOB.mentors, "[key_name_mentor(src)] has started answering [key_name_mentor(C)]'s mentorhelp.", confidential=TRUE) + to_chat((GLOB.permissions.admins - GLOB.permissions.deadmins) | GLOB.mentors, "[key_name_mentor(src)] has started answering [key_name_mentor(C)]'s mentorhelp.", confidential=TRUE) msg = input(src,"Message:", "Private message") as text|null if(!msg) if(is_mentor()) - to_chat((GLOB.admins - GLOB.deadmins) | GLOB.mentors, "[key_name_mentor(src)] has decided not to answer [key_name_mentor(C)]'s mentorhelp.", confidential=TRUE) + to_chat((GLOB.permissions.admins - GLOB.permissions.deadmins) | GLOB.mentors, "[key_name_mentor(src)] has decided not to answer [key_name_mentor(C)]'s mentorhelp.", confidential=TRUE) return // Neither party is a mentor, they shouldn't be PMing! @@ -112,7 +112,7 @@ //we don't use message_Mentors here because the sender/receiver might get it too var/show_char_sender = !is_mentor() && CONFIG_GET(flag/mentors_mobname_only) var/show_char_recip = C && !C.is_mentor() && CONFIG_GET(flag/mentors_mobname_only) - for(var/client/X in GLOB.mentors | (GLOB.admins - GLOB.deadmins)) + for(var/client/X in GLOB.mentors | (GLOB.permissions.admins - GLOB.permissions.deadmins)) if(X.key != key && (!C || X.key != C.key)) //check client/X is an Mentor and isn't the sender or recipient if(discord_id) to_chat(X, "Mentor PM: [key_name_mentor(src, X, 0, 0, show_char_sender)]->[discord_mentor_link(whom, discord_id)]: [msg]", confidential=TRUE) //inform X diff --git a/yogstation/code/modules/mentor/mentorsay.dm b/yogstation/code/modules/mentor/mentorsay.dm index bdf7ffcfd633..838bfc50dd14 100644 --- a/yogstation/code/modules/mentor/mentorsay.dm +++ b/yogstation/code/modules/mentor/mentorsay.dm @@ -23,7 +23,7 @@ else msg = "MENTOR: [key_name(src, 0, 0)]: [msg]" - to_chat((GLOB.admins - GLOB.deadmins) | GLOB.mentors, msg, confidential=TRUE) + to_chat((GLOB.permissions.admins - GLOB.permissions.deadmins) | GLOB.mentors, msg, confidential=TRUE) /client/proc/get_mentor_say() var/msg = input(src, null, "msay \"text\"") as text|null