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
-
-
-
- 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
+//
+//
+//
+//
+//
+// | CKEY \[+\] |
+// RANK |
+// PERMISSIONS |
+//
+// "}
+// 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 += "[adm_ckey] [deadminlink]\[-\]\[2FA\] | "
+// output += "[D.rank_name()] | "
+// output += "[rights2text(D.rights," ")] | "
+// output += "
"
+// output += "
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