Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions code/datums/extensions/network_lockable.dm
Original file line number Diff line number Diff line change
@@ -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

2 changes: 1 addition & 1 deletion code/game/machinery/_machines_base/machinery_components.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
177 changes: 115 additions & 62 deletions code/game/machinery/_machines_base/stock_parts/network_lock.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)))
Expand All @@ -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."))

Expand All @@ -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]"

Expand All @@ -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
Expand All @@ -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)
Expand All @@ -199,34 +220,66 @@

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

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

if(href_list["remove_group"])
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)
Expand Down
3 changes: 0 additions & 3 deletions code/game/machinery/camera/camera.dm
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'
Expand Down
Loading