diff --git a/code/datums/extensions/network_lockable.dm b/code/datums/extensions/network_lockable.dm new file mode 100644 index 00000000000..430ae0b0ed3 --- /dev/null +++ b/code/datums/extensions/network_lockable.dm @@ -0,0 +1,39 @@ +// Generic method to copy network lock access onto an atom without requiring a network connection +/datum/extension/network_lockable + base_type = /datum/extension/network_lockable + expected_type = /obj + flags = EXTENSION_FLAG_IMMEDIATE + +// Returns TRUE on interaction, even on failure. +/datum/extension/network_lockable/proc/process_interact(obj/item/I, mob/user) + var/obj/o_holder = holder + if(!istype(o_holder)) + return FALSE + + if(!length(o_holder.req_access)) + if(istype(I, /obj/item/stock_parts/network_receiver/network_lock)) + . = TRUE + var/obj/item/stock_parts/network_receiver/network_lock/lock = I + var/list/copy_access = lock.get_req_access() + if(!length(copy_access)) + to_chat(user, SPAN_WARNING("You must set up the access on \the [lock] first!")) + return + + to_chat(user, SPAN_NOTICE("You begin copying the access codes from \the [lock] into \the [o_holder]...")) + if(do_after(user, 2 SECONDS, o_holder)) + playsound(o_holder, 'sound/machines/ping.ogg', 10, 0) + to_chat(user, SPAN_NOTICE("\The [o_holder] pings as it successfully copies its access codes from \the [lock].")) + o_holder.req_access = copy_access.Copy() + return + else if(IS_MULTITOOL(I)) // Resetting access. + . = TRUE + if(!o_holder.allowed(user)) + to_chat(user, SPAN_WARNING("You lack access to reset the access codes on \the [o_holder]!")) + return + to_chat(user, SPAN_NOTICE("You begin resetting the access codes on \the [o_holder]...")) + if(do_after(user, 8 SECONDS, o_holder)) + playsound(o_holder, 'sound/machines/ping.ogg', 10, 0) + to_chat(user, SPAN_NOTICE("\The [o_holder] pings as it resets its access codes.")) + o_holder.req_access = null + return + diff --git a/code/game/machinery/_machines_base/machinery_components.dm b/code/game/machinery/_machines_base/machinery_components.dm index 7d7dd280131..9a0346c17ed 100644 --- a/code/game/machinery/_machines_base/machinery_components.dm +++ b/code/game/machinery/_machines_base/machinery_components.dm @@ -37,7 +37,7 @@ var/global/list/machine_path_to_circuit_type for(var/access_list in initial_access) // Each part is an AND component. var/obj/item/stock_parts/network_receiver/network_lock/lock = install_component(/obj/item/stock_parts/network_receiver/network_lock/buildable, refresh_parts = FALSE) - lock.groups = islist(access_list) ? access_list : list(access_list) + lock.access_keys = islist(access_list) ? access_list : list(access_list) // Create the parts we are supposed to have. If not full_populate, this is only hard-baked parts, and more will be added later. for(var/component_path in uncreated_component_parts) diff --git a/code/game/machinery/_machines_base/stock_parts/network_lock.dm b/code/game/machinery/_machines_base/stock_parts/network_lock.dm index ca74dc09444..a5f8b4c1608 100644 --- a/code/game/machinery/_machines_base/stock_parts/network_lock.dm +++ b/code/game/machinery/_machines_base/stock_parts/network_lock.dm @@ -7,14 +7,17 @@ part_flags = PART_FLAG_QDEL base_type = /obj/item/stock_parts/network_receiver/network_lock - var/auto_deny_all // Set this to TRUE to deny all access attempts if network connection is lost. + var/requires_network = FALSE // Set this to TRUE to deny all access attempts if network connection is lost. var/initial_network_id // The address to the network var/initial_network_key // network KEY var/selected_parent_group // Current selected parent_group for access assignment. - var/list/groups // List of lists of groups. In order to access the device, users must have membership in at least one - // of the groups in each list. - var/selected_pattern // Index of the group pattern selected. + var/list/access_keys // List of lists of groups and account logins. + // In order to access the device, users must have membership in at least one + // of the groups or accounts in each list. Account logins are denoted with an @ at the end. + + var/selected_pattern // Index of the access key pattern selected. + var/viewing_accounts = FALSE var/emagged // Whether or not this has been emagged. var/error @@ -40,36 +43,40 @@ if(!is_functional()) return list() - . = get_default_access() var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device) - var/datum/computer_network/network = D.get_network(NET_FEATURE_ACCESS) - if(!network) - return + var/datum/computer_network/network = D?.get_network(NET_FEATURE_ACCESS) + var/datum/extension/network_device/acl/access_controller = network?.access_controller - var/datum/extension/network_device/acl/access_controller = network.access_controller - if(!access_controller) - return + var/list/all_groups = access_controller?.get_all_groups() + var/list/all_logins = network?.get_all_logins() + + if(requires_network) + // Network locks require no access if the network is down and requires_network is toggled on + if(!network || !access_controller) + return list() - LAZYINITLIST(groups) + LAZYINITLIST(access_keys) var/list/resulting_access = list() - for(var/list/pattern in groups) + for(var/list/pattern in access_keys) var/list/resulting_pattern = list() - for(var/group in pattern) - if(!(group in access_controller.get_all_groups())) - pattern -= group // This group doesn't exist anymore - delete it. - continue - resulting_pattern |= "[group].[D.network_id]" + for(var/access_key in pattern) + // This is an account login. + if(text_ends_with(access_key, "@")) + if(all_logins && !(copytext(access_key, 1, -1) in all_logins)) // This account doesn't exist anymore - delete it + pattern -= access_key + continue + resulting_pattern |= "[access_key][D.network_id]" // The @ is included in the pattern. + else + if(all_groups && !(access_key in all_groups)) // Ditto for groups + pattern -= access_key + continue + resulting_pattern |= "[access_key].[D.network_id]" if(resulting_pattern.len) resulting_access += list(resulting_pattern) if(!length(resulting_access)) return return resulting_access -/obj/item/stock_parts/network_receiver/network_lock/proc/get_default_access() - if(auto_deny_all) - return list("NO_PERMISSIONS_DENY_ALL") - return list() - /obj/item/stock_parts/network_receiver/network_lock/examine(mob/user) . = ..() if(emagged && user.skill_check_multiple(list(SKILL_FORENSICS = SKILL_EXPERT, SKILL_COMPUTER = SKILL_EXPERT))) @@ -86,8 +93,8 @@ /obj/item/stock_parts/network_receiver/network_lock/afterattack(atom/target, mob/user, proximity_flag, click_parameters) if(istype(target, /obj/item/stock_parts/network_receiver/network_lock)) var/obj/item/stock_parts/network_receiver/network_lock/other_lock = target - if(length(other_lock.groups)) // Prevent mistakingly copying from a blank lock instead of vice versa. - groups = other_lock.groups.Copy() + if(length(other_lock.access_keys)) // Prevent mistakingly copying from a blank lock instead of vice versa. + access_keys = other_lock.access_keys.Copy() playsound(src, 'sound/machines/ping.ogg', 20, 0) to_chat(user, SPAN_NOTICE("\The [src] pings as it successfully copies its access requirements from the other network lock.")) @@ -112,47 +119,59 @@ data["connected"] = FALSE return data["connected"] = TRUE - data["default_state"] = auto_deny_all + data["req_network"] = requires_network if(!network.access_controller) return data["patterns"] = list() var/pattern_index = 0 - for(var/list/pattern in groups) + for(var/list/pattern in access_keys) pattern_index++ data["patterns"].Add(list(list( "index" = "[pattern_index]", - "groups" = english_list(pattern, "No groups assigned!", and_text = " or ") + "access" = english_list(pattern, "No access assigned!", and_text = " or ") ))) - var/list/group_dictionary = network.access_controller.get_group_dict() var/list/parent_groups_data var/list/child_groups_data - - var/list/pattern = LAZYACCESS(groups, selected_pattern) + var/list/account_data + var/list/pattern = LAZYACCESS(access_keys, selected_pattern) if(pattern) - if(selected_parent_group) - if(!(selected_parent_group in group_dictionary)) - selected_parent_group = null - else - var/list/child_groups = group_dictionary[selected_parent_group] - if(child_groups) - child_groups_data = list() - for(var/child_group in child_groups) - child_groups_data.Add(list(list( - "child_group" = child_group, - "assigned" = (LAZYISIN(pattern, child_group)) - ))) - if(!selected_parent_group) // Check again in case we ended up with a non-existent selected parent group instead of breaking the UI. - parent_groups_data = list() - for(var/parent_group in group_dictionary) - parent_groups_data.Add(list(list( - "parent_group" = parent_group, - "assigned" = (LAZYISIN(pattern, parent_group)) - ))) + if(viewing_accounts) + data["viewing_accounts"] = TRUE + var/list/login_list = network.get_all_logins() + if(length(login_list)) + account_data = list() + for(var/login in login_list) + account_data.Add(list(list( + "login" = login, + "assigned" = (LAZYISIN(pattern, (login + "@"))) + ))) + else + var/list/group_dictionary = network.access_controller.get_group_dict() + if(selected_parent_group) + if(!(selected_parent_group in group_dictionary)) + selected_parent_group = null + else + var/list/child_groups = group_dictionary[selected_parent_group] + if(length(child_groups)) + child_groups_data = list() + for(var/child_group in child_groups) + child_groups_data.Add(list(list( + "child_group" = child_group, + "assigned" = (LAZYISIN(pattern, child_group)) + ))) + if(!selected_parent_group) // Check again in case we ended up with a non-existent selected parent group instead of breaking the UI. + parent_groups_data = list() + for(var/parent_group in group_dictionary) + parent_groups_data.Add(list(list( + "parent_group" = parent_group, + "assigned" = (LAZYISIN(pattern, parent_group)) + ))) data["parent_groups"] = parent_groups_data data["child_groups"] = child_groups_data + data["accounts"] = account_data data["selected_parent_group"] = selected_parent_group data["selected_pattern"] = "[selected_pattern]" @@ -165,6 +184,12 @@ if(href_list["refresh"]) error = null return TOPIC_REFRESH + + if(href_list["back"]) + viewing_accounts = FALSE + selected_parent_group = null + return TOPIC_REFRESH + var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device) if(!D) return @@ -173,24 +198,20 @@ D.ui_interact(user) return TOPIC_REFRESH - if(href_list["allow_all"]) - auto_deny_all = FALSE - return TOPIC_REFRESH - - if(href_list["deny_all"]) - auto_deny_all = TRUE + if(href_list["req_network"]) + requires_network = !requires_network return TOPIC_REFRESH if(href_list["add_pattern"]) - if(length(groups) >= MAX_PATTERNS) + if(length(access_keys) >= MAX_PATTERNS) to_chat(usr, SPAN_WARNING("You cannot add more than [MAX_PATTERNS] patterns to \the [src]!")) return TOPIC_HANDLED - LAZYADD(groups, list(list())) + LAZYADD(access_keys, list(list())) return TOPIC_REFRESH if(href_list["remove_pattern"]) var/pattern_index = text2num(href_list["remove_pattern"]) - LAZYREMOVE(groups, list(LAZYACCESS(groups, pattern_index))) // We have to encapsulate the pattern in another list to actually delete it. + LAZYREMOVE(access_keys, list(LAZYACCESS(access_keys, pattern_index))) // We have to encapsulate the pattern in another list to actually delete it. if(selected_pattern == pattern_index) selected_pattern = null else if(selected_pattern > pattern_index) @@ -199,11 +220,15 @@ if(href_list["select_pattern"]) var/pattern_index = text2num(href_list["select_pattern"]) - if(pattern_index > LAZYLEN(groups)) + if(pattern_index > LAZYLEN(access_keys)) return TOPIC_HANDLED selected_pattern = pattern_index return TOPIC_REFRESH + if(href_list["view_accounts"]) + viewing_accounts = TRUE + return TOPIC_REFRESH + if(href_list["select_parent_group"]) selected_parent_group = href_list["select_parent_group"] return TOPIC_REFRESH @@ -211,15 +236,32 @@ if(href_list["info"]) switch(href_list["info"]) if("pattern") - to_chat(user, SPAN_NOTICE("In order to access the device, users must be a member of at least one group in each access pattern.")) + to_chat(user, SPAN_NOTICE("In order to access the device, users must be a member of at least one group or account in each access pattern.")) if("parent_groups") to_chat(user, SPAN_NOTICE("Assigning a parent group to an access pattern will permit any member of its child groups access to the pattern.")) + if("req_network") + to_chat(user, SPAN_NOTICE("If toggled on, the network lock will automatically allow all access attempts if the network goes down. This should rarely be used.")) + + return TOPIC_HANDLED - var/list/pattern_list = LAZYACCESS(groups, selected_pattern) + var/list/pattern_list = LAZYACCESS(access_keys, selected_pattern) if(!pattern_list) return TOPIC_REFRESH + + var/datum/computer_network/network = D.get_network() + if(!network) + return TOPIC_REFRESH + + var/datum/extension/network_device/acl/acl = network.access_controller + if(!acl) + return TOPIC_REFRESH + + if(href_list["assign_group"]) + var/list/valid_groups = acl.get_all_groups() + if(!(href_list["assign_group"] in valid_groups)) + return TOPIC_REFRESH pattern_list |= href_list["assign_group"] return TOPIC_REFRESH @@ -227,6 +269,17 @@ pattern_list -= href_list["remove_group"] return TOPIC_REFRESH + if(href_list["assign_account"]) + var/list/valid_accounts = network.get_all_logins() + if(!(href_list["assign_account"] in valid_accounts)) + return TOPIC_REFRESH + pattern_list |= (href_list["assign_account"] + "@") + return TOPIC_REFRESH + + if(href_list["remove_account"]) + pattern_list -= (href_list["remove_account"] + "@") + return TOPIC_REFRESH + /obj/item/stock_parts/network_receiver/network_lock/ui_interact(mob/user, ui_key, datum/nanoui/ui, force_open, datum/nanoui/master_ui, datum/topic_state/state) var/data = ui_data(user) ui = SSnano.try_update_ui(user, src, ui_key, ui, data, force_open) diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 04426a9e287..48bc5d875c0 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -340,9 +340,6 @@ /decl/stock_part_preset/network_lock/camera expected_part_type = /obj/item/stock_parts/network_receiver/network_lock -/decl/stock_part_preset/network_lock/camera/do_apply(obj/machinery/camera/machine, obj/item/stock_parts/network_receiver/network_lock/part) - part.auto_deny_all = TRUE - /obj/machinery/camera public_methods = list( /decl/public_access/public_method/toggle_camera diff --git a/code/game/objects/structures/crates_lockers/closets/_closet_appearance_definitions.dm b/code/game/objects/structures/crates_lockers/closets/_closet_appearance_definitions.dm index 1e13e508629..92e3ef289e1 100644 --- a/code/game/objects/structures/crates_lockers/closets/_closet_appearance_definitions.dm +++ b/code/game/objects/structures/crates_lockers/closets/_closet_appearance_definitions.dm @@ -501,6 +501,15 @@ /decl/closet_appearance/secure_closet/sol/two/dark color = COLOR_DARK_BLUE_GRAY +/decl/closet_appearance/secure_closet/network + color = COLOR_OFF_WHITE + decals = list( + "lower_side_vent" + ) + extra_decals = list( + "stripe_vertical_mid_full" = COLOR_CYAN_BLUE + ) + // Crates. /decl/closet_appearance/crate decals = null @@ -598,6 +607,13 @@ "crate_stripe_right" = COLOR_YELLOW_GRAY ) +/decl/closet_appearance/crate/secure/network + color = COLOR_OFF_WHITE + extra_decals = list( + "crate_stripe_left" = COLOR_CYAN_BLUE, + "crate_stripe_right" = COLOR_CYAN_BLUE + ) + // Large crates. /decl/closet_appearance/large_crate base_icon = 'icons/obj/closets/bases/large_crate.dmi' diff --git a/code/game/objects/structures/crates_lockers/closets/secure/network.dm b/code/game/objects/structures/crates_lockers/closets/secure/network.dm new file mode 100644 index 00000000000..10b0aaa996d --- /dev/null +++ b/code/game/objects/structures/crates_lockers/closets/secure/network.dm @@ -0,0 +1,22 @@ +/obj/structure/closet/secure_closet/network + name = "network secured locker" + desc = "A storage locker capable of storing access codes from a functioning network lock." + + reinf_material = /decl/material/solid/organic/plastic + closet_appearance = /decl/closet_appearance/secure_closet/network + locked = FALSE + +/obj/structure/closet/secure_closet/Initialize() + . = ..() + set_extension(src, /datum/extension/network_lockable) + +/obj/structure/closet/secure_closet/network/attackby(obj/item/W, mob/user) + if(!opened) + var/datum/extension/network_lockable/lockable = get_extension(src, /datum/extension/network_lockable) + if(lockable.process_interact(W, user)) + return TRUE + + . = ..() + +/obj/structure/closet/secure_closet/network/get_mechanics_info() + return "[..()]
Can have its access set when closed with a functioning network lock.
Can have its access reset with a multitool." \ No newline at end of file diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 695f17f89a6..1d1506e3ab7 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -293,4 +293,27 @@ closet_appearance = /decl/closet_appearance/crate/radiation /obj/structure/closet/crate/uranium/WillContain() - return list(/obj/item/stack/material/puck/mapped/uranium/ten = 5) \ No newline at end of file + return list(/obj/item/stack/material/puck/mapped/uranium/ten = 5) + +/obj/structure/closet/crate/secure/network + name = "network secured crate" + desc = "A storage crate capable of storing access codes from a functioning network lock." + closet_appearance = /decl/closet_appearance/crate/secure/network + + reinf_material = /decl/material/solid/organic/plastic + locked = FALSE + +/obj/structure/closet/crate/secure/network/Initialize() + . = ..() + set_extension(src, /datum/extension/network_lockable) + +/obj/structure/closet/crate/secure/network/attackby(obj/item/W, mob/user) + if(!opened) + var/datum/extension/network_lockable/lockable = get_extension(src, /datum/extension/network_lockable) + if(lockable.process_interact(W, user)) + return TRUE + + . = ..() + +/obj/structure/closet/crate/secure/network/get_mechanics_info() + return "[..()]
Can have its access set when closed with a functioning network lock.
Can have its access reset with a multitool." \ No newline at end of file diff --git a/code/modules/fabrication/designs/industrial/designs_structure.dm b/code/modules/fabrication/designs/industrial/designs_structure.dm new file mode 100644 index 00000000000..417bff901c5 --- /dev/null +++ b/code/modules/fabrication/designs/industrial/designs_structure.dm @@ -0,0 +1,8 @@ +/datum/fabricator_recipe/industrial/structure + category = "Structures" + +/datum/fabricator_recipe/industrial/structure/network_closet + path = /obj/structure/closet/secure_closet/network + +/datum/fabricator_recipe/industrial/structure/network_crate + path = /obj/structure/closet/crate/secure/network \ No newline at end of file diff --git a/code/modules/modular_computers/networking/accounts/_network_accounts.dm b/code/modules/modular_computers/networking/accounts/_network_accounts.dm index ecd642c5bc8..4f8b7a7a0be 100644 --- a/code/modules/modular_computers/networking/accounts/_network_accounts.dm +++ b/code/modules/modular_computers/networking/accounts/_network_accounts.dm @@ -16,6 +16,15 @@ result |= E return result +/datum/computer_network/proc/get_all_logins() + var/list/result = list() + for(var/datum/extension/network_device/mainframe/M in get_mainframes_by_role(MF_ROLE_ACCOUNT_SERVER)) + for(var/datum/computer_file/data/account/E in M.get_all_files()) + if(E.backup) + continue + ADD_SORTED(result, E.login, /proc/cmp_text_asc) + return result + // Return account backups keyed by account login /datum/computer_network/proc/get_account_backups(accesses) var/list/result = list() diff --git a/code/modules/modular_computers/networking/accounts/id_card.dm b/code/modules/modular_computers/networking/accounts/id_card.dm index 87591829b5b..161447c432c 100644 --- a/code/modules/modular_computers/networking/accounts/id_card.dm +++ b/code/modules/modular_computers/networking/accounts/id_card.dm @@ -1,8 +1,14 @@ +#define CACHE_TIME 30 MINUTES /obj/item/card/id/network + name = "network identification card" + desc = "A card used to provide ID and determine access. It connects remotely to a network to find its access codes." var/weakref/current_account color = COLOR_GRAY80 detail_color = COLOR_SKY_BLUE + var/list/cached_network_access = list() + var/last_cached // The real time of day the network access was last cached + /obj/item/card/id/network/Initialize() set_extension(src, /datum/extension/network_device/id_card) return ..() @@ -12,14 +18,24 @@ var/datum/computer_file/data/account/access_account = resolve_account() var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device) var/datum/computer_network/network = D.get_network(NET_FEATURE_ACCESS) - if(network && access_account && access_account.login != ignore_account) - var/location = "[network.network_id]" - if(access_account) - . += "[access_account.login]@[location]" // User access uses '@' + if(network) + var/list/net_access = list() + if(access_account && access_account.login != ignore_account) + var/location = "[network.network_id]" + net_access += "[access_account.login]@[location]" // User access uses '@' for(var/group in access_account.groups) - . += "[group].[location]" // Group access uses '.' + net_access += "[group].[location]" // Group access uses '.' for(var/group in access_account.parent_groups) // Membership in a child group grants access to anything with an access requirement set to the parent group. - . += "[group].[location]" + net_access += "[group].[location]" + + . += net_access + cached_network_access = net_access + last_cached = REALTIMEOFDAY + else if(REALTIMEOFDAY <= (last_cached + CACHE_TIME)) + . += cached_network_access + else if(length(cached_network_access)) + cached_network_access.Cut() + visible_message(SPAN_SUBTLE("\The [src] beeps, indicating that it cleared its cached access."), range = 1) /obj/item/card/id/network/proc/resolve_account(net_feature = NET_FEATURE_ACCESS) if(!current_account) @@ -114,6 +130,9 @@ current_account = weakref(check_account) return TRUE + // Cache the access post login. + GetAccess() + /obj/item/card/id/network/proc/get_network_id() var/datum/extension/network_device/D = get_extension(src, /datum/extension/network_device) var/datum/computer_network/network = D?.get_network() @@ -128,3 +147,5 @@ "You flash your ID card: [html_icon(src)] [src.name]. The assignment on the card: '[src.assignment]'.") src.add_fingerprint(usr) + +#undef CACHE_TIME \ No newline at end of file diff --git a/code/modules/modular_computers/networking/finance/money_accounts.dm b/code/modules/modular_computers/networking/finance/money_accounts.dm index fc1f68fd107..f81eed45091 100644 --- a/code/modules/modular_computers/networking/finance/money_accounts.dm +++ b/code/modules/modular_computers/networking/finance/money_accounts.dm @@ -85,7 +85,7 @@ . = ..() /datum/money_account/child/network/Destroy(force) - var/datum/computer_file/data/account/attached_account = network_account.resolve() + var/datum/computer_file/data/account/attached_account = network_account?.resolve() if(attached_account) attached_account.money_account = null . = ..() diff --git a/mods/persistence/game/machinery/_machines_base/stock_parts/network_lock.dm b/mods/persistence/game/machinery/_machines_base/stock_parts/network_lock.dm index 72ee15d0c76..a40b2fda065 100644 --- a/mods/persistence/game/machinery/_machines_base/stock_parts/network_lock.dm +++ b/mods/persistence/game/machinery/_machines_base/stock_parts/network_lock.dm @@ -9,7 +9,7 @@ initial_network_id = null initial_network_key = null -SAVED_VAR(/obj/item/stock_parts/network_receiver/network_lock, groups) +SAVED_VAR(/obj/item/stock_parts/network_receiver/network_lock, access_keys) SAVED_VAR(/obj/item/stock_parts/network_receiver/network_lock, initial_network_id) SAVED_VAR(/obj/item/stock_parts/network_receiver/network_lock, initial_network_key) SAVED_VAR(/obj/item/stock_parts/network_receiver/network_lock, emagged) \ No newline at end of file diff --git a/nano/templates/network_lock.tmpl b/nano/templates/network_lock.tmpl index 319618960fc..70a0dbadb1d 100644 --- a/nano/templates/network_lock.tmpl +++ b/nano/templates/network_lock.tmpl @@ -13,10 +13,11 @@

