ADMIN_VERB(edit_admin_permissions, R_PERMISSIONS, "Permissions Panel", "Edit admin permissions.", "Admin.Secrets") user.holder.edit_admin_permissions() /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/db_query/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/db_query/query_search_admin_logs = SSdbcore.NewQuery({" SELECT datetime, round_id, IFNULL((SELECT ckey FROM [format_table_name("erro_player")] WHERE ckey = adminckey), adminckey), operation, IF(ckey IS NULL, target, ckey), log FROM [format_table_name("admin_log")] LEFT JOIN [format_table_name("erro_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/db_query/query_check_admin_errors = SSdbcore.NewQuery("SELECT IFNULL((SELECT ckey FROM [format_table_name("erro_player")] WHERE [format_table_name("erro_player")].ckey = [format_table_name("admin")].ckey), 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/db_query/query_check_unused_rank = SSdbcore.NewQuery("SELECT [format_table_name("admin_ranks")].`rank`, flags, exclude_flags, can_edit_flags FROM [format_table_name("admin_ranks")] LEFT JOIN [format_table_name("admin")] ON [format_table_name("admin")].`rank` = [format_table_name("admin_ranks")].`rank` WHERE [format_table_name("admin")].`rank` IS NULL") if(!query_check_unused_rank.warn_execute()) qdel(query_check_unused_rank) return while(query_check_unused_rank.NextRow()) var/admin_rank = query_check_unused_rank.item[1] output += {"Rank [admin_rank] is not held by any admin | \[Remove\]
Permissions: [rights2text(text2num(query_check_unused_rank.item[2])," ")]
Denied: [rights2text(text2num(query_check_unused_rank.item[3])," ", "-")]
Allowed to edit: [rights2text(text2num(query_check_unused_rank.item[4])," ", "*")]
"} qdel(query_check_unused_rank) else if(!action) output += {" Permissions Panel
"} for(var/adm_ckey in GLOB.admin_datums+GLOB.deadmins) var/datum/admins/D = GLOB.admin_datums[adm_ckey] if(!D) D = GLOB.deadmins[adm_ckey] if (!D) continue var/deadminlink = "" if(D.owner) adm_ckey = D.owner.key if (D.deadmined) deadminlink = " \[RA\]" else deadminlink = " \[DA\]" var/verify_link = "" //if (D.blocked_by_2fa) // verify_link += " | \[2FA VERIFY\]" output += "" output += "" output += "" output += "" output += "" output += "
CKEY \[+\] RANK PERMISSIONS
[adm_ckey]
[deadminlink]\[-\]\[SYNC TGDB\][verify_link]
[D.rank_names()][rights2text(D.rank_flags(), " ")]
Search:
" if(QDELETED(usr)) return var/datum/browser/popup = new(owner, "editrights", "Edit Rights", 1000, 650) popup.set_content(jointext(output, "")) popup.open() /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, span_adminprefix("Admin Edit blocked: Advanced ProcCall detected."), confidential = TRUE) return var/datum/asset/permissions_assets = get_asset_datum(/datum/asset/simple/namespaced/common) permissions_assets.send(usr.client) var/admin_key = href_list["key"] var/admin_ckey = ckey(admin_key) var/task = href_list["editrights"] var/datum/admins/target_admin_datum = GLOB.admin_datums[admin_ckey] if(!target_admin_datum) target_admin_datum = GLOB.deadmins[admin_ckey] if (!target_admin_datum && task != "add") return var/use_db var/skip var/legacy_only if(task == "activate" || task == "deactivate" || task == "sync" || task == "verify") 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, span_adminprefix("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((target_admin_datum.ranks & GLOB.protected_ranks).len > 0) to_chat(usr, span_adminprefix("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, span_adminprefix("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 = tgui_alert(usr,"Permanent changes are saved to the database for future rounds, temporary changes will affect only the current round", "Permanent or Temporary?", list("Permanent", "Temporary", "Cancel")) if(use_db == "Cancel") return if(use_db == "Permanent") use_db = TRUE else use_db = FALSE if(QDELETED(usr)) return if(target_admin_datum && (task != "sync" && task != "verify") && !check_if_greater_rights_than_holder(target_admin_datum)) 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 if(!admin_key) // Prevents failures in logging admin rank changes. admin_key = admin_ckey change_admin_rank(admin_ckey, admin_key, use_db, null, legacy_only) if("remove") remove_admin(admin_ckey, admin_key, use_db, target_admin_datum) if("rank") change_admin_rank(admin_ckey, admin_key, use_db, target_admin_datum, legacy_only) if("permissions") change_admin_flags(admin_ckey, admin_key, target_admin_datum) if("activate") force_readmin(admin_key, target_admin_datum) if("deactivate") force_deadmin(admin_key, target_admin_datum) if("sync") sync_lastadminrank(admin_ckey, admin_key, target_admin_datum) /* if("verify") var/msg = "has authenticated [admin_ckey]" message_admins("[key_name_admin(usr)] [msg]") log_admin("[key_name(usr)] [msg]") target_admin_datum.bypass_2fa = TRUE target_admin_datum.associate(GLOB.directory[admin_ckey]) */ edit_admin_permissions() /datum/admins/proc/add_admin(admin_ckey, admin_key, use_db) if(admin_ckey) . = admin_ckey else admin_key = tgui_input_text(usr, "New admin's key","Admin key") . = 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/db_query/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/db_query/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/db_query/query_add_admin_log = SSdbcore.NewQuery({" INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'add admin', :target, CONCAT('New admin added: ', :target)) "}, list("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/remove_admin(admin_ckey, admin_key, use_db, datum/admins/D) if(tgui_alert(usr,"Are you sure you want to remove [admin_ckey]?","Confirm Removal",list("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/db_query/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/db_query/query_add_rank_log = SSdbcore.NewQuery({" INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'remove admin', :admin_ckey, CONCAT('Admin removed: ', :admin_ckey)) "}, list("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/force_readmin(admin_key, datum/admins/D) 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) if(!D || D.deadmined) return message_admins("[key_name_admin(usr)] forcefully deadmined [admin_key]") log_admin("[key_name(usr)] forcefully deadmined [admin_key]") D.deactivate() //after logs so the deadmined admin can see the message. #define RANK_DONE ":) I'm Done" /datum/admins/proc/change_admin_rank(admin_ckey, admin_key, use_db, datum/admins/D, legacy_only) if(!check_rights(R_PERMISSIONS)) return var/list/rank_names = list() if(!use_db || (use_db && !legacy_only)) rank_names += "*New Rank*" for(var/datum/admin_rank/admin_rank as anything in GLOB.admin_ranks) if((admin_rank.rights & usr.client.holder.can_edit_rights_flags()) == admin_rank.rights) rank_names[admin_rank.name] = admin_rank var/list/new_rank_names = list() var/list/custom_ranks = list() while (TRUE) var/list/display_rank_names = list(RANK_DONE) if (new_rank_names.len > 0) display_rank_names += "** SELECTED **" for (var/rank_name in new_rank_names) display_rank_names += rank_name display_rank_names += "---------" for (var/rank_name in rank_names) if (!(rank_name in display_rank_names)) display_rank_names += rank_name var/next_rank = tgui_input_list(usr, "Please select a rank, or select [RANK_DONE] if you are finished.", "Pick Rank", display_rank_names) if (isnull(next_rank)) return if (next_rank == RANK_DONE) break // They clicked "** SELECTED **" or something silly. if (!(next_rank in rank_names)) continue if (next_rank in new_rank_names) new_rank_names -= next_rank continue if (next_rank == "*New Rank*") var/new_rank_name = tgui_input_text(usr, "Please input a new rank", "New custom rank") if (!new_rank_name) return var/datum/admin_rank/custom_rank = rank_names[new_rank_name] if (isnull(custom_rank)) if (D) custom_rank = new(new_rank_name, D.rank_flags()) else custom_rank = new(new_rank_name) GLOB.admin_ranks += custom_rank custom_ranks += custom_rank new_rank_names += new_rank_name new_rank_names += next_rank var/list/new_ranks = list() for (var/datum/admin_rank/admin_rank as anything in GLOB.admin_ranks) if (admin_rank.name in new_rank_names) new_ranks += admin_rank new_rank_names -= admin_rank.name if (new_rank_names.len == 0) break var/joined_rank = join_admin_ranks(new_ranks) var/m1 = "[key_name_admin(usr)] edited the admin rank of [admin_key] to [joined_rank] [use_db ? "permanently" : "temporarily"]" var/m2 = "[key_name(usr)] edited the admin rank of [admin_key] to [joined_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/db_query/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) for (var/datum/admin_rank/custom_rank in custom_ranks) //similarly if a temp rank is created it won't be in the db if someone is permanently changed to it var/datum/db_query/query_rank_in_db = SSdbcore.NewQuery( "SELECT 1 FROM [format_table_name("admin_ranks")] WHERE `rank` = :new_rank", list("new_rank" = custom_rank.name) ) 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/db_query/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" = custom_rank.name)) if(!query_add_rank.warn_execute()) qdel(query_add_rank) return qdel(query_add_rank) var/datum/db_query/query_add_rank_log = SSdbcore.NewQuery({" INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'add rank', :new_rank, CONCAT('New rank added: ', :new_rank)) "}, list("round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "new_rank" = custom_rank.name)) 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/db_query/query_change_rank = SSdbcore.NewQuery( "UPDATE [format_table_name("admin")] SET `rank` = :new_rank WHERE ckey = :admin_ckey", list("new_rank" = joined_rank, "admin_ckey" = admin_ckey) ) if(!query_change_rank.warn_execute()) qdel(query_change_rank) return qdel(query_change_rank) var/datum/db_query/query_change_rank_log = SSdbcore.NewQuery({" INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'change admin rank', :target, CONCAT('Rank of ', :target, ' changed from ', :old_rank, ' to ', :new_rank)) "}, list("round_id" = "[GLOB.round_id]", "adminckey" = usr.ckey, "adminip" = usr.client.address, "target" = admin_ckey, "old_rank" = old_rank, "new_rank" = joined_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.ranks = new_ranks //set the admin_rank as our rank //D.bypass_2fa = TRUE // Another admin has cleared us var/client/C = GLOB.directory[admin_ckey] D.associate(C) else D = new(new_ranks, admin_ckey) //new admin //D.bypass_2fa = TRUE // Another admin has cleared us D.activate() message_admins(m1) log_admin(m2) #undef RANK_DONE /// Changes, for this round only, the flags a particular admin gets to use /datum/admins/proc/change_admin_flags(admin_ckey, admin_key, datum/admins/admin_holder) if(!check_rights(R_PERMISSIONS)) return if(IsAdminAdvancedProcCall()) to_chat(usr, span_adminprefix("Rank Modification blocked: Advanced ProcCall detected."), confidential = TRUE) return var/new_flags = input_bitfield( usr, "Admin rights
This will affect only the current admin [admin_key]", "admin_flags", admin_holder.rank_flags(), 350, 590, allowed_edit_field = usr.client.holder.can_edit_rights_flags(), ) if(isnull(new_flags)) return admin_holder.disassociate() if (findtext(admin_holder.rank_names(), "([admin_ckey])")) var/datum/admin_rank/rank = admin_holder.ranks[1] rank.rights = new_flags rank.include_rights = new_flags rank.exclude_rights = NONE rank.can_edit_rights = rank.can_edit_rights else // Not a modified subrank, need to duplicate the admin_rank datum to prevent modifying others too. var/datum/admin_rank/new_admin_rank = new( /* init_name = */ "[admin_holder.rank_names()]([admin_ckey])", /* init_rights = */ new_flags, // rank_flags() includes the exclude rights, so we no longer need to handle them separately. /* init_exclude_rights = */ NONE, /* init_edit_rights = */ admin_holder.can_edit_rights_flags(), ) admin_holder.ranks = list(new_admin_rank) var/log = "[key_name(usr)] has updated the admin rights of [admin_ckey] into [rights2text(new_flags)]" message_admins(log) log_admin(log) var/client/admin_client = GLOB.directory[admin_ckey] admin_holder.associate(admin_client) /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.can_edit_rights_flags()) == R.rights)) to_chat(usr, span_adminprefix("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, span_adminprefix("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, span_adminprefix("Rank deletion not permitted while database rank loading is disabled."), confidential = TRUE) return var/datum/db_query/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(tgui_alert(usr,"Are you sure you want to remove [admin_rank]?","Confirm Removal",list("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/db_query/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/db_query/query_add_rank_log = SSdbcore.NewQuery({" INSERT INTO [format_table_name("admin_log")] (datetime, round_id, adminckey, adminip, operation, target, log) VALUES (NOW(), :round_id, :adminckey, INET_ATON(:adminip), 'remove rank', :admin_rank, CONCAT('Rank removed: ', :admin_rank)) "}, list("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_names() var/datum/db_query/query_sync_lastadminrank = SSdbcore.NewQuery( "UPDATE [format_table_name("erro_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)