diff --git a/code/defines/procs/helper_list.dm b/code/defines/procs/helper_list.dm index cfc9a6edefe..827a6348c26 100644 --- a/code/defines/procs/helper_list.dm +++ b/code/defines/procs/helper_list.dm @@ -286,4 +286,22 @@ proc/listclearnulls(list/list) if(Li <= L.len) return (result + L.Copy(Li, 0)) - return (result + R.Copy(Ri, 0)) \ No newline at end of file + return (result + R.Copy(Ri, 0)) + + +//Converts a bitfield to a list of numbers (or words if a wordlist is provided) +/proc/bitfield2list(bitfield = 0, list/wordlist) + var/list/r = list() + if(istype(wordlist,/list)) + var/max = min(wordlist.len,16) + var/bit = 1 + for(var/i=1, i<=max, i++) + if(bitfield & bit) + r += wordlist[i] + bit = bit << 1 + else + for(var/bit=1, bit<=65535, bit = bit << 1) + if(bitfield & bit) + r += bit + + return r \ No newline at end of file diff --git a/code/global.dm b/code/global.dm index 5b77d1acc8c..698fa2397f2 100644 --- a/code/global.dm +++ b/code/global.dm @@ -205,3 +205,31 @@ var/forum_authenticated_group = "10" // For FTP requests. (i.e. downloading runtime logs.) // However it'd be ok to use for accessing attack logs and such too, which are even laggier. var/fileaccess_timer = 1800 //Cannot access files by ftp until the game is finished setting up and stuff. + + +#define BUILDMODE 1 +#define ADMIN 2 +#define BAN 4 +#define FUN 8 +#define SERVER 16 +#define ADMDEBUG 32 +#define POSSESS 64 +#define PERMISSIONS 128 +//Keep this list synced with the #defines above +var/global/list/permissionwords = list("BUILDMODE", "ADMIN", "BAN", "FUN", "SERVER", "DEBUG", "POSSESS", "EDITPERMISSIONS") + + + +//Please do not edit these values. The database assigning proper rights relies on this. You can add new values, just don't change existing ones. +//This list is separate from the list used ingame, so that one can be edited with little consequence. This one is tied to the database +//The database admins should be consulted before any edits to this list. +#define SQL_BUILDMODE 1 +#define SQL_ADMIN 2 +#define SQL_BAN 4 +#define SQL_FUN 8 +#define SQL_SERVER 16 +#define SQL_DEBUG 32 +#define SQL_POSSESS 64 +#define SQL_PERMISSIONS 128 +//Same rules apply to this list as to the values above. You can only add stuff to it. +var/global/list/permissionwords_sql = list("BUILDMODE", "ADMIN", "BAN", "FUN", "SERVER", "DEBUG", "POSSESS", "EDITPERMISSIONS") \ No newline at end of file diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 7171236bb9f..bd66109623e 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -40,6 +40,7 @@ if(!need_update) return clear_admin_verbs() + handle_permission_verbs() switch(rank) if("Game Master") diff --git a/code/modules/admin/holder2.dm b/code/modules/admin/holder2.dm index c10d4b494fc..7949e046dca 100644 --- a/code/modules/admin/holder2.dm +++ b/code/modules/admin/holder2.dm @@ -19,6 +19,7 @@ var/list/admin_datums = list() var/fakekey = null var/ooccolor = "#b82e00" var/sound_adminhelp = 0 //If set to 1 this will play a sound when adminhelps are received. + var/sql_permissions = 0 //Permissions for different admin command groups. Must not be editable ingame. var/datum/marked_datum @@ -84,6 +85,49 @@ var/list/admin_datums = list() usr << "\red Unfortunatly there were no candidates available" return + if(href_list["editadminpermissions"]) + if(!usr.client) + return + + var/adm_ckey = href_list["editadminckey"] + if(!adm_ckey) + usr << "\red no valid ckey" + return + + if(!usr.client.holder || !(usr.client.holder.sql_permissions & PERMISSIONS)) + usr << "\red You do not have permission to do this!" + message_admins("[key_name_admin(usr)] attempted to edit the admin permissions of [adm_ckey] without authentication!") + log_admin("[key_name(usr)] attempted to edit the admin permissions of [adm_ckey] without authentication!") + return + + switch(href_list["editadminpermissions"]) + if("permissions") + usr << "Currently unavailable since nothing runs off of permissions" + if("rank") + var/new_rank = input("Please, select a rank", "New rank for player", null, null) as null|anything in list("Game Master","Game Admin", "Trial Admin", "Admin Observer") + if(!new_rank) + return + message_admins("[key_name_admin(usr)] edited the admin rank of [adm_ckey] to [new_rank]") + log_admin("[key_name(usr)] edited the admin rank of [adm_ckey] to [new_rank]") + log_admin_rank_modification(adm_ckey, new_rank) + if("remove") + if(alert("Are you sure you want to remove [adm_ckey]?","Message","Yes","Cancel") == "Yes") + message_admins("[key_name_admin(usr)] removed [adm_ckey] from the admins list") + log_admin("[key_name(usr)] removed [adm_ckey] from the admins list") + log_admin_rank_modification(adm_ckey, "Removed") + if("add") + var/new_ckey = input(usr,"New admin's ckey","Admin ckey", null) as text|null + if(!new_ckey) + return + var/new_rank = input("Please, select a rank", "New rank for player", null, null) as null|anything in list("Game Master","Game Admin", "Trial Admin", "Admin Observer") + if(!new_rank) + return + message_admins("[key_name_admin(usr)] added [new_ckey] as a new admin to the rank [new_rank]") + log_admin("[key_name(usr)] added [new_ckey] as a new admin to the rank [new_rank]") + log_admin_rank_modification(new_ckey, new_rank) + + + if(href_list["call_shuttle"]) if (src.rank in list("Trial Admin", "Badmin", "Game Admin", "Game Master")) if( ticker.mode.name == "blob" ) diff --git a/code/modules/admin/permissionverbs/assignment.dm b/code/modules/admin/permissionverbs/assignment.dm new file mode 100644 index 00000000000..66affaa6969 --- /dev/null +++ b/code/modules/admin/permissionverbs/assignment.dm @@ -0,0 +1,12 @@ + + + +//Before this proc is called, the holder variable must already be set, with the proper rank, level and permissions set. +//This proc also DOES NOT CLEAR EXISTING ADMIN VERBS + +/client/proc/handle_permission_verbs() + if(!holder || !holder.rank || !holder.sql_permissions) + return + + if(holder.sql_permissions & PERMISSIONS) + verbs += /client/proc/edit_admin_permissions \ No newline at end of file diff --git a/code/modules/admin/permissionverbs/permissionedit.dm b/code/modules/admin/permissionverbs/permissionedit.dm new file mode 100644 index 00000000000..f69138f23bb --- /dev/null +++ b/code/modules/admin/permissionverbs/permissionedit.dm @@ -0,0 +1,127 @@ +/client/proc/edit_admin_permissions() + set category = "Admin" + set name = "Permissions Panel" + set desc = "Edit admin permissions" + + if(!holder) + return + holder.edit_admin_permissions() + +/datum/admins/proc/edit_admin_permissions() + if(!usr.client) + return + + if(!usr.client.holder || !(usr.client.holder.sql_permissions & PERMISSIONS)) + usr << "\red You do not have permission to do this!" + return + + var/user = sqlfdbklogin + var/pass = sqlfdbkpass + var/db = sqlfdbkdb + var/address = sqladdress + var/port = sqlport + + var/DBConnection/dbcon = new() + dbcon.Connect("dbi:mysql:[db]:[address]:[port]","[user]","[pass]") + if(!dbcon.IsConnected()) + usr << "\red Failed to establish database connection" + return + + var/DBQuery/select_query = dbcon.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin ORDER BY rank, ckey") + select_query.Execute() + + var/output = "

