diff --git a/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs b/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs index 6335db83de..c2b23f7341 100644 --- a/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs +++ b/Content.Client/Research/UI/ResearchConsoleMenu.xaml.cs @@ -70,7 +70,7 @@ public sealed partial class ResearchConsoleMenu : FancyWindow var hasAccess = _player.LocalPlayer?.ControlledEntity is not { } local || !_entity.TryGetComponent(Entity, out var access) || - _accessReader.IsAllowed(local, access); + _accessReader.IsAllowed(local, Entity, access); foreach (var techId in _technologyDatabase.CurrentTechnologyCards) { var tech = _prototype.Index(techId); diff --git a/Content.Server/Access/Systems/AccessOverriderSystem.cs b/Content.Server/Access/Systems/AccessOverriderSystem.cs index 8184d5cbd8..e8fc837a7c 100644 --- a/Content.Server/Access/Systems/AccessOverriderSystem.cs +++ b/Content.Server/Access/Systems/AccessOverriderSystem.cs @@ -260,6 +260,6 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem return true; var privilegedId = component.PrivilegedIdSlot.Item; - return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, reader); + return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, reader); } } diff --git a/Content.Server/Access/Systems/IdCardConsoleSystem.cs b/Content.Server/Access/Systems/IdCardConsoleSystem.cs index 6730a07137..10bf65d0c6 100644 --- a/Content.Server/Access/Systems/IdCardConsoleSystem.cs +++ b/Content.Server/Access/Systems/IdCardConsoleSystem.cs @@ -188,7 +188,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem return true; var privilegedId = component.PrivilegedIdSlot.Item; - return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, reader); + return privilegedId != null && _accessReader.IsAllowed(privilegedId.Value, uid, reader); } private void UpdateStationRecord(EntityUid uid, EntityUid targetId, string newFullName, string newJobTitle, JobPrototype? newJobProto) diff --git a/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs b/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs index f88a36557e..0ef49be46a 100644 --- a/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AirAlarmSystem.cs @@ -322,13 +322,13 @@ public sealed class AirAlarmSystem : EntitySystem UpdateUI(uid, component); } } - + private void OnCopyDeviceData(EntityUid uid, AirAlarmComponent component, AirAlarmCopyDeviceDataMessage args) { if (!AccessCheck(uid, args.Session.AttachedEntity, component)) { UpdateUI(uid, component); - return; + return; } switch (args.Data) @@ -339,7 +339,7 @@ public sealed class AirAlarmSystem : EntitySystem SetData(uid, addr, args.Data); } break; - + case GasVentScrubberData scrubberData: foreach (string addr in component.ScrubberData.Keys) { @@ -361,7 +361,7 @@ public sealed class AirAlarmSystem : EntitySystem if (user == null) return false; - if (!_access.IsAllowed(user.Value, reader)) + if (!_access.IsAllowed(user.Value, uid, reader)) { _popup.PopupEntity(Loc.GetString("air-alarm-ui-access-denied"), user.Value, user.Value); return false; diff --git a/Content.Server/Communications/CommunicationsConsoleSystem.cs b/Content.Server/Communications/CommunicationsConsoleSystem.cs index 8f64e88f14..b78389b57d 100644 --- a/Content.Server/Communications/CommunicationsConsoleSystem.cs +++ b/Content.Server/Communications/CommunicationsConsoleSystem.cs @@ -174,7 +174,7 @@ namespace Content.Server.Communications if (TryComp(console, out var accessReaderComponent) && !HasComp(console)) { - return _accessReaderSystem.IsAllowed(user, accessReaderComponent); + return _accessReaderSystem.IsAllowed(user, console, accessReaderComponent); } return true; } diff --git a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs index 276e3ebb65..9bbdc890a7 100644 --- a/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs +++ b/Content.Server/DeviceNetwork/Systems/NetworkConfiguratorSystem.cs @@ -192,7 +192,7 @@ public sealed class NetworkConfiguratorSystem : SharedNetworkConfiguratorSystem if (!TryComp(target, out AccessReaderComponent? reader) || user == null) return true; - if (_accessSystem.IsAllowed(user.Value, reader)) + if (_accessSystem.IsAllowed(user.Value, target, reader)) return true; _audioSystem.PlayPvs(component.SoundNoAccess, user.Value, AudioParams.Default.WithVolume(-2f).WithPitchScale(1.2f)); diff --git a/Content.Server/MassMedia/Systems/NewsSystem.cs b/Content.Server/MassMedia/Systems/NewsSystem.cs index 6cca0cc7c3..43d6545100 100644 --- a/Content.Server/MassMedia/Systems/NewsSystem.cs +++ b/Content.Server/MassMedia/Systems/NewsSystem.cs @@ -232,7 +232,7 @@ public sealed class NewsSystem : EntitySystem { if (EntityManager.TryGetComponent(device, out var accessReader) && user.HasValue && - _accessReader.IsAllowed(user.Value, accessReader)) + _accessReader.IsAllowed(user.Value, device, accessReader)) { return true; } diff --git a/Content.Server/Power/EntitySystems/ApcSystem.cs b/Content.Server/Power/EntitySystems/ApcSystem.cs index db07eb7aa9..3f5da2129a 100644 --- a/Content.Server/Power/EntitySystems/ApcSystem.cs +++ b/Content.Server/Power/EntitySystems/ApcSystem.cs @@ -57,7 +57,7 @@ namespace Content.Server.Power.EntitySystems if (args.Session.AttachedEntity == null) return; - if (access == null || _accessReader.IsAllowed(args.Session.AttachedEntity.Value, access)) + if (access == null || _accessReader.IsAllowed(args.Session.AttachedEntity.Value, uid, access)) { component.HasAccess = true; } @@ -82,7 +82,7 @@ namespace Content.Server.Power.EntitySystems if (args.Session.AttachedEntity == null) return; - if (access == null || _accessReader.IsAllowed(args.Session.AttachedEntity.Value, access)) + if (access == null || _accessReader.IsAllowed(args.Session.AttachedEntity.Value, uid, access)) { ApcToggleBreaker(uid, component); } diff --git a/Content.Server/Research/Systems/ResearchSystem.Console.cs b/Content.Server/Research/Systems/ResearchSystem.Console.cs index 2721a9f294..6da547999d 100644 --- a/Content.Server/Research/Systems/ResearchSystem.Console.cs +++ b/Content.Server/Research/Systems/ResearchSystem.Console.cs @@ -25,7 +25,7 @@ public sealed partial class ResearchSystem if (!this.IsPowered(uid, EntityManager)) return; - if (TryComp(uid, out var access) && !_accessReader.IsAllowed(ent, access)) + if (TryComp(uid, out var access) && !_accessReader.IsAllowed(ent, uid, access)) { _popup.PopupEntity(Loc.GetString("research-console-no-access-popup"), ent); return; diff --git a/Content.Server/VendingMachines/VendingMachineSystem.cs b/Content.Server/VendingMachines/VendingMachineSystem.cs index f9d7759d0c..f80d4d600a 100644 --- a/Content.Server/VendingMachines/VendingMachineSystem.cs +++ b/Content.Server/VendingMachines/VendingMachineSystem.cs @@ -226,7 +226,7 @@ namespace Content.Server.VendingMachines if (!TryComp(uid, out var accessReader)) return true; - if (_accessReader.IsAllowed(sender, accessReader) || HasComp(uid)) + if (_accessReader.IsAllowed(sender, uid, accessReader) || HasComp(uid)) return true; Popup.PopupEntity(Loc.GetString("vending-machine-component-try-eject-access-denied"), uid); diff --git a/Content.Shared/Access/Components/AccessReaderComponent.cs b/Content.Shared/Access/Components/AccessReaderComponent.cs index cae0247602..68bbc1e9ad 100644 --- a/Content.Shared/Access/Components/AccessReaderComponent.cs +++ b/Content.Shared/Access/Components/AccessReaderComponent.cs @@ -26,25 +26,30 @@ public sealed partial class AccessReaderComponent : Component public HashSet DenyTags = new(); /// - /// List of access lists to check allowed against. For an access check to pass - /// there has to be an access list that is a subset of the access in the checking list. + /// List of access groups that grant access to this reader. Only a single matching group is required to gain access. + /// A group matches if it is a subset of the set being checked against. /// [DataField("access")] public List> AccessLists = new(); /// - /// A list of valid stationrecordkeys + /// A list of s that grant access. Only a single matching key is required tp gaim + /// access. /// [DataField("accessKeys")] public HashSet AccessKeys = new(); - /// - /// The name of the container in which additional - /// AccessReaderComponents may be found. + /// If specified, then this access reader will instead pull access requirements from entities contained in the + /// given container. /// + /// + /// This effectively causes , , and to be + /// ignored, though is still respected. Access is denied if there are no valid entities or + /// they all deny access. + /// [DataField("containerAccessProvider")] - public string? ContainerAccessProvider = null; + public string? ContainerAccessProvider; } [Serializable, NetSerializable] diff --git a/Content.Shared/Access/Systems/AccessReaderSystem.cs b/Content.Shared/Access/Systems/AccessReaderSystem.cs index 96cd3b1d8c..4e22c3b5e6 100644 --- a/Content.Shared/Access/Systems/AccessReaderSystem.cs +++ b/Content.Shared/Access/Systems/AccessReaderSystem.cs @@ -53,7 +53,7 @@ public sealed class AccessReaderSystem : EntitySystem { if (args.User == null) // AutoLink (and presumably future external linkers) have no user. return; - if (!HasComp(uid) && !IsAllowed(args.User.Value, component)) + if (!HasComp(uid) && !IsAllowed(args.User.Value, uid, component)) args.Cancel(); } @@ -64,80 +64,65 @@ public sealed class AccessReaderSystem : EntitySystem Dirty(reader); } - /// - /// Finds all AccessReaderComponents in the container of the - /// required entity. - /// - /// The entity to search for a container - /// - /// - private bool FindAccessReadersInContainer(EntityUid target, AccessReaderComponent accessReader, out List result) - { - result = new(); - if (accessReader.ContainerAccessProvider == null) - return false; - - if (!_containerSystem.TryGetContainer(target, accessReader.ContainerAccessProvider, out var container)) - return false; - - foreach (var entity in container.ContainedEntities) - { - if (TryComp(entity, out var entityAccessReader)) - result.Add(entityAccessReader); - } - - return result.Any(); - } - /// /// Searches the source for access tags /// then compares it with the all targets accesses to see if it is allowed. /// - /// The entity that wants access. + /// The entity that wants access. /// The entity to search for an access reader /// Optional reader from the target entity - public bool IsAllowed(EntityUid source, EntityUid target, AccessReaderComponent? reader = null) + public bool IsAllowed(EntityUid user, EntityUid target, AccessReaderComponent? reader = null) { if (!Resolve(target, ref reader, false)) return true; - if (FindAccessReadersInContainer(target, reader, out var accessReaderList)) - { - foreach (var access in accessReaderList) - { - if (IsAllowed(source, access)) - return true; - } - - return false; - } - - return IsAllowed(source, reader); - } - /// - /// Searches the given entity for access tags - /// then compares it with the readers access list to see if it is allowed. - /// - /// The entity that wants access. - /// A reader from a different entity - public bool IsAllowed(EntityUid entity, AccessReaderComponent reader) - { - // Access reader is totally disabled, so access is always allowed. if (!reader.Enabled) return true; - var allEnts = FindPotentialAccessItems(entity); + var accessSources = FindPotentialAccessItems(user); + var access = FindAccessTags(user, accessSources); + FindStationRecordKeys(user, out var stationKeys, accessSources); - if (AreAccessTagsAllowed(FindAccessTags(entity, allEnts), reader)) + return IsAllowed(access, stationKeys, target, reader); + } + + /// + /// Check whether the given access permissions satisfy an access reader's requirements. + /// + public bool IsAllowed( + ICollection access, + ICollection stationKeys, + EntityUid target, + AccessReaderComponent reader) + { + if (!reader.Enabled) return true; - if (FindStationRecordKeys(entity, out var recordKeys, allEnts) - && AreStationRecordKeysAllowed(recordKeys, reader)) - return true; + if (reader.ContainerAccessProvider == null) + return IsAllowedInternal(access, stationKeys, reader); + + if (!_containerSystem.TryGetContainer(target, reader.ContainerAccessProvider, out var container)) + return false; + + foreach (var entity in container.ContainedEntities) + { + if (!TryComp(entity, out AccessReaderComponent? containedReader)) + continue; + + if (IsAllowed(access, stationKeys, entity, containedReader)) + return true; + } return false; } + private bool IsAllowedInternal(ICollection access, ICollection stationKeys, AccessReaderComponent reader) + { + return !reader.Enabled + || AreAccessTagsAllowed(access, reader) + || AreStationRecordKeysAllowed(stationKeys, reader); + } + /// /// Compares the given tags with the readers access list to see if it is allowed. /// @@ -155,7 +140,16 @@ public sealed class AccessReaderSystem : EntitySystem return false; } - return reader.AccessLists.Count == 0 || reader.AccessLists.Any(a => a.IsSubsetOf(accessTags)); + if (reader.AccessLists.Count == 0) + return true; + + foreach (var set in reader.AccessLists) + { + if (set.IsSubsetOf(accessTags)) + return true; + } + + return false; } /// @@ -163,7 +157,13 @@ public sealed class AccessReaderSystem : EntitySystem /// public bool AreStationRecordKeysAllowed(ICollection keys, AccessReaderComponent reader) { - return keys.Any() && reader.AccessKeys.Any(keys.Contains); + foreach (var key in reader.AccessKeys) + { + if (keys.Contains(key)) + return true; + } + + return false; } /// diff --git a/Content.Shared/Doors/Systems/SharedDoorSystem.cs b/Content.Shared/Doors/Systems/SharedDoorSystem.cs index 4b2da83df7..7dc7d13017 100644 --- a/Content.Shared/Doors/Systems/SharedDoorSystem.cs +++ b/Content.Shared/Doors/Systems/SharedDoorSystem.cs @@ -509,9 +509,9 @@ public abstract partial class SharedDoorSystem : EntitySystem return AccessType switch { // Some game modes modify access rules. - AccessTypes.AllowAllIdExternal => !isExternal || _accessReaderSystem.IsAllowed(user.Value, access), + AccessTypes.AllowAllIdExternal => !isExternal || _accessReaderSystem.IsAllowed(user.Value, uid, access), AccessTypes.AllowAllNoExternal => !isExternal, - _ => _accessReaderSystem.IsAllowed(user.Value, access) + _ => _accessReaderSystem.IsAllowed(user.Value, uid, access) }; } diff --git a/Content.Shared/Lock/LockSystem.cs b/Content.Shared/Lock/LockSystem.cs index 7b2500b1d9..97baa28bf9 100644 --- a/Content.Shared/Lock/LockSystem.cs +++ b/Content.Shared/Lock/LockSystem.cs @@ -183,7 +183,7 @@ public sealed class LockSystem : EntitySystem if (!Resolve(uid, ref reader, false)) return true; - if (_accessReader.IsAllowed(user, reader)) + if (_accessReader.IsAllowed(user, uid, reader)) return true; if (!quiet)