Deadmin tweaks: Admins without +AUTOLOGIN start deadmined. AUTOLOGIN defaults to on. (#33480)

* Deadmin tweaks, Admins without +ADMIN start deadmined.
Deadmining no longer destroys the admin datum.
Admins without +ADMIN start deadmined, reloading admins re-deadmins them.
Moved some code around to make it more sane
People with +PERMISSION can now deadmin or readmin other admins at will.

* Adds new flag for if the role should automatically log in, defaults to on, can be removed with -AUTOLOGIN
Also fixes a bug in permission panel not handling these cases gracefully
This commit is contained in:
Kyle Spier-Swenson
2017-12-13 19:03:37 -08:00
committed by Joan Lung
parent 5ffec027af
commit 4e929c74a9
11 changed files with 171 additions and 105 deletions

View File

@@ -20,19 +20,22 @@
#define BANTYPE_ANY_JOB 9 //used to remove jobbans
//Admin Permissions
#define R_BUILDMODE 1
#define R_ADMIN 2
#define R_BAN 4
#define R_FUN 8
#define R_SERVER 16
#define R_DEBUG 32
#define R_POSSESS 64
#define R_PERMISSIONS 128
#define R_STEALTH 256
#define R_POLL 512
#define R_VAREDIT 1024
#define R_SOUNDS 2048
#define R_SPAWN 4096
#define R_BUILDMODE 0x1
#define R_ADMIN 0x2
#define R_BAN 0x4
#define R_FUN 0x8
#define R_SERVER 0x10
#define R_DEBUG 0x20
#define R_POSSESS 0x40
#define R_PERMISSIONS 0x80
#define R_STEALTH 0x100
#define R_POLL 0x200
#define R_VAREDIT 0x400
#define R_SOUNDS 0x800
#define R_SPAWN 0x1000
#define R_AUTOLOGIN 0x2000
#define R_DEFAULT R_AUTOLOGIN
#if DM_VERSION > 512
#error Remove the flag below , its been long enough

View File

@@ -158,7 +158,7 @@ SUBSYSTEM_DEF(vote)
var/admin = FALSE
var/ckey = ckey(initiator_key)
if((GLOB.admin_datums[ckey]) || (ckey in GLOB.deadmins))
if(GLOB.admin_datums[ckey])
admin = TRUE
if(next_allowed_time > world.time && !admin)

View File

@@ -18,7 +18,7 @@
return list("reason"="invalid login data", "desc"="Error: Could not check ban status, Please try again. Error message: Your computer provided an invalid Computer ID.)")
var/admin = 0
var/ckey = ckey(key)
if((ckey in GLOB.admin_datums) || (ckey in GLOB.deadmins))
if(GLOB.admin_datums[ckey] || GLOB.deadmins[ckey])
admin = 1
//Whitelist

View File

@@ -3,7 +3,7 @@ GLOBAL_PROTECT(admin_ranks)
/datum/admin_rank
var/name = "NoRank"
var/rights = 0
var/rights = R_DEFAULT
var/list/adds
var/list/subs
@@ -56,11 +56,13 @@ GLOBAL_PROTECT(admin_ranks)
if("varedit")
flag = R_VAREDIT
if("everything","host","all")
flag = 65535
flag = ALL
if("sound","sounds")
flag = R_SOUNDS
if("spawn","create")
flag = R_SPAWN
if("autologin", "autoadmin")
flag = R_AUTOLOGIN
if("@","prev")
flag = previous_rights
if("rejuv","rejuvinate")
@@ -171,17 +173,15 @@ GLOBAL_PROTECT(admin_ranks)
#endif
/proc/load_admins(target = null)
if(IsAdminAdvancedProcCall())
to_chat(usr, "<span class='admin prefix'>Admin Reload blocked: Advanced ProcCall detected.</span>")
return
/proc/load_admins()
//clear the datums references
if(!target)
GLOB.admin_datums.Cut()
for(var/client/C in GLOB.admins)
C.remove_admin_verbs()
C.holder = null
GLOB.admins.Cut()
GLOB.deadmins.Cut()
load_admin_ranks()
//Clear profile access
for(var/A in world.GetConfig("admin"))
@@ -208,13 +208,11 @@ GLOBAL_PROTECT(admin_ranks)
var/ckey = ckey(entry[1])
var/rank = ckeyEx(entry[2])
if(!ckey || !rank || (target && ckey != target))
if(!ckey || !rank)
continue
var/datum/admins/D = new(rank_names[rank], ckey) //create the admin datum and store it for later use
if(!D)
continue //will occur if an invalid rank is provided
D.associate(GLOB.directory[ckey]) //find the client for a ckey if they are connected and associate them with the new admin datum
new /datum/admins(rank_names[rank], ckey)
else
if(!SSdbcore.Connect())
log_world("Failed to connect to database in load_admins(). Reverting to legacy system.")
@@ -229,19 +227,12 @@ GLOBAL_PROTECT(admin_ranks)
while(query_load_admins.NextRow())
var/ckey = ckey(query_load_admins.item[1])
var/rank = ckeyEx(query_load_admins.item[2])
if(target && ckey != target)
continue
if(rank_names[rank] == null)
WARNING("Admin rank ([rank]) does not exist.")
continue
var/datum/admins/D = new(rank_names[rank], ckey) //create the admin datum and store it for later use
if(!D)
continue //will occur if an invalid rank is provided
if(D.rank.rights & R_DEBUG) //grant profile access
world.SetConfig("APP/admin", ckey, "role=admin")
D.associate(GLOB.directory[ckey]) //find the client for a ckey if they are connected and associate them with the new admin datum
new /datum/admins(rank_names[rank], ckey)
#ifdef TESTING
var/msg = "Admins Built:\n"
@@ -298,6 +289,8 @@ GLOBAL_PROTECT(admin_ranks)
return
var/datum/admins/D = GLOB.admin_datums[adm_ckey]
if (!D)
D = GLOB.deadmins[adm_ckey]
switch(task)
if("remove")
@@ -309,6 +302,7 @@ GLOBAL_PROTECT(admin_ranks)
log_admin("[key_name(usr)] attempted to remove [adm_ckey] from the admins list without sufficient rights.")
return
GLOB.admin_datums -= adm_ckey
GLOB.deadmins -= adm_ckey
D.disassociate()
updateranktodb(adm_ckey, "player")
@@ -350,11 +344,9 @@ GLOBAL_PROTECT(admin_ranks)
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
D.associate()
else
D = new(R,adm_ckey) //new admin
var/client/C = GLOB.directory[adm_ckey] //find the client with the specified ckey (if they are logged in)
D.associate(C) //link up with the client and add verbs
D = new(R, adm_ckey, TRUE) //new admin
updateranktodb(adm_ckey, new_rank)
message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]")
@@ -387,6 +379,22 @@ GLOBAL_PROTECT(admin_ranks)
message_admins("[key_name(usr)] added keyword [keyword] to permission of [adm_ckey]")
log_admin("[key_name(usr)] added keyword [keyword] to permission of [adm_ckey]")
log_admin_permission_modification(adm_ckey, D.rank.rights)
if("activate") //forcefully readmin
if(!D || !D.deadmined)
return
D.activate()
message_admins("[key_name_admin(usr)] forcefully readmined [adm_ckey]")
log_admin("[key_name(usr)] forcefully readmined [adm_ckey]")
if("deactivate") //forcefully deadmin
if(!D || D.deadmined)
return
message_admins("[key_name_admin(usr)] forcefully deadmined [adm_ckey]")
log_admin("[key_name(usr)] forcefully deadmined [adm_ckey]")
D.deactivate() //after logs so the deadmined admin can see the message.
edit_admin_permissions()