Current admins

" + + output += "Add new admin" + + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + output += "" + + var/color1 = "#f4f4f4" + var/color2 = "#e7e7e7" + var/i = 1 //Used to determine the color of each row + + while(select_query.NextRow()) + i = !i + var/adm_ckey = select_query.item[1] + var/adm_rank = select_query.item[2] + var/adm_level = select_query.item[3] + var/adm_flags = text2num(select_query.item[4]) + output += "" + output += "" + output += "" + output += "" + var/list/permissionlist = bitfield2list(adm_flags, permissionwords_sql) + output += "" + output += "" + output += "" + + output += "
CKEYRANKLEVELPERMISSIONSOPTIONS
[adm_ckey][adm_rank][adm_level]" + for(var/word in permissionlist) + output += "[word]
" + output += "
" + + //Options + output += "PERMISSIONS
" + output += "RANK
" + output += "REMOVE" + + output += "
" + + usr << browse(output,"window=editadminpermissions;size=600x500") + + +/datum/admins/proc/log_admin_rank_modification(var/adm_ckey, var/new_rank) + if(!usr.client) + return + + if(!usr.client.holder || !(usr.client.holder.sql_permissions & PERMISSIONS)) + usr << "\red You do not have permission to do this!" + return + + var/user = sqlfdbklogin + var/pass = sqlfdbkpass + var/db = sqlfdbkdb + var/address = sqladdress + var/port = sqlport + + var/DBConnection/dbcon = new() + dbcon.Connect("dbi:mysql:[db]:[address]:[port]","[user]","[pass]") + if(!dbcon.IsConnected()) + usr << "\red Failed to establish database connection" + return + + if(!adm_ckey || !new_rank) + return + + if(!istext(adm_ckey) || !istext(new_rank)) + return + + var/DBQuery/select_query = dbcon.NewQuery("SELECT id FROM erro_admin WHERE ckey = '[adm_ckey]'") + select_query.Execute() + + var/new_admin = 1 + var/admin_id + while(select_query.NextRow()) + new_admin = 0 + admin_id = text2num(select_query.item[1]) + + if(new_admin) + var/DBQuery/insert_query = dbcon.NewQuery("INSERT INTO `erro_admin` (`id`, `ckey`, `rank`, `level`, `flags`) VALUES (null, '[adm_ckey]', '[new_rank]', -1, 0)") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Added new admin [adm_ckey] to rank [new_rank]');") + log_query.Execute() + usr << "\blue New admin added." + else + if(!isnull(admin_id) && isnum(admin_id)) + var/DBQuery/insert_query = dbcon.NewQuery("UPDATE `erro_admin` SET rank = '[new_rank]' WHERE id = [admin_id]") + insert_query.Execute() + var/DBQuery/log_query = dbcon.NewQuery("INSERT INTO `test`.`erro_admin_log` (`id` ,`datetime` ,`adminckey` ,`adminip` ,`log` ) VALUES (NULL , NOW( ) , '[usr.ckey]', '[usr.client.address]', 'Edited the rank of [adm_ckey] to [new_rank]');") + log_query.Execute() + usr << "\blue Admin rank changed." diff --git a/code/modules/admin/verbs/modifyvariables.dm b/code/modules/admin/verbs/modifyvariables.dm index 43fb36977de..877fd47182e 100644 --- a/code/modules/admin/verbs/modifyvariables.dm +++ b/code/modules/admin/verbs/modifyvariables.dm @@ -4,12 +4,14 @@ var/list/forbidden_varedit_object_types = list( /datum/feedback_variable //Prevents people messing with feedback gathering ) +/* /client/proc/cmd_modify_object_variables(obj/O as obj|mob|turf|area in world) set category = "Debug" set name = "Edit Variables" set desc="(target) Edit a target item's variables" src.modify_variables(O) feedback_add_details("admin_verb","EDITV") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! +*/ /client/proc/cmd_modify_ticker_variables() set category = "Debug" diff --git a/code/world.dm b/code/world.dm index c79e20cf8f4..7ef8e8e55c9 100644 --- a/code/world.dm +++ b/code/world.dm @@ -189,7 +189,6 @@ Starting up. [time2text(world.timeofday, "hh:mm.ss")] /world/proc/load_motd() join_motd = file2text("config/motd.txt") - /world/proc/load_admins() if(config.admin_legacy_system) //Legacy admin system uses admins.txt @@ -228,14 +227,43 @@ Starting up. [time2text(world.timeofday, "hh:mm.ss")] load_admins() return - var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank, level FROM erro_admin") + var/DBQuery/query = dbcon.NewQuery("SELECT ckey, rank, level, flags FROM erro_admin") query.Execute() while(query.NextRow()) var/adminckey = query.item[1] var/adminrank = query.item[2] var/adminlevel = query.item[3] + if(istext(adminlevel)) + adminlevel = text2num(adminlevel) + var/permissions = query.item[4] + if(istext(permissions)) + permissions = text2num(permissions) + + //This list of stuff translates the permission defines the database uses to the permission structure that the game uses. + var/permissions_actual = 0 + if(permissions & SQL_BUILDMODE) + permissions_actual |= BUILDMODE + if(permissions & SQL_ADMIN) + permissions_actual |= ADMIN + if(permissions & SQL_BAN) + permissions_actual |= BAN + if(permissions & SQL_FUN) + permissions_actual |= FUN + if(permissions & SQL_SERVER) + permissions_actual |= SERVER + if(permissions & SQL_DEBUG) + permissions_actual |= ADMDEBUG + if(permissions & SQL_POSSESS) + permissions_actual |= POSSESS + if(permissions & SQL_PERMISSIONS) + permissions_actual |= PERMISSIONS + + if(adminrank == "Removed") + return //This person was de-adminned. They are only in the admin list for archive purposes. + var/datum/admins/AD = new /datum/admins(adminrank) - AD.level = adminlevel + AD.level = adminlevel //Legacy support for old verbs + AD.sql_permissions = permissions_actual admins[adminckey] = AD if(!admins) @@ -244,8 +272,6 @@ Starting up. [time2text(world.timeofday, "hh:mm.ss")] load_admins() return - - /world/proc/load_configuration() config = new /datum/configuration() config.load("config/config.txt") diff --git a/tgstation.dme b/tgstation.dme index 22a4387fbc4..79a55391560 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -93,6 +93,7 @@ #define FILE_DIR "code/modules" #define FILE_DIR "code/modules/admin" #define FILE_DIR "code/modules/admin/DB ban" +#define FILE_DIR "code/modules/admin/permissionverbs" #define FILE_DIR "code/modules/admin/verbs" #define FILE_DIR "code/modules/assembly" #define FILE_DIR "code/modules/awaymissions" @@ -205,6 +206,7 @@ #define FILE_DIR "icons/vending_icons" #define FILE_DIR "interface" #define FILE_DIR "maps" +#define FILE_DIR "maps/backup" #define FILE_DIR "maps/RandomZLevels" #define FILE_DIR "sound" #define FILE_DIR "sound/AI" @@ -820,6 +822,8 @@ #include "code\modules\admin\player_panel.dm" #include "code\modules\admin\ToRban.dm" #include "code\modules\admin\DB ban\functions.dm" +#include "code\modules\admin\permissionverbs\assignment.dm" +#include "code\modules\admin\permissionverbs\permissionedit.dm" #include "code\modules\admin\verbs\adminhelp.dm" #include "code\modules\admin\verbs\adminjump.dm" #include "code\modules\admin\verbs\adminpm.dm"