Disconnected from network.

{{else}}
-
Device Default State:
+
Network Requirement:
- {{:helper.link('ALLOW ALL', null, { "allow_all" : 1 }, !data.default_state ? 'disabled' : null)}} - {{:helper.link('DENY ALL', null, { "deny_all" : 1 }, data.default_state ? 'disabled' : null)}} + {{:helper.link('On', null, { "req_network" : 1 }, data.req_network ? 'selected' : null)}} + {{:helper.link('Off', null, { "req_network" : 1 }, !data.req_network ? 'selected' : null)}} + {{:helper.link('?', null, { "info" : "req_network" }, null)}}

@@ -26,7 +27,7 @@ Operations {{for data.patterns}} {{:helper.link("Pattern " + value.index, null, { "select_pattern" : value.index}, (data.selected_pattern == value.index) ? 'selected' : null)}} - {{:value.groups}} + {{:value.access}} {{:helper.link('Delete pattern', null, { "remove_pattern" : value.index})}} {{/for}} @@ -34,23 +35,7 @@ {{:helper.link('Add pattern', null, { "add_pattern" : 1}, null)}} {{:helper.link('?', null, { "info" : "pattern" }, null)}}
- {{if data.parent_groups}} -

Parent Groups:

- {{:helper.link('?', null, { "info" : "parent_groups" }, null)}} - -
Group - Operations - {{for data.parent_groups}} -
{{:helper.link(value.parent_group, null, { "select_parent_group" : value.parent_group })}} - - {{if value.assigned}} - {{:helper.link('REMOVE', null, { "remove_group" : value.parent_group })}} - {{else}} - {{:helper.link('ASSIGN', null, { "assign_group" : value.parent_group })}} - {{/if}} - {{/for}} -
- {{else data.selected_parent_group}} + {{if data.selected_parent_group}}