View File

@@ -638,12 +638,7 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
if(has_antag_hud())
toggle_antag_hud()
holder.disassociate()
qdel(holder)
GLOB.deadmins += ckey
GLOB.admin_datums -= ckey
verbs += /client/proc/readmin
holder.deactivate()
to_chat(src, "<span class='interface'>You are now a normal player.</span>")
log_admin("[src] deadmined themself.")
@@ -655,13 +650,20 @@ GLOBAL_LIST_INIT(admin_verbs_hideable, list(
set category = "Admin"
set desc = "Regain your admin powers."
load_admins(ckey)
var/datum/admins/A = GLOB.deadmins[ckey]
if(!holder) // Something went wrong...
if(!A)
A = GLOB.admin_datums[ckey]
if (!A)
var/msg = " is trying to readmin but they have no deadmin entry"
message_admins("[key_name_admin(src)][msg]")
log_admin_private("[key_name(src)][msg]")
return
GLOB.deadmins -= ckey
verbs -= /client/proc/readmin
A.associate(src)
if (!holder)
return //This can happen if an admin attempts to vv themself into somebody elses's deadmin datum by getting ref via brute force
to_chat(src, "<span class='interface'>You are now an admin.</span>")
message_admins("[src] re-adminned themselves.")

View File

@@ -7,6 +7,8 @@ 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
var/client/owner = null
var/fakekey = null
@@ -19,9 +21,12 @@ GLOBAL_PROTECT(href_token)
var/datum/newscaster/wanted_message/admincaster_wanted_message = new /datum/newscaster/wanted_message
var/datum/newscaster/feed_channel/admincaster_feed_channel = new /datum/newscaster/feed_channel
var/admin_signature
var/href_token
/datum/admins/New(datum/admin_rank/R, ckey)
var/deadmined
/datum/admins/New(datum/admin_rank/R, ckey, force_active = FALSE)
if(!ckey)
QDEL_IN(src, 0)
throw EXCEPTION("Admin datum created without a ckey")
@@ -30,34 +35,36 @@ GLOBAL_PROTECT(href_token)
QDEL_IN(src, 0)
throw EXCEPTION("Admin datum created without a rank")
return
target = ckey
name = "[ckey]'s admin datum ([R])"
rank = R
admin_signature = "Nanotrasen Officer #[rand(0,9)][rand(0,9)][rand(0,9)]"
href_token = GenerateToken()
GLOB.admin_datums[ckey] = src
if(R.rights & R_DEBUG) //grant profile access
world.SetConfig("APP/admin", ckey, "role=admin")
//only admins with +ADMIN start admined
if (force_active || (R.rights & R_AUTOLOGIN))
activate()
else
deactivate()
/proc/GenerateToken()
. = ""
for(var/I in 1 to 32)
. += "[rand(10)]"
/proc/RawHrefToken(forceGlobal = FALSE)
var/tok = GLOB.href_token
if(!forceGlobal && usr)
var/client/C = usr.client
if(!C)
CRASH("No client for HrefToken()!")
var/datum/admins/holder = C.holder
if(holder)
tok = holder.href_token
return tok
/datum/admins/proc/activate()
GLOB.deadmins -= target
GLOB.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
/proc/HrefToken(forceGlobal = FALSE)
return "admin_token=[RawHrefToken(forceGlobal)]"
/proc/HrefTokenFormField(forceGlobal = FALSE)
return "<input type='hidden' name='admin_token' value='[RawHrefToken(forceGlobal)]'>"
/datum/admins/proc/deactivate()
GLOB.deadmins[target] = src
GLOB.admin_datums -= target
deadmined = TRUE
var/client/C
if ((C = owner) || (C = GLOB.directory[target]))
disassociate()
C.verbs += /client/proc/readmin
/datum/admins/proc/associate(client/C)
if(IsAdminAdvancedProcCall())
@@ -65,10 +72,18 @@ GLOBAL_PROTECT(href_token)
message_admins("[key_name_admin(usr)][msg]")
log_admin_private("[key_name(usr)][msg]")
return
if(istype(C))
if(C.ckey != target)
var/msg = " has attempted to associate with [target]'s admin datum"
message_admins("[key_name_admin(C)][msg]")
log_admin_private("[key_name(C)][msg]")
return
if (deadmined)
activate()
owner = C
owner.holder = src
owner.add_admin_verbs() //TODO
owner.add_admin_verbs() //TODO <--- todo what? the proc clearly exists and works since its the backbone to our entire admin system
owner.verbs -= /client/proc/readmin
GLOB.admins |= C
@@ -79,6 +94,12 @@ GLOBAL_PROTECT(href_token)
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
@@ -128,8 +149,28 @@ you will have to do something like if(client.rights & R_ADMIN) yourself.
//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 && subject.holder.rank)
if(rights_required && !(rights_required & subject.holder.rank.rights))
return 0
return 1
if(subject && subject.holder)
return subject.holder.check_for_rights(rights_required)
return 0
/proc/GenerateToken()
. = ""
for(var/I in 1 to 32)
. += "[rand(10)]"
/proc/RawHrefToken(forceGlobal = FALSE)
var/tok = GLOB.href_token
if(!forceGlobal && usr)
var/client/C = usr.client
if(!C)
CRASH("No client for HrefToken()!")
var/datum/admins/holder = C.holder
if(holder)
tok = holder.href_token
return tok
/proc/HrefToken(forceGlobal = FALSE)
return "admin_token=[RawHrefToken(forceGlobal)]"
/proc/HrefTokenFormField(forceGlobal = FALSE)
return "<input type='hidden' name='admin_token' value='[RawHrefToken(forceGlobal)]'>"

View File

@@ -10,7 +10,7 @@
if(!check_rights(R_PERMISSIONS))
return
var/output = {"<!DOCTYPE html>
var/list/output = list({"<!DOCTYPE html>
<html>
<head>
<title>Permissions Panel</title>
@@ -25,18 +25,26 @@
<th style='width:375px;'>PERMISSIONS</th>
<th style='width:100%;'>VERB-OVERRIDES</th>
</tr>
"}
"})
for(var/adm_ckey in GLOB.admin_datums)
for(var/adm_ckey in GLOB.admin_datums+GLOB.deadmins)
var/datum/admins/D = GLOB.admin_datums[adm_ckey]
if(!D)
D = GLOB.deadmins[adm_ckey]
if (!D)
continue
var/rights = rights2text(D.rank.rights," ")
if(!rights) rights = "*none*"
if(!rights)
rights = "*none*"
var/deadminlink = ""
if (D.deadmined)
deadminlink = " <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=activate;ckey=[adm_ckey]'>\[RA\]</a>"
else
deadminlink = " <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=deactivate;ckey=[adm_ckey]'>\[DA\]</a>"
output += "<tr>"
output += "<td style='text-align:right;'>[adm_ckey] <a class='small' href='?src=[REF(src)];[HrefToken()];editrights=remove;ckey=[adm_ckey]'>\[-\]</a></td>"
output += "<td style='text-align:right;'>[adm_ckey] [deadminlink]<a class='small' href='?src=[REF(src)];[HrefToken()];editrights=remove;ckey=[adm_ckey]'>\[-\]</a></td>"
output += "<td><a href='?src=[REF(src)];[HrefToken()];editrights=rank;ckey=[adm_ckey]'>[D.rank.name]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights]</a></td>"
output += "<td><a class='small' href='?src=[REF(src)];[HrefToken()];editrights=permissions;ckey=[adm_ckey]'>[rights2text(0," ",D.rank.adds,D.rank.subs)]</a></td>"
@@ -48,7 +56,7 @@
</body>
</html>"}
usr << browse(output,"window=editrights;size=900x650")
usr << browse(jointext(output, ""),"window=editrights;size=900x650")
/datum/admins/proc/log_admin_rank_modification(adm_ckey, new_rank)
if(CONFIG_GET(flag/admin_legacy_system))

View File

@@ -161,7 +161,7 @@ GLOBAL_LIST(external_rsc_urls)
GLOB.directory[ckey] = src
GLOB.ahelp_tickets.ClientLogin(src)
var/connecting_admin = FALSE //because de-admined admins connecting should be treated like admins.
//Admin Authorisation
var/localhost_addresses = list("127.0.0.1", "::1")
if(address && (address in localhost_addresses))
@@ -185,6 +185,11 @@ GLOBAL_LIST(external_rsc_urls)
if(holder)
GLOB.admins |= src
holder.owner = src
connecting_admin = TRUE
else if(GLOB.deadmins[ckey])
verbs += /client/proc/readmin
connecting_admin = TRUE
//preferences datum - also holds some persistent data for the client (because we may as well keep these datums to a minimum)
prefs = GLOB.preferences_datums[ckey]
@@ -234,7 +239,7 @@ GLOBAL_LIST(external_rsc_urls)
chatOutput.start() // Starts the chat
if(alert_mob_dupe_login)
set waitfor = FALSE
spawn()
alert(mob, "You have logged in already with another key this round, please log out of this one NOW or risk being banned!")
connection_time = world.time
@@ -249,7 +254,7 @@ GLOBAL_LIST(external_rsc_urls)
to_chat(src, "Your version: [byond_version]")
to_chat(src, "Required version: [cev] or later")
to_chat(src, "Visit http://www.byond.com/download/ to get the latest version of byond.")
if (holder)
if (connecting_admin)
to_chat(src, "Because you are an admin, you are being allowed to walk past this limitation, But it is still STRONGLY suggested you upgrade")
else
qdel(src)
@@ -269,7 +274,7 @@ GLOBAL_LIST(external_rsc_urls)
to_chat(src, "Required version to remove this message: [cwv] or later")
to_chat(src, "Visit http://www.byond.com/download/ to get the latest version of byond.")
if (connection == "web" && !holder)
if (connection == "web" && !connecting_admin)
if (!CONFIG_GET(flag/allow_webclient))
to_chat(src, "Web client is disabled")
qdel(src)
@@ -423,7 +428,7 @@ GLOBAL_LIST(external_rsc_urls)
if (src.holder && src.holder.rank)
admin_rank = src.holder.rank.name
else
if (check_randomizer(connectiontopic))
if (!GLOB.deadmins[ckey] && check_randomizer(connectiontopic))
return
var/sql_ip = sanitizeSQL(address)
var/sql_computerid = sanitizeSQL(computer_id)
@@ -433,7 +438,7 @@ GLOBAL_LIST(external_rsc_urls)
if(!query_client_in_db.Execute())
return
if(!query_client_in_db.NextRow())
if (CONFIG_GET(flag/panic_bunker) && !holder && !(ckey in GLOB.deadmins))
if (CONFIG_GET(flag/panic_bunker) && !holder && !GLOB.deadmins[ckey])
log_access("Failed Login: [key] - New account attempting to connect during panic bunker")
message_admins("<span class='adminnotice'>Failed Login: [key] - New account attempting to connect during panic bunker</span>")
to_chat(src, "Sorry but the server is currently not accepting connections from never before seen players.")

View File

@@ -26,9 +26,6 @@
reload_fullscreen() // Reload any fullscreen overlays this mob has.
if(ckey in GLOB.deadmins)
verbs += /client/proc/readmin
add_click_catcher()
sync_mind()

View File

@@ -27,17 +27,19 @@
# +RIGHTS (or +PERMISSIONS) = allows you to promote and/or demote people.
# +SOUND (or +SOUNDS) = allows you to upload and play sounds
# +SPAWN (or +CREATE) = mob transformations, spawning of most atoms including mobs (high-risk atoms, e.g. blackholes, will require the +FUN flag too)
# +AUTOLOGIN = admin gains powers upon connect. This defaults to on, you can use -AUTOLOGIN to make a role require using the readmin verb to gain powers. (this does not effect the admin's ability to walk past bans or other on-connect limitations like panic bunker or pop limit.)
# +EVERYTHING (or +HOST or +ALL) = Simply gives you everything without having to type every flag
# END_KEYWORDS
Admin Observer
Admin Observer = -AUTOLOGIN
Moderator = +ADMIN
Admin Candidate = +@
Trial Admin = +@ +SPAWN +VAREDIT +BAN
Badmin = +@ +POSSESS +POLL +BUILDMODE +SERVER +FUN
Game Admin = +@ +STEALTH +SOUNDS +DEBUG
Game Master = +EVERYTHING
Lazy Master = +EVERYTHING -AUTOLOGIN
Host = +EVERYTHING
Coder = +DEBUG +VAREDIT +SERVER +SPAWN +POLL
Coder = +DEBUG +VAREDIT +SERVER +SPAWN +POLL -AUTOLOGIN

View File

@@ -11,7 +11,7 @@ Optimumtact = Host
NewSta = Game Master
Expletives = Game Master
kingofkosmos = Game Master
MrStonedOne = Game Master
MrStonedOne = Lazy Master
microscopics = Game Master
Gun Hog = Game Master
KorPhaeron = Game Master