Make DB admin ranks more useful (#29906)

* Make DB admin ranks more useful

* Avoid error message when cancelling permission toggle, allow adding localhost admins as real admins.

* Lint.

* Don't SQL error when a previously-unseen player connects.

* Use ckey of permission editor, not mob name.

* Strikethrough

* Order in the list.

* Deadmin, readmin, and 2fa.

* Correct merge error

---------

Co-authored-by: Burzah <116982774+Burzah@users.noreply.github.com>
This commit is contained in:
Charlie Nolan
2025-08-10 12:05:19 +01:00
committed by GitHub
co-authored by Burzah
parent f67dd166cb
commit 2b98749d32
20 changed files with 1037 additions and 469 deletions
+123 -32
View File
@@ -48,41 +48,119 @@ GLOBAL_PROTECT(admin_ranks) // this shit is being protected for obvious reasons
testing(msg)
#endif
/proc/reload_one_admin(admin_ckey, silent = FALSE)
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='boldannounce'>Admin reload blocked: Advanced ProcCall detected.</span>")
message_admins("[key_name(usr)] attempted to reload an admin via advanced proc-call")
log_admin("[key_name(usr)] attempted to reload an admin via advanced proc-call")
return
// Make sure it's actually in ckey format.
admin_ckey = ckey(admin_ckey)
// Remove any existing permissions.
var/datum/admins/admin_datum = GLOB.admin_datums[admin_ckey]
if(admin_datum)
qdel(admin_datum)
if(admin_ckey in (GLOB.de_admins + GLOB.de_mentors))
if(!silent)
message_admins("<span class='notice'>Admin permissions for [admin_ckey] will reload when they re-admin.</span>")
return
if(admin_ckey in GLOB.directory)
var/client/admin = GLOB.directory[admin_ckey]
var/localhosted = admin.try_localhost_autoadmin()
if(localhosted)
if(!silent)
message_admins("<span class='notice'>Admin permissions for [admin_ckey] reloaded. Full permissions granted as they are currently connected from localhost.</span>")
return
if(GLOB.configuration.admin.use_database_admins)
var/datum/db_query/get_admin = SSdbcore.NewQuery({"
SELECT
-- Use the display_rank if set, otherwise the name of their permissions_rank.
IFNULL(admin.display_rank, admin_ranks.name),
-- Permissions start with their admin rank permissions (if any)
(IFNULL(admin_ranks.default_permissions, 0)
-- Then add in any extra permissions they've been granted.
| admin.extra_permissions)
-- And exclude any permissions they've had removed.
& ~admin.removed_permissions
FROM admin
-- We want all admins, and admin_ranks where available.
LEFT OUTER JOIN admin_ranks
ON admin.permissions_rank = admin_ranks.id
WHERE admin.ckey=:admin_ckey"}, list(
"admin_ckey" = admin_ckey
))
if(!get_admin.warn_execute())
qdel(get_admin)
return
if(get_admin.NextRow())
var/rank = get_admin.item[1]
var/rights = get_admin.item[2]
if(rights == 0)
// If you have no rights, you don't get an admin datum.
qdel(get_admin)
if(!silent)
message_admins("<span class='notice'>Admin permissions for [admin_ckey] have been reloaded.</span>")
return
admin_datum = new(rank, rights, admin_ckey)
qdel(get_admin)
else
var/rank = GLOB.configuration.admin.ckey_rank_map[admin_ckey]
// Load permissions associated with this rank
var/rights = GLOB.admin_ranks[rank]
// Create their admin datum.
admin_datum = new /datum/admins(rank, rights, admin_ckey)
// Set up their permissions.
if(admin_datum)
admin_datum.associate(GLOB.directory[admin_ckey])
if(admin_ckey in GLOB.directory)
var/client/admin = GLOB.directory[admin_ckey]
to_chat(admin, "Holder? [admin.holder] Permissions? [admin.holder?.rights]")
if(!silent)
message_admins("<span class='notice'>Admin permissions for [admin_ckey] have been reloaded.</span>")
/proc/load_admins(run_async = FALSE)
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='boldannounce'>Admin reload blocked: Advanced ProcCall detected.</span>")
message_admins("[key_name(usr)] attempted to reload admins via advanced proc-call")
log_admin("[key_name(usr)] attempted to reload admins via advanced proc-call")
return
//clear the datums references
// Revoke all permissions.
var/list/localhost_admins = list()
for(var/datum/admins/admin_datum in GLOB.admin_datums)
if(admin_datum.is_localhost_autoadmin && admin_datum.owner)
localhost_admins += admin_datum.owner
qdel(admin_datum)
GLOB.admin_datums.Cut()
for(var/client/C in GLOB.admins)
C.hide_verbs()
C.holder = null
GLOB.admins.Cut()
// Remove all profiler access
// Just to be double-sure, revoke all profiler access
for(var/A in world.GetConfig("admin"))
world.SetConfig("APP/admin", A, null)
if(!GLOB.configuration.admin.use_database_admins)
load_admin_ranks()
//process each line seperately
for(var/iterator_key in GLOB.configuration.admin.ckey_rank_map)
var/ckey = ckey(iterator_key) // Snip out formatting
var/rank = GLOB.configuration.admin.ckey_rank_map[iterator_key]
for(var/ckey in GLOB.configuration.admin.ckey_rank_map)
var/rank = GLOB.configuration.admin.ckey_rank_map[ckey]
//load permissions associated with this rank
// Load permissions associated with this rank
var/rights = GLOB.admin_ranks[rank]
//create the admin datum and store it for later use
// Create their admin datum.
var/datum/admins/D = new /datum/admins(rank, rights, ckey)
if(D.rights & R_DEBUG || D.rights & R_VIEWRUNTIMES) // Grants profiler access to anyone with R_DEBUG or R_VIEWRUNTIMES
world.SetConfig("APP/admin", ckey, "role=admin")
//find the client for a ckey if they are connected and associate them with the new admin datum
// Set up their admin permissions.
D.associate(GLOB.directory[ckey])
else
@@ -93,34 +171,47 @@ GLOBAL_PROTECT(admin_ranks) // this shit is being protected for obvious reasons
load_admins()
return
var/datum/db_query/query = SSdbcore.NewQuery("SELECT ckey, admin_rank, level, flags FROM admin")
if(!query.warn_execute(async=run_async))
qdel(query)
var/datum/db_query/get_admins = SSdbcore.NewQuery({"
SELECT
admin.ckey,
-- Use the display_rank if set, otherwise the name of their permissions_rank.
IFNULL(admin.display_rank, admin_ranks.name),
-- Permissions start with their admin rank permissions (if any)
(IFNULL(admin_ranks.default_permissions, 0)
-- Then add in any extra permissions they've been granted.
| admin.extra_permissions)
-- And exclude any permissions they've had removed.
& ~admin.removed_permissions
FROM admin
-- We want all admins, and admin_ranks where available.
LEFT OUTER JOIN admin_ranks
ON admin.permissions_rank = admin_ranks.id"})
if(!get_admins.warn_execute(async=run_async))
qdel(get_admins)
return
while(query.NextRow())
var/ckey = query.item[1]
var/rank = query.item[2]
if(rank == "Removed") continue //This person was de-adminned. They are only in the admin list for archive purposes.
var/rights = query.item[4]
if(istext(rights)) rights = text2num(rights)
while(get_admins.NextRow())
var/ckey = get_admins.item[1]
var/rank = get_admins.item[2]
var/rights = get_admins.item[3]
if(rights == 0)
// If you have no rights, you don't get an admin datum.
continue
var/datum/admins/D = new /datum/admins(rank, rights, ckey)
if(D.rights & R_DEBUG || D.rights & R_VIEWRUNTIMES) // Grants profiler access to anyone with R_DEBUG or R_VIEWRUNTIMES
world.SetConfig("APP/admin", ckey, "role=admin")
//find the client for a ckey if they are connected and associate them with the new admin datum
// Set up their admin permissions.
D.associate(GLOB.directory[ckey])
qdel(get_admins)
qdel(query)
if(!GLOB.admin_datums)
if(!length(GLOB.admin_datums))
log_world("The database query in load_admins() resulted in no admins being added to the list. Reverting to legacy system.")
GLOB.configuration.admin.use_database_admins = FALSE
load_admins()
return
for(var/client/localhost_admin in localhost_admins)
localhost_admin.try_localhost_autoadmin()
#ifdef TESTING
var/msg = "Admins Built:\n"
for(var/ckey in GLOB.admin_datums)