Viewing Child Groups for: {{:data.selected_parent_group}}

{{if data.child_groups}}

Child Groups:

@@ -71,12 +56,50 @@ No child groups found! {{/if}}
- {{:helper.link('Back to parent group listing', null, { "select_parent_group" : null })}} + {{:helper.link('Back to access listings', null, { "back" : 1 })}}
- {{else}} - {{if data.selected_pattern}} - No groups found on network! + {{else data.viewing_accounts}} +

Viewing accounts:

+ {{if data.accounts}} + +
Account + Operations + {{for data.accounts}} +
{{:value.login}} + + {{if value.assigned}} + {{:helper.link('REMOVE', null, { "remove_account" : value.login })}} + {{else}} + {{:helper.link('ASSIGN', null, { "assign_account" : value.login })}} + {{/if}} + {{/for}} +
+ {{else}} + No accounts found on network! {{/if}} + {{:helper.link('Back to access listings', null, { "back" : 1 })}} + {{else data.selected_pattern}} +

Parent Groups:

+ {{if data.parent_groups}} + {{:helper.link('?', null, { "info" : "parent_groups" }, null)}} + +
Group + Operations + {{for data.parent_groups}} +
{{:helper.link(value.parent_group, null, { "select_parent_group" : value.parent_group })}} + + {{if value.assigned}} + {{:helper.link('REMOVE', null, { "remove_group" : value.parent_group })}} + {{else}} + {{:helper.link('ASSIGN', null, { "assign_group" : value.parent_group })}} + {{/if}} + {{/for}} +
+ {{else}} + No groups found on network! + {{/if}} +
+ {{:helper.link('View accounts', null, { "view_accounts" : "1" }, null)}} {{/if}} {{/if}} {{/if}} \ No newline at end of file diff --git a/nebula.dme b/nebula.dme index 5098fb8015a..9d04ce19bc5 100644 --- a/nebula.dme +++ b/nebula.dme @@ -330,6 +330,7 @@ #include "code\datums\extensions\label.dm" #include "code\datums\extensions\local_network.dm" #include "code\datums\extensions\lockable.dm" +#include "code\datums\extensions\network_lockable.dm" #include "code\datums\extensions\parts_stash.dm" #include "code\datums\extensions\penetration.dm" #include "code\datums\extensions\radio_provider.dm" @@ -1360,6 +1361,7 @@ #include "code\game\objects\structures\crates_lockers\closets\secure\guncabinet.dm" #include "code\game\objects\structures\crates_lockers\closets\secure\hydroponics.dm" #include "code\game\objects\structures\crates_lockers\closets\secure\medical.dm" +#include "code\game\objects\structures\crates_lockers\closets\secure\network.dm" #include "code\game\objects\structures\crates_lockers\closets\secure\nuke.dm" #include "code\game\objects\structures\crates_lockers\closets\secure\personal.dm" #include "code\game\objects\structures\crates_lockers\closets\secure\scientist.dm" @@ -2029,6 +2031,7 @@ #include "code\modules\fabrication\designs\industrial\_designs_industrial.dm" #include "code\modules\fabrication\designs\industrial\designs_armour.dm" #include "code\modules\fabrication\designs\industrial\designs_exosuit_components.dm" +#include "code\modules\fabrication\designs\industrial\designs_structure.dm" #include "code\modules\fabrication\designs\meat\_designs_meat.dm" #include "code\modules\fabrication\designs\meat\designs_organs.dm" #include "code\modules\fabrication\designs\micro\designs_cutlery.dm"