mirror of
https://github.com/PolarisSS13/Polaris.git
synced 2026-08-29 16:18:35 +01:00
Implements a path-based, player-specific whitelisting system (#8725)
* Implements a path-based, player-specific whitelisting system * Readds ported functions * Local testing * Copy-paste error * VV modifications, player panel interface * Testing admin operations * data review
This commit is contained in:
@@ -97,6 +97,11 @@ var/global/floorIsLava = 0
|
||||
"}
|
||||
|
||||
if (M.client)
|
||||
body += "<br><br><b>Whitelists:</b><br>"
|
||||
body += jointext(M.client.get_whitelists_list(), ", ")
|
||||
body += "<br><A href='?src=\ref[src];modify_whitelist=\ref[M.client];set_value=1'>Add Whitelist</A>"
|
||||
body += " - <A href='?src=\ref[src];modify_whitelist=\ref[M.client];set_value=0'>Remove Whitelist</A>"
|
||||
|
||||
if(!istype(M, /mob/new_player))
|
||||
body += "<br><br>"
|
||||
body += "<b>Transformation:</b>"
|
||||
|
||||
@@ -107,7 +107,9 @@ var/global/list/admin_verbs_admin = list(
|
||||
/datum/admins/proc/sendFax,
|
||||
/client/proc/despawn_player,
|
||||
/datum/admins/proc/view_feedback,
|
||||
/client/proc/debug_global_variables
|
||||
/client/proc/debug_global_variables,
|
||||
/client/proc/admin_add_whitelist,
|
||||
/client/proc/admin_del_whitelist
|
||||
)
|
||||
|
||||
var/global/list/admin_verbs_ban = list(
|
||||
@@ -236,7 +238,9 @@ var/global/list/admin_verbs_debug = list(
|
||||
/datum/admins/proc/view_feedback,
|
||||
/client/proc/debug_global_variables,
|
||||
/client/proc/ping_webhook,
|
||||
/client/proc/reload_webhooks
|
||||
/client/proc/reload_webhooks,
|
||||
/client/proc/admin_add_whitelist,
|
||||
/client/proc/admin_del_whitelist
|
||||
)
|
||||
|
||||
var/global/list/admin_verbs_paranoid_debug = list(
|
||||
|
||||
@@ -23,7 +23,7 @@ var/global/jobban_keylist[0] //to store the keys & ranks
|
||||
if (guest_jobbans(rank))
|
||||
if(config.guest_jobban && IsGuestKey(M.key))
|
||||
return "Guest Job-ban"
|
||||
if(config.usewhitelist && !check_whitelist(M))
|
||||
if(config.usewhitelist && !M.client.is_whitelisted(rank)) // This outright doesn't work, but at least compiles. AFAIK we don't use this system at present. ~Ater
|
||||
return "Whitelisted Job"
|
||||
|
||||
return ckey_is_jobbanned(M.ckey, rank)
|
||||
|
||||
@@ -1940,6 +1940,18 @@
|
||||
PlayerNotesFilter()
|
||||
return
|
||||
|
||||
if(href_list["modify_whitelist"])
|
||||
var/client/C = locate(href_list["modify_whitelist"])
|
||||
if(!istype(C))
|
||||
return
|
||||
var/entry = input(usr, "Please enter the path of the whitelist you wish to modify:", "Whitelist target", "") as text|null
|
||||
if(!entry || !ispath(text2path(entry)))
|
||||
return
|
||||
if(href_list["set_value"] == "1")
|
||||
C.add_whitelist(entry)
|
||||
else if(href_list["set_value"] == "0")
|
||||
C.remove_whitelist(entry)
|
||||
|
||||
/mob/living/proc/can_centcom_reply()
|
||||
return 0
|
||||
|
||||
|
||||
@@ -76,7 +76,6 @@ GLOBAL_DATUM(debug_real_globals, /debug_real_globals) // :3c
|
||||
"admin_log",
|
||||
"admin_ranks",
|
||||
"admin_state",
|
||||
"alien_whitelist",
|
||||
"alldirs",
|
||||
"ahelp_tickets",
|
||||
"adminfaxes",
|
||||
|
||||
@@ -77,7 +77,7 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
|
||||
// That last list is entirely arbitrary. Take complaints up with Kholdstare.
|
||||
if((istype(client) && check_rights(R_ADMIN | R_EVENT | R_FUN, 0, client)) || \
|
||||
(LAZYLEN(instance.species_allowed) && species && (species in instance.species_allowed)) || \
|
||||
(config.genemod_whitelist && is_genemod_whitelisted(src) && LAZYLEN(instance.whitelist_allowed) && (species in instance.whitelist_allowed)))
|
||||
(config.genemod_whitelist && client.is_whitelisted(/whitelist/genemod) && LAZYLEN(instance.whitelist_allowed) && (species in instance.whitelist_allowed)))
|
||||
.[instance.name] = instance
|
||||
|
||||
/datum/category_item/player_setup_item/general/body
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
// Rewrites the client's whitelists to disk
|
||||
/proc/write_whitelist(var/key, var/list/whitelist)
|
||||
var/filename = "data/player_saves/[copytext(ckey(key),1,2)]/[ckey(key)]/whitelist.json"
|
||||
log_admin("Writing whitelists to disk for [key] at `[filename]`")
|
||||
try
|
||||
// Byond doesn't have a mechanism for in-place modification of a file, so we have to make a new one and then overwrite the old one.
|
||||
// If this is interrupted, the .tmp file exists and can be loaded at the start of the next round.
|
||||
// The in-game list represents the set of valid entries within the whitelist file, so we may as well remove invalid lines in the process.
|
||||
text2file(json_encode(whitelist), filename + ".tmp")
|
||||
if(fexists(filename) && !fdel(filename))
|
||||
error("Exception when overwriting whitelist file [filename]")
|
||||
if(fcopy(filename + ".tmp", filename))
|
||||
if(!fdel(filename + ".tmp"))
|
||||
error("Exception when deleting tmp whitelist file [filename].tmp")
|
||||
catch(var/exception/E)
|
||||
error("Exception when writing to whitelist file [filename]: [E]")
|
||||
|
||||
|
||||
// Add the selected path to the player's whitelists, if it's valid.
|
||||
/client/proc/add_whitelist(var/path)
|
||||
if(istext(path))
|
||||
path = text2path(path)
|
||||
if(!ispath(path))
|
||||
return
|
||||
// If they're already whitelisted, do nothing (Also loads the whitelist)
|
||||
if(is_whitelisted(path))
|
||||
return
|
||||
|
||||
log_and_message_admins("[usr ? usr : "SYSTEM"] giving [path] whitelist to [src]", usr)
|
||||
src.whitelists[path] = TRUE
|
||||
write_whitelist(src.ckey, src.whitelists)
|
||||
|
||||
// Remove the selected path from the player's whitelists.
|
||||
/client/proc/remove_whitelist(var/path)
|
||||
if(istext(path))
|
||||
path = text2path(path)
|
||||
if(!ispath(path))
|
||||
return
|
||||
// If they're not whitelisted, do nothing (Also loads the whitelist)
|
||||
if(!is_whitelisted(path))
|
||||
return
|
||||
|
||||
log_and_message_admins("[usr ? usr : "SYSTEM"] removing [path] whitelist from [src]", usr)
|
||||
src.whitelists -= path
|
||||
write_whitelist(src.ckey, src.whitelists)
|
||||
|
||||
|
||||
/client/proc/admin_add_whitelist()
|
||||
set name = "Whitelist Add Player"
|
||||
set category = "Admin"
|
||||
set desc = "Give a whitelist to a target player"
|
||||
admin_modify_whitelist(TRUE)
|
||||
|
||||
|
||||
/client/proc/admin_del_whitelist()
|
||||
set name = "Whitelist Remove Player"
|
||||
set category = "Admin"
|
||||
set desc = "Remove a whitelist from the target player"
|
||||
admin_modify_whitelist(FALSE)
|
||||
|
||||
|
||||
/client/proc/admin_modify_whitelist(var/set_value)
|
||||
if(!check_rights(R_ADMIN|R_DEBUG))
|
||||
return
|
||||
|
||||
// Get the person to whitelist.
|
||||
var/key = input(src, "Please enter the CKEY of the player whose whitelist you wish to modify:", "Whitelist ckey", "") as text|null
|
||||
if(!key || !length(key))
|
||||
return
|
||||
|
||||
key = ckey(key)
|
||||
if(!fexists("data/player_saves/[copytext(key,1,2)]/[key]/preferences.sav"))
|
||||
to_chat(src, "That player doesn't seem to exist...")
|
||||
return
|
||||
|
||||
// Get the whitelist thing to modify.
|
||||
var/entry = input(src, "Please enter the path of the whitelist you wish to modify:", "Whitelist target", "") as text|null
|
||||
if(!entry || !ispath(text2path(entry)))
|
||||
return
|
||||
|
||||
// If they're logged in, modify it directly.
|
||||
var/client/C = ckey2client(key)
|
||||
if(istype(C))
|
||||
set_value ? C.add_whitelist(entry) : C.remove_whitelist(entry)
|
||||
return
|
||||
|
||||
log_and_message_admins("[src] [set_value ? "giving [entry] whitelist to" : "removing [entry] whitelist from"] [key]", src)
|
||||
|
||||
// Else, we have to find and modify the whitelist file ourselves.
|
||||
var/list/whitelists = load_whitelist(key)
|
||||
|
||||
// They're already whitelisted.
|
||||
if(whitelists[entry] == set_value)
|
||||
return
|
||||
|
||||
whitelists[entry] = set_value
|
||||
write_whitelist(key, whitelists)
|
||||
|
||||
|
||||
/client/vv_get_dropdown()
|
||||
. = ..()
|
||||
VV_DROPDOWN_OPTION(VV_HK_ADD_WHITELIST, "Add whitelist")
|
||||
VV_DROPDOWN_OPTION(VV_HK_DEL_WHITELIST, "Remove whitelist")
|
||||
|
||||
|
||||
/client/vv_do_topic(list/href_list)
|
||||
. = ..()
|
||||
IF_VV_OPTION(VV_HK_ADD_WHITELIST)
|
||||
if(!check_rights(R_ADMIN|R_DEBUG))
|
||||
return
|
||||
var/entry = input(usr, "Please enter the path of the whitelist you wish to add:", "Whitelist target", "") as text|null
|
||||
if(!entry || !ispath(text2path(entry)))
|
||||
return
|
||||
var/client/C = locate(href_list["target"])
|
||||
if(istype(C))
|
||||
C.add_whitelist(entry)
|
||||
IF_VV_OPTION(VV_HK_DEL_WHITELIST)
|
||||
if(!check_rights(R_ADMIN|R_DEBUG))
|
||||
return
|
||||
var/entry = input(usr, "Please enter the path of the whitelist you wish to remove:", "Whitelist target", "") as text|null
|
||||
if(!entry || !ispath(text2path(entry)))
|
||||
return
|
||||
var/client/C = locate(href_list["target"])
|
||||
if(istype(C))
|
||||
C.remove_whitelist(entry)
|
||||
@@ -0,0 +1,65 @@
|
||||
// Proc to convert the old whitelist systems into the new, shiny, modern system.
|
||||
// This has to do a lot of file I/O because it handles _everyone's_ whitelists, so it's expected to be pretty expensive.
|
||||
// Hence, this has to be manually called by an admin.
|
||||
/proc/load_legacy_whitelist()
|
||||
var/static/list/alienwhitelist_dict = list(
|
||||
"Alai" = /datum/language/tajsign,
|
||||
"Common Skrellian" = /datum/language/skrell,
|
||||
"Diona" = /datum/species/diona,
|
||||
"Promethean" = /datum/species/shapeshifter/promethean,
|
||||
"Schechi" = /datum/language/teshari,
|
||||
"Siik" = /datum/language/tajaran,
|
||||
"Sinta'Unathi" = /datum/language/unathi,
|
||||
"Skrell" = /datum/species/skrell,
|
||||
"Sol Common" = /datum/language/human,
|
||||
"Tajara" = /datum/species/tajaran,
|
||||
"Teshari" = /datum/species/teshari,
|
||||
"Unathi" = /datum/species/unathi,
|
||||
"Zaddat" = /datum/species/zaddat,
|
||||
)
|
||||
|
||||
var/list/whitelists_to_write = list()
|
||||
|
||||
// Load in the alien whitelists
|
||||
for(var/line in read_lines("config/alienwhitelist.txt"))
|
||||
if(!length(line))
|
||||
continue
|
||||
var/static/regex/R = regex(" - ")
|
||||
var/list/tok = splittext(line, R)
|
||||
// Whitelist is no longer valid.
|
||||
if(length(tok) < 2 || !(tok[2] in alienwhitelist_dict))
|
||||
continue
|
||||
|
||||
var/key = ckey(tok[1])
|
||||
// If they don't have a preferences save file, then they probably don't play here any more.
|
||||
if(!fexists("data/player_saves/[copytext(key,1,2)]/[key]/preferences.sav"))
|
||||
continue
|
||||
|
||||
LAZYADDASSOC(whitelists_to_write[key], alienwhitelist_dict[tok[2]], TRUE)
|
||||
|
||||
// Load in the genemod whitelist
|
||||
for(var/line in read_commentable("config/genemodwhitelist.txt"))
|
||||
var/key = ckey(line)
|
||||
// If they don't have a preferences save file, then they probably don't play here any more.
|
||||
if(!fexists("data/player_saves/[copytext(key,1,2)]/[key]/preferences.sav"))
|
||||
continue
|
||||
|
||||
LAZYADDASSOC(whitelists_to_write[key], /whitelist/genemod, TRUE)
|
||||
|
||||
// Write the whitelists to files. Second stage, so we don't make a million individual writes.
|
||||
for(var/key in whitelists_to_write)
|
||||
var/filename = "data/player_saves/[copytext(key,1,2)]/[key]/whitelist.json"
|
||||
var/list/whitelist = whitelists_to_write[key]
|
||||
try
|
||||
// If the file already exists, don't just blindly overwrite it.
|
||||
if(fexists(filename))
|
||||
var/prior_whitelist = file2text(filename) || ""
|
||||
if(length(prior_whitelist))
|
||||
whitelist |= json_decode(prior_whitelist)
|
||||
text2file(json_encode(whitelist), filename + ".tmp")
|
||||
if(fexists(filename) && !fdel(filename))
|
||||
error("Error overwriting whitelist file [filename]")
|
||||
if(!fcopy(filename + ".tmp", filename) || !fdel(filename + ".tmp"))
|
||||
error("Exception when deleting tmp whitelist file [filename].tmp")
|
||||
catch(var/exception/E)
|
||||
error("Exception when writing to whitelist file [filename]: [E]")
|
||||
@@ -0,0 +1,4 @@
|
||||
// This file is reserved for creating paths that can be used to whitelist
|
||||
// things that aren't directly attributable to specific paths.
|
||||
/whitelist/genemod // This whitelists a collection of sprite_accessories
|
||||
var/name = "Genemods"
|
||||
@@ -0,0 +1,99 @@
|
||||
/client/var/list/whitelists = null
|
||||
|
||||
|
||||
// Prints the client's whitelist entries
|
||||
/client/verb/print_whitelist()
|
||||
set name = "Show Whitelist Entries"
|
||||
set desc = "Print the set of things you're whitelisted for."
|
||||
set category = "OOC"
|
||||
|
||||
to_chat(src, "You are whitelisted for:")
|
||||
to_chat(src, jointext(get_whitelists_list(), "\n"))
|
||||
|
||||
/client/proc/get_whitelists_list()
|
||||
. = list()
|
||||
if(src.whitelists == null)
|
||||
src.whitelists = load_whitelist(src.ckey)
|
||||
for(var/key in src.whitelists)
|
||||
try
|
||||
. += initial(initial(key:name))
|
||||
catch()
|
||||
. += key
|
||||
|
||||
|
||||
/proc/load_whitelist(var/key)
|
||||
var/filename = "data/player_saves/[copytext(ckey(key),1,2)]/[ckey(key)]/whitelist.json"
|
||||
try
|
||||
// Check the player-specific whitelist file, if it exists.
|
||||
if(fexists(filename))
|
||||
// Load the whitelist entries from file, or empty string if empty.`
|
||||
. = list()
|
||||
for(var/T in json_decode(file2text(filename) || ""))
|
||||
T = text2path(T)
|
||||
if(!ispath(T))
|
||||
continue
|
||||
.[T] = TRUE
|
||||
|
||||
// Something was removing an entry from the whitelist and interrupted mid-overwrite.
|
||||
else if(fexists(filename + ".tmp") && fcopy(filename + ".tmp", filename))
|
||||
. = load_whitelist(key)
|
||||
if(!fdel(filename + ".tmp"))
|
||||
error("Exception when deleting tmp whitelist file [filename].tmp")
|
||||
|
||||
// Whitelist file doesn't exist, so they aren't whitelisted for anything. Create the file.
|
||||
else if(fexists("data/player_saves/[copytext(ckey(key),1,2)]/[ckey(key)]/preferences.sav"))
|
||||
text2file("", filename)
|
||||
. = list()
|
||||
|
||||
catch(var/exception/E)
|
||||
error("Exception when loading whitelist file [filename]: [E]")
|
||||
|
||||
|
||||
// Returns true if the specified path is in the player's whitelists, false otw.
|
||||
/client/proc/is_whitelisted(var/path)
|
||||
if(istext(path))
|
||||
path = text2path(path)
|
||||
if(!ispath(path))
|
||||
return
|
||||
// If it hasn't already been loaded, load it.
|
||||
if(src.whitelists == null)
|
||||
src.whitelists = load_whitelist(src.ckey)
|
||||
return src.whitelists[path]
|
||||
|
||||
|
||||
/proc/is_alien_whitelisted(mob/M, var/datum/species/species)
|
||||
//They are admin or the whitelist isn't in use
|
||||
if(whitelist_overrides(M))
|
||||
return TRUE
|
||||
|
||||
//You did something wrong
|
||||
if(!M || !species)
|
||||
return FALSE
|
||||
|
||||
//The species isn't even whitelisted
|
||||
if(!(species.spawn_flags & SPECIES_IS_WHITELISTED))
|
||||
return TRUE
|
||||
|
||||
var/client/C = (!isclient(M)) ? M.client : M
|
||||
return C.is_whitelisted(species.type)
|
||||
|
||||
|
||||
/proc/is_lang_whitelisted(mob/M, var/datum/language/language)
|
||||
//They are admin or the whitelist isn't in use
|
||||
if(whitelist_overrides(M))
|
||||
return TRUE
|
||||
|
||||
//You did something wrong
|
||||
if(!M || !language || !M.client)
|
||||
return FALSE
|
||||
|
||||
//The language isn't even whitelisted
|
||||
if(!(language.flags & WHITELISTED))
|
||||
return TRUE
|
||||
|
||||
var/client/C = (!isclient(M)) ? M.client : M
|
||||
return C.is_whitelisted(language.type)
|
||||
|
||||
|
||||
/proc/whitelist_overrides(mob/M)
|
||||
return !config.usealienwhitelist || check_rights(R_ADMIN|R_EVENT, 0, M)
|
||||
Reference in New Issue
Block a user